summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2007-05-16 20:58:53 +0000
committerPierre Schmitz <pierre@archlinux.de>2007-05-16 20:58:53 +0000
commitcecb985bee3bdd252e1b8dc0bd500b37cd52be01 (patch)
tree17266aa237742640aabee7856f0202317a45d540 /includes
parent0bac06c301f2a83edb0236e4c2434da16848d549 (diff)
Aktualisierung auf MediaWiki 1.10.0
Plugins angepasst und verbessert kleine Korrekturen am Design
Diffstat (limited to 'includes')
-rw-r--r--includes/AjaxDispatcher.php23
-rw-r--r--includes/AjaxFunctions.php95
-rw-r--r--includes/AjaxResponse.php70
-rw-r--r--includes/Article.php463
-rw-r--r--includes/AuthPlugin.php8
-rw-r--r--includes/AutoLoader.php49
-rw-r--r--includes/BagOStuff.php41
-rw-r--r--includes/Block.php100
-rw-r--r--includes/CacheDependency.php21
-rw-r--r--includes/CategoryPage.php35
-rw-r--r--includes/Categoryfinder.php73
-rw-r--r--includes/ChangesList.php78
-rw-r--r--includes/CoreParserFunctions.php23
-rw-r--r--includes/Credits.php1
-rw-r--r--includes/Database.php295
-rw-r--r--includes/DatabaseFunctions.php53
-rw-r--r--includes/DatabaseOracle.php788
-rw-r--r--includes/DatabasePostgres.php332
-rw-r--r--includes/DateFormatter.php10
-rw-r--r--includes/DefaultSettings.php198
-rw-r--r--includes/Defines.php1
-rw-r--r--includes/DifferenceEngine.php127
-rw-r--r--includes/DjVuImage.php134
-rw-r--r--includes/EditPage.php615
-rw-r--r--includes/Exception.php30
-rw-r--r--includes/Exif.php30
-rw-r--r--includes/Export.php38
-rw-r--r--includes/ExternalEdit.php4
-rw-r--r--includes/ExternalStore.php1
-rw-r--r--includes/ExternalStoreDB.php2
-rw-r--r--includes/ExternalStoreHttp.php1
-rw-r--r--includes/FakeTitle.php3
-rw-r--r--includes/Feed.php18
-rw-r--r--includes/FileStore.php16
-rw-r--r--includes/GlobalFunctions.php197
-rw-r--r--includes/HTMLCacheUpdate.php9
-rw-r--r--includes/HTMLFileCache.php4
-rw-r--r--includes/HTMLForm.php4
-rw-r--r--includes/HistoryBlob.php98
-rw-r--r--includes/Hooks.php3
-rw-r--r--includes/IP.php279
-rw-r--r--includes/Image.php1279
-rw-r--r--includes/ImageFunctions.php2
-rw-r--r--includes/ImageGallery.php137
-rw-r--r--includes/ImagePage.php256
-rw-r--r--includes/ImageQueryPage.php68
-rw-r--r--includes/JobQueue.php125
-rw-r--r--includes/Licenses.php12
-rw-r--r--includes/LinkBatch.php7
-rw-r--r--includes/LinkCache.php15
-rw-r--r--includes/LinkFilter.php46
-rw-r--r--includes/Linker.php322
-rw-r--r--includes/LinksUpdate.php16
-rw-r--r--includes/LoadBalancer.php13
-rw-r--r--includes/LogPage.php56
-rw-r--r--includes/MacBinary.php7
-rw-r--r--includes/MagicWord.php7
-rw-r--r--includes/Math.php8
-rw-r--r--includes/MediaTransformOutput.php166
-rw-r--r--includes/MemcachedSessions.php1
-rw-r--r--includes/MessageCache.php10
-rw-r--r--includes/Metadata.php5
-rw-r--r--includes/MimeMagic.php39
-rw-r--r--includes/Namespace.php16
-rw-r--r--includes/ObjectCache.php6
-rw-r--r--includes/OutputHandler.php64
-rw-r--r--includes/OutputPage.php122
-rw-r--r--includes/PageHistory.php59
-rw-r--r--includes/PageQueryPage.php26
-rw-r--r--includes/Pager.php57
-rw-r--r--includes/Parser.php522
-rw-r--r--includes/ParserCache.php28
-rw-r--r--includes/ParserOptions.php119
-rw-r--r--includes/ParserOutput.php133
-rw-r--r--includes/PatrolLog.php83
-rw-r--r--includes/Profiler.php11
-rw-r--r--includes/ProfilerSimple.php19
-rw-r--r--includes/ProfilerSimpleUDP.php11
-rw-r--r--includes/ProtectionForm.php183
-rw-r--r--includes/ProxyTools.php50
-rw-r--r--includes/QueryPage.php223
-rw-r--r--includes/RawPage.php35
-rw-r--r--includes/RecentChange.php12
-rw-r--r--includes/Revision.php85
-rw-r--r--includes/Sanitizer.php184
-rw-r--r--includes/SearchEngine.php48
-rw-r--r--includes/SearchMySQL.php13
-rw-r--r--includes/SearchMySQL4.php12
-rw-r--r--includes/SearchOracle.php235
-rw-r--r--includes/SearchPostgres.php176
-rw-r--r--includes/SearchTsearch2.php20
-rw-r--r--includes/SearchUpdate.php16
-rw-r--r--includes/Setup.php4
-rw-r--r--includes/SiteConfiguration.php10
-rw-r--r--includes/SiteStats.php53
-rw-r--r--includes/Skin.php111
-rw-r--r--includes/SkinTemplate.php114
-rw-r--r--includes/SpecialAllmessages.php9
-rw-r--r--includes/SpecialAllpages.php61
-rw-r--r--includes/SpecialAncientpages.php10
-rw-r--r--includes/SpecialBlockip.php293
-rw-r--r--includes/SpecialBlockme.php3
-rw-r--r--includes/SpecialBooksources.php9
-rw-r--r--includes/SpecialBrokenRedirects.php43
-rw-r--r--includes/SpecialCategories.php103
-rw-r--r--includes/SpecialConfirmemail.php16
-rw-r--r--includes/SpecialContributions.php568
-rw-r--r--includes/SpecialDeadendpages.php18
-rw-r--r--includes/SpecialDisambiguations.php129
-rw-r--r--includes/SpecialDoubleRedirects.php30
-rw-r--r--includes/SpecialEmailuser.php23
-rw-r--r--includes/SpecialExport.php71
-rw-r--r--includes/SpecialFewestrevisions.php65
-rw-r--r--includes/SpecialImagelist.php22
-rw-r--r--includes/SpecialImport.php123
-rw-r--r--includes/SpecialIpblocklist.php61
-rw-r--r--includes/SpecialListredirects.php10
-rw-r--r--includes/SpecialListusers.php301
-rw-r--r--includes/SpecialLockdb.php10
-rw-r--r--includes/SpecialLog.php173
-rw-r--r--includes/SpecialLonelypages.php11
-rw-r--r--includes/SpecialLongpages.php15
-rw-r--r--includes/SpecialMIMEsearch.php14
-rw-r--r--includes/SpecialMostcategories.php9
-rw-r--r--includes/SpecialMostimages.php29
-rw-r--r--includes/SpecialMostlinked.php13
-rw-r--r--includes/SpecialMostlinkedcategories.php10
-rw-r--r--includes/SpecialMostrevisions.php8
-rw-r--r--includes/SpecialMovepage.php35
-rw-r--r--includes/SpecialNewimages.php33
-rw-r--r--includes/SpecialNewpages.php31
-rw-r--r--includes/SpecialPage.php41
-rw-r--r--includes/SpecialPopularpages.php34
-rw-r--r--includes/SpecialPreferences.php49
-rw-r--r--includes/SpecialPrefixindex.php13
-rw-r--r--includes/SpecialProtectedpages.php260
-rw-r--r--includes/SpecialRandompage.php142
-rw-r--r--includes/SpecialRandomredirect.php47
-rw-r--r--includes/SpecialRecentchanges.php17
-rw-r--r--includes/SpecialRecentchangeslinked.php54
-rw-r--r--includes/SpecialResetpass.php9
-rw-r--r--includes/SpecialRevisiondelete.php9
-rw-r--r--includes/SpecialSearch.php22
-rw-r--r--includes/SpecialShortpages.php8
-rw-r--r--includes/SpecialSpecialpages.php5
-rw-r--r--includes/SpecialStatistics.php17
-rw-r--r--includes/SpecialUncategorizedcategories.php8
-rw-r--r--includes/SpecialUncategorizedimages.php26
-rw-r--r--includes/SpecialUncategorizedpages.php10
-rw-r--r--includes/SpecialUndelete.php204
-rw-r--r--includes/SpecialUnlockdb.php6
-rw-r--r--includes/SpecialUnusedcategories.php8
-rw-r--r--includes/SpecialUnusedimages.php39
-rw-r--r--includes/SpecialUnusedtemplates.php13
-rw-r--r--includes/SpecialUnwatchedpages.php14
-rw-r--r--includes/SpecialUpload.php151
-rw-r--r--includes/SpecialUploadMogile.php10
-rw-r--r--includes/SpecialUserlogin.php88
-rw-r--r--includes/SpecialUserlogout.php3
-rw-r--r--includes/SpecialUserrights.php89
-rw-r--r--includes/SpecialVersion.php28
-rw-r--r--includes/SpecialWantedcategories.php12
-rw-r--r--includes/SpecialWantedpages.php10
-rw-r--r--includes/SpecialWatchlist.php46
-rw-r--r--includes/SpecialWhatlinkshere.php170
-rw-r--r--includes/SpecialWithoutinterwiki.php56
-rw-r--r--includes/SquidUpdate.php6
-rw-r--r--includes/StringUtils.php4
-rw-r--r--includes/StubObject.php7
-rw-r--r--includes/Title.php829
-rw-r--r--includes/User.php249
-rw-r--r--includes/UserMailer.php42
-rw-r--r--includes/Utf8Case.php3
-rw-r--r--includes/WatchedItem.php29
-rw-r--r--includes/WebRequest.php46
-rw-r--r--includes/WebResponse.php14
-rw-r--r--includes/WebStart.php12
-rw-r--r--includes/Wiki.php107
-rw-r--r--includes/WikiError.php9
-rw-r--r--includes/Xml.php12
-rw-r--r--includes/XmlFunctions.php2
-rw-r--r--includes/ZhClient.php2
-rw-r--r--includes/ZhConversion.php1
-rw-r--r--includes/api/ApiBase.php9
-rw-r--r--includes/api/ApiFeedWatchlist.php6
-rw-r--r--includes/api/ApiFormatBase.php9
-rw-r--r--includes/api/ApiFormatJson.php8
-rw-r--r--includes/api/ApiFormatJson_json.php51
-rw-r--r--includes/api/ApiFormatPhp.php8
-rw-r--r--includes/api/ApiFormatWddx.php8
-rw-r--r--includes/api/ApiFormatXml.php8
-rw-r--r--includes/api/ApiFormatYaml.php8
-rw-r--r--includes/api/ApiFormatYaml_spyc.php13
-rw-r--r--includes/api/ApiHelp.php8
-rw-r--r--includes/api/ApiLogin.php6
-rw-r--r--includes/api/ApiMain.php9
-rw-r--r--includes/api/ApiOpenSearch.php8
-rw-r--r--includes/api/ApiPageSet.php16
-rw-r--r--includes/api/ApiQuery.php10
-rw-r--r--includes/api/ApiQueryAllpages.php8
-rw-r--r--includes/api/ApiQueryBacklinks.php10
-rw-r--r--includes/api/ApiQueryBase.php9
-rw-r--r--includes/api/ApiQueryInfo.php6
-rw-r--r--includes/api/ApiQueryLogEvents.php8
-rw-r--r--includes/api/ApiQueryRecentChanges.php10
-rw-r--r--includes/api/ApiQueryRevisions.php8
-rw-r--r--includes/api/ApiQuerySiteinfo.php8
-rw-r--r--includes/api/ApiQueryUserContributions.php8
-rw-r--r--includes/api/ApiQueryWatchlist.php6
-rw-r--r--includes/api/ApiResult.php8
-rw-r--r--includes/media/BMP.php31
-rw-r--r--includes/media/Bitmap.php236
-rw-r--r--includes/media/DjVu.php206
-rw-r--r--includes/media/Generic.php298
-rw-r--r--includes/media/SVG.php97
-rw-r--r--includes/memcached-client.php2
-rw-r--r--includes/mime.info5
-rw-r--r--includes/mime.types1
-rw-r--r--includes/normal/CleanUpTest.php36
-rw-r--r--includes/normal/Makefile36
-rw-r--r--includes/normal/README4
-rw-r--r--includes/normal/RandomTest.php4
-rw-r--r--includes/normal/Utf8Test.php2
-rw-r--r--includes/normal/UtfNormal.php39
-rw-r--r--includes/normal/UtfNormalBench.php10
-rw-r--r--includes/normal/UtfNormalData.inc9
-rw-r--r--includes/normal/UtfNormalDataK.inc3
-rw-r--r--includes/normal/UtfNormalGenerate.php4
-rw-r--r--includes/normal/UtfNormalTest.php2
-rw-r--r--includes/normal/UtfNormalUtil.php2
-rw-r--r--includes/proxy_check.php1
-rw-r--r--includes/templates/Userlogin.php9
-rw-r--r--includes/tidy.conf18
233 files changed, 10573 insertions, 6034 deletions
diff --git a/includes/AjaxDispatcher.php b/includes/AjaxDispatcher.php
index 39ec19f8..ca129029 100644
--- a/includes/AjaxDispatcher.php
+++ b/includes/AjaxDispatcher.php
@@ -1,7 +1,8 @@
<?php
-if( !defined( 'MEDIAWIKI' ) )
- die( 1 );
+if( !defined( 'MEDIAWIKI' ) ) {
+ die( 1 );
+}
if ( ! $wgUseAjax ) {
die( 1 );
@@ -9,12 +10,16 @@ if ( ! $wgUseAjax ) {
require_once( 'AjaxFunctions.php' );
+/**
+ * Object-Oriented Ajax functions.
+ * @addtogroup Ajax
+ */
class AjaxDispatcher {
var $mode;
var $func_name;
var $args;
- function AjaxDispatcher() {
+ function __construct() {
wfProfileIn( __METHOD__ );
$this->mode = "";
@@ -28,14 +33,14 @@ class AjaxDispatcher {
}
if ($this->mode == "get") {
- $this->func_name = $_GET["rs"];
+ $this->func_name = isset( $_GET["rs"] ) ? $_GET["rs"] : '';
if (! empty($_GET["rsargs"])) {
$this->args = $_GET["rsargs"];
} else {
$this->args = array();
}
} else {
- $this->func_name = $_POST["rs"];
+ $this->func_name = isset( $_POST["rs"] ) ? $_POST["rs"] : '';
if (! empty($_POST["rsargs"])) {
$this->args = $_POST["rsargs"];
} else {
@@ -47,7 +52,7 @@ class AjaxDispatcher {
function performAction() {
global $wgAjaxExportList, $wgOut;
-
+
if ( empty( $this->mode ) ) {
return;
}
@@ -59,7 +64,7 @@ class AjaxDispatcher {
} else {
try {
$result = call_user_func_array($this->func_name, $this->args);
-
+
if ( $result === false || $result === NULL ) {
wfHttpError( 500, 'Internal Error',
"{$this->func_name} returned no data" );
@@ -68,7 +73,7 @@ class AjaxDispatcher {
if ( is_string( $result ) ) {
$result= new AjaxResponse( $result );
}
-
+
$result->sendHeaders();
$result->printText();
}
@@ -82,7 +87,7 @@ class AjaxDispatcher {
}
}
}
-
+
wfProfileOut( __METHOD__ );
$wgOut = null;
}
diff --git a/includes/AjaxFunctions.php b/includes/AjaxFunctions.php
index eee2a1a4..86f853db 100644
--- a/includes/AjaxFunctions.php
+++ b/includes/AjaxFunctions.php
@@ -1,7 +1,13 @@
<?php
-if( !defined( 'MEDIAWIKI' ) )
- die( 1 );
+/**
+ * @package MediaWiki
+ * @addtogroup Ajax
+ */
+
+if( !defined( 'MEDIAWIKI' ) ) {
+ die( 1 );
+}
/**
* Function converts an Javascript escaped string back into a string with
@@ -13,40 +19,39 @@ if( !defined( 'MEDIAWIKI' ) )
* @return string
*/
function js_unescape($source, $iconv_to = 'UTF-8') {
- $decodedStr = '';
- $pos = 0;
- $len = strlen ($source);
- while ($pos < $len) {
- $charAt = substr ($source, $pos, 1);
- if ($charAt == '%') {
- $pos++;
- $charAt = substr ($source, $pos, 1);
- if ($charAt == 'u') {
- // we got a unicode character
- $pos++;
- $unicodeHexVal = substr ($source, $pos, 4);
- $unicode = hexdec ($unicodeHexVal);
- $decodedStr .= code2utf($unicode);
- $pos += 4;
- }
- else {
- // we have an escaped ascii character
- $hexVal = substr ($source, $pos, 2);
- $decodedStr .= chr (hexdec ($hexVal));
- $pos += 2;
- }
- }
- else {
- $decodedStr .= $charAt;
- $pos++;
- }
- }
-
- if ($iconv_to != "UTF-8") {
- $decodedStr = iconv("UTF-8", $iconv_to, $decodedStr);
- }
-
- return $decodedStr;
+ $decodedStr = '';
+ $pos = 0;
+ $len = strlen ($source);
+
+ while ($pos < $len) {
+ $charAt = substr ($source, $pos, 1);
+ if ($charAt == '%') {
+ $pos++;
+ $charAt = substr ($source, $pos, 1);
+ if ($charAt == 'u') {
+ // we got a unicode character
+ $pos++;
+ $unicodeHexVal = substr ($source, $pos, 4);
+ $unicode = hexdec ($unicodeHexVal);
+ $decodedStr .= code2utf($unicode);
+ $pos += 4;
+ } else {
+ // we have an escaped ascii character
+ $hexVal = substr ($source, $pos, 2);
+ $decodedStr .= chr (hexdec ($hexVal));
+ $pos += 2;
+ }
+ } else {
+ $decodedStr .= $charAt;
+ $pos++;
+ }
+ }
+
+ if ($iconv_to != "UTF-8") {
+ $decodedStr = iconv("UTF-8", $iconv_to, $decodedStr);
+ }
+
+ return $decodedStr;
}
/**
@@ -71,7 +76,7 @@ function code2utf($num){
function wfSajaxSearch( $term ) {
global $wgContLang, $wgOut;
$limit = 16;
-
+
$l = new Linker;
$term = str_replace( ' ', '_', $wgContLang->ucfirst(
@@ -81,7 +86,7 @@ function wfSajaxSearch( $term ) {
if ( strlen( str_replace( '_', '', $term ) )<3 )
return;
- $db =& wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_SLAVE );
$res = $db->select( 'page', 'page_title',
array( 'page_namespace' => 0,
"page_title LIKE '". $db->strencode( $term) ."%'" ),
@@ -108,8 +113,8 @@ function wfSajaxSearch( $term ) {
$subtitlemsg = ( Title::newFromText($term) ? 'searchsubtitle' : 'searchsubtitleinvalid' );
$subtitle = $wgOut->parse( wfMsg( $subtitlemsg, wfEscapeWikiText($term) ) ); #FIXME: parser is missing mTitle !
- $term = htmlspecialchars( $term );
- $html = '<div style="float:right; border:solid 1px black;background:gainsboro;padding:2px;"><a onclick="Searching_Hide_Results();">'
+ $term = urlencode( $term );
+ $html = '<div style="float:right; border:solid 1px black;background:gainsboro;padding:2px;"><a onclick="Searching_Hide_Results();">'
. wfMsg( 'hideresults' ) . '</a></div>'
. '<h1 class="firstHeading">'.wfMsg('search')
. '</h1><div id="contentSub">'. $subtitle . '</div><ul><li>'
@@ -121,11 +126,11 @@ function wfSajaxSearch( $term ) {
"search=$term&go=Go" )
. "</li></ul><h2>" . wfMsg( 'articletitles', $term ) . "</h2>"
. '<ul>' .$r .'</ul>'.$more;
-
+
$response = new AjaxResponse( $html );
-
+
$response->setCacheDuration( 30*60 );
-
+
return $response;
}
@@ -152,14 +157,14 @@ function wfAjaxWatch($pageID = "", $watch = "") {
if($watch) {
if(!$watching) {
- $dbw =& wfGetDB(DB_MASTER);
+ $dbw = wfGetDB(DB_MASTER);
$dbw->begin();
$article->doWatch();
$dbw->commit();
}
} else {
if($watching) {
- $dbw =& wfGetDB(DB_MASTER);
+ $dbw = wfGetDB(DB_MASTER);
$dbw->begin();
$article->doUnwatch();
$dbw->commit();
diff --git a/includes/AjaxResponse.php b/includes/AjaxResponse.php
index a59c73bb..cb4af1b5 100644
--- a/includes/AjaxResponse.php
+++ b/includes/AjaxResponse.php
@@ -1,28 +1,32 @@
<?php
+if( !defined( 'MEDIAWIKI' ) ) {
+ die( 1 );
+}
-if( !defined( 'MEDIAWIKI' ) )
- die( 1 );
-
+/**
+ * @todo document
+ * @addtogroup Ajax
+ */
class AjaxResponse {
var $mCacheDuration;
var $mVary;
-
+
var $mDisabled;
var $mText;
var $mResponseCode;
var $mLastModified;
var $mContentType;
- function AjaxResponse( $text = NULL ) {
+ function __construct( $text = NULL ) {
$this->mCacheDuration = NULL;
$this->mVary = NULL;
-
+
$this->mDisabled = false;
$this->mText = '';
$this->mResponseCode = '200 OK';
$this->mLastModified = false;
$this->mContentType= 'text/html; charset=utf-8';
-
+
if ( $text ) {
$this->addText( $text );
}
@@ -39,15 +43,15 @@ class AjaxResponse {
function setResponseCode( $code ) {
$this->mResponseCode = $code;
}
-
+
function setContentType( $type ) {
$this->mContentType = $type;
}
-
+
function disable() {
$this->mDisabled = true;
}
-
+
function addText( $text ) {
if ( ! $this->mDisabled && $text ) {
$this->mText .= $text;
@@ -59,62 +63,62 @@ class AjaxResponse {
print $this->mText;
}
}
-
+
function sendHeaders() {
global $wgUseSquid, $wgUseESI;
-
+
if ( $this->mResponseCode ) {
$n = preg_replace( '/^ *(\d+)/', '\1', $this->mResponseCode );
header( "Status: " . $this->mResponseCode, true, (int)$n );
}
-
+
header ("Content-Type: " . $this->mContentType );
-
+
if ( $this->mLastModified ) {
header ("Last-Modified: " . $this->mLastModified );
}
else {
header ("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
}
-
+
if ( $this->mCacheDuration ) {
-
+
# If squid caches are configured, tell them to cache the response,
# and tell the client to always check with the squid. Otherwise,
# tell the client to use a cached copy, without a way to purge it.
-
+
if( $wgUseSquid ) {
-
+
# Expect explicite purge of the proxy cache, but require end user agents
# to revalidate against the proxy on each visit.
# Surrogate-Control controls our Squid, Cache-Control downstream caches
-
+
if ( $wgUseESI ) {
header( 'Surrogate-Control: max-age='.$this->mCacheDuration.', content="ESI/1.0"');
header( 'Cache-Control: s-maxage=0, must-revalidate, max-age=0' );
} else {
header( 'Cache-Control: s-maxage='.$this->mCacheDuration.', must-revalidate, max-age=0' );
}
-
+
} else {
-
+
# Let the client do the caching. Cache is not purged.
header ("Expires: " . gmdate( "D, d M Y H:i:s", time() + $this->mCacheDuration ) . " GMT");
header ("Cache-Control: s-max-age={$this->mCacheDuration},public,max-age={$this->mCacheDuration}");
}
-
+
} else {
# always expired, always modified
header ("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header ("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header ("Pragma: no-cache"); // HTTP/1.0
}
-
+
if ( $this->mVary ) {
header ( "Vary: " . $this->mVary );
}
}
-
+
/**
* checkLastModified tells the client to use the client-cached response if
* possible. If sucessful, the AjaxResponse is disabled so that
@@ -154,9 +158,9 @@ class AjaxResponse {
$this->setResponseCode( "304 Not Modified" );
$this->disable();
$this->mLastModified = $lastmod;
-
+
wfDebug( "$fname: CACHED client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp ; site $wgCacheEpoch\n", false );
-
+
return true;
} else {
wfDebug( "$fname: READY client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp ; site $wgCacheEpoch\n", false );
@@ -167,11 +171,11 @@ class AjaxResponse {
$this->mLastModified = $lastmod;
}
}
-
+
function loadFromMemcached( $mckey, $touched ) {
global $wgMemc;
if ( !$touched ) return false;
-
+
$mcvalue = $wgMemc->get( $mckey );
if ( $mcvalue ) {
# Check to see if the value has been invalidated
@@ -183,20 +187,20 @@ class AjaxResponse {
wfDebug( "$mckey has expired\n" );
}
}
-
+
return false;
}
-
+
function storeInMemcached( $mckey, $expiry = 86400 ) {
global $wgMemc;
-
- $wgMemc->set( $mckey,
+
+ $wgMemc->set( $mckey,
array(
'timestamp' => wfTimestampNow(),
'value' => $this->mText
), $expiry
);
-
+
return true;
}
}
diff --git a/includes/Article.php b/includes/Article.php
index 6b4f5270..0130ceba 100644
--- a/includes/Article.php
+++ b/includes/Article.php
@@ -1,7 +1,6 @@
<?php
/**
* File for articles
- * @package MediaWiki
*/
/**
@@ -11,7 +10,6 @@
* Note: edit user interface and cache support functions have been
* moved to separate EditPage and HTMLFileCache classes.
*
- * @package MediaWiki
*/
class Article {
/**@{{
@@ -43,7 +41,7 @@ class Article {
* @param $title Reference to a Title object.
* @param $oldId Integer revision ID, null to fetch from request, zero for current
*/
- function Article( &$title, $oldId = null ) {
+ function __construct( &$title, $oldId = null ) {
$this->mTitle =& $title;
$this->mOldId = $oldId;
$this->clear();
@@ -57,14 +55,14 @@ class Article {
function setRedirectedFrom( $from ) {
$this->mRedirectedFrom = $from;
}
-
+
/**
* @return mixed false, Title of in-wiki target, or string with URL
*/
function followRedirect() {
$text = $this->getContent();
$rt = Title::newFromRedirect( $text );
-
+
# process if title object is valid and not special:userlogout
if( $rt ) {
if( $rt->getInterwiki() != '' ) {
@@ -73,7 +71,7 @@ class Article {
//
// This can be hard to reverse and may produce loops,
// so they may be disabled in the site configuration.
-
+
$source = $this->mTitle->getFullURL( 'redirect=no' );
return $rt->getFullURL( 'rdfrom=' . urlencode( $source ) );
}
@@ -84,7 +82,7 @@ class Article {
// the rest of the page we're on.
//
// This can be hard to reverse, so they may be disabled.
-
+
if( $rt->isSpecial( 'Userlogout' ) ) {
// rolleyes
} else {
@@ -94,7 +92,7 @@ class Article {
return $rt;
}
}
-
+
// No or invalid redirect
return false;
}
@@ -247,7 +245,7 @@ class Article {
* @param array $conditions
* @private
*/
- function pageData( &$dbr, $conditions ) {
+ function pageData( $dbr, $conditions ) {
$fields = array(
'page_id',
'page_namespace',
@@ -273,7 +271,7 @@ class Article {
* @param Database $dbr
* @param Title $title
*/
- function pageDataFromTitle( &$dbr, $title ) {
+ function pageDataFromTitle( $dbr, $title ) {
return $this->pageData( $dbr, array(
'page_namespace' => $title->getNamespace(),
'page_title' => $title->getDBkey() ) );
@@ -283,7 +281,7 @@ class Article {
* @param Database $dbr
* @param int $id
*/
- function pageDataFromId( &$dbr, $id ) {
+ function pageDataFromId( $dbr, $id ) {
return $this->pageData( $dbr, array( 'page_id' => $id ) );
}
@@ -296,17 +294,18 @@ class Article {
*/
function loadPageData( $data = 'fromdb' ) {
if ( $data === 'fromdb' ) {
- $dbr =& $this->getDB();
+ $dbr = $this->getDB();
$data = $this->pageDataFromId( $dbr, $this->getId() );
}
-
+
$lc =& LinkCache::singleton();
if ( $data ) {
$lc->addGoodLinkObj( $data->page_id, $this->mTitle );
$this->mTitle->mArticleID = $data->page_id;
+
+ # Old-fashioned restrictions.
$this->mTitle->loadRestrictions( $data->page_restrictions );
- $this->mTitle->mRestrictionsLoaded = true;
$this->mCounter = $data->page_counter;
$this->mTouched = wfTimestamp( TS_MW, $data->page_touched );
@@ -333,7 +332,7 @@ class Article {
return $this->mContent;
}
- $dbr =& $this->getDB();
+ $dbr = $this->getDB();
# Pre-fill content with error message so that if something
# fails we'll have something telling us what we intended.
@@ -405,9 +404,8 @@ class Article {
*
* @return Database
*/
- function &getDB() {
- $ret =& wfGetDB( DB_MASTER );
- return $ret;
+ function getDB() {
+ return wfGetDB( DB_MASTER );
}
/**
@@ -455,7 +453,7 @@ class Article {
if ( $id == 0 ) {
$this->mCounter = 0;
} else {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$this->mCounter = $dbr->selectField( 'page', 'page_counter', array( 'page_id' => $id ),
'Article::getCount', $this->getSelectOptions() );
}
@@ -471,12 +469,12 @@ class Article {
* @return bool
*/
function isCountable( $text ) {
- global $wgUseCommaCount, $wgContentNamespaces;
+ global $wgUseCommaCount;
$token = $wgUseCommaCount ? ',' : '[[';
return
- array_search( $this->mTitle->getNamespace(), $wgContentNamespaces ) !== false
- && ! $this->isRedirect( $text )
+ $this->mTitle->isContentPage()
+ && !$this->isRedirect( $text )
&& in_string( $token, $text );
}
@@ -573,7 +571,7 @@ class Article {
# XXX: this is expensive; cache this info somewhere.
$contribs = array();
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$revTable = $dbr->tableName( 'revision' );
$userTable = $dbr->tableName( 'user' );
$user = $this->getUser();
@@ -613,7 +611,7 @@ class Article {
$parserCache =& ParserCache::singleton();
$ns = $this->mTitle->getNamespace(); # shortcut
-
+
# Get variables from query string
$oldid = $this->getOldID();
@@ -627,16 +625,21 @@ class Article {
$diff = $wgRequest->getVal( 'diff' );
$rcid = $wgRequest->getVal( 'rcid' );
$rdfrom = $wgRequest->getVal( 'rdfrom' );
+ $diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) );
$wgOut->setArticleFlag( true );
- if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
+
+ # Discourage indexing of printable versions, but encourage following
+ if( $wgOut->isPrintable() ) {
+ $policy = 'noindex,follow';
+ } elseif( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
+ # Honour customised robot policies for this namespace
$policy = $wgNamespaceRobotPolicies[$ns];
} else {
- # The default policy. Dev note: make sure you change the documentation
- # in DefaultSettings.php before changing it.
+ # Default to encourage indexing and following links
$policy = 'index,follow';
}
- $wgOut->setRobotpolicy( $policy );
+ $wgOut->setRobotPolicy( $policy );
# If we got diff and oldid in the query, we want to see a
# diff page instead of the article.
@@ -647,8 +650,8 @@ class Article {
$de = new DifferenceEngine( $this->mTitle, $oldid, $diff, $rcid );
// DifferenceEngine directly fetched the revision:
$this->mRevIdFetched = $de->mNewid;
- $de->showDiffPage();
-
+ $de->showDiffPage( $diffOnly );
+
// Needed to get the page's current revision
$this->loadPageData();
if( $diff == 0 || $diff == $this->mLatest ) {
@@ -658,7 +661,7 @@ class Article {
wfProfileOut( __METHOD__ );
return;
}
-
+
if ( empty( $oldid ) && $this->checkTouched() ) {
$wgOut->setETag($parserCache->getETag($this, $wgUser));
@@ -713,11 +716,11 @@ class Article {
$wasRedirected = true;
}
}
-
+
$outputDone = false;
+ wfRunHooks( 'ArticleViewHeader', array( &$this ) );
if ( $pcache ) {
if ( $wgOut->tryParserCache( $this, $wgUser ) ) {
- wfRunHooks( 'ArticleViewHeader', array( &$this ) );
$outputDone = true;
}
}
@@ -764,17 +767,12 @@ class Article {
}
}
if( !$outputDone ) {
- /**
- * @fixme: this hook doesn't work most of the time, as it doesn't
- * trigger when the parser cache is used.
- */
- wfRunHooks( 'ArticleViewHeader', array( &$this ) ) ;
$wgOut->setRevisionId( $this->getRevIdFetched() );
# wrap user css and user js in pre and don't parse
# XXX: use $this->mTitle->usCssJsSubpage() when php is fixed/ a workaround is found
if (
$ns == NS_USER &&
- preg_match('/\\/[\\w]+\\.(css|js)$/', $this->mTitle->getDBkey())
+ preg_match('/\\/[\\w]+\\.(?:css|js)$/', $this->mTitle->getDBkey())
) {
$wgOut->addWikiText( wfMsg('clearyourcache'));
$wgOut->addHTML( '<pre>'.htmlspecialchars($this->mContent)."\n</pre>" );
@@ -795,7 +793,7 @@ class Article {
$wgOut->addParserOutputNoText( $parseout );
} else if ( $pcache ) {
# Display content and save to parser cache
- $wgOut->addPrimaryWikiText( $text, $this );
+ $this->outputWikiText( $text );
} else {
# Display content, don't attempt to save to parser cache
# Don't show section-edit links on old revisions... this way lies madness.
@@ -803,11 +801,21 @@ class Article {
$oldEditSectionSetting = $wgOut->parserOptions()->setEditSection( false );
}
# Display content and don't save to parser cache
- $wgOut->addPrimaryWikiText( $text, $this, false );
+ # With timing hack -- TS 2006-07-26
+ $time = -wfTime();
+ $this->outputWikiText( $text, false );
+ $time += wfTime();
+
+ # Timing hack
+ if ( $time > 3 ) {
+ wfDebugLog( 'slow-parse', sprintf( "%-5.2f %s", $time,
+ $this->mTitle->getPrefixedDBkey()));
+ }
if( !$this->isCurrent() ) {
$wgOut->parserOptions()->setEditSection( $oldEditSectionSetting );
}
+
}
}
/* title may have been set from the cache */
@@ -827,8 +835,9 @@ class Article {
if ( $wgUseRCPatrol && !is_null( $rcid ) && $rcid != 0 && $wgUser->isAllowed( 'patrol' ) ) {
$wgOut->addHTML(
"<div class='patrollink'>" .
- wfMsg ( 'markaspatrolledlink',
- $sk->makeKnownLinkObj( $this->mTitle, wfMsg('markaspatrolledtext'), "action=markpatrolled&rcid=$rcid" )
+ wfMsgHtml( 'markaspatrolledlink',
+ $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml('markaspatrolledtext'),
+ "action=markpatrolled&rcid=$rcid" )
) .
'</div>'
);
@@ -845,7 +854,7 @@ class Article {
function addTrackbacks() {
global $wgOut, $wgUser;
- $dbr =& wfGetDB(DB_SLAVE);
+ $dbr = wfGetDB(DB_SLAVE);
$tbs = $dbr->select(
/* FROM */ 'trackbacks',
/* SELECT */ array('tb_id', 'tb_title', 'tb_url', 'tb_ex', 'tb_name'),
@@ -891,7 +900,7 @@ class Article {
return;
}
- $db =& wfGetDB(DB_MASTER);
+ $db = wfGetDB(DB_MASTER);
$db->delete('trackbacks', array('tb_id' => $wgRequest->getInt('tbid')));
$wgTitle->invalidateCache();
$wgOut->addWikiText(wfMsg('trackbackdeleteok'));
@@ -910,7 +919,7 @@ class Article {
function purge() {
global $wgUser, $wgRequest, $wgOut;
- if ( $wgUser->isLoggedIn() || $wgRequest->wasPosted() ) {
+ if ( $wgUser->isAllowed( 'purge' ) || $wgRequest->wasPosted() ) {
if( wfRunHooks( 'ArticlePurge', array( &$this ) ) ) {
$this->doPurge();
}
@@ -928,7 +937,7 @@ class Article {
$wgOut->addHTML( $msg );
}
}
-
+
/**
* Perform the actions of a page purging
*/
@@ -957,11 +966,10 @@ class Article {
* Best if all done inside a transaction.
*
* @param Database $dbw
- * @param string $restrictions
* @return int The newly created page_id key
* @private
*/
- function insertOn( &$dbw, $restrictions = '' ) {
+ function insertOn( $dbw ) {
wfProfileIn( __METHOD__ );
$page_id = $dbw->nextSequenceValue( 'page_page_id_seq' );
@@ -970,7 +978,7 @@ class Article {
'page_namespace' => $this->mTitle->getNamespace(),
'page_title' => $this->mTitle->getDBkey(),
'page_counter' => 0,
- 'page_restrictions' => $restrictions,
+ 'page_restrictions' => '',
'page_is_redirect' => 0, # Will set this shortly...
'page_is_new' => 1,
'page_random' => wfRandom(),
@@ -996,7 +1004,7 @@ class Article {
* when different from the currently set value.
* Giving 0 indicates the new page flag should
* be set on.
- * @param bool $lastRevIsRedirect If given, will optimize adding and
+ * @param bool $lastRevIsRedirect If given, will optimize adding and
* removing rows in redirect table.
* @return bool true on success, false on failure
* @private
@@ -1006,7 +1014,7 @@ class Article {
$text = $revision->getText();
$rt = Title::newFromRedirect( $text );
-
+
$conditions = array( 'page_id' => $this->getId() );
if( !is_null( $lastRevision ) ) {
# An extra check against threads stepping on each other
@@ -1028,20 +1036,20 @@ class Article {
if ($result) {
// FIXME: Should the result from updateRedirectOn() be returned instead?
- $this->updateRedirectOn( $dbw, $rt, $lastRevIsRedirect );
+ $this->updateRedirectOn( $dbw, $rt, $lastRevIsRedirect );
}
-
+
wfProfileOut( __METHOD__ );
return $result;
}
/**
- * Add row to the redirect table if this is a redirect, remove otherwise.
+ * Add row to the redirect table if this is a redirect, remove otherwise.
*
* @param Database $dbw
* @param $redirectTitle a title object pointing to the redirect target,
- * or NULL if this is not a redirect
- * @param bool $lastRevIsRedirect If given, will optimize adding and
+ * or NULL if this is not a redirect
+ * @param bool $lastRevIsRedirect If given, will optimize adding and
* removing rows in redirect table.
* @return bool true on success, false on failure
* @private
@@ -1067,7 +1075,7 @@ class Article {
$dbw->replace( 'redirect', array( 'rd_from' ), $set, __METHOD__ );
} else {
- // This is not a redirect, remove row from redirect table
+ // This is not a redirect, remove row from redirect table
$where = array( 'rd_from' => $this->getId() );
$dbw->delete( 'redirect', $where, __METHOD__);
}
@@ -1075,7 +1083,7 @@ class Article {
wfProfileOut( __METHOD__ );
return ( $dbw->affectedRows() != 0 );
}
-
+
return true;
}
@@ -1119,14 +1127,14 @@ class Article {
*/
function replaceSection($section, $text, $summary = '', $edittime = NULL) {
wfProfileIn( __METHOD__ );
-
+
if( $section == '' ) {
// Whole-page edit; let the text through unmolested.
} else {
if( is_null( $edittime ) ) {
$rev = Revision::newFromTitle( $this->mTitle );
} else {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$rev = Revision::loadFromTimestamp( $dbw, $this->mTitle, $edittime );
}
if( is_null( $rev ) ) {
@@ -1166,10 +1174,10 @@ class Article {
if ( $comment && $summary != "" ) {
$text = "== {$summary} ==\n\n".$text;
}
-
+
$this->doEdit( $text, $summary, $flags );
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
if ($watchthis) {
if (!$this->mTitle->userIsWatching()) {
$dbw->begin();
@@ -1196,7 +1204,7 @@ class Article {
$good = $this->doEdit( $text, $summary, $flags );
if ( $good ) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
if ($watchthis) {
if (!$this->mTitle->userIsWatching()) {
$dbw->begin();
@@ -1219,7 +1227,7 @@ class Article {
/**
* Article::doEdit()
*
- * Change an existing article or create a new article. Updates RC and all necessary caches,
+ * Change an existing article or create a new article. Updates RC and all necessary caches,
* optionally via the deferred update array.
*
* $wgUser must be set before calling this function.
@@ -1241,9 +1249,9 @@ class Article {
* Defer some of the updates until the end of index.php
* EDIT_AUTOSUMMARY
* Fill in blank summaries with generated text where possible
- *
- * If neither EDIT_NEW nor EDIT_UPDATE is specified, the status of the article will be detected.
- * If EDIT_UPDATE is specified and the article doesn't exist, the function will return false. If
+ *
+ * If neither EDIT_NEW nor EDIT_UPDATE is specified, the status of the article will be detected.
+ * If EDIT_UPDATE is specified and the article doesn't exist, the function will return false. If
* EDIT_NEW is specified and the article does exist, a duplicate key error will cause an exception
* to be thrown from the Database. These two conditions are also possible with auto-detection due
* to MediaWiki's performance-optimised locking strategy.
@@ -1267,7 +1275,7 @@ class Article {
if( !wfRunHooks( 'ArticleSave', array( &$this, &$wgUser, &$text,
&$summary, $flags & EDIT_MINOR,
- null, null, &$flags ) ) )
+ null, null, &$flags ) ) )
{
wfDebug( __METHOD__ . ": ArticleSave hook aborted save!\n" );
wfProfileOut( __METHOD__ );
@@ -1288,9 +1296,9 @@ class Article {
$text = $this->preSaveTransform( $text );
$newsize = strlen( $text );
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$now = wfTimestampNow();
-
+
if ( $flags & EDIT_UPDATE ) {
# Update article, but only if changed.
@@ -1316,7 +1324,7 @@ class Article {
wfProfileOut( __METHOD__ );
return false;
}
-
+
$revision = new Revision( array(
'page' => $this->getId(),
'comment' => $summary,
@@ -1340,10 +1348,11 @@ class Article {
$rcid = RecentChange::notifyEdit( $now, $this->mTitle, $isminor, $wgUser, $summary,
$lastRevision, $this->getTimestamp(), $bot, '', $oldsize, $newsize,
$revisionId );
-
+
# Mark as patrolled if the user can do so
- if( $wgUser->isAllowed( 'autopatrol' ) ) {
+ if( $GLOBALS['wgUseRCPatrol'] && $wgUser->isAllowed( 'autopatrol' ) ) {
RecentChange::markPatrolled( $rcid );
+ PatrolLog::record( $rcid, true );
}
}
$wgUser->incEditCount();
@@ -1362,19 +1371,19 @@ class Article {
}
if ( $good ) {
- # Invalidate cache of this article and all pages using this article
+ # Invalidate cache of this article and all pages using this article
# as a template. Partly deferred.
Article::onArticleEdit( $this->mTitle );
-
+
# Update links tables, site stats, etc.
$changed = ( strcmp( $oldtext, $text ) != 0 );
$this->editUpdates( $text, $summary, $isminor, $now, $revisionId, $changed );
}
} else {
# Create new article
-
+
# Set statistics members
- # We work out if it's countable after PST to avoid counter drift
+ # We work out if it's countable after PST to avoid counter drift
# when articles are created with {{subst:}}
$this->mGoodAdjustment = (int)$this->isCountable( $text );
$this->mTotalAdjustment = 1;
@@ -1403,8 +1412,9 @@ class Article {
$rcid = RecentChange::notifyNew( $now, $this->mTitle, $isminor, $wgUser, $summary, $bot,
'', strlen( $text ), $revisionId );
# Mark as patrolled if the user can
- if( $wgUser->isAllowed( 'autopatrol' ) ) {
+ if( $GLOBALS['wgUseRCPatrol'] && $wgUser->isAllowed( 'autopatrol' ) ) {
RecentChange::markPatrolled( $rcid );
+ PatrolLog::record( $rcid, true );
}
}
$wgUser->incEditCount();
@@ -1429,7 +1439,7 @@ class Article {
array( &$this, &$wgUser, $text,
$summary, $flags & EDIT_MINOR,
null, null, &$flags ) );
-
+
wfProfileOut( __METHOD__ );
return $good;
}
@@ -1457,7 +1467,7 @@ class Article {
}
$wgOut->redirect( $this->mTitle->getFullURL( $query ) . $sectionAnchor );
}
-
+
/**
* Mark this particular edit as patrolled
*/
@@ -1470,25 +1480,25 @@ class Article {
$wgOut->errorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
return;
}
-
+
# Check permissions
if( !$wgUser->isAllowed( 'patrol' ) ) {
$wgOut->permissionRequired( 'patrol' );
return;
}
-
+
# If we haven't been given an rc_id value, we can't do anything
$rcid = $wgRequest->getVal( 'rcid' );
if( !$rcid ) {
$wgOut->errorPage( 'markedaspatrollederror', 'markedaspatrollederrortext' );
return;
}
-
+
# Handle the 'MarkPatrolled' hook
if( !wfRunHooks( 'MarkPatrolled', array( $rcid, &$wgUser, false ) ) ) {
return;
}
-
+
$return = SpecialPage::getTitleFor( 'Recentchanges' );
# If it's left up to us, check that the user is allowed to patrol this edit
# If the user has the "autopatrol" right, then we'll assume there are no
@@ -1507,11 +1517,12 @@ class Article {
return;
}
}
-
+
# Mark the edit as patrolled
RecentChange::markPatrolled( $rcid );
+ PatrolLog::record( $rcid );
wfRunHooks( 'MarkPatrolledComplete', array( &$rcid, &$wgUser, false ) );
-
+
# Inform the user
$wgOut->setPageTitle( wfMsg( 'markedaspatrolled' ) );
$wgOut->addWikiText( wfMsgNoTrans( 'markedaspatrolledtext' ) );
@@ -1534,7 +1545,7 @@ class Article {
$wgOut->readOnlyPage();
return;
}
-
+
if( $this->doWatch() ) {
$wgOut->setPagetitle( wfMsg( 'addedwatch' ) );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
@@ -1546,7 +1557,7 @@ class Article {
$wgOut->returnToMain( true, $this->mTitle->getPrefixedText() );
}
-
+
/**
* Add this page to $wgUser's watchlist
* @return bool true on successful watch operation
@@ -1556,13 +1567,13 @@ class Article {
if( $wgUser->isAnon() ) {
return false;
}
-
+
if (wfRunHooks('WatchArticle', array(&$wgUser, &$this))) {
$wgUser->addWatch( $this->mTitle );
return wfRunHooks('WatchArticleComplete', array(&$wgUser, &$this));
}
-
+
return false;
}
@@ -1581,7 +1592,7 @@ class Article {
$wgOut->readOnlyPage();
return;
}
-
+
if( $this->doUnwatch() ) {
$wgOut->setPagetitle( wfMsg( 'removedwatch' ) );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
@@ -1593,7 +1604,7 @@ class Article {
$wgOut->returnToMain( true, $this->mTitle->getPrefixedText() );
}
-
+
/**
* Stop watching a page
* @return bool true on successful unwatch
@@ -1609,7 +1620,7 @@ class Article {
return wfRunHooks('UnwatchArticleComplete', array(&$wgUser, &$this));
}
-
+
return false;
}
@@ -1618,7 +1629,7 @@ class Article {
*/
function protect() {
$form = new ProtectionForm( $this );
- $form->show();
+ $form->execute();
}
/**
@@ -1635,14 +1646,21 @@ class Article {
* @param string $reason
* @return bool true on success
*/
- function updateRestrictions( $limit = array(), $reason = '' ) {
+ function updateRestrictions( $limit = array(), $reason = '', $cascade = 0, $expiry = null ) {
global $wgUser, $wgRestrictionTypes, $wgContLang;
-
+
$id = $this->mTitle->getArticleID();
if( !$wgUser->isAllowed( 'protect' ) || wfReadOnly() || $id == 0 ) {
return false;
}
+ if (!$cascade) {
+ $cascade = false;
+ }
+
+ // Take this opportunity to purge out expired restrictions
+ Title::purgeExpiredRestrictions();
+
# FIXME: Same limitations as described in ProtectionForm.php (line 37);
# we expect a single selection, but the schema allows otherwise.
$current = array();
@@ -1651,48 +1669,89 @@ class Article {
$current = Article::flattenRestrictions( $current );
$updated = Article::flattenRestrictions( $limit );
-
+
$changed = ( $current != $updated );
+ $changed = $changed || ($this->mTitle->areRestrictionsCascading() != $cascade);
+ $changed = $changed || ($this->mTitle->mRestrictionsExpiry != $expiry);
$protect = ( $updated != '' );
-
+
# If nothing's changed, do nothing
if( $changed ) {
+ global $wgGroupPermissions;
if( wfRunHooks( 'ArticleProtect', array( &$this, &$wgUser, $limit, $reason ) ) ) {
- $dbw =& wfGetDB( DB_MASTER );
-
+ $dbw = wfGetDB( DB_MASTER );
+
+ $encodedExpiry = Block::encodeExpiry($expiry, $dbw );
+
+ $expiry_description = '';
+ if ( $encodedExpiry != 'infinity' ) {
+ $expiry_description = ' (' . wfMsgForContent( 'protect-expiring', $wgContLang->timeanddate( $expiry ) ).')';
+ }
+
# Prepare a null revision to be added to the history
$comment = $wgContLang->ucfirst( wfMsgForContent( $protect ? 'protectedarticle' : 'unprotectedarticle', $this->mTitle->getPrefixedText() ) );
+
+ foreach( $limit as $action => $restrictions ) {
+ # Check if the group level required to edit also can protect pages
+ # Otherwise, people who cannot normally protect can "protect" pages via transclusion
+ $cascade = ( $cascade && isset($wgGroupPermissions[$restrictions]['protect']) && $wgGroupPermissions[$restrictions]['protect'] );
+ }
+
+ $cascade_description = '';
+ if ($cascade) {
+ $cascade_description = ' ['.wfMsg('protect-summary-cascade').']';
+ }
+
if( $reason )
$comment .= ": $reason";
if( $protect )
$comment .= " [$updated]";
+ if ( $expiry_description && $protect )
+ $comment .= "$expiry_description";
+ if ( $cascade )
+ $comment .= "$cascade_description";
+
$nullRevision = Revision::newNullRevision( $dbw, $id, $comment, true );
$nullRevId = $nullRevision->insertOn( $dbw );
-
+
+ # Update restrictions table
+ foreach( $limit as $action => $restrictions ) {
+ if ($restrictions != '' ) {
+ $dbw->replace( 'page_restrictions', array(array('pr_page', 'pr_type')),
+ array( 'pr_page' => $id, 'pr_type' => $action
+ , 'pr_level' => $restrictions, 'pr_cascade' => $cascade ? 1 : 0
+ , 'pr_expiry' => $encodedExpiry ), __METHOD__ );
+ } else {
+ $dbw->delete( 'page_restrictions', array( 'pr_page' => $id,
+ 'pr_type' => $action ), __METHOD__ );
+ }
+ }
+
# Update page record
$dbw->update( 'page',
array( /* SET */
'page_touched' => $dbw->timestamp(),
- 'page_restrictions' => $updated,
+ 'page_restrictions' => '',
'page_latest' => $nullRevId
), array( /* WHERE */
'page_id' => $id
), 'Article::protect'
);
wfRunHooks( 'ArticleProtectComplete', array( &$this, &$wgUser, $limit, $reason ) );
-
+
# Update the protection log
$log = new LogPage( 'protect' );
+
if( $protect ) {
- $log->addEntry( 'protect', $this->mTitle, trim( $reason . " [$updated]" ) );
+ $log->addEntry( 'protect', $this->mTitle, trim( $reason . " [$updated]$cascade_description$expiry_description" ) );
} else {
$log->addEntry( 'unprotect', $this->mTitle, $reason );
}
-
+
} # End hook
} # End "changed" check
-
+
return true;
}
@@ -1745,9 +1804,9 @@ class Article {
}
$wgOut->setPagetitle( wfMsg( 'confirmdelete' ) );
-
+
# Better double-check that it hasn't been deleted yet!
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$conds = $this->mTitle->pageCond();
$latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ );
if ( $latest === false ) {
@@ -1769,7 +1828,7 @@ class Article {
# and insert a warning if it does
$maxRevisions = 20;
$authors = $this->getLastNAuthors( $maxRevisions, $latest );
-
+
if( count( $authors ) > 1 && !$confirm ) {
$skin=$wgUser->getSkin();
$wgOut->addHTML( '<strong>' . wfMsg( 'historywarning' ) . ' ' . $skin->historyLink() . '</strong>' );
@@ -1813,7 +1872,7 @@ class Article {
$reason = wfMsgForContent( 'exblank' );
}
- if( $length < 500 && $reason === '' ) {
+ if( $reason === '' ) {
# comment field=255, let's grep the first 150 to have some user
# space left
global $wgContLang;
@@ -1849,7 +1908,7 @@ class Article {
// First try the slave
// If that doesn't have the latest revision, try the master
$continue = 2;
- $db =& wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_SLAVE );
do {
$res = $db->select( array( 'page', 'revision' ),
array( 'rev_id', 'rev_user_text' ),
@@ -1868,7 +1927,7 @@ class Article {
}
$row = $db->fetchObject( $res );
if ( $continue == 2 && $revLatest && $row->rev_id != $revLatest ) {
- $db =& wfGetDB( DB_MASTER );
+ $db = wfGetDB( DB_MASTER );
$continue--;
} else {
$continue = 0;
@@ -1882,7 +1941,7 @@ class Article {
wfProfileOut( __METHOD__ );
return $authors;
}
-
+
/**
* Output deletion confirmation dialog
*/
@@ -1929,6 +1988,23 @@ class Article {
</form>\n" );
$wgOut->returnToMain( false );
+
+ $this->showLogExtract( $wgOut );
+ }
+
+
+ /**
+ * Fetch deletion log
+ */
+ function showLogExtract( &$out ) {
+ # Show relevant lines from the deletion log:
+ $out->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'delete' ) ) . "</h2>\n" );
+ $logViewer = new LogViewer(
+ new LogReader(
+ new FauxRequest(
+ array( 'page' => $this->mTitle->getPrefixedText(),
+ 'type' => 'delete' ) ) ) );
+ $logViewer->showList( $out );
}
@@ -1969,7 +2045,7 @@ class Article {
wfDebug( __METHOD__."\n" );
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$ns = $this->mTitle->getNamespace();
$t = $this->mTitle->getDBkey();
$id = $this->mTitle->getArticleID();
@@ -2004,12 +2080,16 @@ class Article {
'ar_text_id' => 'rev_text_id',
'ar_text' => '\'\'', // Be explicit to appease
'ar_flags' => '\'\'', // MySQL's "strict mode"...
+ 'ar_len' => 'rev_len'
), array(
'page_id' => $id,
'page_id = rev_page'
), __METHOD__
);
+ # Delete restrictions for it
+ $dbw->delete( 'page_restrictions', array ( 'pr_page' => $id ), __METHOD__ );
+
# Now that it's safely backed up, delete it
$dbw->delete( 'page', array( 'page_id' => $id ), __METHOD__);
@@ -2078,7 +2158,7 @@ class Article {
$wgOut->addWikiText( wfMsg( 'sessionfailure' ) );
return;
}
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
# Enhanced rollback, marks edits rc_bot=1
$bot = $wgRequest->getBool( 'bot' );
@@ -2103,7 +2183,7 @@ class Article {
if( $current->getComment() != '') {
$wgOut->addHTML(
wfMsg( 'editcomment',
- htmlspecialchars( $current->getComment() ) ) );
+ $wgUser->getSkin()->formatComment( $current->getComment() ) ) );
}
return;
}
@@ -2189,7 +2269,7 @@ class Article {
* Do standard deferred updates after page edit.
* Update links tables, site stats, search index and message cache.
* Every 1000th edit, prune the recent changes table.
- *
+ *
* @private
* @param $text New text of the article
* @param $summary Edit summary
@@ -2222,7 +2302,7 @@ class Article {
# Periodically flush old entries from the recentchanges table.
global $wgRCMaxAge;
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$cutoff = $dbw->timestamp( time() - $wgRCMaxAge );
$recentchanges = $dbw->tableName( 'recentchanges' );
$sql = "DELETE FROM $recentchanges WHERE rc_timestamp < '{$cutoff}'";
@@ -2269,13 +2349,13 @@ class Article {
wfProfileOut( __METHOD__ );
}
-
+
/**
* Perform article updates on a special page creation.
*
* @param Revision $rev
*
- * @fixme This is a shitty interface function. Kill it and replace the
+ * @todo This is a shitty interface function. Kill it and replace the
* other shitty functions like editUpdates and such so it's not needed
* anymore.
*/
@@ -2299,8 +2379,8 @@ class Article {
global $wgLang, $wgOut, $wgUser;
if ( !wfRunHooks( 'DisplayOldSubtitle', array(&$this, &$oldid) ) ) {
- return;
- }
+ return;
+ }
$revision = Revision::newFromId( $oldid );
@@ -2326,10 +2406,10 @@ class Article {
$nextdiff = $current
? wfMsg( 'diff' )
: $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'diff' ), 'diff=next&oldid='.$oldid );
-
+
$userlinks = $sk->userLink( $revision->getUser(), $revision->getUserText() )
. $sk->userToolLinks( $revision->getUser(), $revision->getUserText() );
-
+
$r = "\n\t\t\t\t<div id=\"mw-revision-info\">" . wfMsg( 'revision-info', $td, $userlinks ) . "</div>\n" .
"\n\t\t\t\t<div id=\"mw-revision-nav\">" . wfMsg( 'revision-nav', $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>\n\t\t\t";
$wgOut->setSubtitle( $r );
@@ -2381,25 +2461,40 @@ class Article {
* @return bool
*/
function isFileCacheable() {
- global $wgUser, $wgUseFileCache, $wgShowIPinHeader, $wgRequest;
+ global $wgUser, $wgUseFileCache, $wgShowIPinHeader, $wgRequest, $wgLang, $wgContLang;
$action = $wgRequest->getVal( 'action' );
$oldid = $wgRequest->getVal( 'oldid' );
$diff = $wgRequest->getVal( 'diff' );
$redirect = $wgRequest->getVal( 'redirect' );
$printable = $wgRequest->getVal( 'printable' );
+ $page = $wgRequest->getVal( 'page' );
+
+ //check for non-standard user language; this covers uselang,
+ //and extensions for auto-detecting user language.
+ $ulang = $wgLang->getCode();
+ $clang = $wgContLang->getCode();
- return $wgUseFileCache
- and (!$wgShowIPinHeader)
- and ($this->getID() != 0)
- and ($wgUser->isAnon())
- and (!$wgUser->getNewtalk())
- and ($this->mTitle->getNamespace() != NS_SPECIAL )
- and (empty( $action ) || $action == 'view')
- and (!isset($oldid))
- and (!isset($diff))
- and (!isset($redirect))
- and (!isset($printable))
- and (!$this->mRedirectedFrom);
+ $cacheable = $wgUseFileCache
+ && (!$wgShowIPinHeader)
+ && ($this->getID() != 0)
+ && ($wgUser->isAnon())
+ && (!$wgUser->getNewtalk())
+ && ($this->mTitle->getNamespace() != NS_SPECIAL )
+ && (empty( $action ) || $action == 'view')
+ && (!isset($oldid))
+ && (!isset($diff))
+ && (!isset($redirect))
+ && (!isset($printable))
+ && !isset($page)
+ && (!$this->mRedirectedFrom)
+ && ($ulang === $clang);
+
+ if ( $cacheable ) {
+ //extension may have reason to disable file caching on some pages.
+ $cacheable = wfRunHooks( 'IsFileCacheable', array( $this ) );
+ }
+
+ return $cacheable;
}
/**
@@ -2446,7 +2541,7 @@ class Article {
function quickEdit( $text, $comment = '', $minor = 0 ) {
wfProfileIn( __METHOD__ );
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->begin();
$revision = new Revision( array(
'page' => $this->getId(),
@@ -2471,7 +2566,7 @@ class Article {
$id = intval( $id );
global $wgHitcounterUpdateFreq, $wgDBtype;
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$pageTable = $dbw->tableName( 'page' );
$hitcounterTable = $dbw->tableName( 'hitcounter' );
$acchitsTable = $dbw->tableName( 'acchits' );
@@ -2555,7 +2650,7 @@ class Article {
$title->touchLinks();
$title->purgeSquid();
-
+
# File cache
if ( $wgUseFileCache ) {
$cm = new HTMLFileCache( $title );
@@ -2617,7 +2712,7 @@ class Article {
$wgOut->addHTML(wfMsg( $wgUser->isLoggedIn() ? 'noarticletext' : 'noarticletextanon' ) );
}
} else {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$wl_clause = array(
'wl_title' => $page->getDBkey(),
'wl_namespace' => $page->getNamespace() );
@@ -2659,7 +2754,7 @@ class Article {
return false;
}
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$rev_clause = array( 'rev_page' => $id );
@@ -2693,7 +2788,7 @@ class Article {
return array();
}
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( array( 'templatelinks' ),
array( 'tl_namespace', 'tl_title' ),
array( 'tl_from' => $id ),
@@ -2708,7 +2803,7 @@ class Article {
$dbr->freeResult( $res );
return $result;
}
-
+
/**
* Return an auto-generated summary if the text provided is a redirect.
*
@@ -2785,6 +2880,84 @@ class Article {
return $summary;
}
+
+ /**
+ * Add the primary page-view wikitext to the output buffer
+ * Saves the text into the parser cache if possible.
+ * Updates templatelinks if it is out of date.
+ *
+ * @param string $text
+ * @param bool $cache
+ */
+ public function outputWikiText( $text, $cache = true ) {
+ global $wgParser, $wgUser, $wgOut;
+
+ $popts = $wgOut->parserOptions();
+ $popts->setTidy(true);
+ $parserOutput = $wgParser->parse( $text, $this->mTitle,
+ $popts, true, true, $this->getRevIdFetched() );
+ $popts->setTidy(false);
+ if ( $cache && $this && $parserOutput->getCacheTime() != -1 ) {
+ $parserCache =& ParserCache::singleton();
+ $parserCache->save( $parserOutput, $this, $wgUser );
+ }
+
+ if ( !wfReadOnly() && $this->mTitle->areRestrictionsCascading() ) {
+ // templatelinks table may have become out of sync,
+ // especially if using variable-based transclusions.
+ // For paranoia, check if things have changed and if
+ // so apply updates to the database. This will ensure
+ // that cascaded protections apply as soon as the changes
+ // are visible.
+
+ # Get templates from templatelinks
+ $id = $this->mTitle->getArticleID();
+
+ $tlTemplates = array();
+
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select( array( 'templatelinks' ),
+ array( 'tl_namespace', 'tl_title' ),
+ array( 'tl_from' => $id ),
+ 'Article:getUsedTemplates' );
+
+ global $wgContLang;
+
+ if ( false !== $res ) {
+ if ( $dbr->numRows( $res ) ) {
+ while ( $row = $dbr->fetchObject( $res ) ) {
+ $tlTemplates[] = $wgContLang->getNsText( $row->tl_namespace ) . ':' . $row->tl_title ;
+ }
+ }
+ }
+
+ # Get templates from parser output.
+ $poTemplates_allns = $parserOutput->getTemplates();
+
+ $poTemplates = array ();
+ foreach ( $poTemplates_allns as $ns_templates ) {
+ $poTemplates = array_merge( $poTemplates, $ns_templates );
+ }
+
+ # Get the diff
+ $templates_diff = array_diff( $poTemplates, $tlTemplates );
+
+ if ( count( $templates_diff ) > 0 ) {
+ # Whee, link updates time.
+ $u = new LinksUpdate( $this->mTitle, $parserOutput );
+
+ $dbw = wfGetDb( DB_MASTER );
+ $dbw->begin();
+
+ $u->doUpdate();
+
+ $dbw->commit();
+ }
+ }
+
+ $wgOut->addParserOutput( $parserOutput );
+ }
+
}
?>
diff --git a/includes/AuthPlugin.php b/includes/AuthPlugin.php
index e33ef1bf..9395032f 100644
--- a/includes/AuthPlugin.php
+++ b/includes/AuthPlugin.php
@@ -1,6 +1,5 @@
<?php
/**
- * @package MediaWiki
*/
# Copyright (C) 2004 Brion Vibber <brion@pobox.com>
# http://www.mediawiki.org/
@@ -33,7 +32,6 @@
* This interface is new, and might change a bit before 1.4.0 final is
* done...
*
- * @package MediaWiki
*/
class AuthPlugin {
/**
@@ -187,12 +185,14 @@ class AuthPlugin {
* Add a user to the external authentication database.
* Return true if successful.
*
- * @param User $user
+ * @param User $user - only the name should be assumed valid at this point
* @param string $password
+ * @param string $email
+ * @param string $realname
* @return bool
* @public
*/
- function addUser( $user, $password ) {
+ function addUser( $user, $password, $email='', $realname='' ) {
return true;
}
diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php
index 8de5608f..72a71c71 100644
--- a/includes/AutoLoader.php
+++ b/includes/AutoLoader.php
@@ -8,9 +8,11 @@ function __autoload($className) {
global $wgAutoloadClasses;
static $localClasses = array(
+ # Includes
'AjaxDispatcher' => 'includes/AjaxDispatcher.php',
'AjaxCachePolicy' => 'includes/AjaxFunctions.php',
'AjaxResponse' => 'includes/AjaxResponse.php',
+ 'AlphabeticPager' => 'includes/Pager.php',
'Article' => 'includes/Article.php',
'AuthPlugin' => 'includes/AuthPlugin.php',
'BagOStuff' => 'includes/BagOStuff.php',
@@ -39,9 +41,8 @@ function __autoload($className) {
'Database' => 'includes/Database.php',
'DatabaseMysql' => 'includes/Database.php',
'ResultWrapper' => 'includes/Database.php',
- 'OracleBlob' => 'includes/DatabaseOracle.php',
- 'DatabaseOracle' => 'includes/DatabaseOracle.php',
'DatabasePostgres' => 'includes/DatabasePostgres.php',
+ 'DatabaseOracle' => 'includes/DatabaseOracle.php',
'DateFormatter' => 'includes/DateFormatter.php',
'DifferenceEngine' => 'includes/DifferenceEngine.php',
'_DiffOp' => 'includes/DifferenceEngine.php',
@@ -95,6 +96,7 @@ function __autoload($className) {
'HTMLCacheUpdateJob' => 'includes/HTMLCacheUpdate.php',
'Http' => 'includes/HttpFunctions.php',
'Image' => 'includes/Image.php',
+ 'ArchivedFile' => 'includes/Image.php',
'IP' => 'includes/IP.php',
'ThumbnailImage' => 'includes/Image.php',
'ImageGallery' => 'includes/ImageGallery.php',
@@ -114,6 +116,10 @@ function __autoload($className) {
'MacBinary' => 'includes/MacBinary.php',
'MagicWord' => 'includes/MagicWord.php',
'MathRenderer' => 'includes/Math.php',
+ 'MediaTransformOutput' => 'includes/MediaTransformOutput.php',
+ 'ThumbnailImage' => 'includes/MediaTransformOutput.php',
+ 'MediaTransformError' => 'includes/MediaTransformOutput.php',
+ 'TransformParameterError' => 'includes/MediaTransformOutput.php',
'MessageCache' => 'includes/MessageCache.php',
'MimeMagic' => 'includes/MimeMagic.php',
'Namespace' => 'includes/Namespace.php',
@@ -124,16 +130,18 @@ function __autoload($className) {
'ReverseChronologicalPager' => 'includes/Pager.php',
'TablePager' => 'includes/Pager.php',
'Parser' => 'includes/Parser.php',
- 'ParserOutput' => 'includes/Parser.php',
- 'ParserOptions' => 'includes/Parser.php',
+ 'ParserOutput' => 'includes/ParserOutput.php',
+ 'ParserOptions' => 'includes/ParserOptions.php',
'ParserCache' => 'includes/ParserCache.php',
+ 'PatrolLog' => 'includes/PatrolLog.php',
'ProfilerSimple' => 'includes/ProfilerSimple.php',
'ProfilerSimpleUDP' => 'includes/ProfilerSimpleUDP.php',
'Profiler' => 'includes/Profiler.php',
'ProxyTools' => 'includes/ProxyTools.php',
'ProtectionForm' => 'includes/ProtectionForm.php',
'QueryPage' => 'includes/QueryPage.php',
- 'PageQueryPage' => 'includes/QueryPage.php',
+ 'PageQueryPage' => 'includes/PageQueryPage.php',
+ 'ImageQueryPage' => 'includes/ImageQueryPage.php',
'RawPage' => 'includes/RawPage.php',
'RecentChange' => 'includes/RecentChange.php',
'Revision' => 'includes/Revision.php',
@@ -148,6 +156,7 @@ function __autoload($className) {
'SearchPostgres' => 'includes/SearchPostgres.php',
'SearchUpdate' => 'includes/SearchUpdate.php',
'SearchUpdateMyISAM' => 'includes/SearchUpdate.php',
+ 'SearchOracle' => 'includes/SearchOracle.php',
'SiteConfiguration' => 'includes/SiteConfiguration.php',
'SiteStats' => 'includes/SiteStats.php',
'SiteStatsUpdate' => 'includes/SiteStats.php',
@@ -160,7 +169,6 @@ function __autoload($className) {
'IPBlockForm' => 'includes/SpecialBlockip.php',
'SpecialBookSources' => 'includes/SpecialBooksources.php',
'BrokenRedirectsPage' => 'includes/SpecialBrokenRedirects.php',
- 'CategoriesPage' => 'includes/SpecialCategories.php',
'EmailConfirmation' => 'includes/SpecialConfirmemail.php',
'ContributionsPage' => 'includes/SpecialContributions.php',
'DeadendPagesPage' => 'includes/SpecialDeadendpages.php',
@@ -173,7 +181,6 @@ function __autoload($className) {
'ImportStreamSource' => 'includes/SpecialImport.php',
'IPUnblockForm' => 'includes/SpecialIpblocklist.php',
'ListredirectsPage' => 'includes/SpecialListredirects.php',
- 'ListUsersPage' => 'includes/SpecialListusers.php',
'DBLockForm' => 'includes/SpecialLockdb.php',
'LogReader' => 'includes/SpecialLog.php',
'LogViewer' => 'includes/SpecialLog.php',
@@ -185,6 +192,7 @@ function __autoload($className) {
'MostlinkedPage' => 'includes/SpecialMostlinked.php',
'MostlinkedCategoriesPage' => 'includes/SpecialMostlinkedcategories.php',
'MostrevisionsPage' => 'includes/SpecialMostrevisions.php',
+ 'FewestrevisionsPage' => 'includes/SpecialFewestrevisions.php',
'MovePageForm' => 'includes/SpecialMovepage.php',
'NewbieContributionsPage' => 'includes/SpecialNewbieContributions.php',
'NewPagesPage' => 'includes/SpecialNewpages.php',
@@ -194,6 +202,7 @@ function __autoload($className) {
'PopularPagesPage' => 'includes/SpecialPopularpages.php',
'PreferencesForm' => 'includes/SpecialPreferences.php',
'SpecialPrefixindex' => 'includes/SpecialPrefixindex.php',
+ 'PasswordResetForm' => 'includes/SpecialResetpass.php',
'RevisionDeleteForm' => 'includes/SpecialRevisiondelete.php',
'RevisionDeleter' => 'includes/SpecialRevisiondelete.php',
'SpecialSearch' => 'includes/SpecialSearch.php',
@@ -215,6 +224,7 @@ function __autoload($className) {
'WantedCategoriesPage' => 'includes/SpecialWantedcategories.php',
'WantedPagesPage' => 'includes/SpecialWantedpages.php',
'WhatLinksHerePage' => 'includes/SpecialWhatlinkshere.php',
+ 'WithoutInterwikiPage' => 'includes/SpecialWithoutinterwiki.php',
'SquidUpdate' => 'includes/SquidUpdate.php',
'ReplacementArray' => 'includes/StringUtils.php',
'Replacer' => 'includes/StringUtils.php',
@@ -237,13 +247,27 @@ function __autoload($className) {
'Xml' => 'includes/Xml.php',
'ZhClient' => 'includes/ZhClient.php',
'memcached' => 'includes/memcached-client.php',
+
+ # Media
+ 'BitmapHandler' => 'includes/media/Bitmap.php',
+ 'BmpHandler' => 'includes/media/BMP.php',
+ 'DjVuHandler' => 'includes/media/DjVu.php',
+ 'MediaHandler' => 'includes/media/Generic.php',
+ 'ImageHandler' => 'includes/media/Generic.php',
+ 'SvgHandler' => 'includes/media/SVG.php',
+
+ # Normal
'UtfNormal' => 'includes/normal/UtfNormal.php',
+
+ # Templates
'UsercreateTemplate' => 'includes/templates/Userlogin.php',
'UserloginTemplate' => 'includes/templates/Userlogin.php',
+
+ # Languages
'Language' => 'languages/Language.php',
- 'PasswordResetForm' => 'includes/SpecialResetpass.php',
+ 'RandomPage' => 'includes/SpecialRandompage.php',
- // API classes
+ # API
'ApiBase' => 'includes/api/ApiBase.php',
'ApiFormatFeedWrapper' => 'includes/api/ApiFormatBase.php',
'ApiFeedWatchlist' => 'includes/api/ApiFeedWatchlist.php',
@@ -274,6 +298,7 @@ function __autoload($className) {
'ApiResult' => 'includes/api/ApiResult.php',
);
+ wfProfileIn( __METHOD__ );
if ( isset( $localClasses[$className] ) ) {
$filename = $localClasses[$className];
} elseif ( isset( $wgAutoloadClasses[$className] ) ) {
@@ -290,6 +315,7 @@ function __autoload($className) {
}
if ( !$filename ) {
# Give up
+ wfProfileOut( __METHOD__ );
return;
}
}
@@ -300,6 +326,7 @@ function __autoload($className) {
$filename = "$IP/$filename";
}
require( $filename );
+ wfProfileOut( __METHOD__ );
}
function wfLoadAllExtensions() {
@@ -311,10 +338,10 @@ function wfLoadAllExtensions() {
# guaranteed by entering special pages via SpecialPage members such as
# executePath(), but here we have to take a more explicit measure.
- require_once( 'SpecialPage.php' );
+ require_once( dirname(__FILE__) . '/SpecialPage.php' );
foreach( $wgAutoloadClasses as $class => $file ) {
- if ( ! class_exists( $class ) ) {
+ if( !( class_exists( $class ) || interface_exists( $class ) ) ) {
require( $file );
}
}
diff --git a/includes/BagOStuff.php b/includes/BagOStuff.php
index c720807d..2a04b9dd 100644
--- a/includes/BagOStuff.php
+++ b/includes/BagOStuff.php
@@ -19,7 +19,6 @@
# http://www.gnu.org/copyleft/gpl.html
/**
*
- * @package MediaWiki
*/
/**
@@ -29,15 +28,16 @@
* the PHP memcached client.
*
* backends for local hash array and SQL table included:
- * $bag = new HashBagOStuff();
- * $bag = new MysqlBagOStuff($tablename); # connect to db first
+ * <code>
+ * $bag = new HashBagOStuff();
+ * $bag = new MysqlBagOStuff($tablename); # connect to db first
+ * </code>
*
- * @package MediaWiki
*/
class BagOStuff {
var $debugmode;
- function BagOStuff() {
+ function __construct() {
$this->set_debug( false );
}
@@ -163,7 +163,6 @@ class BagOStuff {
/**
* Functional versions!
* @todo document
- * @package MediaWiki
*/
class HashBagOStuff extends BagOStuff {
/*
@@ -218,7 +217,6 @@ CREATE TABLE objectcache (
/**
* @todo document
* @abstract
- * @package MediaWiki
*/
abstract class SqlBagOStuff extends BagOStuff {
var $table;
@@ -386,34 +384,32 @@ abstract class SqlBagOStuff extends BagOStuff {
/**
* @todo document
- * @package MediaWiki
*/
class MediaWikiBagOStuff extends SqlBagOStuff {
var $tableInitialised = false;
function _doquery($sql) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
return $dbw->query($sql, 'MediaWikiBagOStuff::_doquery');
}
function _doinsert($t, $v) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
return $dbw->insert($t, $v, 'MediaWikiBagOStuff::_doinsert',
array( 'IGNORE' ) );
}
function _fetchobject($result) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
return $dbw->fetchObject($result);
}
function _freeresult($result) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
return $dbw->freeResult($result);
}
function _dberror($result) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
return $dbw->lastError();
}
function _maxdatetime() {
- $dbw =& wfGetDB(DB_MASTER);
if ( time() > 0x7fffffff ) {
return $this->_fromunixtime( 1<<62 );
} else {
@@ -421,24 +417,24 @@ class MediaWikiBagOStuff extends SqlBagOStuff {
}
}
function _fromunixtime($ts) {
- $dbw =& wfGetDB(DB_MASTER);
+ $dbw = wfGetDB(DB_MASTER);
return $dbw->timestamp($ts);
}
function _strencode($s) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
return $dbw->strencode($s);
}
function _blobencode($s) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
return $dbw->encodeBlob($s);
}
function _blobdecode($s) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
return $dbw->decodeBlob($s);
}
function getTableName() {
if ( !$this->tableInitialised ) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
/* This is actually a hack, we should be able
to use Language classes here... or not */
if (!$dbw)
@@ -463,7 +459,6 @@ class MediaWikiBagOStuff extends SqlBagOStuff {
* that Turck's serializer is faster, so a possible future extension would be
* to use it for arrays but not for objects.
*
- * @package MediaWiki
*/
class TurckBagOStuff extends BagOStuff {
function get($key) {
@@ -498,9 +493,7 @@ class TurckBagOStuff extends BagOStuff {
/**
* This is a wrapper for APC's shared memory functions
*
- * @package MediaWiki
*/
-
class APCBagOStuff extends BagOStuff {
function get($key) {
$val = apc_fetch($key);
@@ -528,7 +521,6 @@ class APCBagOStuff extends BagOStuff {
* This is basically identical to the Turck MMCache version,
* mostly because eAccelerator is based on Turck MMCache.
*
- * @package MediaWiki
*/
class eAccelBagOStuff extends BagOStuff {
function get($key) {
@@ -560,6 +552,9 @@ class eAccelBagOStuff extends BagOStuff {
}
}
+/**
+ * @todo document
+ */
class DBABagOStuff extends BagOStuff {
var $mHandler, $mFile, $mReader, $mWriter, $mDisabled;
diff --git a/includes/Block.php b/includes/Block.php
index ff813ba3..94bfa5b4 100644
--- a/includes/Block.php
+++ b/includes/Block.php
@@ -1,7 +1,6 @@
<?php
/**
* Blocks and bans object
- * @package MediaWiki
*/
/**
@@ -12,22 +11,24 @@
* Globals used: $wgAutoblockExpiry, $wgAntiLockFlags
*
* @todo This could be used everywhere, but it isn't.
- * @package MediaWiki
*/
class Block
{
/* public*/ var $mAddress, $mUser, $mBy, $mReason, $mTimestamp, $mAuto, $mId, $mExpiry,
- $mRangeStart, $mRangeEnd, $mAnonOnly, $mEnableAutoblock;
+ $mRangeStart, $mRangeEnd, $mAnonOnly, $mEnableAutoblock, $mHideName;
/* private */ var $mNetworkBits, $mIntegerAddr, $mForUpdate, $mFromMaster, $mByName;
const EB_KEEP_EXPIRED = 1;
const EB_FOR_UPDATE = 2;
const EB_RANGE_ONLY = 4;
- function Block( $address = '', $user = 0, $by = 0, $reason = '',
- $timestamp = '' , $auto = 0, $expiry = '', $anonOnly = 0, $createAccount = 0, $enableAutoblock = 0 )
+ function __construct( $address = '', $user = 0, $by = 0, $reason = '',
+ $timestamp = '' , $auto = 0, $expiry = '', $anonOnly = 0, $createAccount = 0, $enableAutoblock = 0,
+ $hideName = 0 )
{
$this->mId = 0;
+ # Expand valid IPv6 addresses
+ $address = IP::sanitizeIP( $address );
$this->mAddress = $address;
$this->mUser = $user;
$this->mBy = $by;
@@ -38,6 +39,7 @@ class Block
$this->mCreateAccount = $createAccount;
$this->mExpiry = self::decodeExpiry( $expiry );
$this->mEnableAutoblock = $enableAutoblock;
+ $this->mHideName = $hideName;
$this->mForUpdate = false;
$this->mFromMaster = false;
@@ -58,7 +60,7 @@ class Block
static function newFromID( $id )
{
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->resultObject( $dbr->select( 'ipblocks', '*',
array( 'ipb_id' => $id ), __METHOD__ ) );
$block = new Block;
@@ -74,7 +76,7 @@ class Block
$this->mAddress = $this->mReason = $this->mTimestamp = '';
$this->mId = $this->mAnonOnly = $this->mCreateAccount =
$this->mEnableAutoblock = $this->mAuto = $this->mUser =
- $this->mBy = 0;
+ $this->mBy = $this->mHideName = 0;
$this->mByName = false;
}
@@ -85,14 +87,14 @@ class Block
{
global $wgAntiLockFlags;
if ( $this->mForUpdate || $this->mFromMaster ) {
- $db =& wfGetDB( DB_MASTER );
+ $db = wfGetDB( DB_MASTER );
if ( !$this->mForUpdate || ($wgAntiLockFlags & ALF_NO_BLOCK_LOCK) ) {
$options = array();
} else {
$options = array( 'FOR UPDATE' );
}
} else {
- $db =& wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_SLAVE );
$options = array();
}
return $db;
@@ -147,7 +149,7 @@ class Block
}
# Try range block
- if ( $this->loadRange( $address, $killExpired, $user == 0 ) ) {
+ if ( $this->loadRange( $address, $killExpired, $user ) ) {
if ( $user && $this->mAnonOnly ) {
$this->clear();
return false;
@@ -176,7 +178,8 @@ class Block
/**
* Fill in member variables from a result wrapper
*/
- function loadFromResult( ResultWrapper $res, $killExpired = true ) {
+ function loadFromResult( ResultWrapper $res, $killExpired = true )
+ {
$ret = false;
if ( 0 != $res->numRows() ) {
# Get first block
@@ -211,7 +214,7 @@ class Block
* Search the database for any range blocks matching the given address, and
* load the row if one is found.
*/
- function loadRange( $address, $killExpired = true )
+ function loadRange( $address, $killExpired = true, $user = 0 )
{
$iaddr = IP::toHex( $address );
if ( $iaddr === false ) {
@@ -230,6 +233,10 @@ class Block
"ipb_range_start <= '$iaddr'",
"ipb_range_end >= '$iaddr'"
);
+
+ if ( $user ) {
+ $conds['ipb_anon_only'] = 0;
+ }
$res = $db->resultObject( $db->select( 'ipblocks', '*', $conds, __METHOD__, $options ) );
$success = $this->loadFromResult( $res, $killExpired );
@@ -255,6 +262,7 @@ class Block
$this->mAnonOnly = $row->ipb_anon_only;
$this->mCreateAccount = $row->ipb_create_account;
$this->mEnableAutoblock = $row->ipb_enable_autoblock;
+ $this->mHideName = $row->ipb_deleted;
$this->mId = $row->ipb_id;
$this->mExpiry = self::decodeExpiry( $row->ipb_expiry );
if ( isset( $row->user_name ) ) {
@@ -286,7 +294,7 @@ class Block
$block = new Block();
if ( $flags & Block::EB_FOR_UPDATE ) {
- $db =& wfGetDB( DB_MASTER );
+ $db = wfGetDB( DB_MASTER );
if ( $wgAntiLockFlags & ALF_NO_BLOCK_LOCK ) {
$options = '';
} else {
@@ -294,7 +302,7 @@ class Block
}
$block->forUpdate( true );
} else {
- $db =& wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_SLAVE );
$options = '';
}
if ( $flags & Block::EB_RANGE_ONLY ) {
@@ -341,7 +349,7 @@ class Block
throw new MWException( "Block::delete() now requires that the mId member be filled\n" );
}
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'ipblocks', array( 'ipb_id' => $this->mId ), __METHOD__ );
return $dbw->affectedRows() > 0;
}
@@ -353,8 +361,7 @@ class Block
function insert()
{
wfDebug( "Block::insert; timestamp {$this->mTimestamp}\n" );
- $dbw =& wfGetDB( DB_MASTER );
- $dbw->begin();
+ $dbw = wfGetDB( DB_MASTER );
# Unset ipb_anon_only for user blocks, makes no sense
if ( $this->mUser ) {
@@ -385,6 +392,7 @@ class Block
'ipb_expiry' => self::encodeExpiry( $this->mExpiry, $dbw ),
'ipb_range_start' => $this->mRangeStart,
'ipb_range_end' => $this->mRangeEnd,
+ 'ipb_deleted' => $this->mHideName
), 'Block::insert', array( 'IGNORE' )
);
$affected = $dbw->affectedRows();
@@ -418,20 +426,20 @@ class Block
} else {
#Limit is 1, so no loop needed.
$retroblockip = $row->rc_ip;
- return $this->doAutoblock($retroblockip);
+ return $this->doAutoblock( $retroblockip, true );
}
}
}
/**
* Autoblocks the given IP, referring to this Block.
- * @param $autoblockip The IP to autoblock.
+ * @param string $autoblockip The IP to autoblock.
+ * @param bool $justInserted The main block was just inserted
* @return bool Whether or not an autoblock was inserted.
*/
- function doAutoblock( $autoblockip ) {
+ function doAutoblock( $autoblockip, $justInserted = false ) {
# Check if this IP address is already blocked
- $dbw =& wfGetDB( DB_MASTER );
- $dbw->begin();
+ $dbw = wfGetDB( DB_MASTER );
# If autoblocks are disabled, go away.
if ( !$this->mEnableAutoblock ) {
@@ -480,7 +488,9 @@ class Block
return;
}
# Just update the timestamp
- $ipblock->updateTimestamp();
+ if ( !$justInserted ) {
+ $ipblock->updateTimestamp();
+ }
return;
} else {
$ipblock = new Block;
@@ -495,6 +505,8 @@ class Block
$ipblock->mTimestamp = wfTimestampNow();
$ipblock->mAuto = 1;
$ipblock->mCreateAccount = $this->mCreateAccount;
+ # Continue suppressing the name if needed
+ $ipblock->mHideName = $this->mHideName;
# If the user is already blocked with an expiry date, we don't
# want to pile on top of that!
@@ -544,7 +556,7 @@ class Block
$this->mTimestamp = wfTimestamp();
$this->mExpiry = Block::getAutoblockExpiry( $this->mTimestamp );
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->update( 'ipblocks',
array( /* SET */
'ipb_timestamp' => $dbw->timestamp($this->mTimestamp),
@@ -628,16 +640,36 @@ class Block
global $wgAutoblockExpiry;
return wfTimestamp( TS_MW, wfTimestamp( TS_UNIX, $timestamp ) + $wgAutoblockExpiry );
}
-
- static function normaliseRange( $range )
- {
+
+ /**
+ * Gets rid of uneeded numbers in quad-dotted/octet IP strings
+ * For example, 127.111.113.151/24 -> 127.111.113.0/24
+ */
+ static function normaliseRange( $range ) {
$parts = explode( '/', $range );
if ( count( $parts ) == 2 ) {
- $shift = 32 - $parts[1];
- $ipint = IP::toUnsigned( $parts[0] );
- $ipint = $ipint >> $shift << $shift;
- $newip = long2ip( $ipint );
- $range = "$newip/{$parts[1]}";
+ // IPv6
+ if ( IP::isIPv6($range) && $parts[1] >= 64 && $parts[1] <= 128 ) {
+ $bits = $parts[1];
+ $ipint = IP::toUnsigned6( $parts[0] );
+ # Native 32 bit functions WONT work here!!!
+ # Convert to a padded binary number
+ $network = wfBaseConvert( $ipint, 10, 2, 128 );
+ # Truncate the last (128-$bits) bits and replace them with zeros
+ $network = str_pad( substr( $network, 0, $bits ), 128, 0, STR_PAD_RIGHT );
+ # Convert back to an integer
+ $network = wfBaseConvert( $network, 2, 10 );
+ # Reform octet address
+ $newip = IP::toOctet( $network );
+ $range = "$newip/{$parts[1]}";
+ } // IPv4
+ else if ( IP::isIPv4($range) && $parts[1] >= 16 && $parts[1] <= 32 ) {
+ $shift = 32 - $parts[1];
+ $ipint = IP::toUnsigned( $parts[0] );
+ $ipint = $ipint >> $shift << $shift;
+ $newip = long2ip( $ipint );
+ $range = "$newip/{$parts[1]}";
+ }
}
return $range;
}
@@ -646,7 +678,7 @@ class Block
* Purge expired blocks from the ipblocks table
*/
static function purgeExpired() {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'ipblocks', array( 'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ), __METHOD__ );
}
@@ -658,7 +690,7 @@ class Block
/*
static $infinity;
if ( !isset( $infinity ) ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$infinity = $dbr->bigTimestamp();
}
return $infinity;
diff --git a/includes/CacheDependency.php b/includes/CacheDependency.php
index 4bb3d328..bb5c5437 100644
--- a/includes/CacheDependency.php
+++ b/includes/CacheDependency.php
@@ -4,6 +4,7 @@
* This class stores an arbitrary value along with its dependencies.
* Users should typically only use DependencyWrapper::getFromCache(), rather
* than instantiating one of these objects directly.
+ * @addtogroup Cache
*/
class DependencyWrapper {
var $value;
@@ -95,6 +96,9 @@ class DependencyWrapper {
}
}
+/**
+ * @addtogroup Cache
+ */
abstract class CacheDependency {
/**
* Returns true if the dependency is expired, false otherwise
@@ -107,6 +111,9 @@ abstract class CacheDependency {
function loadDependencyValues() {}
}
+/**
+ * @addtogroup Cache
+ */
class FileDependency extends CacheDependency {
var $filename, $timestamp;
@@ -163,6 +170,9 @@ class FileDependency extends CacheDependency {
}
}
+/**
+ * @addtogroup Cache
+ */
class TitleDependency extends CacheDependency {
var $titleObj;
var $ns, $dbk;
@@ -219,6 +229,9 @@ class TitleDependency extends CacheDependency {
}
}
+/**
+ * @addtogroup Cache
+ */
class TitleListDependency extends CacheDependency {
var $linkBatch;
var $timestamps;
@@ -244,7 +257,7 @@ class TitleListDependency extends CacheDependency {
# Do the query
if ( count( $timestamps ) ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$where = $this->getLinkBatch()->constructSet( 'page', $dbr );
$res = $dbr->select( 'page',
array( 'page_namespace', 'page_title', 'page_touched' ),
@@ -299,6 +312,9 @@ class TitleListDependency extends CacheDependency {
}
}
+/**
+ * @addtogroup Cache
+ */
class GlobalDependency extends CacheDependency {
var $name, $value;
@@ -312,6 +328,9 @@ class GlobalDependency extends CacheDependency {
}
}
+/**
+ * @addtogroup Cache
+ */
class ConstantDependency extends CacheDependency {
var $name, $value;
diff --git a/includes/CategoryPage.php b/includes/CategoryPage.php
index 0086a2f9..356f9ea2 100644
--- a/includes/CategoryPage.php
+++ b/includes/CategoryPage.php
@@ -3,17 +3,23 @@
* Special handling for category description pages
* Modelled after ImagePage.php
*
- * @package MediaWiki
*/
if( !defined( 'MEDIAWIKI' ) )
die( 1 );
/**
- * @package MediaWiki
*/
class CategoryPage extends Article {
function view() {
+ global $wgRequest, $wgUser;
+
+ $diff = $wgRequest->getVal( 'diff' );
+ $diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) );
+
+ if ( isset( $diff ) && $diffOnly )
+ return Article::view();
+
if(!wfRunHooks('CategoryPageView', array(&$this))) return;
if ( NS_CATEGORY == $this->mTitle->getNamespace() ) {
@@ -175,7 +181,7 @@ class CategoryViewer {
}
function doCategoryQuery() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
if( $this->from != '' ) {
$pageCondition = 'cl_sortkey >= ' . $dbr->addQuotes( $this->from );
$this->flip = false;
@@ -196,6 +202,7 @@ class CategoryViewer {
#+ $pageCondition,
__METHOD__,
array( 'ORDER BY' => $this->flip ? 'cl_sortkey DESC' : 'cl_sortkey',
+ 'USE INDEX' => 'cl_sortkey',
'LIMIT' => $this->limit + 1 ) );
$count = 0;
@@ -234,11 +241,12 @@ class CategoryViewer {
function getSubcategorySection() {
# Don't show subcategories section if there are none.
$r = '';
- if( count( $this->children ) > 0 ) {
+ $c = count( $this->children );
+ if( $c > 0 ) {
# Showing subcategories
$r .= "<div id=\"mw-subcategories\">\n";
$r .= '<h2>' . wfMsg( 'subcategories' ) . "</h2>\n";
- $r .= wfMsgExt( 'subcategorycount', array( 'parse' ), count( $this->children) );
+ $r .= wfMsgExt( 'subcategorycount', array( 'parse' ), $c );
$r .= $this->formatList( $this->children, $this->children_start_char );
$r .= "\n</div>";
}
@@ -247,11 +255,16 @@ class CategoryViewer {
function getPagesSection() {
$ti = htmlspecialchars( $this->title->getText() );
- $r = "<div id=\"mw-pages\">\n";
- $r .= '<h2>' . wfMsg( 'category_header', $ti ) . "</h2>\n";
- $r .= wfMsgExt( 'categoryarticlecount', array( 'parse' ), count( $this->articles) );
- $r .= $this->formatList( $this->articles, $this->articles_start_char );
- $r .= "\n</div>";
+ # Don't show articles section if there are none.
+ $r = '';
+ $c = count( $this->articles );
+ if( $c > 0 ) {
+ $r = "<div id=\"mw-pages\">\n";
+ $r .= '<h2>' . wfMsg( 'category_header', $ti ) . "</h2>\n";
+ $r .= wfMsgExt( 'categoryarticlecount', array( 'parse' ), $c );
+ $r .= $this->formatList( $this->articles, $this->articles_start_char );
+ $r .= "\n</div>";
+ }
return $r;
}
@@ -391,7 +404,7 @@ class CategoryViewer {
*/
function pagingLinks( $title, $first, $last, $limit, $query = array() ) {
global $wgUser, $wgLang;
- $sk =& $this->getSkin();
+ $sk = $this->getSkin();
$limitText = $wgLang->formatNum( $limit );
$prevLink = htmlspecialchars( wfMsg( 'prevn', $limitText ) );
diff --git a/includes/Categoryfinder.php b/includes/Categoryfinder.php
index a8cdf3ce..7faae935 100644
--- a/includes/Categoryfinder.php
+++ b/includes/Categoryfinder.php
@@ -1,26 +1,27 @@
<?php
-/*
-The "Categoryfinder" class takes a list of articles, creates an internal representation of all their parent
-categories (as well as parents of parents etc.). From this representation, it determines which of these articles
-are in one or all of a given subset of categories.
-
-Example use :
-
- # Determines wether the article with the page_id 12345 is in both
- # "Category 1" and "Category 2" or their subcategories, respectively
-
- $cf = new Categoryfinder ;
- $cf->seed (
- array ( 12345 ) ,
- array ( "Category 1","Category 2" ) ,
- "AND"
- ) ;
- $a = $cf->run() ;
- print implode ( "," , $a ) ;
-
-*/
-
+/**
+ * The "Categoryfinder" class takes a list of articles, creates an internal
+ * representation of all their parent categories (as well as parents of
+ * parents etc.). From this representation, it determines which of these
+ * articles are in one or all of a given subset of categories.
+ *
+ * Example use :
+ * <code>
+ * # Determines whether the article with the page_id 12345 is in both
+ * # "Category 1" and "Category 2" or their subcategories, respectively
+ *
+ * $cf = new Categoryfinder ;
+ * $cf->seed (
+ * array ( 12345 ) ,
+ * array ( "Category 1","Category 2" ) ,
+ * "AND"
+ * ) ;
+ * $a = $cf->run() ;
+ * print implode ( "," , $a ) ;
+ * </code>
+ *
+ */
class Categoryfinder {
var $articles = array () ; # The original article IDs passed to the seed function
@@ -34,8 +35,8 @@ class Categoryfinder {
/**
* Constructor (currently empty).
- */
- function Categoryfinder () {
+ */
+ function __construct() {
}
/**
@@ -61,10 +62,10 @@ class Categoryfinder {
/**
* Iterates through the parent tree starting with the seed values,
* then checks the articles if they match the conditions
- @return array of page_ids (those given to seed() that match the conditions)
- */
+ * @return array of page_ids (those given to seed() that match the conditions)
+ */
function run () {
- $this->dbr =& wfGetDB( DB_SLAVE );
+ $this->dbr = wfGetDB( DB_SLAVE );
while ( count ( $this->next ) > 0 ) {
$this->scan_next_layer () ;
}
@@ -83,20 +84,20 @@ class Categoryfinder {
/**
* This functions recurses through the parent representation, trying to match the conditions
- @param $id The article/category to check
- @param $conds The array of categories to match
- @return bool Does this match the conditions?
- */
+ * @param $id The article/category to check
+ * @param $conds The array of categories to match
+ * @return bool Does this match the conditions?
+ */
function check ( $id , &$conds ) {
# Shortcut (runtime paranoia): No contitions=all matched
if ( count ( $conds ) == 0 ) return true ;
-
+
if ( !isset ( $this->parents[$id] ) ) return false ;
# iterate through the parents
foreach ( $this->parents[$id] AS $p ) {
$pname = $p->cl_to ;
-
+
# Is this a condition?
if ( isset ( $conds[$pname] ) ) {
# This key is in the category list!
@@ -113,7 +114,7 @@ class Categoryfinder {
}
}
}
-
+
# Not done yet, try sub-parents
if ( !isset ( $this->name2id[$pname] ) ) {
# No sub-parent
@@ -130,10 +131,10 @@ class Categoryfinder {
/**
* Scans a "parent layer" of the articles/categories in $this->next
- */
+ */
function scan_next_layer () {
$fname = "Categoryfinder::scan_next_layer" ;
-
+
# Find all parents of the article currently in $this->next
$layer = array () ;
$res = $this->dbr->select(
@@ -161,7 +162,7 @@ class Categoryfinder {
$this->dbr->freeResult( $res ) ;
$this->next = array() ;
-
+
# Find the IDs of all category pages in $layer, if they exist
if ( count ( $layer ) > 0 ) {
$res = $this->dbr->select(
diff --git a/includes/ChangesList.php b/includes/ChangesList.php
index a2c1a265..bc141579 100644
--- a/includes/ChangesList.php
+++ b/includes/ChangesList.php
@@ -1,15 +1,7 @@
<?php
-/**
- * @package MediaWiki
- * Contain class to show various lists of change:
- * - what's link here
- * - related changes
- * - recent changes
- */
/**
* @todo document
- * @package MediaWiki
*/
class RCCacheEntry extends RecentChange
{
@@ -17,8 +9,7 @@ class RCCacheEntry extends RecentChange
var $curlink , $difflink, $lastlink , $usertalklink , $versionlink ;
var $userlink, $timestamp, $watched;
- function newFromParent( $rc )
- {
+ function newFromParent( $rc ) {
$rc2 = new RCCacheEntry;
$rc2->mAttribs = $rc->mAttribs;
$rc2->mExtra = $rc->mExtra;
@@ -27,14 +18,17 @@ class RCCacheEntry extends RecentChange
} ;
/**
- * @package MediaWiki
+ * Class to show various lists of changes:
+ * - what links here
+ * - related changes
+ * - recent changes
*/
class ChangesList {
# Called by history lists and recent changes
#
/** @todo document */
- function ChangesList( &$skin ) {
+ function __construct( &$skin ) {
$this->skin =& $skin;
$this->preCacheMessages();
}
@@ -47,7 +41,7 @@ class ChangesList {
* @return ChangesList derivative
*/
public static function newFromUser( &$user ) {
- $sk =& $user->getSkin();
+ $sk = $user->getSkin();
$list = NULL;
if( wfRunHooks( 'FetchChangesList', array( &$user, &$sk, &$list ) ) ) {
return $user->getOption( 'usenewrc' ) ? new EnhancedChangesList( $sk ) : new OldChangesList( $sk );
@@ -64,7 +58,7 @@ class ChangesList {
// Precache various messages
if( !isset( $this->message ) ) {
foreach( explode(' ', 'cur diff hist minoreditletter newpageletter last '.
- 'blocklink changes history boteditletter' ) as $msg ) {
+ 'blocklink history boteditletter' ) as $msg ) {
$this->message[$msg] = wfMsgExt( $msg, array( 'escape') );
}
}
@@ -212,6 +206,23 @@ class ChangesList {
global $wgUseRCPatrol, $wgUser;
return( $wgUseRCPatrol && $wgUser->isAllowed( 'patrol' ) );
}
+
+ /**
+ * Returns the string which indicates the number of watching users
+ */
+ function numberofWatchingusers( $count ) {
+ global $wgLang;
+ static $cache = array();
+ if ( $count > 0 ) {
+ if ( !isset( $cache[$count] ) ) {
+ $cache[$count] = wfMsgExt('number_of_watching_users_RCview',
+ array('parsemag', 'escape'), $wgLang->formatNum($count));
+ }
+ return $cache[$count];
+ } else {
+ return '';
+ }
+ }
}
@@ -229,6 +240,7 @@ class OldChangesList extends ChangesList {
wfProfileIn( $fname );
# Extract DB fields into local scope
+ // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( $rc->mAttribs );
# Should patrol-related stuff be shown?
@@ -273,9 +285,7 @@ class OldChangesList extends ChangesList {
$this->insertUserRelatedLinks($s,$rc);
$this->insertComment($s, $rc);
- if($rc->numberofWatchingusers > 0) {
- $s .= ' ' . wfMsg('number_of_watching_users_RCview', $wgContLang->formatNum($rc->numberofWatchingusers));
- }
+ $s .= rtrim(' ' . $this->numberofWatchingusers($rc->numberofWatchingusers));
$s .= "</li>\n";
@@ -301,6 +311,7 @@ class EnhancedChangesList extends ChangesList {
$rc = RCCacheEntry::newFromParent( $baseRC );
# Extract fields from DB into the function scope (rc_xxxx variables)
+ // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( $rc->mAttribs );
$curIdEq = 'curid=' . $rc_cur_id;
@@ -405,7 +416,7 @@ class EnhancedChangesList extends ChangesList {
* Enhanced RC group
*/
function recentChangesBlockGroup( $block ) {
- global $wgContLang, $wgRCShowChangedSize;
+ global $wgLang, $wgContLang, $wgRCShowChangedSize;
$r = '';
# Collate list of users
@@ -467,22 +478,32 @@ class EnhancedChangesList extends ChangesList {
$currentRevision = $block[0]->mAttribs['rc_this_oldid'];
if( $block[0]->mAttribs['rc_type'] != RC_LOG ) {
# Changes
- $r .= ' ('.count($block).' ';
+
+ $n = count($block);
+ static $nchanges = array();
+ if ( !isset( $nchanges[$n] ) ) {
+ $nchanges[$n] = wfMsgExt( 'nchanges', array( 'parsemag', 'escape'),
+ $wgLang->formatNum( $n ) );
+ }
+
+ $r .= ' (';
if( $isnew ) {
- $r .= $this->message['changes'];
+ $r .= $nchanges[$n];
} else {
$r .= $this->skin->makeKnownLinkObj( $block[0]->getTitle(),
- $this->message['changes'], $curIdEq."&diff=$currentRevision&oldid=$oldid" );
+ $nchanges[$n], $curIdEq."&diff=$currentRevision&oldid=$oldid" );
}
+ $r .= ') . . ';
+
# Character difference
$chardiff = $rcObj->getCharacterDifference( $block[ count( $block ) - 1 ]->mAttribs['rc_old_len'],
$block[0]->mAttribs['rc_new_len'] );
if( $chardiff == '' ) {
- $r .= '; ';
+ $r .= ' (';
} else {
- $r .= '; ' . $chardiff . ' ';
+ $r .= ' ' . $chardiff. ' . . (';
}
@@ -494,16 +515,14 @@ class EnhancedChangesList extends ChangesList {
$r .= $users;
- if($block[0]->numberofWatchingusers > 0) {
- global $wgContLang;
- $r .= wfMsg('number_of_watching_users_RCview', $wgContLang->formatNum($block[0]->numberofWatchingusers));
- }
+ $r .= $this->numberofWatchingusers($block[0]->numberofWatchingusers);
$r .= "<br />\n";
# Sub-entries
$r .= '<div id="'.$rci.'" style="display:none">';
foreach( $block as $rcObj ) {
# Get rc_xxxx variables
+ // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( $rcObj->mAttribs );
$r .= $this->spacerArrow();
@@ -607,6 +626,7 @@ class EnhancedChangesList extends ChangesList {
global $wgContLang, $wgRCShowChangedSize;
# Get rc_xxxx variables
+ // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( $rcObj->mAttribs );
$curIdEq = 'curid='.$rc_cur_id;
@@ -647,9 +667,7 @@ class EnhancedChangesList extends ChangesList {
$r .= $this->skin->commentBlock( $rc_comment, $rcObj->getTitle() );
}
- if( $rcObj->numberofWatchingusers > 0 ) {
- $r .= wfMsg('number_of_watching_users_RCview', $wgContLang->formatNum($rcObj->numberofWatchingusers));
- }
+ $r .= $this->numberofWatchingusers($rcObj->numberofWatchingusers);
$r .= "<br />\n";
return $r;
diff --git a/includes/CoreParserFunctions.php b/includes/CoreParserFunctions.php
index 402a3ba9..72ceb45f 100644
--- a/includes/CoreParserFunctions.php
+++ b/includes/CoreParserFunctions.php
@@ -2,8 +2,8 @@
/**
* Various core parser functions, registered in Parser::firstCallInit()
+ * @addtogroup Parser
*/
-
class CoreParserFunctions {
static function intFunction( $parser, $part1 = '' /*, ... */ ) {
if ( strval( $part1 ) !== '' ) {
@@ -87,7 +87,7 @@ class CoreParserFunctions {
static function formatNum( $parser, $num = '' ) {
return $parser->getFunctionLang()->formatNum( $num );
}
-
+
static function grammar( $parser, $case = '', $word = '' ) {
return $parser->getFunctionLang()->convertGrammar( $word, $case );
}
@@ -135,6 +135,7 @@ class CoreParserFunctions {
static function numberofarticles( $parser, $raw = null ) { return self::statisticsFunction( 'articles', $raw ); }
static function numberoffiles( $parser, $raw = null ) { return self::statisticsFunction( 'images', $raw ); }
static function numberofadmins( $parser, $raw = null ) { return self::statisticsFunction( 'admins', $raw ); }
+ static function numberofedits( $parser, $raw = null ) { return self::statisticsFunction( 'edits', $raw ); }
static function pagesinnamespace( $parser, $namespace = 0, $raw = null ) {
$count = SiteStats::pagesInNs( intval( $namespace ) );
@@ -151,7 +152,7 @@ class CoreParserFunctions {
$lang = $wgContLang->getLanguageName( strtolower( $arg ) );
return $lang != '' ? $lang : $arg;
}
-
+
static function pad( $string = '', $length = 0, $char = 0, $direction = STR_PAD_RIGHT ) {
$length = min( max( $length, 0 ), 500 );
$char = substr( $char, 0, 1 );
@@ -159,17 +160,21 @@ class CoreParserFunctions {
? str_pad( $string, $length, (string)$char, $direction )
: $string;
}
-
+
static function padleft( $parser, $string = '', $length = 0, $char = 0 ) {
return self::pad( $string, $length, $char, STR_PAD_LEFT );
}
-
+
static function padright( $parser, $string = '', $length = 0, $char = 0 ) {
return self::pad( $string, $length, $char );
}
-
+
static function anchorencode( $parser, $text ) {
- return strtr( urlencode( $text ) , array( '%' => '.' , '+' => '_' ) );
+ $a = urlencode( $text );
+ $a = strtr( $a, array( '%' => '.', '+' => '_' ) );
+ # leave colons alone, however
+ $a = str_replace( '.3A', ':', $a );
+ return $a;
}
static function special( $parser, $text ) {
@@ -180,14 +185,12 @@ class CoreParserFunctions {
return wfMsgForContent( 'nosuchspecialpage' );
}
}
-
+
public static function defaultsort( $parser, $text ) {
$text = trim( $text );
if( strlen( $text ) > 0 )
$parser->setDefaultSort( $text );
return '';
}
-
}
-
?>
diff --git a/includes/Credits.php b/includes/Credits.php
index 62f0b256..87382a86 100644
--- a/includes/Credits.php
+++ b/includes/Credits.php
@@ -18,7 +18,6 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
* @author <evan@wikitravel.org>
- * @package MediaWiki
*/
/**
diff --git a/includes/Database.php b/includes/Database.php
index eb1ee135..3fd6ad16 100644
--- a/includes/Database.php
+++ b/includes/Database.php
@@ -2,7 +2,6 @@
/**
* This file deals with MySQL interface functions
* and query specifics/optimisations
- * @package MediaWiki
*/
/** Number of times to re-try an operation in case of deadlock */
@@ -16,6 +15,10 @@ define( 'DEADLOCK_DELAY_MAX', 1500000 );
* Utility classes
*****************************************************************************/
+/**
+ * Utility class.
+ * @addtogroup Database
+ */
class DBObject {
public $mData;
@@ -32,12 +35,66 @@ class DBObject {
}
};
+/**
+ * Utility class.
+ * @addtogroup Database
+ */
+class MySQLField {
+ private $name, $tablename, $default, $max_length, $nullable,
+ $is_pk, $is_unique, $is_key, $type;
+ function __construct ($info) {
+ $this->name = $info->name;
+ $this->tablename = $info->table;
+ $this->default = $info->def;
+ $this->max_length = $info->max_length;
+ $this->nullable = !$info->not_null;
+ $this->is_pk = $info->primary_key;
+ $this->is_unique = $info->unique_key;
+ $this->is_multiple = $info->multiple_key;
+ $this->is_key = ($this->is_pk || $this->is_unique || $this->is_multiple);
+ $this->type = $info->type;
+ }
+
+ function name() {
+ return $this->name;
+ }
+
+ function tableName() {
+ return $this->tableName;
+ }
+
+ function defaultValue() {
+ return $this->default;
+ }
+
+ function maxLength() {
+ return $this->max_length;
+ }
+
+ function nullable() {
+ return $this->nullable;
+ }
+
+ function isKey() {
+ return $this->is_key;
+ }
+
+ function isMultipleKey() {
+ return $this->is_multiple;
+ }
+
+ function type() {
+ return $this->type;
+ }
+}
+
/******************************************************************************
* Error classes
*****************************************************************************/
/**
* Database error base class
+ * @addtogroup Database
*/
class DBError extends MWException {
public $db;
@@ -53,6 +110,9 @@ class DBError extends MWException {
}
}
+/**
+ * @addtogroup Database
+ */
class DBConnectionError extends DBError {
public $error;
@@ -154,6 +214,7 @@ border=\"0\" ALT=\"Google\"></A>
$cache = new HTMLFileCache( $t );
if( $cache->isFileCached() ) {
+ // FIXME: $msg is not defined on the next line.
$msg = '<p style="color: red"><b>'.$msg."<br />\n" .
$cachederror . "</b></p>\n";
@@ -169,6 +230,9 @@ border=\"0\" ALT=\"Google\"></A>
}
}
+/**
+ * @addtogroup Database
+ */
class DBQueryError extends DBError {
public $error, $errno, $sql, $fname;
@@ -222,13 +286,16 @@ class DBQueryError extends DBError {
}
}
+/**
+ * @addtogroup Database
+ */
class DBUnexpectedError extends DBError {}
/******************************************************************************/
/**
* Database abstraction object
- * @package MediaWiki
+ * @addtogroup Database
*/
class Database {
@@ -247,9 +314,6 @@ class Database {
protected $mTrxLevel = 0;
protected $mErrorCount = 0;
protected $mLBInfo = array();
- protected $mCascadingDeletes = false;
- protected $mCleanupTriggers = false;
- protected $mStrictIPs = false;
#------------------------------------------------------------------------------
# Accessors
@@ -344,14 +408,14 @@ class Database {
* Returns true if this database supports (and uses) cascading deletes
*/
function cascadingDeletes() {
- return $this->mCascadingDeletes;
+ return false;
}
/**
* Returns true if this database supports (and uses) triggers (e.g. on the page table)
*/
function cleanupTriggers() {
- return $this->mCleanupTriggers;
+ return false;
}
/**
@@ -359,7 +423,7 @@ class Database {
* Specifically, it uses a NULL value instead of an empty string.
*/
function strictIPs() {
- return $this->mStrictIPs;
+ return false;
}
/**
@@ -376,6 +440,14 @@ class Database {
return true;
}
+ /**
+ * Returns true if this database can do a native search on IP columns
+ * e.g. this works as expected: .. WHERE rc_ip = '127.42.12.102/32';
+ */
+ function searchableIPs() {
+ return false;
+ }
+
/**#@+
* Get function
*/
@@ -407,13 +479,11 @@ class Database {
#------------------------------------------------------------------------------
/**@{{
+ * Constructor.
* @param string $server database server host
* @param string $user database user name
* @param string $password database user password
* @param string $dbname database name
- */
-
- /**
* @param failFunction
* @param $flags
* @param $tablePrefix String: database table prefixes. By default use the prefix gave in LocalSettings.php
@@ -463,8 +533,7 @@ class Database {
* @param failFunction
* @param $flags
*/
- static function newFromParams( $server, $user, $password, $dbName,
- $failFunction = false, $flags = 0 )
+ static function newFromParams( $server, $user, $password, $dbName, $failFunction = false, $flags = 0 )
{
return new Database( $server, $user, $password, $dbName, $failFunction, $flags );
}
@@ -514,7 +583,7 @@ class Database {
}
if ($this->mConn === false) {
$iplus = $i + 1;
- wfLogDBError("Connect loop error $iplus of $max ($server): " . mysql_errno() . " - " . mysql_error()."\n");
+ #wfLogDBError("Connect loop error $iplus of $max ($server): " . mysql_errno() . " - " . mysql_error()."\n");
}
}
@@ -541,12 +610,19 @@ class Database {
}
if ( $success ) {
- global $wgDBmysql5;
- if( $wgDBmysql5 ) {
+ $version = $this->getServerVersion();
+ if ( version_compare( $version, '4.1' ) >= 0 ) {
// Tell the server we're communicating with it in UTF-8.
// This may engage various charset conversions.
- $this->query( 'SET NAMES utf8' );
+ global $wgDBmysql5;
+ if( $wgDBmysql5 ) {
+ $this->query( 'SET NAMES utf8', __METHOD__ );
+ }
+ // Turn off strict mode
+ $this->query( "SET sql_mode = ''", __METHOD__ );
}
+
+ // Turn off strict mode if it is on
} else {
$this->reportConnectionError();
}
@@ -599,10 +675,15 @@ class Database {
}
/**
- * Usually aborts on failure
- * If errors are explicitly ignored, returns success
+ * Usually aborts on failure. If errors are explicitly ignored, returns success.
+ *
+ * @param $sql String: SQL query
+ * @param $fname String: Name of the calling function, for profiling/SHOW PROCESSLIST comment (you can use __METHOD__ or add some extra info)
+ * @param $tempIgnore Bool: Whether to avoid throwing an exception on errors... maybe best to catch the exception instead?
+ * @return Result object to feed to fetchObject, fetchRow, ...; or false on failure if $tempIgnore set
+ * @throws DBQueryError Thrown when the database returns an error of any kind
*/
- function query( $sql, $fname = '', $tempIgnore = false ) {
+ public function query( $sql, $fname = '', $tempIgnore = false ) {
global $wgProfiling;
if ( $wgProfiling ) {
@@ -626,11 +707,21 @@ class Database {
$this->mLastQuery = $sql;
# Add a comment for easy SHOW PROCESSLIST interpretation
- if ( $fname ) {
- $commentedSql = preg_replace('/\s/', " /* $fname */ ", $sql, 1);
- } else {
- $commentedSql = $sql;
- }
+ #if ( $fname ) {
+ global $wgUser;
+ if ( is_object( $wgUser ) && !($wgUser instanceof StubObject) ) {
+ $userName = $wgUser->getName();
+ if ( strlen( $userName ) > 15 ) {
+ $userName = substr( $userName, 0, 15 ) . '...';
+ }
+ $userName = str_replace( '/', '', $userName );
+ } else {
+ $userName = '';
+ }
+ $commentedSql = preg_replace('/\s/', " /* $fname $userName */ ", $sql, 1);
+ #} else {
+ # $commentedSql = $sql;
+ #}
# If DBO_TRX is set, start a transaction
if ( ( $this->mFlags & DBO_TRX ) && !$this->trxLevel() &&
@@ -655,6 +746,11 @@ class Database {
wfDebug( "Connection lost, reconnecting...\n" );
if ( $this->ping() ) {
wfDebug( "Reconnected\n" );
+ $sqlx = substr( $commentedSql, 0, 500 );
+ $sqlx = strtr( $sqlx, "\t\n", ' ' );
+ global $wgRequestTime;
+ $elapsed = round( microtime(true) - $wgRequestTime, 3 );
+ wfLogDBError( "Connection lost and reconnected after {$elapsed}s, query: $sqlx\n" );
$ret = $this->doQuery( $commentedSql );
} else {
wfDebug( "Failed\n" );
@@ -674,9 +770,11 @@ class Database {
/**
* The DBMS-dependent part of query()
- * @param string $sql SQL query.
+ * @param $sql String: SQL query.
+ * @return Result object to feed to fetchObject, fetchRow, ...; or false on failure
+ * @access private
*/
- function doQuery( $sql ) {
+ /*private*/ function doQuery( $sql ) {
if( $this->bufferResults() ) {
$ret = mysql_query( $sql, $this->mConn );
} else {
@@ -817,7 +915,13 @@ class Database {
}
/**
- * Fetch the next row from the given result object, in object form
+ * Fetch the next row from the given result object, in object form.
+ * Fields can be retrieved with $row->fieldname, with fields acting like
+ * member variables.
+ *
+ * @param $res SQL result object as returned from Database::query(), etc.
+ * @return MySQL row object
+ * @throws DBUnexpectedError Thrown if the database returns an error
*/
function fetchObject( $res ) {
@/**/$row = mysql_fetch_object( $res );
@@ -828,8 +932,12 @@ class Database {
}
/**
- * Fetch the next row from the given result object
- * Returns an array
+ * Fetch the next row from the given result object, in associative array
+ * form. Fields are retrieved with $row['fieldname'].
+ *
+ * @param $res SQL result object as returned from Database::query(), etc.
+ * @return MySQL row object
+ * @throws DBUnexpectedError Thrown if the database returns an error
*/
function fetchRow( $res ) {
@/**/$row = mysql_fetch_array( $res );
@@ -972,7 +1080,7 @@ class Database {
* @return array
*/
function makeSelectOptions( $options ) {
- $tailOpts = '';
+ $preLimitTail = $postLimitTail = '';
$startOpts = '';
$noKeyOptions = array();
@@ -982,16 +1090,17 @@ class Database {
}
}
- if ( isset( $options['GROUP BY'] ) ) $tailOpts .= " GROUP BY {$options['GROUP BY']}";
- if ( isset( $options['ORDER BY'] ) ) $tailOpts .= " ORDER BY {$options['ORDER BY']}";
+ if ( isset( $options['GROUP BY'] ) ) $preLimitTail .= " GROUP BY {$options['GROUP BY']}";
+ if ( isset( $options['ORDER BY'] ) ) $preLimitTail .= " ORDER BY {$options['ORDER BY']}";
- if (isset($options['LIMIT'])) {
- $tailOpts .= $this->limitResult('', $options['LIMIT'],
- isset($options['OFFSET']) ? $options['OFFSET'] : false);
- }
-
- if ( isset( $noKeyOptions['FOR UPDATE'] ) ) $tailOpts .= ' FOR UPDATE';
- if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) $tailOpts .= ' LOCK IN SHARE MODE';
+ //if (isset($options['LIMIT'])) {
+ // $tailOpts .= $this->limitResult('', $options['LIMIT'],
+ // isset($options['OFFSET']) ? $options['OFFSET']
+ // : false);
+ //}
+
+ if ( isset( $noKeyOptions['FOR UPDATE'] ) ) $postLimitTail .= ' FOR UPDATE';
+ if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) $postLimitTail .= ' LOCK IN SHARE MODE';
if ( isset( $noKeyOptions['DISTINCT'] ) && isset( $noKeyOptions['DISTINCTROW'] ) ) $startOpts .= 'DISTINCT';
# Various MySQL extensions
@@ -1010,7 +1119,7 @@ class Database {
$useIndex = '';
}
- return array( $startOpts, $useIndex, $tailOpts );
+ return array( $startOpts, $useIndex, $preLimitTail, $postLimitTail );
}
/**
@@ -1038,20 +1147,33 @@ class Database {
else
$from = ' FROM ' . implode( ',', array_map( array( &$this, 'tableName' ), $table ) );
} elseif ($table!='') {
- $from = ' FROM ' . $this->tableName( $table );
+ if ($table{0}==' ') {
+ $from = ' FROM ' . $table;
+ } else {
+ $from = ' FROM ' . $this->tableName( $table );
+ }
} else {
$from = '';
}
- list( $startOpts, $useIndex, $tailOpts ) = $this->makeSelectOptions( $options );
+ list( $startOpts, $useIndex, $preLimitTail, $postLimitTail ) = $this->makeSelectOptions( $options );
if( !empty( $conds ) ) {
if ( is_array( $conds ) ) {
$conds = $this->makeList( $conds, LIST_AND );
}
- $sql = "SELECT $startOpts $vars $from $useIndex WHERE $conds $tailOpts";
+ $sql = "SELECT $startOpts $vars $from $useIndex WHERE $conds $preLimitTail";
} else {
- $sql = "SELECT $startOpts $vars $from $useIndex $tailOpts";
+ $sql = "SELECT $startOpts $vars $from $useIndex $preLimitTail";
+ }
+
+ if (isset($options['LIMIT']))
+ $sql = $this->limitResult($sql, $options['LIMIT'],
+ isset($options['OFFSET']) ? $options['OFFSET'] : false);
+ $sql = "$sql $postLimitTail";
+
+ if (isset($options['EXPLAIN'])) {
+ $sql = 'EXPLAIN ' . $sql;
}
return $this->query( $sql, $fname );
@@ -1085,6 +1207,33 @@ class Database {
return $obj;
}
+
+ /**
+ * Estimate rows in dataset
+ * Returns estimated count, based on EXPLAIN output
+ * Takes same arguments as Database::select()
+ */
+
+ function estimateRowCount( $table, $vars='*', $conds='', $fname = 'Database::estimateRowCount', $options = array() ) {
+ $options['EXPLAIN']=true;
+ $res = $this->select ($table, $vars, $conds, $fname, $options );
+ if ( $res === false )
+ return false;
+ if (!$this->numRows($res)) {
+ $this->freeResult($res);
+ return 0;
+ }
+
+ $rows=1;
+
+ while( $plan = $this->fetchObject( $res ) ) {
+ $rows *= ($plan->rows > 0)?$plan->rows:1; // avoid resetting to zero
+ }
+
+ $this->freeResult($res);
+ return $rows;
+ }
+
/**
* Removes most variables from an SQL query and replaces them with X or N for numbers.
@@ -1207,7 +1356,7 @@ class Database {
for( $i = 0; $i < $n; $i++ ) {
$meta = mysql_fetch_field( $res, $i );
if( $field == $meta->name ) {
- return $meta;
+ return new MySQLField($meta);
}
}
return false;
@@ -1417,7 +1566,7 @@ class Database {
}
/**
- * @desc: Fetch a number of table names into an zero-indexed numerical array
+ * Fetch a number of table names into an zero-indexed numerical array
* This is handy when you need to construct SQL for joins
*
* Example:
@@ -1952,20 +2101,50 @@ class Database {
}
/**
+ * Override database's default connection timeout.
+ * May be useful for very long batch queries such as
+ * full-wiki dumps, where a single query reads out
+ * over hours or days.
+ * @param int $timeout in seconds
+ */
+ public function setTimeout( $timeout ) {
+ $this->query( "SET net_read_timeout=$timeout" );
+ $this->query( "SET net_write_timeout=$timeout" );
+ }
+
+ /**
* Read and execute SQL commands from a file.
* Returns true on success, error string on failure
+ * @param string $filename File name to open
+ * @param callback $lineCallback Optional function called before reading each line
+ * @param callback $resultCallback Optional function called for each MySQL result
*/
- function sourceFile( $filename ) {
+ function sourceFile( $filename, $lineCallback = false, $resultCallback = false ) {
$fp = fopen( $filename, 'r' );
if ( false === $fp ) {
return "Could not open \"{$filename}\".\n";
}
+ $error = $this->sourceStream( $fp, $lineCallback, $resultCallback );
+ fclose( $fp );
+ return $error;
+ }
+ /**
+ * Read and execute commands from an open file handle
+ * Returns true on success, error string on failure
+ * @param string $fp File handle
+ * @param callback $lineCallback Optional function called before reading each line
+ * @param callback $resultCallback Optional function called for each MySQL result
+ */
+ function sourceStream( $fp, $lineCallback = false, $resultCallback = false ) {
$cmd = "";
$done = false;
$dollarquote = false;
while ( ! feof( $fp ) ) {
+ if ( $lineCallback ) {
+ call_user_func( $lineCallback );
+ }
$line = trim( fgets( $fp, 1024 ) );
$sl = strlen( $line ) - 1;
@@ -1995,7 +2174,10 @@ class Database {
if ( $done ) {
$cmd = str_replace(';;', ";", $cmd);
$cmd = $this->replaceVars( $cmd );
- $res = $this->query( $cmd, 'dbsource', true );
+ $res = $this->query( $cmd, __METHOD__, true );
+ if ( $resultCallback ) {
+ call_user_func( $resultCallback, $this->resultObject( $res ) );
+ }
if ( false === $res ) {
$err = $this->lastError();
@@ -2006,10 +2188,10 @@ class Database {
$done = false;
}
}
- fclose( $fp );
return true;
}
+
/**
* Replace variables in sourced SQL
*/
@@ -2017,7 +2199,7 @@ class Database {
$varnames = array(
'wgDBserver', 'wgDBname', 'wgDBintlname', 'wgDBuser',
'wgDBpassword', 'wgDBsqluser', 'wgDBsqlpassword',
- 'wgDBadminuser', 'wgDBadminpassword',
+ 'wgDBadminuser', 'wgDBadminpassword', 'wgDBTableOptions',
);
// Ordinary variables
@@ -2050,7 +2232,7 @@ class Database {
* Database abstraction object for mySQL
* Inherit all methods and properties of Database::Database()
*
- * @package MediaWiki
+ * @addtogroup Database
* @see Database
*/
class DatabaseMysql extends Database {
@@ -2060,8 +2242,7 @@ class DatabaseMysql extends Database {
/**
* Result wrapper for grabbing data queried by someone else
- *
- * @package MediaWiki
+ * @addtogroup Database
*/
class ResultWrapper {
var $db, $result;
@@ -2107,6 +2288,12 @@ class ResultWrapper {
function seek( $row ) {
$this->db->dataSeek( $this->result, $row );
}
+
+ function rewind() {
+ if ($this->numRows()) {
+ $this->db->dataSeek($this->result, 0);
+ }
+ }
}
diff --git a/includes/DatabaseFunctions.php b/includes/DatabaseFunctions.php
index ca83b9e5..4b31b4f0 100644
--- a/includes/DatabaseFunctions.php
+++ b/includes/DatabaseFunctions.php
@@ -3,7 +3,6 @@
* Legacy database functions, for compatibility with pre-1.3 code
* NOTE: this file is no longer loaded by default.
*
- * @package MediaWiki
*/
/**
@@ -18,7 +17,7 @@ function wfQuery( $sql, $db, $fname = '' ) {
# Someone has tried to call this the old way
throw new FatalError( wfMsgNoDB( 'wrong_wfQuery_params', $db, $sql ) );
}
- $c =& wfGetDB( $db );
+ $c = wfGetDB( $db );
if ( $c !== false ) {
return $c->query( $sql, $fname );
} else {
@@ -34,7 +33,7 @@ function wfQuery( $sql, $db, $fname = '' ) {
* @return Array: first row from the database
*/
function wfSingleQuery( $sql, $dbi, $fname = '' ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
$res = $db->query($sql, $fname );
$row = $db->fetchRow( $res );
$ret = $row[0];
@@ -54,7 +53,7 @@ function wfSingleQuery( $sql, $dbi, $fname = '' ) {
* @return Returns the previous state.
*/
function wfIgnoreSQLErrors( $newstate, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->ignoreErrors( $newstate );
} else {
@@ -73,7 +72,7 @@ function wfIgnoreSQLErrors( $newstate, $dbi = DB_LAST ) {
*/
function wfFreeResult( $res, $dbi = DB_LAST )
{
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
$db->freeResult( $res );
return true;
@@ -87,7 +86,7 @@ function wfFreeResult( $res, $dbi = DB_LAST )
* @return object|false object we requested
*/
function wfFetchObject( $res, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->fetchObject( $res, $dbi = DB_LAST );
} else {
@@ -100,7 +99,7 @@ function wfFetchObject( $res, $dbi = DB_LAST ) {
* @return object|false row we requested
*/
function wfFetchRow( $res, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->fetchRow ( $res, $dbi = DB_LAST );
} else {
@@ -113,7 +112,7 @@ function wfFetchRow( $res, $dbi = DB_LAST ) {
* @return integer|false number of rows
*/
function wfNumRows( $res, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->numRows( $res, $dbi = DB_LAST );
} else {
@@ -126,7 +125,7 @@ function wfNumRows( $res, $dbi = DB_LAST ) {
* @return integer|false number of fields
*/
function wfNumFields( $res, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->numFields( $res );
} else {
@@ -143,7 +142,7 @@ function wfNumFields( $res, $dbi = DB_LAST ) {
*/
function wfFieldName( $res, $n, $dbi = DB_LAST )
{
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->fieldName( $res, $n, $dbi = DB_LAST );
} else {
@@ -156,7 +155,7 @@ function wfFieldName( $res, $n, $dbi = DB_LAST )
* @todo document function
*/
function wfInsertId( $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->insertId();
} else {
@@ -168,7 +167,7 @@ function wfInsertId( $dbi = DB_LAST ) {
* @todo document function
*/
function wfDataSeek( $res, $row, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->dataSeek( $res, $row );
} else {
@@ -180,7 +179,7 @@ function wfDataSeek( $res, $row, $dbi = DB_LAST ) {
* @todo document function
*/
function wfLastErrno( $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->lastErrno();
} else {
@@ -192,7 +191,7 @@ function wfLastErrno( $dbi = DB_LAST ) {
* @todo document function
*/
function wfLastError( $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->lastError();
} else {
@@ -204,7 +203,7 @@ function wfLastError( $dbi = DB_LAST ) {
* @todo document function
*/
function wfAffectedRows( $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->affectedRows();
} else {
@@ -216,7 +215,7 @@ function wfAffectedRows( $dbi = DB_LAST ) {
* @todo document function
*/
function wfLastDBquery( $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->lastQuery();
} else {
@@ -235,7 +234,7 @@ function wfLastDBquery( $dbi = DB_LAST ) {
*/
function wfSetSQL( $table, $var, $value, $cond, $dbi = DB_MASTER )
{
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->set( $table, $var, $value, $cond );
} else {
@@ -254,7 +253,7 @@ function wfSetSQL( $table, $var, $value, $cond, $dbi = DB_MASTER )
*/
function wfGetSQL( $table, $var, $cond='', $dbi = DB_LAST )
{
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->selectField( $table, $var, $cond );
} else {
@@ -271,7 +270,7 @@ function wfGetSQL( $table, $var, $cond='', $dbi = DB_LAST )
* @return Result of Database::fieldExists() or false.
*/
function wfFieldExists( $table, $field, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->fieldExists( $table, $field );
} else {
@@ -288,7 +287,7 @@ function wfFieldExists( $table, $field, $dbi = DB_LAST ) {
* @return Result of Database::indexExists() or false.
*/
function wfIndexExists( $table, $index, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->indexExists( $table, $index );
} else {
@@ -306,7 +305,7 @@ function wfIndexExists( $table, $index, $dbi = DB_LAST ) {
* @return result of Database::insert() or false.
*/
function wfInsertArray( $table, $array, $fname = 'wfInsertArray', $dbi = DB_MASTER ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->insert( $table, $array, $fname );
} else {
@@ -325,7 +324,7 @@ function wfInsertArray( $table, $array, $fname = 'wfInsertArray', $dbi = DB_MAST
* @return result of Database::getArray() or false.
*/
function wfGetArray( $table, $vars, $conds, $fname = 'wfGetArray', $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->getArray( $table, $vars, $conds, $fname );
} else {
@@ -344,7 +343,7 @@ function wfGetArray( $table, $vars, $conds, $fname = 'wfGetArray', $dbi = DB_LAS
* @todo document function
*/
function wfUpdateArray( $table, $values, $conds, $fname = 'wfUpdateArray', $dbi = DB_MASTER ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
$db->update( $table, $values, $conds, $fname );
return true;
@@ -357,7 +356,7 @@ function wfUpdateArray( $table, $values, $conds, $fname = 'wfUpdateArray', $dbi
* @todo document function
*/
function wfTableName( $name, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->tableName( $name );
} else {
@@ -369,7 +368,7 @@ function wfTableName( $name, $dbi = DB_LAST ) {
* @todo document function
*/
function wfStrencode( $s, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->strencode( $s );
} else {
@@ -381,7 +380,7 @@ function wfStrencode( $s, $dbi = DB_LAST ) {
* @todo document function
*/
function wfNextSequenceValue( $seqName, $dbi = DB_MASTER ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->nextSequenceValue( $seqName );
} else {
@@ -393,7 +392,7 @@ function wfNextSequenceValue( $seqName, $dbi = DB_MASTER ) {
* @todo document function
*/
function wfUseIndexClause( $index, $dbi = DB_SLAVE ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->useIndexClause( $index );
} else {
diff --git a/includes/DatabaseOracle.php b/includes/DatabaseOracle.php
index 1a6f62f2..2b720df7 100644
--- a/includes/DatabaseOracle.php
+++ b/includes/DatabaseOracle.php
@@ -1,44 +1,141 @@
<?php
/**
- * Oracle.
- *
- * @package MediaWiki
+ * This is the Oracle database abstraction layer.
+ * @addtogroup Database
*/
+class ORABlob {
+ var $mData;
-class OracleBlob extends DBObject {
- function isLOB() {
- return true;
+ function __construct($data) {
+ $this->mData = $data;
}
- function data() {
+
+ function getData() {
return $this->mData;
}
-};
+}
+
+/**
+ * The oci8 extension is fairly weak and doesn't support oci_num_rows, among
+ * other things. We use a wrapper class to handle that and other
+ * Oracle-specific bits, like converting column names back to lowercase.
+ * @addtogroup Database
+ */
+class ORAResult {
+ private $rows;
+ private $cursor;
+ private $stmt;
+ private $nrows;
+ private $db;
+
+ function __construct(&$db, $stmt) {
+ $this->db =& $db;
+ if (($this->nrows = oci_fetch_all($stmt, $this->rows, 0, -1, OCI_FETCHSTATEMENT_BY_ROW | OCI_NUM)) === false) {
+ $e = oci_error($stmt);
+ $db->reportQueryError($e['message'], $e['code'], '', __FUNCTION__);
+ return;
+ }
+
+ $this->cursor = 0;
+ $this->stmt = $stmt;
+ }
+
+ function free() {
+ oci_free_statement($this->stmt);
+ }
+
+ function seek($row) {
+ $this->cursor = min($row, $this->nrows);
+ }
+
+ function numRows() {
+ return $this->nrows;
+ }
+
+ function numFields() {
+ return oci_num_fields($this->stmt);
+ }
+
+ function fetchObject() {
+ if ($this->cursor >= $this->nrows)
+ return false;
+
+ $row = $this->rows[$this->cursor++];
+ $ret = new stdClass();
+ foreach ($row as $k => $v) {
+ $lc = strtolower(oci_field_name($this->stmt, $k + 1));
+ $ret->$lc = $v;
+ }
+
+ return $ret;
+ }
+
+ function fetchAssoc() {
+ if ($this->cursor >= $this->nrows)
+ return false;
+
+ $row = $this->rows[$this->cursor++];
+ $ret = array();
+ foreach ($row as $k => $v) {
+ $lc = strtolower(oci_field_name($this->stmt, $k + 1));
+ $ret[$lc] = $v;
+ $ret[$k] = $v;
+ }
+ return $ret;
+ }
+}
/**
- *
- * @package MediaWiki
+ * @addtogroup Database
*/
class DatabaseOracle extends Database {
var $mInsertId = NULL;
var $mLastResult = NULL;
- var $mFetchCache = array();
- var $mFetchID = array();
- var $mNcols = array();
- var $mFieldNames = array(), $mFieldTypes = array();
- var $mAffectedRows = array();
- var $mErr;
+ var $numeric_version = NULL;
+ var $lastResult = null;
+ var $cursor = 0;
+ var $mAffectedRows;
function DatabaseOracle($server = false, $user = false, $password = false, $dbName = false,
- $failFunction = false, $flags = 0, $tablePrefix = 'get from global' )
+ $failFunction = false, $flags = 0 )
{
- Database::Database( $server, $user, $password, $dbName, $failFunction, $flags, $tablePrefix );
+
+ global $wgOut;
+ # Can't get a reference if it hasn't been set yet
+ if ( !isset( $wgOut ) ) {
+ $wgOut = NULL;
+ }
+ $this->mOut =& $wgOut;
+ $this->mFailFunction = $failFunction;
+ $this->mFlags = $flags;
+ $this->open( $server, $user, $password, $dbName);
+
+ }
+
+ function cascadingDeletes() {
+ return true;
+ }
+ function cleanupTriggers() {
+ return true;
+ }
+ function strictIPs() {
+ return true;
+ }
+ function realTimestamps() {
+ return true;
+ }
+ function implicitGroupby() {
+ return false;
+ }
+ function searchableIPs() {
+ return true;
}
- /* static */ function newFromParams( $server = false, $user = false, $password = false, $dbName = false,
- $failFunction = false, $flags = 0, $tablePrefix = 'get from global' )
+ static function newFromParams( $server = false, $user = false, $password = false, $dbName = false,
+ $failFunction = false, $flags = 0)
{
- return new DatabaseOracle( $server, $user, $password, $dbName, $failFunction, $flags, $tablePrefix );
+ return new DatabaseOracle( $server, $user, $password, $dbName, $failFunction, $flags );
}
/**
@@ -47,23 +144,33 @@ class DatabaseOracle extends Database {
*/
function open( $server, $user, $password, $dbName ) {
if ( !function_exists( 'oci_connect' ) ) {
- throw new DBConnectionError( $this, "Oracle functions missing, have you compiled PHP with the --with-oci8 option?\n" );
+ throw new DBConnectionError( $this, "Oracle functions missing, have you compiled PHP with the --with-oci8 option?\n (Note: if you recently installed PHP, you may need to restart your webserver and database)\n" );
}
+
+ # Needed for proper UTF-8 functionality
+ putenv("NLS_LANG=AMERICAN_AMERICA.AL32UTF8");
+
$this->close();
$this->mServer = $server;
$this->mUser = $user;
$this->mPassword = $password;
$this->mDBname = $dbName;
- $this->mConn = oci_new_connect($user, $password, $dbName, "AL32UTF8");
- if ( $this->mConn === false ) {
- wfDebug( "DB connection error\n" );
- wfDebug( "Server: $server, Database: $dbName, User: $user, Password: "
- . substr( $password, 0, 3 ) . "...\n" );
- wfDebug( $this->lastError()."\n" );
- } else {
- $this->mOpened = true;
+ if (!strlen($user)) { ## e.g. the class is being loaded
+ return;
+ }
+
+ error_reporting( E_ALL );
+ $this->mConn = oci_connect($user, $password, $dbName);
+
+ if ($this->mConn == false) {
+ wfDebug("DB connection error\n");
+ wfDebug("Server: $server, Database: $dbName, User: $user, Password: " . substr( $password, 0, 3 ) . "...\n");
+ wfDebug($this->lastError()."\n");
+ return false;
}
+
+ $this->mOpened = true;
return $this->mConn;
}
@@ -73,116 +180,67 @@ class DatabaseOracle extends Database {
*/
function close() {
$this->mOpened = false;
- if ($this->mConn) {
- return oci_close($this->mConn);
+ if ( $this->mConn ) {
+ return oci_close( $this->mConn );
} else {
return true;
}
}
- function parseStatement($sql) {
- $this->mErr = $this->mLastResult = false;
- if (($stmt = oci_parse($this->mConn, $sql)) === false) {
- $this->lastError();
- return $this->mLastResult = false;
- }
- $this->mAffectedRows[$stmt] = 0;
- return $this->mLastResult = $stmt;
+ function execFlags() {
+ return $this->mTrxLevel ? OCI_DEFAULT : OCI_COMMIT_ON_SUCCESS;
}
function doQuery($sql) {
- if (($stmt = $this->parseStatement($sql)) === false)
- return false;
- return $this->executeStatement($stmt);
- }
+ wfDebug("SQL: [$sql]\n");
+ if (!mb_check_encoding($sql)) {
+ throw new MWException("SQL encoding is invalid");
+ }
- function executeStatement($stmt) {
- if (!oci_execute($stmt, OCI_DEFAULT)) {
- $this->lastError();
- oci_free_statement($stmt);
- return false;
+ if (($this->mLastResult = $stmt = oci_parse($this->mConn, $sql)) === false) {
+ $e = oci_error($this->mConn);
+ $this->reportQueryError($e['message'], $e['code'], $sql, __FUNCTION__);
}
- $this->mAffectedRows[$stmt] = oci_num_rows($stmt);
- $this->mFetchCache[$stmt] = array();
- $this->mFetchID[$stmt] = 0;
- $this->mNcols[$stmt] = oci_num_fields($stmt);
- if ($this->mNcols[$stmt] == 0)
- return $this->mLastResult;
- for ($i = 1; $i <= $this->mNcols[$stmt]; $i++) {
- $this->mFieldNames[$stmt][$i] = oci_field_name($stmt, $i);
- $this->mFieldTypes[$stmt][$i] = oci_field_type($stmt, $i);
+
+ if (oci_execute($stmt, $this->execFlags()) == false) {
+ $e = oci_error($stmt);
+ $this->reportQueryError($e['message'], $e['code'], $sql, __FUNCTION__);
}
- while (($o = oci_fetch_array($stmt)) !== false) {
- foreach ($o as $key => $value) {
- if (is_object($value)) {
- $o[$key] = $value->load();
- }
- }
- $this->mFetchCache[$stmt][] = $o;
+ if (oci_statement_type($stmt) == "SELECT")
+ return new ORAResult($this, $stmt);
+ else {
+ $this->mAffectedRows = oci_num_rows($stmt);
+ return true;
}
- return $this->mLastResult;
}
- function queryIgnore( $sql, $fname = '' ) {
- return $this->query( $sql, $fname, true );
+ function queryIgnore($sql, $fname = '') {
+ return $this->query($sql, $fname, true);
}
- function freeResult( $res ) {
- if (!oci_free_statement($res)) {
- throw new DBUnexpectedError( $this, "Unable to free Oracle result\n" );
- }
- unset($this->mFetchID[$res]);
- unset($this->mFetchCache[$res]);
- unset($this->mNcols[$res]);
- unset($this->mFieldNames[$res]);
- unset($this->mFieldTypes[$res]);
+ function freeResult($res) {
+ $res->free();
}
- function fetchAssoc($res) {
- if ($this->mFetchID[$res] >= count($this->mFetchCache[$res]))
- return false;
-
- for ($i = 1; $i <= $this->mNcols[$res]; $i++) {
- $name = $this->mFieldNames[$res][$i];
- if (isset($this->mFetchCache[$res][$this->mFetchID[$res]][$name]))
- $value = $this->mFetchCache[$res][$this->mFetchID[$res]][$name];
- else $value = NULL;
- $key = strtolower($name);
- wfdebug("'$key' => '$value'\n");
- $ret[$key] = $value;
- }
- $this->mFetchID[$res]++;
- return $ret;
+ function fetchObject($res) {
+ return $res->fetchObject();
}
function fetchRow($res) {
- $r = $this->fetchAssoc($res);
- if (!$r)
- return false;
- $i = 0;
- $ret = array();
- foreach ($r as $value) {
- wfdebug("ret[$i]=[$value]\n");
- $ret[$i++] = $value;
- }
- return $ret;
+ return $res->fetchAssoc();
}
- function fetchObject($res) {
- $row = $this->fetchAssoc($res);
- if (!$row)
- return false;
- $ret = new stdClass;
- foreach ($row as $key => $value)
- $ret->$key = $value;
- return $ret;
+ function numRows($res) {
+ return $res->numRows();
}
- function numRows($res) {
- return count($this->mFetchCache[$res]);
+ function numFields($res) {
+ return $res->numFields();
+ }
+
+ function fieldName($stmt, $n) {
+ return pg_field_name($stmt, $n);
}
- function numFields( $res ) { return pg_num_fields( $res ); }
- function fieldName( $res, $n ) { return pg_field_name( $res, $n ); }
/**
* This must be called after nextSequenceVal
@@ -192,139 +250,153 @@ class DatabaseOracle extends Database {
}
function dataSeek($res, $row) {
- $this->mFetchID[$res] = $row;
+ $res->seek($row);
}
function lastError() {
- if ($this->mErr === false) {
- if ($this->mLastResult !== false) {
- $what = $this->mLastResult;
- } else if ($this->mConn !== false) {
- $what = $this->mConn;
- } else {
- $what = false;
- }
- $err = ($what !== false) ? oci_error($what) : oci_error();
- if ($err === false) {
- $this->mErr = 'no error';
- } else {
- $this->mErr = $err['message'];
- }
- }
- return str_replace("\n", '<br />', $this->mErr);
+ if ($this->mConn === false)
+ $e = oci_error();
+ else
+ $e = oci_error($this->mConn);
+ return $e['message'];
}
+
function lastErrno() {
- return 0;
+ if ($this->mConn === false)
+ $e = oci_error();
+ else
+ $e = oci_error($this->mConn);
+ return $e['code'];
}
function affectedRows() {
- return $this->mAffectedRows[$this->mLastResult];
+ return $this->mAffectedRows;
}
/**
* Returns information about an index
* If errors are explicitly ignored, returns NULL on failure
*/
- function indexInfo ($table, $index, $fname = 'Database::indexInfo' ) {
- $table = $this->tableName($table, true);
- if ($index == 'PRIMARY')
- $index = "${table}_pk";
- $sql = "SELECT uniqueness FROM all_indexes WHERE table_name='" .
- $table . "' AND index_name='" .
- $this->strencode(strtoupper($index)) . "'";
- $res = $this->query($sql, $fname);
- if (!$res)
- return NULL;
- if (($row = $this->fetchObject($res)) == NULL)
- return false;
- $this->freeResult($res);
- $row->Non_unique = !$row->uniqueness;
- return $row;
-
- // BUG: !!!! This code needs to be synced up with database.php
-
+ function indexInfo( $table, $index, $fname = 'Database::indexExists' ) {
+ return false;
}
- function indexUnique ($table, $index, $fname = 'indexUnique') {
- if (!($i = $this->indexInfo($table, $index, $fname)))
- return $i;
- return $i->uniqueness == 'UNIQUE';
+ function indexUnique ($table, $index, $fname = 'Database::indexUnique' ) {
+ return false;
}
- function fieldInfo( $table, $field ) {
- $o = new stdClass;
- $o->multiple_key = true; /* XXX */
- return $o;
- }
+ function insert( $table, $a, $fname = 'Database::insert', $options = array() ) {
+ if (!is_array($options))
+ $options = array($options);
- function getColumnInformation($table, $field) {
- $table = $this->tableName($table, true);
- $field = strtoupper($field);
+ #if (in_array('IGNORE', $options))
+ # $oldIgnore = $this->ignoreErrors(true);
- $res = $this->doQuery("SELECT * FROM all_tab_columns " .
- "WHERE table_name='".$table."' " .
- "AND column_name='".$field."'");
- if (!$res)
- return false;
- $o = $this->fetchObject($res);
- $this->freeResult($res);
- return $o;
- }
+ # IGNORE is performed using single-row inserts, ignoring errors in each
+ # FIXME: need some way to distiguish between key collision and other types of error
+ //$oldIgnore = $this->ignoreErrors(true);
+ if (!is_array(reset($a))) {
+ $a = array($a);
+ }
+ foreach ($a as $row) {
+ $this->insertOneRow($table, $row, $fname);
+ }
+ //$this->ignoreErrors($oldIgnore);
+ $retVal = true;
- function fieldExists( $table, $field, $fname = 'Database::fieldExists' ) {
- $column = $this->getColumnInformation($table, $field);
- if (!$column)
- return false;
- return true;
+ //if (in_array('IGNORE', $options))
+ // $this->ignoreErrors($oldIgnore);
+
+ return $retVal;
}
- function tableName($name, $forddl = false) {
- # First run any transformations from the parent object
- $name = parent::tableName( $name );
+ function insertOneRow($table, $row, $fname) {
+ // "INSERT INTO tables (a, b, c)"
+ $sql = "INSERT INTO " . $this->tableName($table) . " (" . join(',', array_keys($row)) . ')';
+ $sql .= " VALUES (";
+
+ // for each value, append ":key"
+ $first = true;
+ $returning = '';
+ foreach ($row as $col => $val) {
+ if (is_object($val)) {
+ $what = "EMPTY_BLOB()";
+ assert($returning === '');
+ $returning = " RETURNING $col INTO :bval";
+ $blobcol = $col;
+ } else
+ $what = ":$col";
+
+ if ($first)
+ $sql .= "$what";
+ else
+ $sql.= ", $what";
+ $first = false;
+ }
+ $sql .= ") $returning";
+
+ $stmt = oci_parse($this->mConn, $sql);
+ foreach ($row as $col => $val) {
+ if (!is_object($val)) {
+ if (oci_bind_by_name($stmt, ":$col", $row[$col]) === false)
+ $this->reportQueryError($this->lastErrno(), $this->lastError(), $sql, __METHOD__);
+ }
+ }
+
+ if (($bval = oci_new_descriptor($this->mConn, OCI_D_LOB)) === false) {
+ $e = oci_error($stmt);
+ throw new DBUnexpectedError($this, "Cannot create LOB descriptor: " . $e['message']);
+ }
+
+ if (strlen($returning))
+ oci_bind_by_name($stmt, ":bval", $bval, -1, SQLT_BLOB);
- # Replace backticks into empty
- # Note: "foo" and foo are not the same in Oracle!
- $name = str_replace('`', '', $name);
+ if (oci_execute($stmt, OCI_DEFAULT) === false) {
+ $e = oci_error($stmt);
+ $this->reportQueryError($e['message'], $e['code'], $sql, __METHOD__);
+ }
+ if (strlen($returning)) {
+ $bval->save($row[$blobcol]->getData());
+ $bval->free();
+ }
+ if (!$this->mTrxLevel)
+ oci_commit($this->mConn);
- # Now quote Oracle reserved keywords
+ oci_free_statement($stmt);
+ }
+
+ function tableName( $name ) {
+ # Replace reserved words with better ones
switch( $name ) {
case 'user':
- case 'group':
- case 'validate':
- if ($forddl)
- return $name;
- else
- return '"' . $name . '"';
-
+ return 'mwuser';
+ case 'text':
+ return 'pagecontent';
default:
- return strtoupper($name);
+ return $name;
}
}
- function strencode( $s ) {
- return str_replace("'", "''", $s);
- }
-
/**
* Return the next in a sequence, save the value for retrieval via insertId()
*/
- function nextSequenceValue( $seqName ) {
- $r = $this->doQuery("SELECT $seqName.nextval AS val FROM dual");
- $o = $this->fetchObject($r);
- $this->freeResult($r);
- return $this->mInsertId = (int)$o->val;
+ function nextSequenceValue($seqName) {
+ $res = $this->query("SELECT $seqName.nextval FROM dual");
+ $row = $this->fetchRow($res);
+ $this->mInsertId = $row[0];
+ $this->freeResult($res);
+ return $this->mInsertId;
}
/**
- * USE INDEX clause
- * PostgreSQL doesn't have them and returns ""
+ * Oracle does not have a "USE INDEX" clause, so return an empty string
*/
- function useIndexClause( $index ) {
+ function useIndexClause($index) {
return '';
}
# REPLACE query wrapper
- # PostgreSQL simulates this with a DELETE followed by INSERT
+ # Oracle simulates this with a DELETE followed by INSERT
# $row is the row to insert, an associative array
# $uniqueIndexes is an array of indexes. Each element may be either a
# field name or an array of field names
@@ -333,15 +405,15 @@ class DatabaseOracle extends Database {
# However if you do this, you run the risk of encountering errors which wouldn't have
# occurred in MySQL
function replace( $table, $uniqueIndexes, $rows, $fname = 'Database::replace' ) {
- $table = $this->tableName( $table );
+ $table = $this->tableName($table);
if (count($rows)==0) {
return;
}
# Single row case
- if ( !is_array( reset( $rows ) ) ) {
- $rows = array( $rows );
+ if (!is_array(reset($rows))) {
+ $rows = array($rows);
}
foreach( $rows as $row ) {
@@ -377,14 +449,14 @@ class DatabaseOracle extends Database {
# Now insert the row
$sql = "INSERT INTO $table (" . $this->makeList( array_keys( $row ), LIST_NAMES ) .') VALUES (' .
$this->makeList( $row, LIST_COMMA ) . ')';
- $this->query( $sql, $fname );
+ $this->query($sql, $fname);
}
}
# DELETE where the condition is a join
function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = "Database::deleteJoin" ) {
if ( !$conds ) {
- throw new DBUnexpectedError( $this, 'Database::deleteJoin() called with empty $conds' );
+ throw new DBUnexpectedError($this, 'Database::deleteJoin() called with empty $conds' );
}
$delTable = $this->tableName( $delTable );
@@ -421,17 +493,14 @@ class DatabaseOracle extends Database {
}
function limitResult($sql, $limit, $offset) {
- $ret = "SELECT * FROM ($sql) WHERE ROWNUM < " . ((int)$limit + (int)($offset+1));
- if (is_numeric($offset))
- $ret .= " AND ROWNUM >= " . (int)$offset;
- return $ret;
- }
- function limitResultForUpdate($sql, $limit) {
- return $sql;
+ if ($offset === false)
+ $offset = 0;
+ return "SELECT * FROM ($sql) WHERE rownum >= (1 + $offset) AND rownum < 1 + $limit + $offset";
}
+
/**
* Returns an SQL expression for a simple conditional.
- * Uses CASE on PostgreSQL.
+ * Uses CASE on Oracle
*
* @param string $cond SQL expression which will result in a boolean value
* @param string $trueVal SQL expression to return if true
@@ -442,15 +511,12 @@ class DatabaseOracle extends Database {
return " (CASE WHEN $cond THEN $trueVal ELSE $falseVal END) ";
}
- # FIXME: actually detecting deadlocks might be nice
function wasDeadlock() {
- return false;
+ return $this->lastErrno() == 'OCI-00060';
}
- # Return DB-style timestamp used for MySQL schema
function timestamp($ts = 0) {
- return $this->strencode(wfTimestamp(TS_ORACLE, $ts));
-# return "TO_TIMESTAMP('" . $this->strencode(wfTimestamp(TS_DB, $ts)) . "', 'RRRR-MM-DD HH24:MI:SS')";
+ return wfTimestamp(TS_ORACLE, $ts);
}
/**
@@ -460,13 +526,25 @@ class DatabaseOracle extends Database {
return $valuedata;
}
+ function reportQueryError($error, $errno, $sql, $fname, $tempIgnore = false) {
+ # Ignore errors during error handling to avoid infinite
+ # recursion
+ $ignore = $this->ignoreErrors(true);
+ ++$this->mErrorCount;
- function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
- $message = "A database error has occurred\n" .
- "Query: $sql\n" .
- "Function: $fname\n" .
- "Error: $errno $error\n";
- throw new DBUnexpectedError($this, $message);
+ if ($ignore || $tempIgnore) {
+echo "error ignored! query = [$sql]\n";
+ wfDebug("SQL ERROR (ignored): $error\n");
+ $this->ignoreErrors( $ignore );
+ }
+ else {
+echo "error!\n";
+ $message = "A database error has occurred\n" .
+ "Query: $sql\n" .
+ "Function: $fname\n" .
+ "Error: $errno $error\n";
+ throw new DBUnexpectedError($this, $message);
+ }
}
/**
@@ -483,209 +561,125 @@ class DatabaseOracle extends Database {
return oci_server_version($this->mConn);
}
- function setSchema($schema=false) {
- $schemas=$this->mSchemas;
- if ($schema) { array_unshift($schemas,$schema); }
- $searchpath=$this->makeList($schemas,LIST_NAMES);
- $this->query("SET search_path = $searchpath");
+ /**
+ * Query whether a given table exists (in the given schema, or the default mw one if not given)
+ */
+ function tableExists($table) {
+ $etable= $this->addQuotes($table);
+ $SQL = "SELECT 1 FROM user_tables WHERE table_name='$etable'";
+ $res = $this->query($SQL);
+ $count = $res ? oci_num_rows($res) : 0;
+ if ($res)
+ $this->freeResult($res);
+ return $count;
}
- function begin() {
+ /**
+ * Query whether a given column exists in the mediawiki schema
+ */
+ function fieldExists( $table, $field ) {
+ return true; // XXX
}
- function immediateCommit( $fname = 'Database::immediateCommit' ) {
- oci_commit($this->mConn);
- $this->mTrxLevel = 0;
+ function fieldInfo( $table, $field ) {
+ return false; // XXX
}
- function rollback( $fname = 'Database::rollback' ) {
- oci_rollback($this->mConn);
- $this->mTrxLevel = 0;
+
+ function begin( $fname = '' ) {
+ $this->mTrxLevel = 1;
}
- function getLag() {
- return false;
+ function immediateCommit( $fname = '' ) {
+ return true;
}
- function getStatus($which=null) {
- $result = array('Threads_running' => 0, 'Threads_connected' => 0);
- return $result;
+ function commit( $fname = '' ) {
+ oci_commit($this->mConn);
+ $this->mTrxLevel = 0;
}
- /**
- * Returns an optional USE INDEX clause to go after the table, and a
- * string to go at the end of the query
- *
- * @access private
- *
- * @param array $options an associative array of options to be turned into
- * an SQL query, valid keys are listed in the function.
- * @return array
- */
- function makeSelectOptions($options) {
- $tailOpts = '';
-
- if (isset( $options['ORDER BY'])) {
- $tailOpts .= " ORDER BY {$options['ORDER BY']}";
- }
+ /* Not even sure why this is used in the main codebase... */
+ function limitResultForUpdate($sql, $num) {
+ return $sql;
+ }
- return array('', $tailOpts);
+ function strencode($s) {
+ return str_replace("'", "''", $s);
}
- function maxListLen() {
- return 1000;
+ function encodeBlob($b) {
+ return new ORABlob($b);
+ }
+ function decodeBlob($b) {
+ return $b; //return $b->load();
}
- /**
- * Query whether a given table exists
- */
- function tableExists( $table ) {
- $table = $this->tableName($table, true);
- $res = $this->query( "SELECT COUNT(*) as NUM FROM user_tables WHERE table_name='"
- . $table . "'" );
- if (!$res)
- return false;
- $row = $this->fetchObject($res);
- $this->freeResult($res);
- return $row->num >= 1;
+ function addQuotes( $s ) {
+ global $wgLang;
+ $s = $wgLang->checkTitleEncoding($s);
+ return "'" . $this->strencode($s) . "'";
}
- /**
- * UPDATE wrapper, takes a condition array and a SET array
- */
- function update( $table, $values, $conds, $fname = 'Database::update' ) {
- $table = $this->tableName( $table );
+ function quote_ident( $s ) {
+ return $s;
+ }
- $sql = "UPDATE $table SET ";
- $first = true;
- foreach ($values as $field => $v) {
- if ($first)
- $first = false;
- else
- $sql .= ", ";
- $sql .= "$field = :n$field ";
- }
- if ( $conds != '*' ) {
- $sql .= " WHERE " . $this->makeList( $conds, LIST_AND );
- }
- $stmt = $this->parseStatement($sql);
- if ($stmt === false) {
- $this->reportQueryError( $this->lastError(), $this->lastErrno(), $stmt );
- return false;
- }
- if ($this->debug())
- wfDebug("SQL: $sql\n");
- $s = '';
- foreach ($values as $field => $v) {
- oci_bind_by_name($stmt, ":n$field", $values[$field]);
- if ($this->debug())
- $s .= " [$field] = [$v]\n";
- }
- if ($this->debug())
- wfdebug(" PH: $s\n");
- $ret = $this->executeStatement($stmt);
- return $ret;
+ /* For now, does nothing */
+ function selectDB( $db ) {
+ return true;
}
/**
- * INSERT wrapper, inserts an array into a table
+ * Returns an optional USE INDEX clause to go after the table, and a
+ * string to go at the end of the query
*
- * $a may be a single associative array, or an array of these with numeric keys, for
- * multi-row insert.
+ * @private
*
- * Usually aborts on failure
- * If errors are explicitly ignored, returns success
+ * @param array $options an associative array of options to be turned into
+ * an SQL query, valid keys are listed in the function.
+ * @return array
*/
- function insert( $table, $a, $fname = 'Database::insert', $options = array() ) {
- # No rows to insert, easy just return now
- if ( !count( $a ) ) {
- return true;
- }
-
- $table = $this->tableName( $table );
- if (!is_array($options))
- $options = array($options);
-
- $oldIgnore = false;
- if (in_array('IGNORE', $options))
- $oldIgnore = $this->ignoreErrors( true );
-
- if ( isset( $a[0] ) && is_array( $a[0] ) ) {
- $multi = true;
- $keys = array_keys( $a[0] );
- } else {
- $multi = false;
- $keys = array_keys( $a );
+ function makeSelectOptions( $options ) {
+ $preLimitTail = $postLimitTail = '';
+ $startOpts = '';
+
+ $noKeyOptions = array();
+ foreach ( $options as $key => $option ) {
+ if ( is_numeric( $key ) ) {
+ $noKeyOptions[$option] = true;
+ }
}
- $sql = "INSERT INTO $table (" . implode( ',', $keys ) . ') VALUES (';
- $return = '';
- $first = true;
- foreach ($a as $key => $value) {
- if ($first)
- $first = false;
- else
- $sql .= ", ";
- if (is_object($value) && $value->isLOB()) {
- $sql .= "EMPTY_BLOB()";
- $return = "RETURNING $key INTO :bobj";
- } else
- $sql .= ":$key";
+ if ( isset( $options['GROUP BY'] ) ) $preLimitTail .= " GROUP BY {$options['GROUP BY']}";
+ if ( isset( $options['ORDER BY'] ) ) $preLimitTail .= " ORDER BY {$options['ORDER BY']}";
+
+ if (isset($options['LIMIT'])) {
+ // $tailOpts .= $this->limitResult('', $options['LIMIT'],
+ // isset($options['OFFSET']) ? $options['OFFSET']
+ // : false);
}
- $sql .= ") $return";
- if ($this->debug()) {
- wfDebug("SQL: $sql\n");
- }
+ #if ( isset( $noKeyOptions['FOR UPDATE'] ) ) $tailOpts .= ' FOR UPDATE';
+ #if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) $tailOpts .= ' LOCK IN SHARE MODE';
+ if ( isset( $noKeyOptions['DISTINCT'] ) && isset( $noKeyOptions['DISTINCTROW'] ) ) $startOpts .= 'DISTINCT';
- if (($stmt = $this->parseStatement($sql)) === false) {
- $this->reportQueryError($this->lastError(), $this->lastErrno(), $sql, $fname);
- $this->ignoreErrors($oldIgnore);
- return false;
+ if ( isset( $options['USE INDEX'] ) && ! is_array( $options['USE INDEX'] ) ) {
+ $useIndex = $this->useIndexClause( $options['USE INDEX'] );
+ } else {
+ $useIndex = '';
}
+
+ return array( $startOpts, $useIndex, $preLimitTail, $postLimitTail );
+ }
- /*
- * If we're inserting multiple rows, parse the statement once and
- * execute it for each set of values. Otherwise, convert it into an
- * array and pretend.
- */
- if (!$multi)
- $a = array($a);
-
- foreach ($a as $key => $row) {
- $blob = false;
- $bdata = false;
- $s = '';
- foreach ($row as $k => $value) {
- if (is_object($value) && $value->isLOB()) {
- $blob = oci_new_descriptor($this->mConn, OCI_D_LOB);
- $bdata = $value->data();
- oci_bind_by_name($stmt, ":bobj", $blob, -1, OCI_B_BLOB);
- } else
- oci_bind_by_name($stmt, ":$k", $a[$key][$k], -1);
- if ($this->debug())
- $s .= " [$k] = {$row[$k]}";
- }
- if ($this->debug())
- wfDebug(" PH: $s\n");
- if (($s = $this->executeStatement($stmt)) === false) {
- $this->reportQueryError($this->lastError(), $this->lastErrno(), $sql, $fname);
- $this->ignoreErrors($oldIgnore);
- return false;
- }
-
- if ($blob) {
- $blob->save($bdata);
- }
- }
- $this->ignoreErrors($oldIgnore);
- return $this->mLastResult = $s;
+ public function setTimeout( $timeout ) {
+ // @todo fixme no-op
}
function ping() {
+ wfDebug( "Function ping() not written for DatabasePostgres.php yet");
return true;
}
- function encodeBlob($b) {
- return new OracleBlob($b);
- }
-}
+
+} // end DatabaseOracle class
?>
diff --git a/includes/DatabasePostgres.php b/includes/DatabasePostgres.php
index 803c0e26..7158e2d1 100644
--- a/includes/DatabasePostgres.php
+++ b/includes/DatabasePostgres.php
@@ -7,12 +7,69 @@
* than MySQL ones, some of them should be moved to parent
* Database class.
*
- * @package MediaWiki
+ * @addtogroup Database
*/
+class PostgresField {
+ private $name, $tablename, $type, $nullable, $max_length;
+
+ static function fromText($db, $table, $field) {
+ global $wgDBmwschema;
+
+ $q = <<<END
+SELECT typname, attnotnull, attlen
+FROM pg_class, pg_namespace, pg_attribute, pg_type
+WHERE relnamespace=pg_namespace.oid
+AND relkind='r'
+AND attrelid=pg_class.oid
+AND atttypid=pg_type.oid
+AND nspname=%s
+AND relname=%s
+AND attname=%s;
+END;
+ $res = $db->query(sprintf($q,
+ $db->addQuotes($wgDBmwschema),
+ $db->addQuotes($table),
+ $db->addQuotes($field)));
+ $row = $db->fetchObject($res);
+ if (!$row)
+ return null;
+ $n = new PostgresField;
+ $n->type = $row->typname;
+ $n->nullable = ($row->attnotnull == 'f');
+ $n->name = $field;
+ $n->tablename = $table;
+ $n->max_length = $row->attlen;
+ return $n;
+ }
+
+ function name() {
+ return $this->name;
+ }
+
+ function tableName() {
+ return $this->tablename;
+ }
+
+ function type() {
+ return $this->type;
+ }
+
+ function nullable() {
+ return $this->nullable;
+ }
+ function maxLength() {
+ return $this->max_length;
+ }
+}
+
+/**
+ * @addtogroup Database
+ */
class DatabasePostgres extends Database {
var $mInsertId = NULL;
var $mLastResult = NULL;
+ var $numeric_version = NULL;
function DatabasePostgres($server = false, $user = false, $password = false, $dbName = false,
$failFunction = false, $flags = 0 )
@@ -25,24 +82,31 @@ class DatabasePostgres extends Database {
}
$this->mOut =& $wgOut;
$this->mFailFunction = $failFunction;
- $this->mCascadingDeletes = true;
- $this->mCleanupTriggers = true;
- $this->mStrictIPs = true;
$this->mFlags = $flags;
$this->open( $server, $user, $password, $dbName);
}
+ function cascadingDeletes() {
+ return true;
+ }
+ function cleanupTriggers() {
+ return true;
+ }
+ function strictIPs() {
+ return true;
+ }
function realTimestamps() {
return true;
}
-
function implicitGroupby() {
return false;
}
+ function searchableIPs() {
+ return true;
+ }
- static function newFromParams( $server = false, $user = false, $password = false, $dbName = false,
- $failFunction = false, $flags = 0)
+ static function newFromParams( $server, $user, $password, $dbName, $failFunction = false, $flags = 0)
{
return new DatabasePostgres( $server, $user, $password, $dbName, $failFunction, $flags );
}
@@ -57,9 +121,12 @@ class DatabasePostgres extends Database {
throw new DBConnectionError( $this, "Postgres functions missing, have you compiled PHP with the --with-pgsql option?\n (Note: if you recently installed PHP, you may need to restart your webserver and database)\n" );
}
-
global $wgDBport;
+ if (!strlen($user)) { ## e.g. the class is being loaded
+ return;
+ }
+
$this->close();
$this->mServer = $server;
$port = $wgDBport;
@@ -75,9 +142,6 @@ class DatabasePostgres extends Database {
$hstring .= "port=$port ";
}
- if (!strlen($user)) { ## e.g. the class is being loaded
- return;
- }
error_reporting( E_ALL );
@$this->mConn = pg_connect("$hstring dbname=$dbName user=$user password=$password");
@@ -94,21 +158,15 @@ class DatabasePostgres extends Database {
if (defined('MEDIAWIKI_INSTALL')) {
global $wgDBname, $wgDBuser, $wgDBpassword, $wgDBsuperuser, $wgDBmwschema,
$wgDBts2schema;
- print "OK</li>\n";
print "<li>Checking the version of Postgres...";
- $version = pg_fetch_result($this->doQuery("SELECT version()"),0,0);
- $thisver = array();
- if (!preg_match('/PostgreSQL (\d+\.\d+)(\S+)/', $version, $thisver)) {
- print "<b>FAILED</b> (could not determine the version)</li>\n";
- dieout("</ul>");
- }
+ $version = $this->getServerVersion();
$PGMINVER = "8.1";
- if ($thisver[1] < $PGMINVER) {
- print "<b>FAILED</b>. Required version is $PGMINVER. You have $thisver[1]$thisver[2]</li>\n";
+ if ($this->numeric_version < $PGMINVER) {
+ print "<b>FAILED</b>. Required version is $PGMINVER. You have $this->numeric_version ($version)</li>\n";
dieout("</ul>");
}
- print "version $thisver[1]$thisver[2] is OK.</li>\n";
+ print "version $this->numeric_version is OK.</li>\n";
$safeuser = $this->quote_ident($wgDBuser);
## Are we connecting as a superuser for the first time?
@@ -232,7 +290,8 @@ class DatabasePostgres extends Database {
$wgDBsuperuser = '';
return true; ## Reconnect as regular user
- }
+
+ } ## end superuser
if (!defined('POSTGRES_SEARCHPATH')) {
@@ -249,13 +308,24 @@ class DatabasePostgres extends Database {
## Does this user have the rights to the tsearch2 tables?
$ctype = pg_fetch_result($this->doQuery("SHOW lc_ctype"),0,0);
print "<li>Checking tsearch2 permissions...";
+ ## Let's check all four, just to be safe
+ error_reporting( 0 );
+ $ts2tables = array('cfg','cfgmap','dict','parser');
+ foreach ( $ts2tables AS $tname ) {
+ $SQL = "SELECT count(*) FROM $wgDBts2schema.pg_ts_$tname";
+ $res = $this->doQuery($SQL);
+ if (!$res) {
+ print "<b>FAILED</b> to access pg_ts_$tname. Make sure that the user ".
+ "\"$wgDBuser\" has SELECT access to all four tsearch2 tables</li>\n";
+ dieout("</ul>");
+ }
+ }
$SQL = "SELECT ts_name FROM $wgDBts2schema.pg_ts_cfg WHERE locale = '$ctype'";
$SQL .= " ORDER BY CASE WHEN ts_name <> 'default' THEN 1 ELSE 0 END";
- error_reporting( 0 );
$res = $this->doQuery($SQL);
error_reporting( E_ALL );
if (!$res) {
- print "<b>FAILED</b>. Make sure that the user \"$wgDBuser\" has SELECT access to the tsearch2 tables</li>\n";
+ print "<b>FAILED</b>. Could not determine the tsearch2 locale information</li>\n";
dieout("</ul>");
}
print "OK</li>";
@@ -282,7 +352,7 @@ class DatabasePostgres extends Database {
$res = $this->doQuery($SQL);
if (!$res) {
print "<b>FAILED</b>. ";
- print "Please make sure that the locale in pg_ts_cfg for \"default\" is set to \"ctype\"</li>\n";
+ print "Please make sure that the locale in pg_ts_cfg for \"default\" is set to \"$ctype\"</li>\n";
dieout("</ul>");
}
print "OK</li>";
@@ -325,9 +395,13 @@ class DatabasePostgres extends Database {
$result = $this->schemaExists($wgDBmwschema);
if (!$result) {
print "<li>Creating schema <b>$wgDBmwschema</b> ...";
+ error_reporting( 0 );
$result = $this->doQuery("CREATE SCHEMA $wgDBmwschema");
+ error_reporting( E_ALL );
if (!$result) {
- print "<b>FAILED</b>.</li>\n";
+ print "<b>FAILED</b>. The user \"$wgDBuser\" must be able to access the schema. ".
+ "You can try making them the owner of the database, or try creating the schema with a ".
+ "different user, and then grant access to the \"$wgDBuser\" user.</li>\n";
dieout("</ul>");
}
print "OK</li>\n";
@@ -339,6 +413,39 @@ class DatabasePostgres extends Database {
print "<li>Schema \"$wgDBmwschema\" exists and is owned by \"$user\". Excellent.</li>\n";
}
+ ## Always return GMT time to accomodate the existing integer-based timestamp assumption
+ print "<li>Setting the timezone to GMT for user \"$user\" ...";
+ $SQL = "ALTER USER $safeuser SET timezone = 'GMT'";
+ $result = pg_query($this->mConn, $SQL);
+ if (!$result) {
+ print "<b>FAILED</b>.</li>\n";
+ dieout("</ul>");
+ }
+ print "OK</li>\n";
+ ## Set for the rest of this session
+ $SQL = "SET timezone = 'GMT'";
+ $result = pg_query($this->mConn, $SQL);
+ if (!$result) {
+ print "<li>Failed to set timezone</li>\n";
+ dieout("</ul>");
+ }
+
+ print "<li>Setting the datestyle to ISO, YMD for user \"$user\" ...";
+ $SQL = "ALTER USER $safeuser SET datestyle = 'ISO, YMD'";
+ $result = pg_query($this->mConn, $SQL);
+ if (!$result) {
+ print "<b>FAILED</b>.</li>\n";
+ dieout("</ul>");
+ }
+ print "OK</li>\n";
+ ## Set for the rest of this session
+ $SQL = "SET datestyle = 'ISO, YMD'";
+ $result = pg_query($this->mConn, $SQL);
+ if (!$result) {
+ print "<li>Failed to set datestyle</li>\n";
+ dieout("</ul>");
+ }
+
## Fix up the search paths if needed
print "<li>Setting the search path for user \"$user\" ...";
$path = $this->quote_ident($wgDBmwschema);
@@ -455,6 +562,30 @@ class DatabasePostgres extends Database {
}
/**
+ * Estimate rows in dataset
+ * Returns estimated count, based on EXPLAIN output
+ * This is not necessarily an accurate estimate, so use sparingly
+ * Returns -1 if count cannot be found
+ * Takes same arguments as Database::select()
+ */
+
+ function estimateRowCount( $table, $vars='*', $conds='', $fname = 'Database::estimateRowCount', $options = array() ) {
+ $options['EXPLAIN'] = true;
+ $res = $this->select( $table, $vars, $conds, $fname, $options );
+ $rows = -1;
+ if ( $res ) {
+ $row = $this->fetchRow( $res );
+ $count = array();
+ if( preg_match( '/rows=(\d+)/', $row[0], $count ) ) {
+ $rows = $count[1];
+ }
+ $this->freeResult($res);
+ }
+ return $rows;
+ }
+
+
+ /**
* Returns information about an index
* If errors are explicitly ignored, returns NULL on failure
*/
@@ -645,7 +776,7 @@ class DatabasePostgres extends Database {
return '';
}
- function limitResult($sql, $limit,$offset) {
+ function limitResult($sql, $limit,$offset=false) {
return "$sql LIMIT $limit ".(is_numeric($offset)?" OFFSET {$offset} ":"");
}
@@ -707,26 +838,31 @@ class DatabasePostgres extends Database {
* @return string Version information from the database
*/
function getServerVersion() {
- $res = $this->query( "SELECT version()" );
- $row = $this->fetchRow( $res );
- $version = $row[0];
- $this->freeResult( $res );
+ $version = pg_fetch_result($this->doQuery("SELECT version()"),0,0);
+ $thisver = array();
+ if (!preg_match('/PostgreSQL (\d+\.\d+)(\S+)/', $version, $thisver)) {
+ die("Could not determine the numeric version from $version!");
+ }
+ $this->numeric_version = $thisver[1];
return $version;
}
/**
- * Query whether a given table exists (in the given schema, or the default mw one if not given)
+ * Query whether a given relation exists (in the given schema, or the
+ * default mw one if not given)
*/
- function tableExists( $table, $schema = false ) {
+ function relationExists( $table, $types, $schema = false ) {
global $wgDBmwschema;
+ if (!is_array($types))
+ $types = array($types);
if (! $schema )
$schema = $wgDBmwschema;
- $etable = preg_replace("/'/", "''", $table);
- $eschema = preg_replace("/'/", "''", $schema);
+ $etable = $this->addQuotes($table);
+ $eschema = $this->addQuotes($schema);
$SQL = "SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "
- . "WHERE c.relnamespace = n.oid AND c.relname = '$etable' AND n.nspname = '$eschema' "
- . "AND c.relkind IN ('r','v')";
+ . "WHERE c.relnamespace = n.oid AND c.relname = $etable AND n.nspname = $eschema "
+ . "AND c.relkind IN ('" . implode("','", $types) . "')";
$res = $this->query( $SQL );
$count = $res ? pg_num_rows($res) : 0;
if ($res)
@@ -734,6 +870,61 @@ class DatabasePostgres extends Database {
return $count;
}
+ /*
+ * For backward compatibility, this function checks both tables and
+ * views.
+ */
+ function tableExists ($table, $schema = false) {
+ return $this->relationExists($table, array('r', 'v'), $schema);
+ }
+
+ function sequenceExists ($sequence, $schema = false) {
+ return $this->relationExists($sequence, 'S', $schema);
+ }
+
+ function triggerExists($table, $trigger) {
+ global $wgDBmwschema;
+
+ $q = <<<END
+ SELECT 1 FROM pg_class, pg_namespace, pg_trigger
+ WHERE relnamespace=pg_namespace.oid AND relkind='r'
+ AND tgrelid=pg_class.oid
+ AND nspname=%s AND relname=%s AND tgname=%s
+END;
+ $res = $this->query(sprintf($q,
+ $this->addQuotes($wgDBmwschema),
+ $this->addQuotes($table),
+ $this->addQuotes($trigger)));
+ if (!$res)
+ return NULL;
+ $rows = pg_num_rows($res);
+ $this->freeResult($res);
+ return $rows;
+ }
+
+ function ruleExists($table, $rule) {
+ global $wgDBmwschema;
+ $exists = $this->selectField("pg_rules", "rulename",
+ array( "rulename" => $rule,
+ "tablename" => $table,
+ "schemaname" => $wgDBmwschema));
+ return $exists === $rule;
+ }
+
+ function constraintExists($table, $constraint) {
+ global $wgDBmwschema;
+ $SQL = sprintf("SELECT 1 FROM information_schema.table_constraints ".
+ "WHERE constraint_schema = %s AND table_name = %s AND constraint_name = %s",
+ $this->addQuotes($wgDBmwschema),
+ $this->addQuotes($table),
+ $this->addQuotes($constraint));
+ $res = $this->query($SQL);
+ if (!$res)
+ return NULL;
+ $rows = pg_num_rows($res);
+ $this->freeResult($res);
+ return $rows;
+ }
/**
* Query whether a given schema exists. Returns the name of the owner
@@ -752,7 +943,7 @@ class DatabasePostgres extends Database {
/**
* Query whether a given column exists in the mediawiki schema
*/
- function fieldExists( $table, $field ) {
+ function fieldExists( $table, $field, $fname = 'DatabasePostgres::fieldExists' ) {
global $wgDBmwschema;
$etable = preg_replace("/'/", "''", $table);
$eschema = preg_replace("/'/", "''", $wgDBmwschema);
@@ -760,7 +951,7 @@ class DatabasePostgres extends Database {
$SQL = "SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n, pg_catalog.pg_attribute a "
. "WHERE c.relnamespace = n.oid AND c.relname = '$etable' AND n.nspname = '$eschema' "
. "AND a.attrelid = c.oid AND a.attname = '$ecol'";
- $res = $this->query( $SQL );
+ $res = $this->query( $SQL, $fname );
$count = $res ? pg_num_rows($res) : 0;
if ($res)
$this->freeResult( $res );
@@ -768,12 +959,10 @@ class DatabasePostgres extends Database {
}
function fieldInfo( $table, $field ) {
- $res = $this->query( "SELECT $field FROM $table LIMIT 1" );
- $type = pg_field_type( $res, 0 );
- return $type;
+ return PostgresField::fromText($this, $table, $field);
}
- function begin( $fname = 'DatabasePostgrs::begin' ) {
+ function begin( $fname = 'DatabasePostgres::begin' ) {
$this->query( 'BEGIN', $fname );
$this->mTrxLevel = 1;
}
@@ -791,10 +980,36 @@ class DatabasePostgres extends Database {
}
function setup_database() {
- global $wgVersion, $wgDBmwschema, $wgDBts2schema, $wgDBport;
+ global $wgVersion, $wgDBmwschema, $wgDBts2schema, $wgDBport, $wgDBuser;
+
+ ## Make sure that we can write to the correct schema
+ ## If not, Postgres will happily and silently go to the next search_path item
+ $ctest = "mw_test_table";
+ if ($this->tableExists($ctest, $wgDBmwschema)) {
+ $this->doQuery("DROP TABLE $wgDBmwschema.$ctest");
+ }
+ $SQL = "CREATE TABLE $wgDBmwschema.$ctest(a int)";
+ error_reporting( 0 );
+ $res = $this->doQuery($SQL);
+ error_reporting( E_ALL );
+ if (!$res) {
+ print "<b>FAILED</b>. Make sure that the user \"$wgDBuser\" can write to the schema \"$wgDBmwschema\"</li>\n";
+ dieout("</ul>");
+ }
+ $this->doQuery("DROP TABLE $wgDBmwschema.mw_test_table");
dbsource( "../maintenance/postgres/tables.sql", $this);
+ ## Version-specific stuff
+ if ($this->numeric_version == 8.1) {
+ $this->doQuery("CREATE INDEX ts2_page_text ON pagecontent USING gist(textvector)");
+ $this->doQuery("CREATE INDEX ts2_page_title ON page USING gist(titlevector)");
+ }
+ else {
+ $this->doQuery("CREATE INDEX ts2_page_text ON pagecontent USING gin(textvector)");
+ $this->doQuery("CREATE INDEX ts2_page_title ON page USING gin(titlevector)");
+ }
+
## Update version information
$mwv = $this->addQuotes($wgVersion);
$pgv = $this->addQuotes($this->getServerVersion());
@@ -827,6 +1042,8 @@ class DatabasePostgres extends Database {
$this->query("$SQL $matches[1],$matches[2])");
}
print " (table interwiki successfully populated)...\n";
+
+ $this->doQuery("COMMIT");
}
function encodeBlob($b) {
@@ -870,7 +1087,7 @@ class DatabasePostgres extends Database {
* @return array
*/
function makeSelectOptions( $options ) {
- $tailOpts = '';
+ $preLimitTail = $postLimitTail = '';
$startOpts = '';
$noKeyOptions = array();
@@ -880,16 +1097,17 @@ class DatabasePostgres extends Database {
}
}
- if ( isset( $options['GROUP BY'] ) ) $tailOpts .= " GROUP BY {$options['GROUP BY']}";
- if ( isset( $options['ORDER BY'] ) ) $tailOpts .= " ORDER BY {$options['ORDER BY']}";
+ if ( isset( $options['GROUP BY'] ) ) $preLimitTail .= " GROUP BY " . $options['GROUP BY'];
+ if ( isset( $options['ORDER BY'] ) ) $preLimitTail .= " ORDER BY " . $options['ORDER BY'];
- if (isset($options['LIMIT'])) {
- $tailOpts .= $this->limitResult('', $options['LIMIT'],
- isset($options['OFFSET']) ? $options['OFFSET'] : false);
- }
-
- if ( isset( $noKeyOptions['FOR UPDATE'] ) ) $tailOpts .= ' FOR UPDATE';
- if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) $tailOpts .= ' LOCK IN SHARE MODE';
+ //if (isset($options['LIMIT'])) {
+ // $tailOpts .= $this->limitResult('', $options['LIMIT'],
+ // isset($options['OFFSET']) ? $options['OFFSET']
+ // : false);
+ //}
+
+ if ( isset( $noKeyOptions['FOR UPDATE'] ) ) $postLimitTail .= ' FOR UPDATE';
+ if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) $postLimitTail .= ' LOCK IN SHARE MODE';
if ( isset( $noKeyOptions['DISTINCT'] ) && isset( $noKeyOptions['DISTINCTROW'] ) ) $startOpts .= 'DISTINCT';
if ( isset( $options['USE INDEX'] ) && ! is_array( $options['USE INDEX'] ) ) {
@@ -898,7 +1116,11 @@ class DatabasePostgres extends Database {
$useIndex = '';
}
- return array( $startOpts, $useIndex, $tailOpts );
+ return array( $startOpts, $useIndex, $preLimitTail, $postLimitTail );
+ }
+
+ public function setTimeout( $timeout ) {
+ // @todo fixme no-op
}
function ping() {
diff --git a/includes/DateFormatter.php b/includes/DateFormatter.php
index c795618a..88a64453 100644
--- a/includes/DateFormatter.php
+++ b/includes/DateFormatter.php
@@ -1,15 +1,9 @@
<?php
-/**
- * Date formatter, recognises dates in plain text and formats them accoding to user preferences.
- *
- * @package MediaWiki
- * @subpackage Parser
- */
/**
+ * Date formatter, recognises dates in plain text and formats them accoding to user preferences.
* @todo preferences, OutputPage
- * @package MediaWiki
- * @subpackage Parser
+ * @addtogroup Parser
*/
class DateFormatter
{
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index 0692401d..169d67c9 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -15,7 +15,6 @@
* Documentation is in the source and on:
* http://www.mediawiki.org/wiki/Help:Configuration_settings
*
- * @package MediaWiki
*/
# This is not a valid entry point, perform no further processing unless MEDIAWIKI is defined
@@ -32,7 +31,7 @@ require_once( 'includes/SiteConfiguration.php' );
$wgConf = new SiteConfiguration;
/** MediaWiki version number */
-$wgVersion = '1.9.3';
+$wgVersion = '1.10.0';
/** Name of the site. It must be changed in LocalSettings.php */
$wgSitename = 'MediaWiki';
@@ -163,7 +162,6 @@ $wgTmpDirectory = false; /// defaults to "{$wgUploadDirectory}/tmp"
$wgUploadBaseUrl = "";
/**#@-*/
-
/**
* By default deleted files are simply discarded; to save them and
* make it possible to undelete images, create a directory which
@@ -192,6 +190,7 @@ $wgFileStore['deleted']['hash'] = 3; // 3-level subdirectory split
*
* Problematic punctuation:
* []{}|# Are needed for link syntax, never enable these
+ * <> Causes problems with HTML escaping, don't use
* % Enabled by default, minor problems with path to query rewrite rules, see below
* + Enabled by default, but doesn't work with path to query rewrite rules, corrupted by apache
* ? Enabled by default, but doesn't work with path to PATH_INFO rewrites
@@ -307,8 +306,8 @@ $wgVerifyMimeType= true;
/** Sets the mime type definition file to use by MimeMagic.php.
* @global string $wgMimeTypeFile
*/
-#$wgMimeTypeFile= "/etc/mime.types";
$wgMimeTypeFile= "includes/mime.types";
+#$wgMimeTypeFile= "/etc/mime.types";
#$wgMimeTypeFile= NULL; #use built-in defaults only.
/** Sets the mime type info file to use by MimeMagic.php.
@@ -372,7 +371,11 @@ $wgSharedUploadDBprefix = '';
$wgCacheSharedUploads = true;
/** Allow for upload to be copied from an URL. Requires Special:Upload?source=web */
$wgAllowCopyUploads = false;
-/** Max size for uploads, in bytes */
+/**
+ * Max size for uploads, in bytes. Currently only works for uploads from URL
+ * via CURL (see $wgAllowCopyUploads). The only way to impose limits on
+ * normal uploads is currently to edit php.ini.
+ */
$wgMaxUploadSize = 1024*1024*100; # 100MB
/**
@@ -502,8 +505,12 @@ $wgDBtype = "mysql";
$wgSearchType = null;
/** Table name prefix */
$wgDBprefix = '';
+/** MySQL table options to use during installation or update */
+$wgDBTableOptions = 'TYPE=InnoDB';
+
/**#@-*/
+
/** Live high performance sites should disable this - some checks acquire giant mysql locks */
$wgCheckDBSchema = true;
@@ -964,6 +971,7 @@ $wgGroupPermissions['user' ]['upload'] = true;
$wgGroupPermissions['user' ]['reupload'] = true;
$wgGroupPermissions['user' ]['reupload-shared'] = true;
$wgGroupPermissions['user' ]['minoredit'] = true;
+$wgGroupPermissions['user' ]['purge'] = true; // can use ?action=purge without clicking "ok"
// Implicit group for accounts that pass $wgAutoConfirmAge
$wgGroupPermissions['autoconfirmed']['autoconfirmed'] = true;
@@ -977,6 +985,7 @@ $wgGroupPermissions['emailconfirmed']['emailconfirmed'] = true;
$wgGroupPermissions['bot' ]['bot'] = true;
$wgGroupPermissions['bot' ]['autoconfirmed'] = true;
$wgGroupPermissions['bot' ]['nominornewtalk'] = true;
+$wgGroupPermissions['bot' ]['autopatrol'] = true;
// Most extra permission abilities go to this group
$wgGroupPermissions['sysop']['block'] = true;
@@ -988,7 +997,7 @@ $wgGroupPermissions['sysop']['import'] = true;
$wgGroupPermissions['sysop']['importupload'] = true;
$wgGroupPermissions['sysop']['move'] = true;
$wgGroupPermissions['sysop']['patrol'] = true;
-$wgGroupPermissions['sysop']['autopatrol'] = true;
+$wgGroupPermissions['sysop']['autopatrol'] = true;
$wgGroupPermissions['sysop']['protect'] = true;
$wgGroupPermissions['sysop']['proxyunbannable'] = true;
$wgGroupPermissions['sysop']['rollback'] = true;
@@ -1029,6 +1038,21 @@ $wgRestrictionTypes = array( 'edit', 'move' );
*/
$wgRestrictionLevels = array( '', 'autoconfirmed', 'sysop' );
+/**
+ * Set the minimum permissions required to edit pages in each
+ * namespace. If you list more than one permission, a user must
+ * have all of them to edit pages in that namespace.
+ */
+$wgNamespaceProtection = array();
+$wgNamespaceProtection[ NS_MEDIAWIKI ] = array( 'editinterface' );
+
+/**
+* Pages in namespaces in this array can not be used as templates.
+* Elements must be numeric namespace ids.
+* Among other things, this may be useful to enforce read-restrictions
+* which may otherwise be bypassed by using the template machanism.
+*/
+$wgNonincludableNamespaces = array();
/**
* Number of seconds an account is required to age before
@@ -1045,6 +1069,11 @@ $wgAutoConfirmAge = 0;
//$wgAutoConfirmAge = 600; // ten minutes
//$wgAutoConfirmAge = 3600*24; // one day
+# Number of edits an account requires before it is autoconfirmed
+# Passing both this AND the time requirement is needed
+$wgAutoConfirmCount = 0;
+//$wgAutoConfirmCount = 50;
+
# Proxy scanner settings
@@ -1096,7 +1125,7 @@ $wgCacheEpoch = '20030516000000';
* to ensure that client-side caches don't keep obsolete copies of global
* styles.
*/
-$wgStyleVersion = '42b';
+$wgStyleVersion = '63';
# Server-side caching:
@@ -1145,6 +1174,11 @@ $wgEnotifRevealEditorAddress = false; # UPO; reply-to address may be filled with
$wgEnotifMinorEdits = true; # UPO; false: "minor edits" on pages do not trigger notification mails.
# # Attention: _every_ change on a user_talk page trigger a notification mail (if the user is not yet notified)
+/**
+ * Array of usernames who will be sent a notification email for every change which occurs on a wiki
+ */
+$wgUsersNotifedOnAllChanges = array();
+
/** Show watching users in recent changes, watchlist and page history views */
$wgRCShowWatchingUsers = false; # UPO
/** Show watching users in Page views */
@@ -1419,8 +1453,19 @@ $wgSiteNotice = '';
# Images settings
#
-/** dynamic server side image resizing ("Thumbnails") */
-$wgUseImageResize = false;
+/**
+ * Plugins for media file type handling.
+ * Each entry in the array maps a MIME type to a class name
+ */
+$wgMediaHandlers = array(
+ 'image/jpeg' => 'BitmapHandler',
+ 'image/png' => 'BitmapHandler',
+ 'image/gif' => 'BitmapHandler',
+ 'image/x-ms-bmp' => 'BmpHandler',
+ 'image/svg+xml' => 'SvgHandler',
+ 'image/vnd.djvu' => 'DjVuHandler',
+);
+
/**
* Resizing can be done using PHP's internal image libraries or using
@@ -1434,6 +1479,12 @@ $wgUseImageMagick = false;
/** The convert command shipped with ImageMagick */
$wgImageMagickConvertCommand = '/usr/bin/convert';
+/** Sharpening parameter to ImageMagick */
+$wgSharpenParameter = '0x0.4';
+
+/** Reduction in linear dimensions below which sharpening will be enabled */
+$wgSharpenReductionThreshold = 0.85;
+
/**
* Use another resizing converter, e.g. GraphicMagick
* %s will be replaced with the source path, %d with the destination
@@ -1451,7 +1502,7 @@ $wgCustomConvertCommand = false;
#
# An external program is required to perform this conversion:
$wgSVGConverters = array(
- 'ImageMagick' => '$path/convert -background white -geometry $width $input $output',
+ 'ImageMagick' => '$path/convert -background white -geometry $width $input PNG:$output',
'sodipodi' => '$path/sodipodi -z -w $width -f $input -e $output',
'inkscape' => '$path/inkscape -z -w $width -f $input -e $output',
'batik' => 'java -Djava.awt.headless=true -jar $path/batik-rasterizer.jar -w $width -d $output $input',
@@ -1499,11 +1550,17 @@ $wgIgnoreImageErrors = false;
*/
$wgGenerateThumbnailOnParse = true;
+/** Obsolete, always true, kept for compatibility with extensions */
+$wgUseImageResize = true;
+
+
/** Set $wgCommandLineMode if it's not set already, to avoid notices */
if( !isset( $wgCommandLineMode ) ) {
$wgCommandLineMode = false;
}
+/** For colorized maintenance script output, is your terminal background dark ? */
+$wgCommandLineDarkBg = false;
#
# Recent changes settings
@@ -1613,18 +1670,22 @@ $wgExportAllowListContributors = false ;
/** Text matching this regular expression will be recognised as spam
* See http://en.wikipedia.org/wiki/Regular_expression */
$wgSpamRegex = false;
-/** Similarly if this function returns true */
+/** Similarly you can get a function to do the job. The function will be given
+ * the following args:
+ * - a Title object for the article the edit is made on
+ * - the text submitted in the textarea (wpTextbox1)
+ * - the section number.
+ * The return should be boolean indicating whether the edit matched some evilness:
+ * - true : block it
+ * - false : let it through
+ *
+ * For a complete example, have a look at the SpamBlacklist extension.
+ */
$wgFilterCallback = false;
/** Go button goes straight to the edit screen if the article doesn't exist. */
$wgGoToEdit = false;
-/** Allow limited user-specified HTML in wiki pages?
- * It will be run through a whitelist for security. Set this to false if you
- * want wiki pages to consist only of wiki markup. Note that replacements do not
- * yet exist for all HTML constructs.*/
-$wgUserHtml = true;
-
/** Allow raw, unchecked HTML in <html>...</html> sections.
* THIS IS VERY DANGEROUS on a publically editable site, so USE wgGroupPermissions
* TO RESTRICT EDITING to only those that you trust
@@ -1633,8 +1694,7 @@ $wgRawHtml = false;
/**
* $wgUseTidy: use tidy to make sure HTML output is sane.
- * This should only be enabled if $wgUserHtml is true.
- * tidy is a free tool that fixes broken HTML.
+ * Tidy is a free tool that fixes broken HTML.
* See http://www.w3.org/People/Raggett/tidy/
* $wgTidyBin should be set to the path of the binary and
* $wgTidyConf to the path of the configuration file.
@@ -1649,7 +1709,7 @@ $wgRawHtml = false;
$wgUseTidy = false;
$wgAlwaysUseTidy = false;
$wgTidyBin = 'tidy';
-$wgTidyConf = $IP.'/extensions/tidy/tidy.conf';
+$wgTidyConf = $IP.'/includes/tidy.conf';
$wgTidyOpts = '';
$wgTidyInternal = function_exists( 'tidy_load_config' );
@@ -1660,7 +1720,7 @@ $wgDefaultSkin = 'monobook';
* Settings added to this array will override the default globals for the user
* preferences used by anonymous visitors and newly created accounts.
* For instance, to disable section editing links:
- *  $wgDefaultUserOptions ['editsection'] = 0;
+ * $wgDefaultUserOptions ['editsection'] = 0;
*
*/
$wgDefaultUserOptions = array(
@@ -1831,9 +1891,28 @@ $wgFeedDiffCutoff = 32768;
$wgExtraNamespaces = NULL;
/**
+ * Namespace aliases
+ * These are alternate names for the primary localised namespace names, which
+ * are defined by $wgExtraNamespaces and the language file. If a page is
+ * requested with such a prefix, the request will be redirected to the primary
+ * name.
+ *
+ * Set this to a map from namespace names to IDs.
+ * Example:
+ * $wgNamespaceAliases = array(
+ * 'Wikipedian' => NS_USER,
+ * 'Help' => 100,
+ * );
+ */
+$wgNamespaceAliases = array();
+
+/**
* Limit images on image description pages to a user-selectable limit. In order
- * to reduce disk usage, limits can only be selected from a list. This is the
- * list of settings the user can choose from:
+ * to reduce disk usage, limits can only be selected from a list.
+ * The user preference is saved as an array offset in the database, by default
+ * the offset is set with $wgDefaultUserOptions['imagesize']. Make sure you
+ * change it if you alter the array (see bug 8858).
+ * This is the list of settings the user can choose from:
*/
$wgImageLimits = array (
array(320,240),
@@ -1883,9 +1962,9 @@ $wgBrowserBlackList = array(
*
* Reference: http://www.psychedelix.com/agents/index.shtml
*/
- '/^Mozilla\/2\.[^ ]+ .*?\((?!compatible).*; [UIN]/',
- '/^Mozilla\/3\.[^ ]+ .*?\((?!compatible).*; [UIN]/',
- '/^Mozilla\/4\.[^ ]+ .*?\((?!compatible).*; [UIN]/',
+ '/^Mozilla\/2\.[^ ]+ [^(]*?\((?!compatible).*; [UIN]/',
+ '/^Mozilla\/3\.[^ ]+ [^(]*?\((?!compatible).*; [UIN]/',
+ '/^Mozilla\/4\.[^ ]+ [^(]*?\((?!compatible).*; [UIN]/',
/**
* MSIE on Mac OS 9 is teh sux0r, converts þ to <thorn>, ð to <eth>, Þ to <THORN> and Ð to <ETH>
@@ -1985,7 +2064,9 @@ $wgLogTypes = array( '',
'delete',
'upload',
'move',
- 'import' );
+ 'import',
+ 'patrol',
+);
/**
* Lists the message key string for each log type. The localized messages
@@ -2001,7 +2082,9 @@ $wgLogNames = array(
'delete' => 'dellogpage',
'upload' => 'uploadlogpage',
'move' => 'movelogpage',
- 'import' => 'importlogpage' );
+ 'import' => 'importlogpage',
+ 'patrol' => 'patrol-log-page',
+);
/**
* Lists the message key string for descriptive text to be shown at the
@@ -2017,7 +2100,9 @@ $wgLogHeaders = array(
'delete' => 'dellogpagetext',
'upload' => 'uploadlogpagetext',
'move' => 'movelogpagetext',
- 'import' => 'importlogpagetext', );
+ 'import' => 'importlogpagetext',
+ 'patrol' => 'patrol-log-header',
+);
/**
* Lists the message key string for formatting individual events of each
@@ -2039,7 +2124,8 @@ $wgLogActions = array(
'move/move' => '1movedto2',
'move/move_redir' => '1movedto2_redir',
'import/upload' => 'import-logentry-upload',
- 'import/interwiki' => 'import-logentry-interwiki' );
+ 'import/interwiki' => 'import-logentry-interwiki',
+);
/**
* Experimental preview feature to fetch rendered text
@@ -2166,6 +2252,9 @@ $wgRateLimits = array(
'mailpassword' => array(
'anon' => NULL,
),
+ 'emailuser' => array(
+ 'user' => null,
+ ),
);
/**
@@ -2235,7 +2324,7 @@ $wgTrustedMediaFormats= array(
MEDIATYPE_BITMAP, //all bitmap formats
MEDIATYPE_AUDIO, //all audio formats
MEDIATYPE_VIDEO, //all plain video formats
- "image/svg", //svg (only needed if inline rendering of svg is not supported)
+ "image/svg+xml", //svg (only needed if inline rendering of svg is not supported)
"application/pdf", //PDF files
#"application/x-shockwave-flash", //flash/shockwave movie
);
@@ -2330,7 +2419,7 @@ $wgAllowDisplayTitle = false ;
$wgReservedUsernames = array(
'MediaWiki default', // Default 'Main Page' and MediaWiki: message pages
'Conversion script', // Used for the old Wikipedia software upgrade
- 'Maintenance script', // ... maintenance/edit.php uses this?
+ 'Maintenance script', // Maintenance scripts which perform editing, image import script
'Template namespace initialisation script', // Used in 1.2->1.3 upgrade
);
@@ -2338,7 +2427,7 @@ $wgReservedUsernames = array(
* MediaWiki will reject HTMLesque tags in uploaded files due to idiotic browsers which can't
* perform basic stuff like MIME detection and which are vulnerable to further idiots uploading
* crap files as images. When this directive is on, <title> will be allowed in files with
- * an "image/svg" MIME type. You should leave this disabled if your web server is misconfigured
+ * an "image/svg+xml" MIME type. You should leave this disabled if your web server is misconfigured
* and doesn't send appropriate MIME types for SVG images.
*/
$wgAllowTitlesInSVG = false;
@@ -2364,25 +2453,42 @@ $wgMaxShellFileSize = 102400;
/**
* DJVU settings
- * Path of the djvutoxml executable
+ * Path of the djvudump executable
* Enable this and $wgDjvuRenderer to enable djvu rendering
*/
-# $wgDjvuToXML = 'djvutoxml';
-$wgDjvuToXML = null;
+# $wgDjvuDump = 'djvudump';
+$wgDjvuDump = null;
/**
* Path of the ddjvu DJVU renderer
- * Enable this and $wgDjvuToXML to enable djvu rendering
+ * Enable this and $wgDjvuDump to enable djvu rendering
*/
# $wgDjvuRenderer = 'ddjvu';
$wgDjvuRenderer = null;
/**
- * Path of the DJVU post processor
- * May include command line options
- * Default: ppmtojpeg, since ddjvu generates ppm output
+ * Path of the djvutoxml executable
+ * This works like djvudump except much, much slower as of version 3.5.
+ *
+ * For now I recommend you use djvudump instead. The djvuxml output is
+ * probably more stable, so we'll switch back to it as soon as they fix
+ * the efficiency problem.
+ * http://sourceforge.net/tracker/index.php?func=detail&aid=1704049&group_id=32953&atid=406583
+ */
+# $wgDjvuToXML = 'djvutoxml';
+$wgDjvuToXML = null;
+
+
+/**
+ * Shell command for the DJVU post processor
+ * Default: pnmtopng, since ddjvu generates ppm output
+ * Set this to false to output the ppm file directly.
+ */
+$wgDjvuPostProcessor = 'pnmtojpeg';
+/**
+ * File extension for the DJVU post processor output
*/
-$wgDjvuPostProcessor = 'ppmtojpeg';
+$wgDjvuOutputExtension = 'jpg';
/**
* Enable direct access to the data API
@@ -2416,4 +2522,14 @@ $wgBreakFrames = false;
*/
$wgDisableQueryPageUpdate = false;
+/**
+ * Set this to false to disable cascading protection
+ */
+$wgEnableCascadingProtection = true;
+
+/**
+ * Disable output compression (enabled by default if zlib is available)
+ */
+$wgDisableOutputCompression = false;
+
?>
diff --git a/includes/Defines.php b/includes/Defines.php
index 84bc4495..98e76277 100644
--- a/includes/Defines.php
+++ b/includes/Defines.php
@@ -1,7 +1,6 @@
<?php
/**
* A few constants that might be needed during LocalSettings.php
- * @package MediaWiki
*/
/**
diff --git a/includes/DifferenceEngine.php b/includes/DifferenceEngine.php
index a72f0153..af65ce3a 100644
--- a/includes/DifferenceEngine.php
+++ b/includes/DifferenceEngine.php
@@ -1,15 +1,14 @@
<?php
/**
* See diff.doc
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @todo indicate where diff.doc can be found.
+ * @addtogroup DifferenceEngine
*/
/**
* @todo document
* @public
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class DifferenceEngine {
/**#@+
@@ -63,8 +62,8 @@ class DifferenceEngine {
$this->mRcidMarkPatrolled = intval($rcid); # force it to be an integer
}
- function showDiffPage() {
- global $wgUser, $wgOut, $wgContLang, $wgUseExternalEditor, $wgUseRCPatrol;
+ function showDiffPage( $diffOnly = false ) {
+ global $wgUser, $wgOut, $wgUseExternalEditor, $wgUseRCPatrol;
$fname = 'DifferenceEngine::showDiffPage';
wfProfileIn( $fname );
@@ -118,6 +117,7 @@ CONTROL;
# is the first version of that article. In that case, V' does not exist.
if ( $this->mOldid === false ) {
$this->showFirstRevision();
+ $this->renderNewRevision(); // should we respect $diffOnly here or not?
wfProfileOut( $fname );
return;
}
@@ -178,15 +178,34 @@ CONTROL;
$oldHeader = "<strong>{$this->mOldtitle}</strong><br />" .
$sk->revUserTools( $this->mOldRev ) . "<br />" .
- $oldminor . $sk->revComment( $this->mOldRev, true ) . "<br />" .
+ $oldminor . $sk->revComment( $this->mOldRev, !$diffOnly ) . "<br />" .
$prevlink;
$newHeader = "<strong>{$this->mNewtitle}</strong><br />" .
$sk->revUserTools( $this->mNewRev ) . " $rollback<br />" .
- $newminor . $sk->revComment( $this->mNewRev, true ) . "<br />" .
+ $newminor . $sk->revComment( $this->mNewRev, !$diffOnly ) . "<br />" .
$nextlink . $patrol;
$this->showDiff( $oldHeader, $newHeader );
+
+ if ( !$diffOnly )
+ $this->renderNewRevision();
+
+ wfProfileOut( $fname );
+ }
+
+ /**
+ * Show the new revision of the page.
+ */
+ function renderNewRevision() {
+ global $wgOut;
+ $fname = 'DifferenceEngine::renderNewRevision';
+ wfProfileIn( $fname );
+
$wgOut->addHTML( "<hr /><h2>{$this->mPagetitle}</h2>\n" );
+ #add deleted rev tag if needed
+ if ( !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) {
+ $wgOut->addWikiText( wfMsg( 'rev-deleted-text-permission' ) );
+ }
if( !$this->mNewRev->isCurrent() ) {
$oldEditSectionSetting = $wgOut->parserOptions()->setEditSection( false );
@@ -196,7 +215,8 @@ CONTROL;
if( is_object( $this->mNewRev ) ) {
$wgOut->setRevisionId( $this->mNewRev->getId() );
}
- $wgOut->addSecondaryWikiText( $this->mNewtext );
+
+ $wgOut->addWikiTextTidy( $this->mNewtext );
if( !$this->mNewRev->isCurrent() ) {
$wgOut->parserOptions()->setEditSection( $oldEditSectionSetting );
@@ -254,15 +274,6 @@ CONTROL;
$wgOut->setSubtitle( wfMsg( 'difference' ) );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
-
- # Show current revision
- #
- $wgOut->addHTML( "<hr /><h2>{$this->mPagetitle}</h2>\n" );
- if( is_object( $this->mNewRev ) ) {
- $wgOut->setRevisionId( $this->mNewRev->getId() );
- }
- $wgOut->addSecondaryWikiText( $this->mNewtext );
-
wfProfileOut( $fname );
}
@@ -322,9 +333,14 @@ CONTROL;
}
}
+ #loadtext is permission safe, this just clears out the diff
if ( !$this->loadText() ) {
wfProfileOut( $fname );
return false;
+ } else if ( $this->mOldRev && !$this->mOldRev->userCan(Revision::DELETED_TEXT) ) {
+ return '';
+ } else if ( $this->mNewRev && !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) {
+ return '';
}
$difftext = $this->generateDiffBody( $this->mOldtext, $this->mNewtext );
@@ -463,6 +479,14 @@ CONTROL;
* Add the header to a diff body
*/
function addHeader( $diff, $otitle, $ntitle, $multi = '' ) {
+ global $wgOut;
+
+ if ( $this->mOldRev && $this->mOldRev->isDeleted(Revision::DELETED_TEXT) ) {
+ $otitle = '<span class="history-deleted">'.$otitle.'</span>';
+ }
+ if ( $this->mNewRev && $this->mNewRev->isDeleted(Revision::DELETED_TEXT) ) {
+ $ntitle = '<span class="history-deleted">'.$ntitle.'</span>';
+ }
$header = "
<table border='0' width='98%' cellpadding='0' cellspacing='4' class='diff'>
<tr>
@@ -523,21 +547,17 @@ CONTROL;
$newLink = $this->mNewPage->escapeLocalUrl();
$this->mPagetitle = htmlspecialchars( wfMsg( 'currentrev' ) );
$newEdit = $this->mNewPage->escapeLocalUrl( 'action=edit' );
- $newUndo = $this->mNewPage->escapeLocalUrl( 'action=edit&undo=' . $this->mNewid );
$this->mNewtitle = "<a href='$newLink'>{$this->mPagetitle}</a> ($timestamp)"
- . " (<a href='$newEdit'>" . htmlspecialchars( wfMsg( 'editold' ) ) . "</a>)"
- . " (<a href='$newUndo'>" . htmlspecialchars( wfMsg( 'editundo' ) ) . "</a>)";
+ . " (<a href='$newEdit'>" . htmlspecialchars( wfMsg( 'editold' ) ) . "</a>)";
} else {
$newLink = $this->mNewPage->escapeLocalUrl( 'oldid=' . $this->mNewid );
$newEdit = $this->mNewPage->escapeLocalUrl( 'action=edit&oldid=' . $this->mNewid );
- $newUndo = $this->mNewPage->escapeLocalUrl( 'action=edit&undo=' . $this->mNewid );
$this->mPagetitle = htmlspecialchars( wfMsg( 'revisionasof', $timestamp ) );
$this->mNewtitle = "<a href='$newLink'>{$this->mPagetitle}</a>"
- . " (<a href='$newEdit'>" . htmlspecialchars( wfMsg( 'editold' ) ) . "</a>)"
- . " (<a href='$newUndo'>" . htmlspecialchars( wfMsg( 'editundo' ) ) . "</a>)";
+ . " (<a href='$newEdit'>" . htmlspecialchars( wfMsg( 'editold' ) ) . "</a>)";
}
// Load the old revision object
@@ -568,6 +588,9 @@ CONTROL;
$oldEdit = $this->mOldPage->escapeLocalUrl( 'action=edit&oldid=' . $this->mOldid );
$this->mOldtitle = "<a href='$oldLink'>" . htmlspecialchars( wfMsg( 'revisionasof', $t ) )
. "</a> (<a href='$oldEdit'>" . htmlspecialchars( wfMsg( 'editold' ) ) . "</a>)";
+ //now that we considered old rev, we can make undo link (bug 8133, multi-edit undo)
+ $newUndo = $this->mNewPage->escapeLocalUrl( 'action=edit&undoafter=' . $this->mOldid . '&undo=' . $this->mNewid);
+ $this->mNewtitle .= " (<a href='$newUndo'>" . htmlspecialchars( wfMsg( 'editundo' ) ) . "</a>)";
}
return true;
@@ -589,13 +612,13 @@ CONTROL;
}
if ( $this->mOldRev ) {
// FIXME: permission tests
- $this->mOldtext = $this->mOldRev->getText();
+ $this->mOldtext = $this->mOldRev->revText();
if ( $this->mOldtext === false ) {
return false;
}
}
if ( $this->mNewRev ) {
- $this->mNewtext = $this->mNewRev->getText();
+ $this->mNewtext = $this->mNewRev->revText();
if ( $this->mNewtext === false ) {
return false;
}
@@ -633,8 +656,7 @@ define('USE_ASSERTS', function_exists('assert'));
/**
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class _DiffOp {
var $type;
@@ -657,8 +679,7 @@ class _DiffOp {
/**
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class _DiffOp_Copy extends _DiffOp {
var $type = 'copy';
@@ -678,8 +699,7 @@ class _DiffOp_Copy extends _DiffOp {
/**
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class _DiffOp_Delete extends _DiffOp {
var $type = 'delete';
@@ -697,8 +717,7 @@ class _DiffOp_Delete extends _DiffOp {
/**
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class _DiffOp_Add extends _DiffOp {
var $type = 'add';
@@ -716,8 +735,7 @@ class _DiffOp_Add extends _DiffOp {
/**
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class _DiffOp_Change extends _DiffOp {
var $type = 'change';
@@ -754,8 +772,7 @@ class _DiffOp_Change extends _DiffOp {
*
* @author Geoffrey T. Dairiki, Tim Starling
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class _DiffEngine
{
@@ -1176,8 +1193,7 @@ class _DiffEngine
* Class representing a 'diff' between two sequences of strings.
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class Diff
{
@@ -1315,11 +1331,9 @@ class Diff
}
/**
- * FIXME: bad name.
- * @todo document
+ * @todo document, bad name.
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class MappedDiff extends Diff
{
@@ -1382,8 +1396,7 @@ class MappedDiff extends Diff
* to obtain fancier outputs.
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class DiffFormatter
{
@@ -1549,8 +1562,7 @@ define('NBSP', '&#160;'); // iso-8859-x non-breaking space.
/**
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class _HWLDF_WordAccumulator {
function _HWLDF_WordAccumulator () {
@@ -1562,9 +1574,12 @@ class _HWLDF_WordAccumulator {
function _flushGroup ($new_tag) {
if ($this->_group !== '') {
- if ($this->_tag == 'mark')
- $this->_line .= '<span class="diffchange">' .
- htmlspecialchars ( $this->_group ) . '</span>';
+ if ($this->_tag == 'ins')
+ $this->_line .= '<ins class="diffchange">' .
+ htmlspecialchars ( $this->_group ) . '</ins>';
+ elseif ($this->_tag == 'del')
+ $this->_line .= '<del class="diffchange">' .
+ htmlspecialchars ( $this->_group ) . '</del>';
else
$this->_line .= htmlspecialchars ( $this->_group );
}
@@ -1608,8 +1623,7 @@ class _HWLDF_WordAccumulator {
/**
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class WordLevelDiff extends MappedDiff
{
@@ -1669,7 +1683,7 @@ class WordLevelDiff extends MappedDiff
if ($edit->type == 'copy')
$orig->addWords($edit->orig);
elseif ($edit->orig)
- $orig->addWords($edit->orig, 'mark');
+ $orig->addWords($edit->orig, 'del');
}
$lines = $orig->getLines();
wfProfileOut( $fname );
@@ -1685,7 +1699,7 @@ class WordLevelDiff extends MappedDiff
if ($edit->type == 'copy')
$closing->addWords($edit->closing);
elseif ($edit->closing)
- $closing->addWords($edit->closing, 'mark');
+ $closing->addWords($edit->closing, 'ins');
}
$lines = $closing->getLines();
wfProfileOut( $fname );
@@ -1697,8 +1711,7 @@ class WordLevelDiff extends MappedDiff
* Wikipedia Table style diff formatter.
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class TableDiffFormatter extends DiffFormatter
{
diff --git a/includes/DjVuImage.php b/includes/DjVuImage.php
index 3b8a68ba..1e423565 100644
--- a/includes/DjVuImage.php
+++ b/includes/DjVuImage.php
@@ -1,11 +1,6 @@
<?php
+
/**
- * Support for detecting/validating DjVu image files and getting
- * some basic file metadata (resolution etc)
- *
- * File format docs are available in source package for DjVuLibre:
- * http://djvulibre.djvuzone.org/
- *
*
* Copyright (C) 2006 Brion Vibber <brion@pobox.com>
* http://www.mediawiki.org/
@@ -25,9 +20,17 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
- * @package MediaWiki
*/
+/**
+ * Support for detecting/validating DjVu image files and getting
+ * some basic file metadata (resolution etc)
+ *
+ * File format docs are available in source package for DjVuLibre:
+ * http://djvulibre.djvuzone.org/
+ *
+ * @addtogroup Media
+ */
class DjVuImage {
function __construct( $filename ) {
$this->mFilename = $filename;
@@ -68,6 +71,7 @@ class DjVuImage {
function dump() {
$file = fopen( $this->mFilename, 'rb' );
$header = fread( $file, 12 );
+ // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( unpack( 'a4magic/a4chunk/NchunkLength', $header ) );
echo "$chunk $chunkLength\n";
$this->dumpForm( $file, $chunkLength, 1 );
@@ -83,6 +87,7 @@ class DjVuImage {
if( $chunkHeader == '' ) {
break;
}
+ // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( unpack( 'a4chunk/NchunkLength', $chunkHeader ) );
echo str_repeat( ' ', $indent * 4 ) . "$chunk $chunkLength\n";
@@ -111,6 +116,7 @@ class DjVuImage {
if( strlen( $header ) < 16 ) {
wfDebug( __METHOD__ . ": too short file header\n" );
} else {
+ // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( unpack( 'a4magic/a4form/NformLength/a4subtype', $header ) );
if( $magic != 'AT&T' ) {
@@ -134,6 +140,7 @@ class DjVuImage {
if( strlen( $header ) < 8 ) {
return array( false, 0 );
} else {
+ // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( unpack( 'a4chunk/Nlength', $header ) );
return array( $chunk, $length );
}
@@ -192,6 +199,7 @@ class DjVuImage {
return false;
}
+ // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( unpack(
'nwidth/' .
'nheight/' .
@@ -214,17 +222,121 @@ class DjVuImage {
* @return string
*/
function retrieveMetaData() {
- global $wgDjvuToXML;
- if ( isset( $wgDjvuToXML ) ) {
- $cmd = $wgDjvuToXML . ' --without-anno --without-text ' .
+ global $wgDjvuToXML, $wgDjvuDump;
+ if ( isset( $wgDjvuDump ) ) {
+ # djvudump is faster as of version 3.5
+ # http://sourceforge.net/tracker/index.php?func=detail&aid=1704049&group_id=32953&atid=406583
+ wfProfileIn( 'djvudump' );
+ $cmd = wfEscapeShellArg( $wgDjvuDump ) . ' ' . wfEscapeShellArg( $this->mFilename );
+ $dump = wfShellExec( $cmd );
+ $xml = $this->convertDumpToXML( $dump );
+ wfProfileOut( 'djvudump' );
+ } elseif ( isset( $wgDjvuToXML ) ) {
+ wfProfileIn( 'djvutoxml' );
+ $cmd = wfEscapeShellArg( $wgDjvuToXML ) . ' --without-anno --without-text ' .
wfEscapeShellArg( $this->mFilename );
$xml = wfShellExec( $cmd );
+ wfProfileOut( 'djvutoxml' );
} else {
$xml = null;
}
return $xml;
}
-
+
+ /**
+ * Hack to temporarily work around djvutoxml bug
+ */
+ function convertDumpToXML( $dump ) {
+ if ( strval( $dump ) == '' ) {
+ return false;
+ }
+
+ $xml = <<<EOT
+<?xml version="1.0" ?>
+<!DOCTYPE DjVuXML PUBLIC "-//W3C//DTD DjVuXML 1.1//EN" "pubtext/DjVuXML-s.dtd">
+<DjVuXML>
+<HEAD></HEAD>
+<BODY>
+EOT;
+
+ $dump = str_replace( "\r", '', $dump );
+ $line = strtok( $dump, "\n" );
+ $m = false;
+ $good = false;
+ if ( preg_match( '/^( *)FORM:DJVU/', $line, $m ) ) {
+ # Single-page
+ if ( $this->parseFormDjvu( $line, $xml ) ) {
+ $good = true;
+ } else {
+ return false;
+ }
+ } elseif ( preg_match( '/^( *)FORM:DJVM/', $line, $m ) ) {
+ # Multi-page
+ $parentLevel = strlen( $m[1] );
+ # Find DIRM
+ $line = strtok( "\n" );
+ while ( $line !== false ) {
+ $childLevel = strspn( $line, ' ' );
+ if ( $childLevel <= $parentLevel ) {
+ # End of chunk
+ break;
+ }
+
+ if ( preg_match( '/^ *DIRM.*indirect/', $line ) ) {
+ wfDebug( "Indirect multi-page DjVu document, bad for server!\n" );
+ return false;
+ }
+ if ( preg_match( '/^ *FORM:DJVU/', $line ) ) {
+ # Found page
+ if ( $this->parseFormDjvu( $line, $xml ) ) {
+ $good = true;
+ } else {
+ return false;
+ }
+ }
+ $line = strtok( "\n" );
+ }
+ }
+ if ( !$good ) {
+ return false;
+ }
+
+ $xml .= "</BODY>\n</DjVuXML>\n";
+ return $xml;
+ }
+
+ function parseFormDjvu( $line, &$xml ) {
+ $parentLevel = strspn( $line, ' ' );
+ $line = strtok( "\n" );
+
+ # Find INFO
+ while ( $line !== false ) {
+ $childLevel = strspn( $line, ' ' );
+ if ( $childLevel <= $parentLevel ) {
+ # End of chunk
+ break;
+ }
+
+ if ( preg_match( '/^ *INFO *\[\d*\] *DjVu *(\d+)x(\d+), *\w*, *(\d+) *dpi, *gamma=([0-9.-]+)/', $line, $m ) ) {
+ $xml .= Xml::tags( 'OBJECT',
+ array(
+ #'data' => '',
+ #'type' => 'image/x.djvu',
+ 'height' => $m[2],
+ 'width' => $m[1],
+ #'usemap' => '',
+ ),
+ "\n" .
+ Xml::element( 'PARAM', array( 'name' => 'DPI', 'value' => $m[3] ) ) . "\n" .
+ Xml::element( 'PARAM', array( 'name' => 'GAMMA', 'value' => $m[4] ) ) . "\n"
+ ) . "\n";
+ return true;
+ }
+ $line = strtok( "\n" );
+ }
+ # Not found
+ return false;
+ }
}
diff --git a/includes/EditPage.php b/includes/EditPage.php
index 7688a64a..bec6e300 100644
--- a/includes/EditPage.php
+++ b/includes/EditPage.php
@@ -1,18 +1,14 @@
<?php
/**
- * Contain the EditPage class
- * @package MediaWiki
+ * Contains the EditPage class
*/
/**
- * Splitting edit page/HTML interface from Article...
+ * The edit page/HTML interface (split from Article)
* The actual database and text munging is still in Article,
* but it should get easier to call those from alternate
* interfaces.
- *
- * @package MediaWiki
*/
-
class EditPage {
var $mArticle;
var $mTitle;
@@ -69,22 +65,26 @@ class EditPage {
/**
* Fetch initial editing page content.
*/
- private function getContent() {
+ private function getContent( $def_text = '' ) {
global $wgOut, $wgRequest, $wgParser;
# Get variables from query string :P
$section = $wgRequest->getVal( 'section' );
$preload = $wgRequest->getVal( 'preload' );
+ $undoafter = $wgRequest->getVal( 'undoafter' );
$undo = $wgRequest->getVal( 'undo' );
wfProfileIn( __METHOD__ );
$text = '';
if( !$this->mTitle->exists() ) {
-
- # If requested, preload some text.
- $text = $this->getPreloadedText( $preload );
-
+ if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
+ # If this is a system message, get the default text.
+ $text = wfMsgWeirdKey ( $this->mTitle->getText() ) ;
+ } else {
+ # If requested, preload some text.
+ $text = $this->getPreloadedText( $preload );
+ }
# We used to put MediaWiki:Newarticletext here if
# $text was empty at this point.
# This is now shown above the edit box instead.
@@ -94,51 +94,63 @@ class EditPage {
// fetch the page record from the high-priority server,
// which is needed to guarantee we don't pick up lagged
// information.
-
+
$text = $this->mArticle->getContent();
- if ( $undo > 0 ) {
- #Undoing a specific edit overrides section editing; section-editing
+ if ( $undo > 0 && $undo > $undoafter ) {
+ # Undoing a specific edit overrides section editing; section-editing
# doesn't work with undoing.
- $undorev = Revision::newFromId($undo);
+ if ( $undoafter ) {
+ $undorev = Revision::newFromId($undo);
+ $oldrev = Revision::newFromId($undoafter);
+ } else {
+ $undorev = Revision::newFromId($undo);
+ $oldrev = $undorev ? $undorev->getPrevious() : null;
+ }
#Sanity check, make sure it's the right page.
# Otherwise, $text will be left as-is.
- if (!is_null($undorev) && $undorev->getPage() == $this->mArticle->getID()) {
- $oldrev = $undorev->getPrevious();
+ if ( !is_null($undorev) && !is_null($oldrev) && $undorev->getPage()==$oldrev->getPage() && $undorev->getPage()==$this->mArticle->getID() ) {
$undorev_text = $undorev->getText();
$oldrev_text = $oldrev->getText();
$currev_text = $text;
#No use doing a merge if it's just a straight revert.
- if ($currev_text != $undorev_text) {
+ if ( $currev_text != $undorev_text ) {
$result = wfMerge($undorev_text, $oldrev_text, $currev_text, $text);
} else {
$text = $oldrev_text;
$result = true;
}
-
- if( $result ) {
- # Inform the user of our success and set an automatic edit summary
- $this->editFormPageTop .= $wgOut->parse( wfMsgNoTrans( 'undo-success' ) );
- $this->summary = wfMsgForContent( 'undo-summary', $undo, $undorev->getUserText() );
- $this->formtype = 'diff';
- } else {
- # Warn the user that something went wrong
- $this->editFormPageTop .= $wgOut->parse( wfMsgNoTrans( 'undo-failure' ) );
+ } else {
+ // Failed basic sanity checks.
+ // Older revisions may have been removed since the link
+ // was created, or we may simply have got bogus input.
+ $result = false;
+ }
+
+ if( $result ) {
+ # Inform the user of our success and set an automatic edit summary
+ $this->editFormPageTop .= $wgOut->parse( wfMsgNoTrans( 'undo-success' ) );
+ $firstrev = $oldrev->getNext();
+ # If we just undid one rev, use an autosummary
+ if ( $firstrev->mId == $undo ) {
+ $this->summary = wfMsgForContent('undo-summary', $undo, $undorev->getUserText());
}
-
+ $this->formtype = 'diff';
+ } else {
+ # Warn the user that something went wrong
+ $this->editFormPageTop .= $wgOut->parse( wfMsgNoTrans( 'undo-failure' ) );
}
- }
- else if( $section != '' ) {
+ } else if( $section != '' ) {
if( $section == 'new' ) {
$text = $this->getPreloadedText( $preload );
} else {
- $text = $wgParser->getSection( $text, $section );
+ $text = $wgParser->getSection( $text, $section, $def_text );
}
}
}
-
+
wfProfileOut( __METHOD__ );
return $text;
}
@@ -282,7 +294,7 @@ class EditPage {
global $wgOut, $wgUser, $wgRequest, $wgTitle;
global $wgEmailConfirmToEdit;
- if ( ! wfRunHooks( 'AlternateEdit', array( &$this ) ) )
+ if ( ! wfRunHooks( 'AlternateEdit', array( &$this ) ) )
return;
$fname = 'EditPage::edit';
@@ -301,7 +313,7 @@ class EditPage {
return;
}
- if ( ! $this->mTitle->userCanEdit() ) {
+ if ( ! $this->mTitle->userCan( 'edit' ) ) {
wfDebug( "$fname: user can't edit\n" );
$wgOut->readOnlyPage( $this->getContent(), true );
wfProfileOut( $fname );
@@ -335,7 +347,7 @@ class EditPage {
wfProfileOut($fname);
return;
}
- if ( !$this->mTitle->userCanCreate() && !$this->mTitle->exists() ) {
+ if ( !$this->mTitle->userCan( 'create' ) && !$this->mTitle->exists() ) {
wfDebug( "$fname: no create permission\n" );
$this->noCreatePermission();
wfProfileOut( $fname );
@@ -421,7 +433,12 @@ class EditPage {
# First time through: get contents, set time for conflict
# checking, etc.
if ( 'initial' == $this->formtype || $this->firsttime ) {
- $this->initialiseForm();
+ if ($this->initialiseForm() === false) {
+ $this->noSuchSectionPage();
+ wfProfileOut( "$fname-business-end" );
+ wfProfileOut( $fname );
+ return;
+ }
if( !$this->mTitle->getArticleId() )
wfRunHooks( 'EditFormPreloadText', array( &$this->textbox1, &$this->mTitle ) );
}
@@ -482,7 +499,7 @@ class EditPage {
// Remember whether a save was requested, so we can indicate
// if we forced preview due to session failure.
$this->mTriedSave = !$this->preview;
-
+
if ( $this->tokenOk( $request ) ) {
# Some browsers will not report any submit button
# if the user hits enter in the comment box.
@@ -519,8 +536,8 @@ class EditPage {
} else {
$this->allowBlankSummary = $request->getBool( 'wpIgnoreBlankSummary' );
}
-
- $this->autoSumm = $request->getText( 'wpAutoSummary' );
+
+ $this->autoSumm = $request->getText( 'wpAutoSummary' );
} else {
# Not a posted form? Start with nothing.
wfDebug( "$fname: Not a posted form.\n" );
@@ -652,7 +669,7 @@ class EditPage {
wfProfileOut( $fname );
return true;
}
-
+
if ( !$wgUser->isAllowed('edit') ) {
if ( $wgUser->isAnon() ) {
$this->userNotLoggedInPage();
@@ -696,7 +713,7 @@ class EditPage {
if ( 0 == $aid ) {
// Late check for create permission, just in case *PARANOIA*
- if ( !$this->mTitle->userCanCreate() ) {
+ if ( !$this->mTitle->userCan( 'create' ) ) {
wfDebug( "$fname: no create permission\n" );
$this->noCreatePermission();
wfProfileOut( $fname );
@@ -723,6 +740,8 @@ class EditPage {
$this->mArticle->clear(); # Force reload of dates, etc.
$this->mArticle->forUpdate( true ); # Lock the article
+ wfDebug("timestamp: {$this->mArticle->getTimestamp()}, edittime: {$this->edittime}\n");
+
if( $this->mArticle->getTimestamp() != $this->edittime ) {
$this->isConflict = true;
if( $this->section == 'new' ) {
@@ -794,7 +813,7 @@ class EditPage {
}
#And a similar thing for new sections
- if( $this->section == 'new' && !$this->allowBlankSummary && $wgUser->getOption( 'forceeditsummary' ) ) {
+ if( $this->section == 'new' && !$this->allowBlankSummary && $wgUser->getOption( 'forceeditsummary' ) ) {
if (trim($this->summary) == '') {
$this->missingSummary = true;
wfProfileOut( $fname );
@@ -860,10 +879,13 @@ class EditPage {
function initialiseForm() {
$this->edittime = $this->mArticle->getTimestamp();
$this->summary = '';
- $this->textbox1 = $this->getContent();
+ $this->textbox1 = $this->getContent(false);
+ if ($this->textbox1 === false) return false;
+
if ( !$this->mArticle->exists() && $this->mArticle->mTitle->getNamespace() == NS_MEDIAWIKI )
- $this->textbox1 = wfMsgWeirdKey( $this->mArticle->mTitle->getText() ) ;
+ $this->textbox1 = wfMsgWeirdKey( $this->mArticle->mTitle->getText() );
wfProxyCheck();
+ return true;
}
/**
@@ -878,7 +900,7 @@ class EditPage {
$fname = 'EditPage::showEditForm';
wfProfileIn( $fname );
- $sk =& $wgUser->getSkin();
+ $sk = $wgUser->getSkin();
wfRunHooks( 'EditPage::showEditForm:initial', array( &$this ) ) ;
@@ -920,15 +942,15 @@ class EditPage {
if ( $this->missingComment ) {
$wgOut->addWikiText( wfMsg( 'missingcommenttext' ) );
}
-
+
if( $this->missingSummary && $this->section != 'new' ) {
$wgOut->addWikiText( wfMsg( 'missingsummary' ) );
}
- if( $this->missingSummary && $this->section == 'new' ) {
- $wgOut->addWikiText( wfMsg( 'missingcommentheader' ) );
- }
-
+ if( $this->missingSummary && $this->section == 'new' ) {
+ $wgOut->addWikiText( wfMsg( 'missingcommentheader' ) );
+ }
+
if( !$this->hookError == '' ) {
$wgOut->addWikiText( $this->hookError );
}
@@ -936,11 +958,15 @@ class EditPage {
if ( !$this->checkUnicodeCompliantBrowser() ) {
$wgOut->addWikiText( wfMsg( 'nonunicodebrowser') );
}
- if ( isset( $this->mArticle )
- && isset( $this->mArticle->mRevision )
- && !$this->mArticle->mRevision->isCurrent() ) {
- $this->mArticle->setOldSubtitle( $this->mArticle->mRevision->getId() );
- $wgOut->addWikiText( wfMsg( 'editingold' ) );
+ if ( isset( $this->mArticle ) && isset( $this->mArticle->mRevision ) ) {
+ // Let sysop know that this will make private content public if saved
+ if( $this->mArticle->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
+ $wgOut->addWikiText( wfMsg( 'rev-deleted-text-view' ) );
+ }
+ if( !$this->mArticle->mRevision->isCurrent() ) {
+ $this->mArticle->setOldSubtitle( $this->mArticle->mRevision->getId() );
+ $wgOut->addWikiText( wfMsg( 'editingold' ) );
+ }
}
}
@@ -958,24 +984,33 @@ class EditPage {
}
}
}
-
- if( $this->mTitle->isProtected( 'edit' ) ) {
- # Is the protection due to the namespace, e.g. interface text?
- if( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
- # Yes; remind the user
- $notice = wfMsg( 'editinginterface' );
- } elseif( $this->mTitle->isSemiProtected() ) {
- # No; semi protected
+
+ if( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
+ # Show a warning if editing an interface message
+ $wgOut->addWikiText( wfMsg( 'editinginterface' ) );
+ } elseif( $this->mTitle->isProtected( 'edit' ) ) {
+ # Is the title semi-protected?
+ if( $this->mTitle->isSemiProtected() ) {
$notice = wfMsg( 'semiprotectedpagewarning' );
- if( wfEmptyMsg( 'semiprotectedpagewarning', $notice ) || $notice == '-' ) {
+ if( wfEmptyMsg( 'semiprotectedpagewarning', $notice ) || $notice == '-' )
$notice = '';
- }
} else {
- # No; regular protection
+ # Then it must be protected based on static groups (regular)
$notice = wfMsg( 'protectedpagewarning' );
}
$wgOut->addWikiText( $notice );
}
+ if ( $this->mTitle->isCascadeProtected() ) {
+ # Is this page under cascading protection from some source pages?
+ list($cascadeSources, $restrictions) = $this->mTitle->getCascadeProtectionSources();
+ if ( count($cascadeSources) > 0 ) {
+ # Explain, and list the titles responsible
+ $notice = wfMsgExt( 'cascadeprotectedwarning', array('parsemag'), count($cascadeSources) ) . "\n";
+ foreach( $cascadeSources as $id => $page )
+ $notice .= '* [[:' . $page->getPrefixedText() . "]]\n";
+ }
+ $wgOut->addWikiText( $notice );
+ }
if ( $this->kblength === false ) {
$this->kblength = (int)(strlen( $this->textbox1 ) / 1024);
@@ -1005,8 +1040,6 @@ class EditPage {
$summary = wfMsg('summary');
$subject = wfMsg('subject');
- $minor = wfMsgExt('minoredit', array('parseinline'));
- $watchthis = wfMsgExt('watchthis', array('parseinline'));
$cancel = $sk->makeKnownLink( $this->mTitle->getPrefixedText(),
wfMsgExt('cancel', array('parseinline')) );
@@ -1041,31 +1074,10 @@ class EditPage {
# Already watched
$this->watchthis = true;
}
-
- if( $wgUser->getOption( 'minordefault' ) ) $this->minoredit = true;
- }
-
- $minoredithtml = '';
-
- if ( $wgUser->isAllowed('minoredit') ) {
- $minoredithtml =
- "<input tabindex='3' type='checkbox' value='1' name='wpMinoredit'".($this->minoredit?" checked='checked'":"").
- " accesskey='".wfMsg('accesskey-minoredit')."' id='wpMinoredit' />\n".
- "<label for='wpMinoredit' title='".wfMsg('tooltip-minoredit')."'>{$minor}</label>\n";
- }
- $watchhtml = '';
-
- if ( $wgUser->isLoggedIn() ) {
- $watchhtml = "<input tabindex='4' type='checkbox' name='wpWatchthis'".
- ($this->watchthis?" checked='checked'":"").
- " accesskey=\"".htmlspecialchars(wfMsg('accesskey-watch'))."\" id='wpWatchthis' />\n".
- "<label for='wpWatchthis' title=\"" .
- htmlspecialchars(wfMsg('tooltip-watch'))."\">{$watchthis}</label>\n";
+ if( $wgUser->getOption( 'minordefault' ) ) $this->minoredit = true;
}
- $checkboxhtml = $minoredithtml . $watchhtml;
-
$wgOut->addHTML( $this->editFormPageTop );
if ( $wgUser->getOption( 'previewontop' ) ) {
@@ -1132,68 +1144,18 @@ class EditPage {
}
}
- $temp = array(
- 'id' => 'wpSave',
- 'name' => 'wpSave',
- 'type' => 'submit',
- 'tabindex' => '5',
- 'value' => wfMsg('savearticle'),
- 'accesskey' => wfMsg('accesskey-save'),
- 'title' => wfMsg('tooltip-save'),
- );
- $buttons['save'] = wfElement('input', $temp, '');
- $temp = array(
- 'id' => 'wpDiff',
- 'name' => 'wpDiff',
- 'type' => 'submit',
- 'tabindex' => '7',
- 'value' => wfMsg('showdiff'),
- 'accesskey' => wfMsg('accesskey-diff'),
- 'title' => wfMsg('tooltip-diff'),
- );
- $buttons['diff'] = wfElement('input', $temp, '');
+ $tabindex = 2;
- global $wgLivePreview;
- if ( $wgLivePreview && $wgUser->getOption( 'uselivepreview' ) ) {
- $temp = array(
- 'id' => 'wpPreview',
- 'name' => 'wpPreview',
- 'type' => 'submit',
- 'tabindex' => '6',
- 'value' => wfMsg('showpreview'),
- 'accesskey' => '',
- 'title' => wfMsg('tooltip-preview'),
- 'style' => 'display: none;',
- );
- $buttons['preview'] = wfElement('input', $temp, '');
- $temp = array(
- 'id' => 'wpLivePreview',
- 'name' => 'wpLivePreview',
- 'type' => 'submit',
- 'tabindex' => '6',
- 'value' => wfMsg('showlivepreview'),
- 'accesskey' => wfMsg('accesskey-preview'),
- 'title' => '',
- 'onclick' => $this->doLivePreviewScript(),
- );
- $buttons['live'] = wfElement('input', $temp, '');
- } else {
- $temp = array(
- 'id' => 'wpPreview',
- 'name' => 'wpPreview',
- 'type' => 'submit',
- 'tabindex' => '6',
- 'value' => wfMsg('showpreview'),
- 'accesskey' => wfMsg('accesskey-preview'),
- 'title' => wfMsg('tooltip-preview'),
- );
- $buttons['preview'] = wfElement('input', $temp, '');
- $buttons['live'] = '';
- }
+ $checkboxes = self::getCheckboxes( $tabindex, $sk,
+ array( 'minor' => $this->minoredit, 'watch' => $this->watchthis ) );
+
+ $checkboxhtml = implode( $checkboxes, "\n" );
+
+ $buttons = $this->getEditButtons( $tabindex );
+ $buttonshtml = implode( $buttons, "\n" );
$safemodehtml = $this->checkUnicodeCompliantBrowser()
- ? ""
- : "<input type='hidden' name=\"safemode\" value='1' />\n";
+ ? '' : Xml::hidden( 'safemode', '1' );
$wgOut->addHTML( <<<END
{$toolbar}
@@ -1205,6 +1167,8 @@ END
call_user_func_array( $formCallback, array( &$wgOut ) );
}
+ wfRunHooks( 'EditPage::showEditForm:fields', array( &$this, &$wgOut ) );
+
// Put these up at the top to ensure they aren't lost on early form submission
$wgOut->addHTML( "
<input type='hidden' value=\"" . htmlspecialchars( $this->section ) . "\" name=\"wpSection\" />
@@ -1236,10 +1200,7 @@ END
$wgOut->addHTML(
"<div class='editButtons'>
- {$buttons['save']}
- {$buttons['preview']}
- {$buttons['live']}
- {$buttons['diff']}
+{$buttonshtml}
<span class='editHelp'>{$cancel} | {$edithelp}</span>
</div><!-- editButtons -->
</div><!-- editOptions -->");
@@ -1282,7 +1243,7 @@ END
if( $this->missingSummary ) {
$wgOut->addHTML( "<input type=\"hidden\" name=\"wpIgnoreBlankSummary\" value=\"1\" />\n" );
}
-
+
# For a bit more sophisticated detection of blank summaries, hash the
# automatic one and pass that in a hidden field.
$autosumm = $this->autoSumm ? $this->autoSumm : md5( $this->summary );
@@ -1308,7 +1269,7 @@ END
} else {
$wgOut->addHTML( '<div id="wikiPreview"></div>' );
}
-
+
if ( $this->formtype == 'diff') {
$wgOut->addHTML( $this->getDiff() );
}
@@ -1361,7 +1322,7 @@ END
}
function getLastDelete() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$fname = 'EditPage::getLastDelete';
$res = $dbr->select(
array( 'logging', 'user' ),
@@ -1425,7 +1386,7 @@ END
# don't parse user css/js, show message about preview
# XXX: stupid php bug won't let us use $wgTitle->isCssJsSubpage() here
-
+
if ( $this->isCssJsSubpage ) {
if(preg_match("/\\.css$/", $wgTitle->getText() ) ) {
$previewtext = wfMsg('usercsspreview');
@@ -1469,16 +1430,16 @@ END
function blockedPage() {
global $wgOut, $wgUser;
$wgOut->blockedPage( false ); # Standard block notice on the top, don't 'return'
-
+
# If the user made changes, preserve them when showing the markup
- # (This happens when a user is blocked during edit, for instance)
+ # (This happens when a user is blocked during edit, for instance)
$first = $this->firsttime || ( !$this->save && $this->textbox1 == '' );
if( $first ) {
$source = $this->mTitle->exists() ? $this->getContent() : false;
} else {
$source = $this->textbox1;
}
-
+
# Spit out the source or the user's modified version
if( $source !== false ) {
$rows = $wgUser->getOption( 'rows' );
@@ -1496,14 +1457,14 @@ END
function userNotLoggedInPage() {
global $wgUser, $wgOut;
$skin = $wgUser->getSkin();
-
+
$loginTitle = SpecialPage::getTitleFor( 'Userlogin' );
$loginLink = $skin->makeKnownLinkObj( $loginTitle, wfMsgHtml( 'loginreqlink' ), 'returnto=' . $this->mTitle->getPrefixedUrl() );
-
+
$wgOut->setPageTitle( wfMsg( 'whitelistedittitle' ) );
$wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->setArticleRelated( false );
-
+
$wgOut->addHtml( wfMsgWikiHtml( 'whitelistedittext', $loginLink ) );
$wgOut->returnToMain( false, $this->mTitle->getPrefixedUrl() );
}
@@ -1519,12 +1480,27 @@ END
$wgOut->setPageTitle( wfMsg( 'confirmedittitle' ) );
$wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->setArticleRelated( false );
-
+
$wgOut->addWikiText( wfMsg( 'confirmedittext' ) );
$wgOut->returnToMain( false );
}
/**
+ * Creates a basic error page which informs the user that
+ * they have attempted to edit a nonexistant section.
+ */
+ function noSuchSectionPage() {
+ global $wgOut;
+
+ $wgOut->setPageTitle( wfMsg( 'nosuchsectiontitle' ) );
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
+ $wgOut->setArticleRelated( false );
+
+ $wgOut->addWikiText( wfMsg( 'nosuchsectiontext', $this->section ) );
+ $wgOut->returnToMain( false );
+ }
+
+ /**
* Produce the stock "your edit contains spam" page
*
* @param $match Text which triggered one or more filters
@@ -1539,7 +1515,7 @@ END
$wgOut->addWikiText( wfMsg( 'spamprotectiontext' ) );
if ( $match )
$wgOut->addWikiText( wfMsg( 'spamprotectionmatch', "<nowiki>{$match}</nowiki>" ) );
-
+
$wgOut->returnToMain( false );
}
@@ -1551,7 +1527,7 @@ END
$fname = 'EditPage::mergeChangesInto';
wfProfileIn( $fname );
- $db =& wfGetDB( DB_MASTER );
+ $db = wfGetDB( DB_MASTER );
// This is the revision the editor started from
$baseRevision = Revision::loadFromTimestamp(
@@ -1645,90 +1621,102 @@ END
* can figure out a way to make them work in IE. However, we should make
* sure these keys are not defined on the edit page.
*/
- $toolarray=array(
- array( 'image'=>'button_bold.png',
- 'open' => '\\\'\\\'\\\'',
- 'close' => '\\\'\\\'\\\'',
- 'sample'=> wfMsg('bold_sample'),
- 'tip' => wfMsg('bold_tip'),
- 'key' => 'B'
- ),
- array( 'image'=>'button_italic.png',
- 'open' => '\\\'\\\'',
- 'close' => '\\\'\\\'',
- 'sample'=> wfMsg('italic_sample'),
- 'tip' => wfMsg('italic_tip'),
- 'key' => 'I'
- ),
- array( 'image'=>'button_link.png',
- 'open' => '[[',
- 'close' => ']]',
- 'sample'=> wfMsg('link_sample'),
- 'tip' => wfMsg('link_tip'),
- 'key' => 'L'
- ),
- array( 'image'=>'button_extlink.png',
- 'open' => '[',
- 'close' => ']',
- 'sample'=> wfMsg('extlink_sample'),
- 'tip' => wfMsg('extlink_tip'),
- 'key' => 'X'
- ),
- array( 'image'=>'button_headline.png',
- 'open' => "\\n== ",
- 'close' => " ==\\n",
- 'sample'=> wfMsg('headline_sample'),
- 'tip' => wfMsg('headline_tip'),
- 'key' => 'H'
- ),
- array( 'image'=>'button_image.png',
- 'open' => '[['.$wgContLang->getNsText(NS_IMAGE).":",
- 'close' => ']]',
- 'sample'=> wfMsg('image_sample'),
- 'tip' => wfMsg('image_tip'),
- 'key' => 'D'
- ),
- array( 'image' =>'button_media.png',
- 'open' => '[['.$wgContLang->getNsText(NS_MEDIA).':',
- 'close' => ']]',
- 'sample'=> wfMsg('media_sample'),
- 'tip' => wfMsg('media_tip'),
- 'key' => 'M'
- ),
- array( 'image' =>'button_math.png',
- 'open' => "<math>",
- 'close' => "<\\/math>",
- 'sample'=> wfMsg('math_sample'),
- 'tip' => wfMsg('math_tip'),
- 'key' => 'C'
- ),
- array( 'image' =>'button_nowiki.png',
- 'open' => "<nowiki>",
- 'close' => "<\\/nowiki>",
- 'sample'=> wfMsg('nowiki_sample'),
- 'tip' => wfMsg('nowiki_tip'),
- 'key' => 'N'
- ),
- array( 'image' =>'button_sig.png',
- 'open' => '--~~~~',
- 'close' => '',
- 'sample'=> '',
- 'tip' => wfMsg('sig_tip'),
- 'key' => 'Y'
- ),
- array( 'image' =>'button_hr.png',
- 'open' => "\\n----\\n",
- 'close' => '',
- 'sample'=> '',
- 'tip' => wfMsg('hr_tip'),
- 'key' => 'R'
- )
+ $toolarray = array(
+ array( 'image' => 'button_bold.png',
+ 'id' => 'mw-editbutton-bold',
+ 'open' => '\\\'\\\'\\\'',
+ 'close' => '\\\'\\\'\\\'',
+ 'sample'=> wfMsg('bold_sample'),
+ 'tip' => wfMsg('bold_tip'),
+ 'key' => 'B'
+ ),
+ array( 'image' => 'button_italic.png',
+ 'id' => 'mw-editbutton-italic',
+ 'open' => '\\\'\\\'',
+ 'close' => '\\\'\\\'',
+ 'sample'=> wfMsg('italic_sample'),
+ 'tip' => wfMsg('italic_tip'),
+ 'key' => 'I'
+ ),
+ array( 'image' => 'button_link.png',
+ 'id' => 'mw-editbutton-link',
+ 'open' => '[[',
+ 'close' => ']]',
+ 'sample'=> wfMsg('link_sample'),
+ 'tip' => wfMsg('link_tip'),
+ 'key' => 'L'
+ ),
+ array( 'image' => 'button_extlink.png',
+ 'id' => 'mw-editbutton-extlink',
+ 'open' => '[',
+ 'close' => ']',
+ 'sample'=> wfMsg('extlink_sample'),
+ 'tip' => wfMsg('extlink_tip'),
+ 'key' => 'X'
+ ),
+ array( 'image' => 'button_headline.png',
+ 'id' => 'mw-editbutton-headline',
+ 'open' => "\\n== ",
+ 'close' => " ==\\n",
+ 'sample'=> wfMsg('headline_sample'),
+ 'tip' => wfMsg('headline_tip'),
+ 'key' => 'H'
+ ),
+ array( 'image' => 'button_image.png',
+ 'id' => 'mw-editbutton-image',
+ 'open' => '[['.$wgContLang->getNsText(NS_IMAGE).":",
+ 'close' => ']]',
+ 'sample'=> wfMsg('image_sample'),
+ 'tip' => wfMsg('image_tip'),
+ 'key' => 'D'
+ ),
+ array( 'image' => 'button_media.png',
+ 'id' => 'mw-editbutton-media',
+ 'open' => '[['.$wgContLang->getNsText(NS_MEDIA).':',
+ 'close' => ']]',
+ 'sample'=> wfMsg('media_sample'),
+ 'tip' => wfMsg('media_tip'),
+ 'key' => 'M'
+ ),
+ array( 'image' => 'button_math.png',
+ 'id' => 'mw-editbutton-math',
+ 'open' => "<math>",
+ 'close' => "<\\/math>",
+ 'sample'=> wfMsg('math_sample'),
+ 'tip' => wfMsg('math_tip'),
+ 'key' => 'C'
+ ),
+ array( 'image' => 'button_nowiki.png',
+ 'id' => 'mw-editbutton-nowiki',
+ 'open' => "<nowiki>",
+ 'close' => "<\\/nowiki>",
+ 'sample'=> wfMsg('nowiki_sample'),
+ 'tip' => wfMsg('nowiki_tip'),
+ 'key' => 'N'
+ ),
+ array( 'image' => 'button_sig.png',
+ 'id' => 'mw-editbutton-signature',
+ 'open' => '--~~~~',
+ 'close' => '',
+ 'sample'=> '',
+ 'tip' => wfMsg('sig_tip'),
+ 'key' => 'Y'
+ ),
+ array( 'image' => 'button_hr.png',
+ 'id' => 'mw-editbutton-hr',
+ 'open' => "\\n----\\n",
+ 'close' => '',
+ 'sample'=> '',
+ 'tip' => wfMsg('hr_tip'),
+ 'key' => 'R'
+ )
);
$toolbar = "<div id='toolbar'>\n";
$toolbar.="<script type='$wgJsMimeType'>\n/*<![CDATA[*/\n";
foreach($toolarray as $tool) {
+ $cssId = $tool['id'];
$image=$wgStylePath.'/common/images/'.$tool['image'];
$open=$tool['open'];
$close=$tool['close'];
@@ -1742,7 +1730,7 @@ END
#$key = $tool["key"];
- $toolbar.="addButton('$image','$tip','$open','$close','$sample');\n";
+ $toolbar.="addButton('$image','$tip','$open','$close','$sample','$cssId');\n";
}
$toolbar.="/*]]>*/\n</script>";
@@ -1751,6 +1739,127 @@ END
}
/**
+ * Returns an array of html code of the following checkboxes:
+ * minor and watch
+ *
+ * @param $tabindex Current tabindex
+ * @param $skin Skin object
+ * @param $checked Array of checkbox => bool, where bool indicates the checked
+ * status of the checkbox
+ *
+ * @return array
+ */
+ public static function getCheckboxes( &$tabindex, $skin, $checked ) {
+ global $wgUser;
+
+ $checkboxes = array();
+
+ $checkboxes['minor'] = '';
+ $minorLabel = wfMsgExt('minoredit', array('parseinline'));
+ if ( $wgUser->isAllowed('minoredit') ) {
+ $attribs = array(
+ 'tabindex' => ++$tabindex,
+ 'accesskey' => wfMsg( 'accesskey-minoredit' ),
+ 'id' => 'wpMinoredit',
+ );
+ $checkboxes['minor'] =
+ Xml::check( 'wpMinoredit', $checked['minor'], $attribs ) .
+ "&nbsp;<label for='wpMinoredit'".$skin->tooltipAndAccesskey('minoredit').">{$minorLabel}</label>";
+ }
+
+ $watchLabel = wfMsgExt('watchthis', array('parseinline'));
+ $checkboxes['watch'] = '';
+ if ( $wgUser->isLoggedIn() ) {
+ $attribs = array(
+ 'tabindex' => ++$tabindex,
+ 'accesskey' => wfMsg( 'accesskey-watch' ),
+ 'id' => 'wpWatchthis',
+ );
+ $checkboxes['watch'] =
+ Xml::check( 'wpWatchthis', $checked['watch'], $attribs ) .
+ "&nbsp;<label for='wpWatchthis'".$skin->tooltipAndAccesskey('watch').">{$watchLabel}</label>";
+ }
+ return $checkboxes;
+ }
+
+ /**
+ * Returns an array of html code of the following buttons:
+ * save, diff, preview and live
+ *
+ * @param $tabindex Current tabindex
+ *
+ * @return array
+ */
+ public function getEditButtons(&$tabindex) {
+ global $wgLivePreview, $wgUser;
+
+ $buttons = array();
+
+ $temp = array(
+ 'id' => 'wpSave',
+ 'name' => 'wpSave',
+ 'type' => 'submit',
+ 'tabindex' => ++$tabindex,
+ 'value' => wfMsg('savearticle'),
+ 'accesskey' => wfMsg('accesskey-save'),
+ 'title' => wfMsg( 'tooltip-save' ).' ['.wfMsg( 'accesskey-save' ).']',
+ );
+ $buttons['save'] = wfElement('input', $temp, '');
+
+ ++$tabindex; // use the same for preview and live preview
+ if ( $wgLivePreview && $wgUser->getOption( 'uselivepreview' ) ) {
+ $temp = array(
+ 'id' => 'wpPreview',
+ 'name' => 'wpPreview',
+ 'type' => 'submit',
+ 'tabindex' => $tabindex,
+ 'value' => wfMsg('showpreview'),
+ 'accesskey' => '',
+ 'title' => wfMsg( 'tooltip-preview' ).' ['.wfMsg( 'accesskey-preview' ).']',
+ 'style' => 'display: none;',
+ );
+ $buttons['preview'] = wfElement('input', $temp, '');
+
+ $temp = array(
+ 'id' => 'wpLivePreview',
+ 'name' => 'wpLivePreview',
+ 'type' => 'submit',
+ 'tabindex' => $tabindex,
+ 'value' => wfMsg('showlivepreview'),
+ 'accesskey' => wfMsg('accesskey-preview'),
+ 'title' => '',
+ 'onclick' => $this->doLivePreviewScript(),
+ );
+ $buttons['live'] = wfElement('input', $temp, '');
+ } else {
+ $temp = array(
+ 'id' => 'wpPreview',
+ 'name' => 'wpPreview',
+ 'type' => 'submit',
+ 'tabindex' => $tabindex,
+ 'value' => wfMsg('showpreview'),
+ 'accesskey' => wfMsg('accesskey-preview'),
+ 'title' => wfMsg( 'tooltip-preview' ).' ['.wfMsg( 'accesskey-preview' ).']',
+ );
+ $buttons['preview'] = wfElement('input', $temp, '');
+ $buttons['live'] = '';
+ }
+
+ $temp = array(
+ 'id' => 'wpDiff',
+ 'name' => 'wpDiff',
+ 'type' => 'submit',
+ 'tabindex' => ++$tabindex,
+ 'value' => wfMsg('showdiff'),
+ 'accesskey' => wfMsg('accesskey-diff'),
+ 'title' => wfMsg( 'tooltip-diff' ).' ['.wfMsg( 'accesskey-diff' ).']',
+ );
+ $buttons['diff'] = wfElement('input', $temp, '');
+
+ return $buttons;
+ }
+
+ /**
* Output preview text only. This can be sucked into the edit page
* via JavaScript, and saves the server time rendering the skin as
* well as theoretically being more robust on the client (doesn't
@@ -1758,8 +1867,8 @@ END
* failure, etc).
*
* @todo This doesn't include category or interlanguage links.
- * Would need to enhance it a bit, maybe wrap them in XML
- * or something... that might also require more skin
+ * Would need to enhance it a bit, <s>maybe wrap them in XML
+ * or something...</s> that might also require more skin
* initialization, so check whether that's a problem.
*/
function livePreview() {
@@ -1767,10 +1876,14 @@ END
$wgOut->disable();
header( 'Content-type: text/xml; charset=utf-8' );
header( 'Cache-control: no-cache' );
- # FIXME
- echo $this->getPreviewText( );
- /* To not shake screen up and down between preview and live-preview */
- echo "<br style=\"clear:both;\" />\n";
+
+ $s =
+ '<?xml version="1.0" encoding="UTF-8" ?>' . "\n" .
+ Xml::openElement( 'livepreview' ) .
+ Xml::element( 'preview', null, $this->getPreviewText() ) .
+ Xml::element( 'br', array( 'style' => 'clear: both;' ) ) .
+ Xml::closeElement( 'livepreview' );
+ echo $s;
}
diff --git a/includes/Exception.php b/includes/Exception.php
index ad7ec14a..4cf0b7ba 100644
--- a/includes/Exception.php
+++ b/includes/Exception.php
@@ -1,5 +1,9 @@
<?php
+/**
+ * MediaWiki exception
+ * @addtogroup Exception
+ */
class MWException extends Exception
{
function useOutputPage() {
@@ -12,6 +16,7 @@ class MWException extends Exception
return is_object( $wgLang );
}
+ /** Get a message from i18n */
function msg( $key, $fallback /*[, params...] */ ) {
$args = array_slice( func_get_args(), 2 );
if ( $this->useMessageCache() ) {
@@ -21,6 +26,7 @@ class MWException extends Exception
}
}
+ /* If wgShowExceptionDetails, return a HTML message with a backtrace to the error. */
function getHTML() {
global $wgShowExceptionDetails;
if( $wgShowExceptionDetails ) {
@@ -33,6 +39,7 @@ class MWException extends Exception
}
}
+ /* If wgShowExceptionDetails, return a text message with a backtrace to the error */
function getText() {
global $wgShowExceptionDetails;
if( $wgShowExceptionDetails ) {
@@ -43,7 +50,8 @@ class MWException extends Exception
"in LocalSettings.php to show detailed debugging information.</p>";
}
}
-
+
+ /* Return titles of this error page */
function getPageTitle() {
if ( $this->useMessageCache() ) {
return wfMsg( 'internalerror' );
@@ -52,7 +60,10 @@ class MWException extends Exception
return "$wgSitename error";
}
}
-
+
+ /** Return the requested URL and point to file and line number from which the
+ * exception occured
+ */
function getLogMessage() {
global $wgRequest;
$file = $this->getFile();
@@ -60,7 +71,8 @@ class MWException extends Exception
$message = $this->getMessage();
return $wgRequest->getRequestURL() . " Exception from line $line of $file: $message";
}
-
+
+ /** Output the exception report using HTML */
function reportHTML() {
global $wgOut;
if ( $this->useOutputPage() ) {
@@ -78,11 +90,15 @@ class MWException extends Exception
echo $this->htmlFooter();
}
}
-
+
+ /** Print the exception report using text */
function reportText() {
echo $this->getText();
}
+ /* Output a report about the exception and takes care of formatting.
+ * It will be either HTML or plain text based on $wgCommandLineMode.
+ */
function report() {
global $wgCommandLineMode;
if ( $wgCommandLineMode ) {
@@ -125,6 +141,7 @@ class MWException extends Exception
/**
* Exception class which takes an HTML error message, and does not
* produce a backtrace. Replacement for OutputPage::fatalError().
+ * @addtogroup Exception
*/
class FatalError extends MWException {
function getHTML() {
@@ -136,6 +153,9 @@ class FatalError extends MWException {
}
}
+/**
+ * @addtogroup Exception
+ */
class ErrorPageError extends MWException {
public $title, $msg;
@@ -203,7 +223,7 @@ function wfReportException( Exception $e ) {
function wfExceptionHandler( $e ) {
global $wgFullyInitialised;
wfReportException( $e );
-
+
// Final cleanup, similar to wfErrorExit()
if ( $wgFullyInitialised ) {
try {
diff --git a/includes/Exif.php b/includes/Exif.php
index 0860d5f7..3a06ca1b 100644
--- a/includes/Exif.php
+++ b/includes/Exif.php
@@ -1,7 +1,6 @@
<?php
/**
- * @package MediaWiki
- * @subpackage Metadata
+ * @addtogroup Media
*
* @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
* @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason
@@ -22,13 +21,12 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
- * @link http://exif.org/Exif2-2.PDF The Exif 2.2 specification
- * @bug 1555, 1947
+ * @see http://exif.org/Exif2-2.PDF The Exif 2.2 specification
*/
/**
- * @package MediaWiki
- * @subpackage Metadata
+ * @todo document (e.g. one-sentence class-overview description)
+ * @addtogroup Media
*/
class Exif {
//@{
@@ -95,9 +93,9 @@ class Exif {
var $basename;
/**
- * The private log to log to
+ * The private log to log to, e.g. 'exif'
*/
- var $log = 'exif';
+ var $log = false;
//@}
@@ -106,7 +104,7 @@ class Exif {
*
* @param $file String: filename.
*/
- function Exif( $file ) {
+ function __construct( $file ) {
/**
* Page numbers here refer to pages in the EXIF 2.2 standard
*
@@ -563,7 +561,10 @@ class Exif {
* @param $fname String:
* @param $action Mixed: , default NULL.
*/
- function debug( $in, $fname, $action = NULL ) {
+ function debug( $in, $fname, $action = NULL ) {
+ if ( !$this->log ) {
+ return;
+ }
$type = gettype( $in );
$class = ucfirst( __CLASS__ );
if ( $type === 'array' )
@@ -588,6 +589,9 @@ class Exif {
* @param $io Boolean: Specify whether we're beginning or ending
*/
function debugFile( $fname, $io ) {
+ if ( !$this->log ) {
+ return;
+ }
$class = ucfirst( __CLASS__ );
if ( $io ) {
wfDebugLog( $this->log, "$class::$fname: begin processing: '{$this->basename}'\n" );
@@ -599,8 +603,8 @@ class Exif {
}
/**
- * @package MediaWiki
- * @subpackage Metadata
+ * @todo document (e.g. one-sentence class-overview description)
+ * @addtogroup Media
*/
class FormatExif {
/**
@@ -733,7 +737,7 @@ class FormatExif {
case 'DateTimeDigitized':
if( $val == '0000:00:00 00:00:00' ) {
$tags[$tag] = wfMsg('exif-unknowndate');
- } elseif( preg_match( '/^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)$/', $val ) ) {
+ } elseif( preg_match( '/^(?:\d{4}):(?:\d\d):(?:\d\d) (?:\d\d):(?:\d\d):(?:\d\d)$/', $val ) ) {
$tags[$tag] = $wgLang->timeanddate( wfTimestamp(TS_MW, $val) );
}
break;
diff --git a/includes/Export.php b/includes/Export.php
index b7e0f9a1..9307795d 100644
--- a/includes/Export.php
+++ b/includes/Export.php
@@ -17,16 +17,15 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# http://www.gnu.org/copyleft/gpl.html
+
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
-
class WikiExporter {
var $list_authors = false ; # Return distinct author list (when not returning full history)
var $author_list = "" ;
-
+
const FULL = 0;
const CURRENT = 1;
@@ -44,14 +43,14 @@ class WikiExporter {
* main query is still running.
*
* @param Database $db
- * @param mixed $history one of WikiExporter::FULL or WikiExporter::CURRENT, or an
+ * @param mixed $history one of WikiExporter::FULL or WikiExporter::CURRENT, or an
* associative array:
* offset: non-inclusive offset at which to start the query
* limit: maximum number of rows to return
* dir: "asc" or "desc" timestamp order
* @param int $buffer one of WikiExporter::BUFFER or WikiExporter::STREAM
*/
- function WikiExporter( &$db, $history = WikiExporter::CURRENT,
+ function __construct( &$db, $history = WikiExporter::CURRENT,
$buffer = WikiExporter::BUFFER, $text = WikiExporter::TEXT ) {
$this->db =& $db;
$this->history = $history;
@@ -140,7 +139,10 @@ class WikiExporter {
$fname = "do_list_authors" ;
wfProfileIn( $fname );
$this->author_list = "<contributors>";
- $sql = "SELECT DISTINCT rev_user_text,rev_user FROM {$page},{$revision} WHERE page_id=rev_page AND " . $cond ;
+ //rev_deleted
+ $nothidden = '(rev_deleted & '.Revision::DELETED_USER.') = 0';
+
+ $sql = "SELECT DISTINCT rev_user_text,rev_user FROM {$page},{$revision} WHERE page_id=rev_page AND $nothidden AND " . $cond ;
$result = $this->db->query( $sql, $fname );
$resultset = $this->db->resultObject( $result );
while( $row = $resultset->fetchObject() ) {
@@ -164,10 +166,10 @@ class WikiExporter {
$page = $this->db->tableName( 'page' );
$revision = $this->db->tableName( 'revision' );
$text = $this->db->tableName( 'text' );
-
+
$order = 'ORDER BY page_id';
$limit = '';
-
+
if( $this->history == WikiExporter::FULL ) {
$join = 'page_id=rev_page';
} elseif( $this->history == WikiExporter::CURRENT ) {
@@ -185,7 +187,7 @@ class WikiExporter {
$order .= ', rev_timestamp DESC';
}
if ( !empty( $this->history['offset'] ) ) {
- $join .= " AND rev_timestamp $op " . $this->db->addQuotes(
+ $join .= " AND rev_timestamp $op " . $this->db->addQuotes(
$this->db->timestamp( $this->history['offset'] ) );
}
if ( !empty( $this->history['limit'] ) ) {
@@ -229,7 +231,7 @@ class WikiExporter {
$result = $this->db->query( $sql, $fname );
$wrapper = $this->db->resultObject( $result );
$this->outputStream( $wrapper );
-
+
if ( $this->list_authors ) {
$this->outputStream( $wrapper );
}
@@ -279,6 +281,9 @@ class WikiExporter {
}
}
+/**
+ * @addtogroup Dump
+ */
class XmlDumpWriter {
/**
@@ -461,6 +466,7 @@ class XmlDumpWriter {
/**
* Base class for output stream; prints to stdout or buffer or whereever.
+ * @addtogroup Dump
*/
class DumpOutput {
function writeOpenStream( $string ) {
@@ -494,6 +500,7 @@ class DumpOutput {
/**
* Stream outputter to send data to a file.
+ * @addtogroup Dump
*/
class DumpFileOutput extends DumpOutput {
var $handle;
@@ -511,6 +518,7 @@ class DumpFileOutput extends DumpOutput {
* Stream outputter to send data to a file via some filter program.
* Even if compression is available in a library, using a separate
* program can allow us to make use of a multi-processor system.
+ * @addtogroup Dump
*/
class DumpPipeOutput extends DumpFileOutput {
function DumpPipeOutput( $command, $file = null ) {
@@ -523,6 +531,7 @@ class DumpPipeOutput extends DumpFileOutput {
/**
* Sends dump output via the gzip compressor.
+ * @addtogroup Dump
*/
class DumpGZipOutput extends DumpPipeOutput {
function DumpGZipOutput( $file ) {
@@ -532,6 +541,7 @@ class DumpGZipOutput extends DumpPipeOutput {
/**
* Sends dump output via the bgzip2 compressor.
+ * @addtogroup Dump
*/
class DumpBZip2Output extends DumpPipeOutput {
function DumpBZip2Output( $file ) {
@@ -541,6 +551,7 @@ class DumpBZip2Output extends DumpPipeOutput {
/**
* Sends dump output via the p7zip compressor.
+ * @addtogroup Dump
*/
class Dump7ZipOutput extends DumpPipeOutput {
function Dump7ZipOutput( $file ) {
@@ -558,6 +569,7 @@ class Dump7ZipOutput extends DumpPipeOutput {
* Dump output filter class.
* This just does output filtering and streaming; XML formatting is done
* higher up, so be careful in what you do.
+ * @addtogroup Dump
*/
class DumpFilter {
function DumpFilter( &$sink ) {
@@ -603,6 +615,7 @@ class DumpFilter {
/**
* Simple dump output filter to exclude all talk pages.
+ * @addtogroup Dump
*/
class DumpNotalkFilter extends DumpFilter {
function pass( $page ) {
@@ -612,6 +625,7 @@ class DumpNotalkFilter extends DumpFilter {
/**
* Dump output filter to include or exclude pages in a given set of namespaces.
+ * @addtogroup Dump
*/
class DumpNamespaceFilter extends DumpFilter {
var $invert = false;
@@ -666,6 +680,7 @@ class DumpNamespaceFilter extends DumpFilter {
/**
* Dump output filter to include only the last revision in each page sequence.
+ * @addtogroup Dump
*/
class DumpLatestFilter extends DumpFilter {
var $page, $pageString, $rev, $revString;
@@ -697,6 +712,7 @@ class DumpLatestFilter extends DumpFilter {
/**
* Base class for output stream; prints to stdout or buffer or whereever.
+ * @addtogroup Dump
*/
class DumpMultiWriter {
function DumpMultiWriter( $sinks ) {
diff --git a/includes/ExternalEdit.php b/includes/ExternalEdit.php
index 14b55fdb..c8ed8bde 100644
--- a/includes/ExternalEdit.php
+++ b/includes/ExternalEdit.php
@@ -3,12 +3,10 @@
* License: Public domain
*
* @author Erik Moeller <moeller@scireview.de>
- * @package MediaWiki
*/
/**
*
- * @package MediaWiki
*
* Support for external editors to modify both text and files
* in external applications. It works as follows: MediaWiki
@@ -22,7 +20,7 @@
class ExternalEdit {
- function ExternalEdit ( $article, $mode ) {
+ function __construct( $article, $mode ) {
global $wgInputEncoding;
$this->mArticle =& $article;
$this->mTitle =& $article->mTitle;
diff --git a/includes/ExternalStore.php b/includes/ExternalStore.php
index 79f1a528..fb66b652 100644
--- a/includes/ExternalStore.php
+++ b/includes/ExternalStore.php
@@ -1,7 +1,6 @@
<?php
/**
*
- * @package MediaWiki
*
* Constructor class for data kept in external repositories
*
diff --git a/includes/ExternalStoreDB.php b/includes/ExternalStoreDB.php
index 861a9939..7b4ffc2f 100644
--- a/includes/ExternalStoreDB.php
+++ b/includes/ExternalStoreDB.php
@@ -1,14 +1,12 @@
<?php
/**
*
- * @package MediaWiki
*
* DB accessable external objects
*
*/
-/** @package MediaWiki */
/**
* External database storage will use one (or more) separate connection pools
diff --git a/includes/ExternalStoreHttp.php b/includes/ExternalStoreHttp.php
index daf62cc4..e6656986 100644
--- a/includes/ExternalStoreHttp.php
+++ b/includes/ExternalStoreHttp.php
@@ -1,7 +1,6 @@
<?php
/**
*
- * @package MediaWiki
*
* Example class for HTTP accessable external objects
*
diff --git a/includes/FakeTitle.php b/includes/FakeTitle.php
index ae05385a..293bdaf0 100644
--- a/includes/FakeTitle.php
+++ b/includes/FakeTitle.php
@@ -14,7 +14,6 @@ class FakeTitle {
function getInterwikiCached() { $this->error(); }
function isLocal() { $this->error(); }
function isTrans() { $this->error(); }
- function touchArray( $titles, $timestamp = '' ) { $this->error(); }
function getText() { $this->error(); }
function getPartialURL() { $this->error(); }
function getDBkey() { $this->error(); }
@@ -41,6 +40,7 @@ class FakeTitle {
function isProtected() { $this->error(); }
function userIsWatching() { $this->error(); }
function userCan() { $this->error(); }
+ function userCanCreate() { $this->error(); }
function userCanEdit() { $this->error(); }
function userCanMove() { $this->error(); }
function isMovable() { $this->error(); }
@@ -71,7 +71,6 @@ class FakeTitle {
function moveOverExistingRedirect() { $this->error(); }
function moveToNewTitle() { $this->error(); }
function isValidMoveTarget() { $this->error(); }
- function createRedirect() { $this->error(); }
function getParentCategories() { $this->error(); }
function getParentCategoryTree() { $this->error(); }
function pageCond() { $this->error(); }
diff --git a/includes/Feed.php b/includes/Feed.php
index 5c14865d..ed4343c3 100644
--- a/includes/Feed.php
+++ b/includes/Feed.php
@@ -1,6 +1,5 @@
<?php
-# Basic support for outputting syndication feeds in RSS, other formats
-#
+
# Copyright (C) 2004 Brion Vibber <brion@pobox.com>
# http://www.mediawiki.org/
#
@@ -20,15 +19,13 @@
# http://www.gnu.org/copyleft/gpl.html
/**
+ * Basic support for outputting syndication feeds in RSS, other formats.
* Contain a feed class as well as classes to build rss / atom ... feeds
* Available feeds are defined in Defines.php
- * @package MediaWiki
*/
-
/**
- * @todo document
- * @package MediaWiki
+ * A base class for basic support for outputting syndication feeds in RSS and other formats.
*/
class FeedItem {
/**#@+
@@ -45,7 +42,7 @@ class FeedItem {
/**#@+
* @todo document
*/
- function FeedItem( $Title, $Description, $Url, $Date = '', $Author = '', $Comments = '' ) {
+ function __construct( $Title, $Description, $Url, $Date = '', $Author = '', $Comments = '' ) {
$this->Title = $Title;
$this->Description = $Description;
$this->Url = $Url;
@@ -77,8 +74,7 @@ class FeedItem {
}
/**
- * @todo document
- * @package MediaWiki
+ * @todo document (needs one-sentence top-level class description).
*/
class ChannelFeed extends FeedItem {
/**#@+
@@ -160,8 +156,6 @@ class ChannelFeed extends FeedItem {
/**
* Generate a RSS feed
- * @todo document
- * @package MediaWiki
*/
class RSSFeed extends ChannelFeed {
@@ -221,8 +215,6 @@ class RSSFeed extends ChannelFeed {
/**
* Generate an Atom feed
- * @todo document
- * @package MediaWiki
*/
class AtomFeed extends ChannelFeed {
/**
diff --git a/includes/FileStore.php b/includes/FileStore.php
index 1fd35b01..dcec71c5 100644
--- a/includes/FileStore.php
+++ b/includes/FileStore.php
@@ -1,5 +1,8 @@
<?php
+/**
+ * @todo document (needs one-sentence top-level class description).
+ */
class FileStore {
const DELETE_ORIGINAL = 1;
@@ -33,7 +36,7 @@ class FileStore {
* suffer an uncaught error the lock will be released when the
* connection is closed.
*
- * @fixme Probably only works on MySQL. Abstract to the Database class?
+ * @todo Probably only works on MySQL. Abstract to the Database class?
*/
static function lock() {
global $wgDBtype;
@@ -106,7 +109,7 @@ class FileStore {
private function copyFile( $sourcePath, $destPath, $flags=0 ) {
if( !file_exists( $sourcePath ) ) {
// Abort! Abort!
- throw new FSException( "missing source file '$sourcePath'\n" );
+ throw new FSException( "missing source file '$sourcePath'" );
}
$transaction = new FSTransaction();
@@ -125,7 +128,7 @@ class FileStore {
if( !$ok ) {
throw new FSException(
- "failed to create directory for '$destPath'\n" );
+ "failed to create directory for '$destPath'" );
}
}
@@ -138,7 +141,7 @@ class FileStore {
$transaction->addRollback( FSTransaction::DELETE_FILE, $destPath );
} else {
throw new FSException(
- __METHOD__." failed to copy '$sourcePath' to '$destPath'\n" );
+ __METHOD__." failed to copy '$sourcePath' to '$destPath'" );
}
}
@@ -175,7 +178,7 @@ class FileStore {
* @throws FSException if file can't be deleted
* @return FSTransaction
*
- * @fixme Might be worth preliminary permissions check
+ * @todo Might be worth preliminary permissions check
*/
static function deleteFile( $path ) {
if( file_exists( $path ) ) {
@@ -368,6 +371,9 @@ class FSTransaction {
}
}
+/**
+ * @addtogroup Exception
+ */
class FSException extends MWException { }
?>
diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php
index de07b321..1ffde741 100644
--- a/includes/GlobalFunctions.php
+++ b/includes/GlobalFunctions.php
@@ -1,8 +1,11 @@
<?php
+if ( !defined( 'MEDIAWIKI' ) ) {
+ die( "This file is part of MediaWiki, it is not a valid entry point" );
+}
+
/**
* Global functions used everywhere
- * @package MediaWiki
*/
/**
@@ -19,9 +22,9 @@ $wgTotalViews = -1;
$wgTotalEdits = -1;
-require_once( 'LogPage.php' );
-require_once( 'normal/UtfNormalUtil.php' );
-require_once( 'XmlFunctions.php' );
+require_once dirname(__FILE__) . '/LogPage.php';
+require_once dirname(__FILE__) . '/normal/UtfNormalUtil.php';
+require_once dirname(__FILE__) . '/XmlFunctions.php';
/**
* Compatibility functions
@@ -57,6 +60,30 @@ if ( !function_exists( 'mb_substr' ) ) {
}
}
+if ( !function_exists( 'mb_strlen' ) ) {
+ /**
+ * Fallback implementation of mb_strlen, hardcoded to UTF-8.
+ * @param string $str
+ * @param string $enc optional encoding; ignored
+ * @return int
+ */
+ function mb_strlen( $str, $enc="" ) {
+ $counts = count_chars( $str );
+ $total = 0;
+
+ // Count ASCII bytes
+ for( $i = 0; $i < 0x80; $i++ ) {
+ $total += $counts[$i];
+ }
+
+ // Count multibyte sequence heads
+ for( $i = 0xc0; $i < 0xff; $i++ ) {
+ $total += $counts[$i];
+ }
+ return $total;
+ }
+}
+
if ( !function_exists( 'array_diff_key' ) ) {
/**
* Exists in PHP 5.1.0+
@@ -168,7 +195,7 @@ function wfDebug( $text, $logonly = false ) {
# Strip unprintables; they can switch terminal modes when binary data
# gets dumped, which is pretty annoying.
$text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text );
- @error_log( $text, 3, $wgDebugLogFile );
+ wfErrorLog( $text, $wgDebugLogFile );
}
}
@@ -187,7 +214,7 @@ function wfDebugLog( $logGroup, $text, $public = true ) {
if( isset( $wgDebugLogGroups[$logGroup] ) ) {
$time = wfTimestamp( TS_DB );
$wiki = wfWikiID();
- @error_log( "$time $wiki: $text", 3, $wgDebugLogGroups[$logGroup] );
+ wfErrorLog( "$time $wiki: $text", $wgDebugLogGroups[$logGroup] );
} else if ( $public === true ) {
wfDebug( $text, true );
}
@@ -198,15 +225,28 @@ function wfDebugLog( $logGroup, $text, $public = true ) {
* @param $text String: database error message.
*/
function wfLogDBError( $text ) {
- global $wgDBerrorLog;
+ global $wgDBerrorLog, $wgDBname;
if ( $wgDBerrorLog ) {
$host = trim(`hostname`);
- $text = date('D M j G:i:s T Y') . "\t$host\t".$text;
- error_log( $text, 3, $wgDBerrorLog );
+ $text = date('D M j G:i:s T Y') . "\t$host\t$wgDBname\t$text";
+ wfErrorLog( $text, $wgDBerrorLog );
}
}
/**
+ * Log to a file without getting "file size exceeded" signals
+ */
+function wfErrorLog( $text, $file ) {
+ wfSuppressWarnings();
+ $exists = file_exists( $file );
+ $size = $exists ? filesize( $file ) : false;
+ if ( !$exists || ( $size !== false && $size + strlen( $text ) < 0x7fffffff ) ) {
+ error_log( $text, 3, $file );
+ }
+ wfRestoreWarnings();
+}
+
+/**
* @todo document
*/
function wfLogProfilingData() {
@@ -232,7 +272,7 @@ function wfLogProfilingData() {
gmdate( 'YmdHis' ), $elapsed,
urldecode( $wgRequest->getRequestURL() . $forward ) );
if ( '' != $wgDebugLogFile && ( $wgRequest->getVal('action') != 'raw' || $wgDebugRawPage ) ) {
- error_log( $log . $prof, 3, $wgDebugLogFile );
+ wfErrorLog( $log . $prof, $wgDebugLogFile );
}
}
}
@@ -376,8 +416,11 @@ function wfMsgNoDBForContent( $key ) {
* @return String: the requested message.
*/
function wfMsgReal( $key, $args, $useDB = true, $forContent=false, $transform = true ) {
+ $fname = 'wfMsgReal';
+ wfProfileIn( $fname );
$message = wfMsgGetKey( $key, $useDB, $forContent, $transform );
$message = wfMsgReplaceArgs( $message, $args );
+ wfProfileOut( $fname );
return $message;
}
@@ -515,11 +558,11 @@ function wfMsgWikiHtml( $key ) {
* Returns message in the requested format
* @param string $key Key of the message
* @param array $options Processing rules:
- * <i>parse<i>: parses wikitext to html
- * <i>parseinline<i>: parses wikitext to html and removes the surrounding p's added by parser or tidy
- * <i>escape<i>: filters message trough htmlspecialchars
- * <i>replaceafter<i>: parameters are substituted after parsing or escaping
- * <i>parsemag<i>: ??
+ * <i>parse</i>: parses wikitext to html
+ * <i>parseinline</i>: parses wikitext to html and removes the surrounding p's added by parser or tidy
+ * <i>escape</i>: filters message trough htmlspecialchars
+ * <i>replaceafter</i>: parameters are substituted after parsing or escaping
+ * <i>parsemag</i>: transform the message using magic phrases
*/
function wfMsgExt( $key, $options ) {
global $wgOut, $wgParser;
@@ -569,7 +612,7 @@ function wfMsgExt( $key, $options ) {
* Just like exit() but makes a note of it.
* Commits open transactions except if the error parameter is set
*
- * @obsolete Please return control to the caller or throw an exception
+ * @deprecated Please return control to the caller or throw an exception
*/
function wfAbruptExit( $error = false ){
global $wgLoadBalancer;
@@ -599,7 +642,7 @@ function wfAbruptExit( $error = false ){
}
/**
- * @obsolete Please return control the caller or throw an exception
+ * @deprecated Please return control the caller or throw an exception
*/
function wfErrorExit() {
wfAbruptExit( true );
@@ -736,7 +779,7 @@ function wfBacktrace() {
*/
function wfShowingResults( $offset, $limit ) {
global $wgLang;
- return wfMsg( 'showingresults', $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ) );
+ return wfMsgExt( 'showingresults', array( 'parseinline' ), $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ) );
}
/**
@@ -744,7 +787,7 @@ function wfShowingResults( $offset, $limit ) {
*/
function wfShowingResultsNum( $offset, $limit, $num ) {
global $wgLang;
- return wfMsg( 'showingresultsnum', $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ), $wgLang->formatNum( $num ) );
+ return wfMsgExt( 'showingresultsnum', array( 'parseinline' ), $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ), $wgLang->formatNum( $num ) );
}
/**
@@ -941,6 +984,26 @@ function wfArrayToCGI( $array1, $array2 = NULL )
}
/**
+ * Append a query string to an existing URL, which may or may not already
+ * have query string parameters already. If so, they will be combined.
+ *
+ * @param string $url
+ * @param string $query
+ * @return string
+ */
+function wfAppendQuery( $url, $query ) {
+ if( $query != '' ) {
+ if( false === strpos( $url, '?' ) ) {
+ $url .= '?';
+ } else {
+ $url .= '&';
+ }
+ $url .= $query;
+ }
+ return $url;
+}
+
+/**
* This is obsolete, use SquidUpdate::purge()
* @deprecated
*/
@@ -1104,9 +1167,15 @@ function wfHttpError( $code, $label, $desc ) {
* Note that some PHP configuration options may add output buffer
* layers which cannot be removed; these are left in place.
*
- * @parameter bool $resetGzipEncoding
+ * @param bool $resetGzipEncoding
*/
function wfResetOutputBuffers( $resetGzipEncoding=true ) {
+ if( $resetGzipEncoding ) {
+ // Suppress Content-Encoding and Content-Length
+ // headers from 1.10+s wfOutputHandler
+ global $wgDisableOutputCompression;
+ $wgDisableOutputCompression = true;
+ }
while( $status = ob_get_status() ) {
if( $status['type'] == 0 /* PHP_OUTPUT_HANDLER_INTERNAL */ ) {
// Probably from zlib.output_compression or other
@@ -1332,7 +1401,7 @@ define('TS_ISO_8601', 4);
/**
* An Exif timestamp (YYYY:MM:DD HH:MM:SS)
*
- * @url http://exif.org/Exif2-2.PDF The Exif 2.2 spec, see page 28 for the
+ * @see http://exif.org/Exif2-2.PDF The Exif 2.2 spec, see page 28 for the
* DateTime tag and page 36 for the DateTimeOriginal and
* DateTimeDigitized tags.
*/
@@ -1624,6 +1693,7 @@ function wfMkdirParents( $fullDir, $mode = 0777 ) {
foreach ( $createList as $dir ) {
# use chmod to override the umask, as suggested by the PHP manual
if ( !mkdir( $dir, $mode ) || !chmod( $dir, $mode ) ) {
+ wfDebugLog( 'mkdir', "Unable to create directory $dir\n" );
return false;
}
}
@@ -1750,14 +1820,14 @@ function wfShellExec( $cmd, &$retval=null ) {
}
if ( php_uname( 's' ) == 'Linux' ) {
- $time = ini_get( 'max_execution_time' );
+ $time = intval( ini_get( 'max_execution_time' ) );
$mem = intval( $wgMaxShellMemory );
$filesize = intval( $wgMaxShellFileSize );
if ( $time > 0 && $mem > 0 ) {
- $script = "$IP/bin/ulimit-tvf.sh";
+ $script = "$IP/bin/ulimit4.sh";
if ( is_executable( $script ) ) {
- $cmd = escapeshellarg( $script ) . " $time $mem $filesize $cmd";
+ $cmd = escapeshellarg( $script ) . " $time $mem $filesize " . escapeshellarg( $cmd );
}
}
} elseif ( php_uname( 's' ) == 'Windows NT' ) {
@@ -1844,23 +1914,83 @@ function wfBaseName( $path ) {
}
/**
+ * Generate a relative path name to the given file.
+ * May explode on non-matching case-insensitive paths,
+ * funky symlinks, etc.
+ *
+ * @param string $path Absolute destination path including target filename
+ * @param string $from Absolute source path, directory only
+ * @return string
+ */
+function wfRelativePath( $path, $from ) {
+ // Normalize mixed input on Windows...
+ $path = str_replace( '/', DIRECTORY_SEPARATOR, $path );
+ $from = str_replace( '/', DIRECTORY_SEPARATOR, $from );
+
+ $pieces = explode( DIRECTORY_SEPARATOR, dirname( $path ) );
+ $against = explode( DIRECTORY_SEPARATOR, $from );
+
+ // Trim off common prefix
+ while( count( $pieces ) && count( $against )
+ && $pieces[0] == $against[0] ) {
+ array_shift( $pieces );
+ array_shift( $against );
+ }
+
+ // relative dots to bump us to the parent
+ while( count( $against ) ) {
+ array_unshift( $pieces, '..' );
+ array_shift( $against );
+ }
+
+ array_push( $pieces, wfBaseName( $path ) );
+
+ return implode( DIRECTORY_SEPARATOR, $pieces );
+}
+
+/**
* Make a URL index, appropriate for the el_index field of externallinks.
*/
function wfMakeUrlIndex( $url ) {
- wfSuppressWarnings();
+ global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
$bits = parse_url( $url );
+ wfSuppressWarnings();
wfRestoreWarnings();
- if ( !$bits || $bits['scheme'] !== 'http' ) {
+ if ( !$bits ) {
return false;
}
+ // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it
+ $delimiter = '';
+ if ( in_array( $bits['scheme'] . '://' , $wgUrlProtocols ) ) {
+ $delimiter = '://';
+ } elseif ( in_array( $bits['scheme'] .':' , $wgUrlProtocols ) ) {
+ $delimiter = ':';
+ // parse_url detects for news: and mailto: the host part of an url as path
+ // We have to correct this wrong detection
+ if ( isset ( $bits['path'] ) ) {
+ $bits['host'] = $bits['path'];
+ $bits['path'] = '';
+ }
+ } else {
+ return false;
+ }
+
// Reverse the labels in the hostname, convert to lower case
- $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) );
+ // For emails reverse domainpart only
+ if ( $bits['scheme'] == 'mailto' ) {
+ $mailparts = explode( '@', $bits['host'] );
+ $domainpart = strtolower( implode( '.', array_reverse( explode( '.', $mailparts[1] ) ) ) );
+ $reversedHost = $domainpart . '@' . $mailparts[0];
+ } else {
+ $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) );
+ }
// Add an extra dot to the end
if ( substr( $reversedHost, -1, 1 ) !== '.' ) {
$reversedHost .= '.';
}
// Reconstruct the pseudo-URL
- $index = "http://$reversedHost";
+ $prot = $bits['scheme'];
+ $index = "$prot$delimiter$reversedHost";
// Leave out user and password. Add the port, path, query and fragment
if ( isset( $bits['port'] ) ) $index .= ':' . $bits['port'];
if ( isset( $bits['path'] ) ) {
@@ -1908,9 +2038,11 @@ function wfExplodeMarkup( $separator, $text ) {
* @param $sourceBase int 2-36
* @param $destBase int 2-36
* @param $pad int 1 or greater
+ * @param $lowercase bool
* @return string or false on invalid input
*/
-function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1 ) {
+function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1, $lowercase=true ) {
+ $input = strval( $input );
if( $sourceBase < 2 ||
$sourceBase > 36 ||
$destBase < 2 ||
@@ -1923,8 +2055,7 @@ function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1 ) {
$input == '' ) {
return false;
}
-
- $digitChars = '0123456789abcdefghijklmnopqrstuvwxyz';
+ $digitChars = ( $lowercase ) ? '0123456789abcdefghijklmnopqrstuvwxyz' : '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$inDigits = array();
$outChars = '';
@@ -2024,7 +2155,7 @@ function wfIsLocalURL( $url ) {
* Initialise php session
*/
function wfSetupSession() {
- global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain;
+ global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain, $wgCookieSecure;
if( $wgSessionsInMemcached ) {
require_once( 'MemcachedSessions.php' );
} elseif( 'files' != ini_get( 'session.save_handler' ) ) {
@@ -2032,7 +2163,7 @@ function wfSetupSession() {
# application, it will end up failing. Try to recover.
ini_set ( 'session.save_handler', 'files' );
}
- session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain );
+ session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure);
session_cache_limiter( 'private, must-revalidate' );
@session_start();
}
diff --git a/includes/HTMLCacheUpdate.php b/includes/HTMLCacheUpdate.php
index bda4720d..9a0b6a08 100644
--- a/includes/HTMLCacheUpdate.php
+++ b/includes/HTMLCacheUpdate.php
@@ -38,7 +38,7 @@ class HTMLCacheUpdate
function doUpdate() {
# Fetch the IDs
$cond = $this->getToCondition();
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( $this->mTable, $this->getFromField(), $cond, __METHOD__ );
$resWrap = new ResultWrapper( $dbr, $res );
if ( $dbr->numRows( $res ) != 0 ) {
@@ -136,7 +136,7 @@ class HTMLCacheUpdate
return;
}
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$timestamp = $dbw->timestamp();
$done = false;
@@ -184,6 +184,9 @@ class HTMLCacheUpdate
}
}
+/**
+ * @todo document (e.g. one-sentence top-level class description).
+ */
class HTMLCacheUpdateJob extends Job {
var $table, $start, $end;
@@ -218,7 +221,7 @@ class HTMLCacheUpdateJob extends Job {
$conds[] = "$fromField <= {$this->end}";
}
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( $this->table, $fromField, $conds, __METHOD__ );
$update->invalidateIDs( new ResultWrapper( $dbr, $res ) );
$dbr->freeResult( $res );
diff --git a/includes/HTMLFileCache.php b/includes/HTMLFileCache.php
index d85a4411..1d3778b2 100644
--- a/includes/HTMLFileCache.php
+++ b/includes/HTMLFileCache.php
@@ -1,8 +1,7 @@
<?php
/**
* Contain the HTMLFileCache class
- * @package MediaWiki
- * @subpackage Cache
+ * @addtogroup Cache
*/
/**
@@ -16,7 +15,6 @@
* $wgUseFileCache
* $wgFileCacheDirectory
* $wgUseGzip
- * @package MediaWiki
*/
class HTMLFileCache {
var $mTitle, $mFileCache;
diff --git a/includes/HTMLForm.php b/includes/HTMLForm.php
index 189e5c79..715c8c88 100644
--- a/includes/HTMLForm.php
+++ b/includes/HTMLForm.php
@@ -2,13 +2,11 @@
/**
* This file contain a class to easily build HTML forms as well as custom
* functions used by SpecialUserrights.php
- * @package MediaWiki
*/
/**
* Class to build various forms
*
- * @package MediaWiki
* @author jeluf, hashar
*/
class HTMLForm {
@@ -125,6 +123,7 @@ class HTMLForm {
function HTMLSelectGroups($selectname, $selectmsg, $selected=array(), $multiple=false, $size=6, $reverse=false) {
$groups = User::getAllGroups();
$out = htmlspecialchars( wfMsg( $selectmsg ) );
+ $out .= "<br />";
if( $multiple ) {
$attribs = array(
@@ -134,6 +133,7 @@ function HTMLSelectGroups($selectname, $selectmsg, $selected=array(), $multiple=
} else {
$attribs = array( 'name' => $selectname );
}
+ $attribs['style'] = 'width: 100%';
$out .= wfElement( 'select', $attribs, null );
foreach( $groups as $group ) {
diff --git a/includes/HistoryBlob.php b/includes/HistoryBlob.php
index a06b620d..9dfd6d61 100644
--- a/includes/HistoryBlob.php
+++ b/includes/HistoryBlob.php
@@ -1,82 +1,86 @@
<?php
/**
*
- * @package MediaWiki
*/
/**
* Pure virtual parent
- * @package MediaWiki
+ * @todo document (needs a one-sentence top-level class description, that answers the question: "what is a HistoryBlob?")
*/
-class HistoryBlob
+interface HistoryBlob
{
/**
* setMeta and getMeta currently aren't used for anything, I just thought
* they might be useful in the future.
* @param $meta String: a single string.
*/
- function setMeta( $meta ) {}
+ public function setMeta( $meta );
/**
* setMeta and getMeta currently aren't used for anything, I just thought
* they might be useful in the future.
* Gets the meta-value
*/
- function getMeta() {}
+ public function getMeta();
/**
* Adds an item of text, returns a stub object which points to the item.
* You must call setLocation() on the stub object before storing it to the
* database
*/
- function addItem() {}
+ public function addItem( $text );
/**
* Get item by hash
*/
- function getItem( $hash ) {}
+ public function getItem( $hash );
# Set the "default text"
# This concept is an odd property of the current DB schema, whereby each text item has a revision
# associated with it. The default text is the text of the associated revision. There may, however,
# be other revisions in the same object
- function setText() {}
+ public function setText( $text );
/**
* Get default text. This is called from Revision::getRevisionText()
*/
- function getText() {}
+ function getText();
}
/**
* The real object
- * @package MediaWiki
+ * @todo document (needs one-sentence top-level class description + function descriptions).
*/
-class ConcatenatedGzipHistoryBlob extends HistoryBlob
+class ConcatenatedGzipHistoryBlob implements HistoryBlob
{
- /* private */ var $mVersion = 0, $mCompressed = false, $mItems = array(), $mDefaultHash = '';
- /* private */ var $mFast = 0, $mSize = 0;
+ public $mVersion = 0, $mCompressed = false, $mItems = array(), $mDefaultHash = '';
+ public $mFast = 0, $mSize = 0;
- function ConcatenatedGzipHistoryBlob() {
+ /** Constructor */
+ public function ConcatenatedGzipHistoryBlob() {
if ( !function_exists( 'gzdeflate' ) ) {
throw new MWException( "Need zlib support to read or write this kind of history object (ConcatenatedGzipHistoryBlob)\n" );
}
}
+ #
+ # HistoryBlob implementation:
+ #
+
/** @todo document */
- function setMeta( $metaData ) {
+ public function setMeta( $metaData ) {
$this->uncompress();
$this->mItems['meta'] = $metaData;
}
/** @todo document */
- function getMeta() {
+ public function getMeta() {
$this->uncompress();
return $this->mItems['meta'];
}
/** @todo document */
- function addItem( $text ) {
+ public function addItem( $text ) {
$this->uncompress();
$hash = md5( $text );
$this->mItems[$hash] = $text;
@@ -87,7 +91,7 @@ class ConcatenatedGzipHistoryBlob extends HistoryBlob
}
/** @todo document */
- function getItem( $hash ) {
+ public function getItem( $hash ) {
$this->uncompress();
if ( array_key_exists( $hash, $this->mItems ) ) {
return $this->mItems[$hash];
@@ -97,13 +101,29 @@ class ConcatenatedGzipHistoryBlob extends HistoryBlob
}
/** @todo document */
- function removeItem( $hash ) {
+ public function setText( $text ) {
+ $this->uncompress();
+ $stub = $this->addItem( $text );
+ $this->mDefaultHash = $stub->mHash;
+ }
+
+ /** @todo document */
+ public function getText() {
+ $this->uncompress();
+ return $this->getItem( $this->mDefaultHash );
+ }
+
+ # HistoryBlob implemented.
+
+
+ /** @todo document */
+ public function removeItem( $hash ) {
$this->mSize -= strlen( $this->mItems[$hash] );
unset( $this->mItems[$hash] );
}
/** @todo document */
- function compress() {
+ public function compress() {
if ( !$this->mCompressed ) {
$this->mItems = gzdeflate( serialize( $this->mItems ) );
$this->mCompressed = true;
@@ -111,25 +131,13 @@ class ConcatenatedGzipHistoryBlob extends HistoryBlob
}
/** @todo document */
- function uncompress() {
+ public function uncompress() {
if ( $this->mCompressed ) {
$this->mItems = unserialize( gzinflate( $this->mItems ) );
$this->mCompressed = false;
}
}
- /** @todo document */
- function getText() {
- $this->uncompress();
- return $this->getItem( $this->mDefaultHash );
- }
-
- /** @todo document */
- function setText( $text ) {
- $this->uncompress();
- $stub = $this->addItem( $text );
- $this->mDefaultHash = $stub->mHash;
- }
/** @todo document */
function __sleep() {
@@ -145,7 +153,7 @@ class ConcatenatedGzipHistoryBlob extends HistoryBlob
/**
* Determines if this object is happy
*/
- function isHappy( $maxFactor, $factorThreshold ) {
+ public function isHappy( $maxFactor, $factorThreshold ) {
if ( count( $this->mItems ) == 0 ) {
return true;
}
@@ -179,7 +187,7 @@ $wgBlobCache = array();
/**
- * @package MediaWiki
+ * @todo document (needs one-sentence top-level class description + some function descriptions).
*/
class HistoryBlobStub {
var $mOldId, $mHash, $mRef;
@@ -197,28 +205,28 @@ class HistoryBlobStub {
$this->mOldId = $id;
}
- /**
- * Sets the location (old_id) of the referring object
- */
+ /**
+ * Sets the location (old_id) of the referring object
+ */
function setReferrer( $id ) {
$this->mRef = $id;
}
- /**
- * Gets the location of the referring object
- */
+ /**
+ * Gets the location of the referring object
+ */
function getReferrer() {
return $this->mRef;
}
/** @todo document */
function getText() {
- $fname = 'HistoryBlob::getText';
+ $fname = 'HistoryBlobStub::getText';
global $wgBlobCache;
if( isset( $wgBlobCache[$this->mOldId] ) ) {
$obj = $wgBlobCache[$this->mOldId];
} else {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow( 'text', array( 'old_flags', 'old_text' ), array( 'old_id' => $this->mOldId ) );
if( !$row ) {
return false;
@@ -273,8 +281,6 @@ class HistoryBlobStub {
*
* Serialized HistoryBlobCurStub objects will be inserted into the text table
* on conversion if $wgFastSchemaUpgrades is set to true.
- *
- * @package MediaWiki
*/
class HistoryBlobCurStub {
var $mCurId;
@@ -294,7 +300,7 @@ class HistoryBlobCurStub {
/** @todo document */
function getText() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow( 'cur', array( 'cur_text' ), array( 'cur_id' => $this->mCurId ) );
if( !$row ) {
return false;
diff --git a/includes/Hooks.php b/includes/Hooks.php
index 2eecfd72..b428b08d 100644
--- a/includes/Hooks.php
+++ b/includes/Hooks.php
@@ -18,7 +18,6 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
* @author Evan Prodromou <evan@wikitravel.org>
- * @package MediaWiki
* @see hooks.txt
*/
@@ -103,7 +102,7 @@ function wfRunHooks($event, $args = null) {
if ( isset( $object ) ) {
$func = get_class( $object ) . '::' . $method;
$callback = array( $object, $method );
- } elseif ( false !== ( $pos = strpos( '::', $func ) ) ) {
+ } elseif ( false !== ( $pos = strpos( $func, '::' ) ) ) {
$callback = array( substr( $func, 0, $pos ), substr( $func, $pos + 2 ) );
} else {
$callback = $func;
diff --git a/includes/IP.php b/includes/IP.php
index edf4af7a..8a2756c9 100644
--- a/includes/IP.php
+++ b/includes/IP.php
@@ -1,8 +1,5 @@
<?php
/*
- * Collection of public static functions to play with IP address
- * and IP blocks.
- *
* @Author "Ashar Voultoiz" <hashar@altern.org>
* @License GPL v2 or later
*/
@@ -12,22 +9,231 @@
// An IP is made of 4 bytes from x00 to xFF which is d0 to d255
define( 'RE_IP_BYTE', '(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|0?[0-9]?[0-9])');
define( 'RE_IP_ADD' , RE_IP_BYTE . '\.' . RE_IP_BYTE . '\.' . RE_IP_BYTE . '\.' . RE_IP_BYTE );
-// An IP block is an IP address and a prefix (d1 to d32)
+// An IPv4 block is an IP address and a prefix (d1 to d32)
define( 'RE_IP_PREFIX', '(3[0-2]|[12]?\d)');
define( 'RE_IP_BLOCK', RE_IP_ADD . '\/' . RE_IP_PREFIX);
// For IPv6 canonicalization (NOT for strict validation; these are quite lax!)
define( 'RE_IPV6_WORD', '([0-9A-Fa-f]{1,4})' );
define( 'RE_IPV6_GAP', ':(?:0+:)*(?::(?:0+:)*)?' );
define( 'RE_IPV6_V4_PREFIX', '0*' . RE_IPV6_GAP . '(?:ffff:)?' );
+// An IPv6 block is an IP address and a prefix (d1 to d128)
+define( 'RE_IPV6_PREFIX', '(12[0-8]|1[01][0-9]|[1-9]?\d)');
+// An IPv6 IP is made up of 8 octets. However abbreviations like "::" can be used. This is lax!
+define( 'RE_IPV6_ADD', '(:(:' . RE_IPV6_WORD . '){1,7}|' . RE_IPV6_WORD . '(:{1,2}' . RE_IPV6_WORD . '|::$){1,7})' );
+define( 'RE_IPV6_BLOCK', RE_IPV6_ADD . '\/' . RE_IPV6_PREFIX );
+// This might be useful for regexps used elsewhere, matches any IPv6 or IPv6 address or network
+define( 'IP_ADDRESS_STRING', RE_IP_ADD . '(\/' . RE_IP_PREFIX . '|)|' . RE_IPV6_ADD . '(\/' . RE_IPV6_PREFIX . '|)');
+/**
+ * A collection of public static functions to play with IP address
+ * and IP blocks.
+ */
class IP {
+ /**
+ * Given a string, determine if it as valid IP
+ * Unlike isValid(), this looks for networks too
+ * @param $ip IP address.
+ * @return string
+ */
+ public static function isIPAddress( $ip ) {
+ if ( !$ip ) return false;
+ if ( is_array( $ip ) ) {
+ throw new MWException( "invalid value passed to " . __METHOD__ );
+ }
+ // IPv6 IPs with two "::" strings are ambiguous and thus invalid
+ return preg_match( '/^' . IP_ADDRESS_STRING . '$/', $ip) && ( substr_count($ip, '::') < 2 );
+ }
+
+ public static function isIPv6( $ip ) {
+ if ( !$ip ) return false;
+ if( is_array( $ip ) ) {
+ throw new MWException( "invalid value passed to " . __METHOD__ );
+ }
+ // IPv6 IPs with two "::" strings are ambiguous and thus invalid
+ return preg_match( '/^' . RE_IPV6_ADD . '(\/' . RE_IPV6_PREFIX . '|)$/', $ip) && ( substr_count($ip, '::') < 2);
+ }
+
+ public static function isIPv4( $ip ) {
+ if ( !$ip ) return false;
+ return preg_match( '/^' . RE_IP_ADD . '(\/' . RE_IP_PREFIX . '|)$/', $ip);
+ }
+
+ /**
+ * Given an IP address in dotted-quad notation, returns an IPv6 octet.
+ * See http://www.answers.com/topic/ipv4-compatible-address
+ * IPs with the first 92 bits as zeros are reserved from IPv6
+ * @param $ip quad-dotted IP address.
+ * @return string
+ */
+ public static function IPv4toIPv6( $ip ) {
+ if ( !$ip ) return null;
+ // Convert only if needed
+ if ( self::isIPv6( $ip ) ) return $ip;
+ // IPv4 CIDRs
+ if ( strpos( $ip, '/' ) !== false ) {
+ $parts = explode( '/', $ip, 2 );
+ if ( count( $parts ) != 2 ) {
+ return false;
+ }
+ $network = self::toUnsigned( $parts[0] );
+ if ( $network !== false && is_numeric( $parts[1] ) && $parts[1] >= 0 && $parts[1] <= 32 ) {
+ $bits = $parts[1] + 96;
+ return self::toOctet( $network ) . "/$bits";
+ } else {
+ return false;
+ }
+ }
+ return self::toOctet( self::toUnsigned( $ip ) );
+ }
/**
+ * Given an IPv6 address in octet notation, returns an unsigned integer.
+ * @param $ip octet ipv6 IP address.
+ * @return string
+ */
+ public static function toUnsigned6( $ip ) {
+ if ( !$ip ) return null;
+ $ip = explode(':', self::sanitizeIP( $ip ) );
+ $r_ip = '';
+ foreach ($ip as $v) {
+ $r_ip .= str_pad( $v, 4, 0, STR_PAD_LEFT );
+ }
+ $r_ip = wfBaseConvert( $r_ip, 16, 10 );
+ return $r_ip;
+ }
+
+ /**
+ * Given an IPv6 address in octet notation, returns the expanded octet.
+ * IPv4 IPs will be trimmed, thats it...
+ * @param $ip octet ipv6 IP address.
+ * @return string
+ */
+ public static function sanitizeIP( $ip ) {
+ if ( !$ip ) return null;
+ // Trim and return IPv4 addresses
+ if ( self::isIPv4($ip) ) return trim($ip);
+ // Only IPv6 addresses can be expanded
+ if ( !self::isIPv6($ip) ) return $ip;
+ // Remove any whitespaces, convert to upper case
+ $ip = strtoupper( trim($ip) );
+ // Expand zero abbreviations
+ if ( strpos( $ip, '::' ) !== false ) {
+ $ip = str_replace('::', str_repeat(':0', 8 - substr_count($ip, ':')) . ':', $ip);
+ }
+ // For IPs that start with "::", correct the final IP so that it starts with '0' and not ':'
+ if ( $ip[0] == ':' ) $ip = "0$ip";
+ // Remove leading zereos from each bloc as needed
+ $ip = preg_replace( '/(^|:)0+' . RE_IPV6_WORD . '/', '$1$2', $ip );
+ return $ip;
+ }
+
+ /**
+ * Given an unsigned integer, returns an IPv6 address in octet notation
+ * @param $ip integer IP address.
+ * @return string
+ */
+ public static function toOctet( $ip_int ) {
+ // Convert to padded uppercase hex
+ $ip_hex = wfBaseConvert($ip_int, 10, 16, 32, false);
+ // Seperate into 8 octets
+ $ip_oct = substr( $ip_hex, 0, 4 );
+ for ($n=1; $n < 8; $n++) {
+ $ip_oct .= ':' . substr($ip_hex, 4*$n, 4);
+ }
+ // NO leading zeroes
+ $ip_oct = preg_replace( '/(^|:)0+' . RE_IPV6_WORD . '/', '$1$2', $ip_oct );
+ return $ip_oct;
+ }
+
+ /**
+ * Convert a network specification in IPv6 CIDR notation to an integer network and a number of bits
+ * @return array(string, int)
+ */
+ public static function parseCIDR6( $range ) {
+ # Expand any IPv6 IP
+ $parts = explode( '/', IP::sanitizeIP( $range ), 2 );
+ if ( count( $parts ) != 2 ) {
+ return array( false, false );
+ }
+ $network = self::toUnsigned6( $parts[0] );
+ if ( $network !== false && is_numeric( $parts[1] ) && $parts[1] >= 0 && $parts[1] <= 128 ) {
+ $bits = $parts[1];
+ if ( $bits == 0 ) {
+ $network = 0;
+ } else {
+ # Native 32 bit functions WONT work here!!!
+ # Convert to a padded binary number
+ $network = wfBaseConvert( $network, 10, 2, 128 );
+ # Truncate the last (128-$bits) bits and replace them with zeros
+ $network = str_pad( substr( $network, 0, $bits ), 128, 0, STR_PAD_RIGHT );
+ # Convert back to an integer
+ $network = wfBaseConvert( $network, 2, 10 );
+ }
+ } else {
+ $network = false;
+ $bits = false;
+ }
+ return array( $network, $bits );
+ }
+
+ /**
+ * Given a string range in a number of formats, return the start and end of
+ * the range in hexadecimal. For IPv6.
+ *
+ * Formats are:
+ * 2001:0db8:85a3::7344/96 CIDR
+ * 2001:0db8:85a3::7344 - 2001:0db8:85a3::7344 Explicit range
+ * 2001:0db8:85a3::7344/96 Single IP
+ * @return array(string, int)
+ */
+ public static function parseRange6( $range ) {
+ # Expand any IPv6 IP
+ $range = IP::sanitizeIP( $range );
+ if ( strpos( $range, '/' ) !== false ) {
+ # CIDR
+ list( $network, $bits ) = self::parseCIDR6( $range );
+ if ( $network === false ) {
+ $start = $end = false;
+ } else {
+ $start = wfBaseConvert( $network, 10, 16, 32, false );
+ # Turn network to binary (again)
+ $end = wfBaseConvert( $network, 10, 2, 128 );
+ # Truncate the last (128-$bits) bits and replace them with ones
+ $end = str_pad( substr( $end, 0, $bits ), 128, 1, STR_PAD_RIGHT );
+ # Convert to hex
+ $end = wfBaseConvert( $end, 2, 16, 32, false );
+ # see toHex() comment
+ $start = "v6-$start"; $end = "v6-$end";
+ }
+ } elseif ( strpos( $range, '-' ) !== false ) {
+ # Explicit range
+ list( $start, $end ) = array_map( 'trim', explode( '-', $range, 2 ) );
+ $start = self::toUnsigned6( $start ); $end = self::toUnsigned6( $end );
+ if ( $start > $end ) {
+ $start = $end = false;
+ } else {
+ $start = wfBaseConvert( $start, 10, 16, 32, false );
+ $end = wfBaseConvert( $end, 10, 16, 32, false );
+ }
+ # see toHex() comment
+ $start = "v6-$start"; $end = "v6-$end";
+ } else {
+ # Single IP
+ $start = $end = self::toHex( $range );
+ }
+ if ( $start === false || $end === false ) {
+ return array( false, false );
+ } else {
+ return array( $start, $end );
+ }
+ }
+
+ /**
* Validate an IP address.
* @return boolean True if it is valid.
*/
public static function isValid( $ip ) {
- return preg_match( '/^' . RE_IP_ADD . '$/', $ip) ;
+ return ( preg_match( '/^' . RE_IP_ADD . '$/', $ip) || preg_match( '/^' . RE_IPV6_ADD . '$/', $ip) );
}
/**
@@ -44,7 +250,7 @@ class IP {
* Comes from ProxyTools.php
*/
public static function isPublic( $ip ) {
- $n = IP::toUnsigned( $ip );
+ $n = self::toUnsigned( $ip );
if ( !$n ) {
return false;
}
@@ -67,8 +273,8 @@ class IP {
}
foreach ( $privateRanges as $r ) {
- $start = IP::toUnsigned( $r[0] );
- $end = IP::toUnsigned( $r[1] );
+ $start = self::toUnsigned( $r[0] );
+ $end = self::toUnsigned( $r[1] );
if ( $n >= $start && $n <= $end ) {
return false;
}
@@ -80,15 +286,17 @@ class IP {
* Split out an IP block as an array of 4 bytes and a mask,
* return false if it can't be determined
*
- * @parameter $ip string A quad dotted IP address
+ * @param $ip string A quad dotted/octet IP address
* @return array
*/
public static function toArray( $ipblock ) {
$matches = array();
- if(! preg_match( '/^' . RE_IP_ADD . '(?:\/(?:'.RE_IP_PREFIX.'))?' . '$/', $ipblock, $matches ) ) {
- return false;
- } else {
+ if( preg_match( '/^' . RE_IP_ADD . '(?:\/(?:'.RE_IP_PREFIX.'))?' . '$/', $ipblock, $matches ) ) {
+ return $matches;
+ } else if ( preg_match( '/^' . RE_IPV6_ADD . '(?:\/(?:'.RE_IPV6_PREFIX.'))?' . '$/', $ipblock, $matches ) ) {
return $matches;
+ } else {
+ return false;
}
}
@@ -100,23 +308,29 @@ class IP {
* function for an IPv6 address will be prefixed with "v6-", a non-
* hexadecimal string which sorts after the IPv4 addresses.
*
- * @param $ip Quad dotted IP address.
+ * @param $ip Quad dotted/octet IP address.
+ * @return hexidecimal
*/
public static function toHex( $ip ) {
$n = self::toUnsigned( $ip );
if ( $n !== false ) {
- $n = sprintf( '%08X', $n );
+ $n = ( self::isIPv6($ip) ) ? "v6-" . wfBaseConvert( $n, 10, 16, 32, false ) : wfBaseConvert( $n, 10, 16, 8, false );
}
return $n;
}
/**
- * Given an IP address in dotted-quad notation, returns an unsigned integer.
+ * Given an IP address in dotted-quad/octet notation, returns an unsigned integer.
* Like ip2long() except that it actually works and has a consistent error return value.
* Comes from ProxyTools.php
* @param $ip Quad dotted IP address.
+ * @return integer
*/
public static function toUnsigned( $ip ) {
+ // Use IPv6 functions if needed
+ if ( self::isIPv6( $ip ) ) {
+ return self::toUnsigned6( $ip );
+ }
if ( $ip == '255.255.255.255' ) {
$n = -1;
} else {
@@ -149,13 +363,14 @@ class IP {
/**
* Convert a network specification in CIDR notation to an integer network and a number of bits
+ * @return array(string, int)
*/
public static function parseCIDR( $range ) {
$parts = explode( '/', $range, 2 );
if ( count( $parts ) != 2 ) {
return array( false, false );
}
- $network = IP::toSigned( $parts[0] );
+ $network = self::toSigned( $parts[0] );
if ( $network !== false && is_numeric( $parts[1] ) && $parts[1] >= 0 && $parts[1] <= 32 ) {
$bits = $parts[1];
if ( $bits == 0 ) {
@@ -182,11 +397,20 @@ class IP {
* 1.2.3.4/24 CIDR
* 1.2.3.4 - 1.2.3.5 Explicit range
* 1.2.3.4 Single IP
+ *
+ * 2001:0db8:85a3::7344/96 CIDR
+ * 2001:0db8:85a3::7344 - 2001:0db8:85a3::7344 Explicit range
+ * 2001:0db8:85a3::7344 Single IP
+ * @return array(string, int)
*/
public static function parseRange( $range ) {
+ // Use IPv6 functions if needed
+ if ( self::isIPv6( $range ) ) {
+ return self::parseRange6( $range );
+ }
if ( strpos( $range, '/' ) !== false ) {
# CIDR
- list( $network, $bits ) = IP::parseCIDR( $range );
+ list( $network, $bits ) = self::parseCIDR( $range );
if ( $network === false ) {
$start = $end = false;
} else {
@@ -196,15 +420,16 @@ class IP {
} elseif ( strpos( $range, '-' ) !== false ) {
# Explicit range
list( $start, $end ) = array_map( 'trim', explode( '-', $range, 2 ) );
+ $start = self::toUnsigned( $start ); $end = self::toUnsigned( $end );
if ( $start > $end ) {
$start = $end = false;
} else {
- $start = IP::toHex( $start );
- $end = IP::toHex( $end );
+ $start = sprintf( '%08X', $start );
+ $end = sprintf( '%08X', $end );
}
} else {
# Single IP
- $start = $end = IP::toHex( $range );
+ $start = $end = self::toHex( $range );
}
if ( $start === false || $end === false ) {
return array( false, false );
@@ -214,18 +439,15 @@ class IP {
}
/**
- * Determine if a given integer IPv4 address is in a given CIDR network
+ * Determine if a given IPv4/IPv6 address is in a given CIDR network
* @param $addr The address to check against the given range.
* @param $range The range to check the given address against.
* @return bool Whether or not the given address is in the given range.
*/
public static function isInRange( $addr, $range ) {
- $unsignedIP = IP::toUnsigned($addr);
- list( $start, $end ) = IP::parseRange($range);
-
- $start = hexdec($start);
- $end = hexdec($end);
-
+ // Convert to IPv6 if needed
+ $unsignedIP = self::toHex( $addr );
+ list( $start, $end ) = self::parseRange( $range );
return (($unsignedIP >= $start) && ($unsignedIP <= $end));
}
@@ -240,10 +462,11 @@ class IP {
* @return valid dotted quad IPv4 address or null
*/
public static function canonicalize( $addr ) {
- if ( IP::isValid( $addr ) )
+ if ( self::isValid( $addr ) )
return $addr;
// IPv6 loopback address
+ $m = array();
if ( preg_match( '/^0*' . RE_IPV6_GAP . '1$/', $addr, $m ) )
return '127.0.0.1';
diff --git a/includes/Image.php b/includes/Image.php
index 7a6442c3..09c2286e 100644
--- a/includes/Image.php
+++ b/includes/Image.php
@@ -1,6 +1,5 @@
<?php
/**
- * @package MediaWiki
*/
/**
@@ -15,17 +14,24 @@
/**
* Bump this number when serialized cache records may be incompatible.
*/
-define( 'MW_IMAGE_VERSION', 1 );
+define( 'MW_IMAGE_VERSION', 2 );
/**
* Class to represent an image
*
* Provides methods to retrieve paths (physical, logical, URL),
* to generate thumbnails or for uploading.
- * @package MediaWiki
+ *
+ * @addtogroup Media
*/
class Image
{
+ const DELETED_FILE = 1;
+ const DELETED_COMMENT = 2;
+ const DELETED_USER = 4;
+ const DELETED_RESTRICTED = 8;
+ const RENDER_NOW = 1;
+
/**#@+
* @private
*/
@@ -43,6 +49,7 @@ class Image
$attr, # /
$type, # MEDIATYPE_xxx (bitmap, drawing, audio...)
$mime, # MIME type, determined by MimeMagic::guessMimeType
+ $extension, # The file extension (constructor)
$size, # Size in bytes (loadFromXxx)
$metadata, # Metadata
$dataLoaded, # Whether or not all this has been loaded from the database (loadFromXxx)
@@ -81,18 +88,16 @@ class Image
}
$this->title =& $title;
$this->name = $title->getDBkey();
- $this->metadata = serialize ( array() ) ;
+ $this->metadata = '';
$n = strrpos( $this->name, '.' );
$this->extension = Image::normalizeExtension( $n ?
substr( $this->name, $n + 1 ) : '' );
$this->historyLine = 0;
- $this->page = 1;
$this->dataLoaded = false;
}
-
/**
* Normalize a file extension to the common form, and ensure it's clean.
* Extensions with non-alphanumeric characters will be discarded.
@@ -115,7 +120,7 @@ class Image
return '';
}
}
-
+
/**
* Get the memcached keys
* Returns an array, first element is the local cache key, second is the shared cache key, if there is one
@@ -264,32 +269,26 @@ class Image
$this->mime = $magic->guessMimeType($this->imagePath,true);
$this->type = $magic->getMediaType($this->imagePath,$this->mime);
+ $handler = MediaHandler::getHandler( $this->mime );
# Get size in bytes
$this->size = filesize( $this->imagePath );
- $magic=& MimeMagic::singleton();
-
- # Height and width
- wfSuppressWarnings();
- if( $this->mime == 'image/svg' ) {
- $gis = wfGetSVGsize( $this->imagePath );
- } elseif( $this->mime == 'image/vnd.djvu' ) {
- $deja = new DjVuImage( $this->imagePath );
- $gis = $deja->getImageSize();
- } elseif ( !$magic->isPHPImageType( $this->mime ) ) {
- # Don't try to get the width and height of sound and video files, that's bad for performance
- $gis = false;
+ # Height, width and metadata
+ if ( $handler ) {
+ $gis = $handler->getImageSize( $this, $this->imagePath );
+ $this->metadata = $handler->getMetadata( $this, $this->imagePath );
} else {
- $gis = getimagesize( $this->imagePath );
+ $gis = false;
+ $this->metadata = '';
}
- wfRestoreWarnings();
wfDebug(__METHOD__.': '.$this->imagePath." loaded, ".$this->size." bytes, ".$this->mime.".\n");
}
else {
$this->mime = NULL;
$this->type = MEDIATYPE_UNKNOWN;
+ $this->metadata = '';
wfDebug(__METHOD__.': '.$this->imagePath." NOT FOUND!\n");
}
@@ -308,13 +307,6 @@ class Image
# as ther's only one thread of execution, this should be safe anyway.
$this->dataLoaded = true;
-
- if ( $this->mime == 'image/vnd.djvu' ) {
- $this->metadata = $deja->retrieveMetaData();
- } else {
- $this->metadata = serialize( $this->retrieveExifData( $this->imagePath ) );
- }
-
if ( isset( $gis['bits'] ) ) $this->bits = $gis['bits'];
else $this->bits = 0;
@@ -328,7 +320,7 @@ class Image
global $wgUseSharedUploads, $wgSharedUploadDBname, $wgSharedUploadDBprefix, $wgContLang;
wfProfileIn( __METHOD__ );
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$this->checkDBSchema($dbr);
$row = $dbr->selectRow( 'image',
@@ -341,15 +333,13 @@ class Image
$this->loadFromRow( $row );
$this->imagePath = $this->getFullPath();
// Check for rows from a previous schema, quietly upgrade them
- if ( is_null($this->type) ) {
- $this->upgradeRow();
- }
+ $this->maybeUpgradeRow();
} elseif ( $wgUseSharedUploads && $wgSharedUploadDBname ) {
# In case we're on a wgCapitalLinks=false wiki, we
# capitalize the first letter of the filename before
# looking it up in the shared repository.
$name = $wgContLang->ucfirst($this->name);
- $dbc =& wfGetDB( DB_SLAVE, 'commons' );
+ $dbc = Image::getCommonsDB();
$row = $dbc->selectRow( "`$wgSharedUploadDBname`.{$wgSharedUploadDBprefix}image",
array(
@@ -364,9 +354,7 @@ class Image
$this->loadFromRow( $row );
// Check for rows from a previous schema, quietly upgrade them
- if ( is_null($this->type) ) {
- $this->upgradeRow();
- }
+ $this->maybeUpgradeRow();
}
}
@@ -378,7 +366,7 @@ class Image
$this->type = 0;
$this->fileExists = false;
$this->fromSharedDirectory = false;
- $this->metadata = serialize ( array() ) ;
+ $this->metadata = '';
$this->mime = false;
}
@@ -405,9 +393,7 @@ class Image
if (!$minor) $minor= "unknown";
$this->mime = $major.'/'.$minor;
}
-
$this->metadata = $row->img_metadata;
- if ( $this->metadata == "" ) $this->metadata = serialize ( array() ) ;
$this->dataLoaded = true;
}
@@ -434,8 +420,21 @@ class Image
}
/**
- * Metadata was loaded from the database, but the row had a marker indicating it needs to be
- * upgraded from the 1.4 schema, which had no width, height, bits or type. Upgrade the row.
+ * Upgrade a row if it needs it
+ */
+ function maybeUpgradeRow() {
+ if ( is_null($this->type) || $this->mime == 'image/svg' ) {
+ $this->upgradeRow();
+ } else {
+ $handler = $this->getHandler();
+ if ( $handler && !$handler->isMetadataValid( $this, $this->metadata ) ) {
+ $this->upgradeRow();
+ }
+ }
+ }
+
+ /**
+ * Fix assorted version-related problems with the image row by reloading it from the file
*/
function upgradeRow() {
global $wgDBname, $wgSharedUploadDBname;
@@ -451,17 +450,16 @@ class Image
// Write to the other DB using selectDB, not database selectors
// This avoids breaking replication in MySQL
- $dbw =& wfGetDB( DB_MASTER, 'commons' );
- $dbw->selectDB( $wgSharedUploadDBname );
+ $dbw = Image::getCommonsDB();
} else {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
}
$this->checkDBSchema($dbw);
list( $major, $minor ) = self::splitMime( $this->mime );
- wfDebug(__METHOD__.': upgrading '.$this->name." to 1.5 schema\n");
+ wfDebug(__METHOD__.': upgrading '.$this->name." to the current schema\n");
$dbw->update( 'image',
array(
@@ -479,7 +477,7 @@ class Image
}
wfProfileOut( __METHOD__ );
}
-
+
/**
* Split an internet media type into its two components; if not
* a two-part name, set the minor type to 'unknown'.
@@ -554,23 +552,49 @@ class Image
/**
* Return the width of the image
*
- * Returns -1 if the file specified is not a known image type
+ * Returns false on error
* @public
*/
- function getWidth() {
+ function getWidth( $page = 1 ) {
$this->load();
- return $this->width;
+ if ( $this->isMultipage() ) {
+ $dim = $this->getHandler()->getPageDimensions( $this, $page );
+ if ( $dim ) {
+ return $dim['width'];
+ } else {
+ return false;
+ }
+ } else {
+ return $this->width;
+ }
}
/**
* Return the height of the image
*
- * Returns -1 if the file specified is not a known image type
+ * Returns false on error
* @public
*/
- function getHeight() {
+ function getHeight( $page = 1 ) {
$this->load();
- return $this->height;
+ if ( $this->isMultipage() ) {
+ $dim = $this->getHandler()->getPageDimensions( $this, $page );
+ if ( $dim ) {
+ return $dim['height'];
+ } else {
+ return false;
+ }
+ } else {
+ return $this->height;
+ }
+ }
+
+ /**
+ * Get handler-specific metadata
+ */
+ function getMetadata() {
+ $this->load();
+ return $this->metadata;
}
/**
@@ -610,58 +634,10 @@ class Image
* @todo remember the result of this check.
*/
function canRender() {
- global $wgUseImageMagick, $wgDjvuRenderer;
-
- if( $this->getWidth()<=0 || $this->getHeight()<=0 ) return false;
-
- $mime= $this->getMimeType();
-
- if (!$mime || $mime==='unknown' || $mime==='unknown/unknown') return false;
-
- #if it's SVG, check if there's a converter enabled
- if ($mime === 'image/svg') {
- global $wgSVGConverters, $wgSVGConverter;
-
- if ($wgSVGConverter && isset( $wgSVGConverters[$wgSVGConverter])) {
- wfDebug( "Image::canRender: SVG is ready!\n" );
- return true;
- } else {
- wfDebug( "Image::canRender: SVG renderer missing\n" );
- }
- }
-
- #image formats available on ALL browsers
- if ( $mime === 'image/gif'
- || $mime === 'image/png'
- || $mime === 'image/jpeg' ) return true;
-
- #image formats that can be converted to the above formats
- if ($wgUseImageMagick) {
- #convertable by ImageMagick (there are more...)
- if ( $mime === 'image/vnd.wap.wbmp'
- || $mime === 'image/x-xbitmap'
- || $mime === 'image/x-xpixmap'
- #|| $mime === 'image/x-icon' #file may be split into multiple parts
- || $mime === 'image/x-portable-anymap'
- || $mime === 'image/x-portable-bitmap'
- || $mime === 'image/x-portable-graymap'
- || $mime === 'image/x-portable-pixmap'
- #|| $mime === 'image/x-photoshop' #this takes a lot of CPU and RAM!
- || $mime === 'image/x-rgb'
- || $mime === 'image/x-bmp'
- || $mime === 'image/tiff' ) return true;
- }
- else {
- #convertable by the PHP GD image lib
- if ( $mime === 'image/vnd.wap.wbmp'
- || $mime === 'image/x-xbitmap' ) return true;
- }
- if ( $mime === 'image/vnd.djvu' && isset( $wgDjvuRenderer ) && $wgDjvuRenderer ) return true;
-
- return false;
+ $handler = $this->getHandler();
+ return $handler && $handler->canRender();
}
-
/**
* Return true if the file is of a type that can't be directly
* rendered by typical browsers and needs to be re-rasterized.
@@ -673,13 +649,8 @@ class Image
* @return bool
*/
function mustRender() {
- $mime= $this->getMimeType();
-
- if ( $mime === "image/gif"
- || $mime === "image/png"
- || $mime === "image/jpeg" ) return false;
-
- return true;
+ $handler = $this->getHandler();
+ return $handler && $handler->mustRender();
}
/**
@@ -745,15 +716,7 @@ class Image
* @public
*/
function getEscapeLocalURL( $query=false) {
- $this->getTitle();
- if ( $query === false ) {
- if ( $this->page != 1 ) {
- $query = 'page=' . $this->page;
- } else {
- $query = '';
- }
- }
- return $this->title->escapeLocalURL( $query );
+ return $this->getTitle()->escapeLocalURL( $query );
}
/**
@@ -801,74 +764,83 @@ class Image
* @todo document
* @private
*/
- function thumbUrl( $width, $subdir='thumb') {
+ function thumbUrlFromName( $thumbName, $subdir = 'thumb' ) {
global $wgUploadPath, $wgUploadBaseUrl, $wgSharedUploadPath;
- global $wgSharedThumbnailScriptPath, $wgThumbnailScriptPath;
+ if($this->fromSharedDirectory) {
+ $base = '';
+ $path = $wgSharedUploadPath;
+ } else {
+ $base = $wgUploadBaseUrl;
+ $path = $wgUploadPath;
+ }
+ if ( Image::isHashed( $this->fromSharedDirectory ) ) {
+ $hashdir = wfGetHashPath($this->name, $this->fromSharedDirectory) .
+ wfUrlencode( $this->name );
+ } else {
+ $hashdir = '';
+ }
+ $url = "{$base}{$path}/{$subdir}{$hashdir}/" . wfUrlencode( $thumbName );
+ return $url;
+ }
- // Generate thumb.php URL if possible
- $script = false;
- $url = false;
+ /**
+ * @deprecated Use $image->transform()->getUrl() or thumbUrlFromName()
+ */
+ function thumbUrl( $width, $subdir = 'thumb' ) {
+ $name = $this->thumbName( array( 'width' => $width ) );
+ if ( strval( $name ) !== '' ) {
+ return array( false, $this->thumbUrlFromName( $name, $subdir ) );
+ } else {
+ return array( false, false );
+ }
+ }
+ function getTransformScript() {
+ global $wgSharedThumbnailScriptPath, $wgThumbnailScriptPath;
if ( $this->fromSharedDirectory ) {
- if ( $wgSharedThumbnailScriptPath ) {
- $script = $wgSharedThumbnailScriptPath;
- }
+ $script = $wgSharedThumbnailScriptPath;
} else {
- if ( $wgThumbnailScriptPath ) {
- $script = $wgThumbnailScriptPath;
- }
+ $script = $wgThumbnailScriptPath;
}
if ( $script ) {
- $url = $script . '?f=' . urlencode( $this->name ) . '&w=' . urlencode( $width );
- if( $this->mustRender() ) {
- $url.= '&r=1';
- }
+ return "$script?f=" . urlencode( $this->name );
} else {
- $name = $this->thumbName( $width );
- if($this->fromSharedDirectory) {
- $base = '';
- $path = $wgSharedUploadPath;
- } else {
- $base = $wgUploadBaseUrl;
- $path = $wgUploadPath;
- }
- if ( Image::isHashed( $this->fromSharedDirectory ) ) {
- $url = "{$base}{$path}/{$subdir}" .
- wfGetHashPath($this->name, $this->fromSharedDirectory)
- . $this->name.'/'.$name;
- $url = wfUrlencode( $url );
- } else {
- $url = "{$base}{$path}/{$subdir}/{$name}";
- }
+ return false;
}
- return array( $script !== false, $url );
}
/**
- * Return the file name of a thumbnail of the specified width
+ * Get a ThumbnailImage which is the same size as the source
+ */
+ function getUnscaledThumb( $page = false ) {
+ if ( $page ) {
+ $params = array(
+ 'page' => $page,
+ 'width' => $this->getWidth( $page )
+ );
+ } else {
+ $params = array( 'width' => $this->getWidth() );
+ }
+ return $this->transform( $params );
+ }
+
+ /**
+ * Return the file name of a thumbnail with the specified parameters
*
- * @param integer $width Width of the thumbnail image
- * @param boolean $shared Does the thumbnail come from the shared repository?
+ * @param array $params Handler-specific parameters
* @private
*/
- function thumbName( $width ) {
- $thumb = $width."px-".$this->name;
- if ( $this->page != 1 ) {
- $thumb = "page{$this->page}-$thumb";
+ function thumbName( $params ) {
+ $handler = $this->getHandler();
+ if ( !$handler ) {
+ return null;
}
-
- if( $this->mustRender() ) {
- if( $this->canRender() ) {
- # Rasterize to PNG (for SVG vector images, etc)
- $thumb .= '.png';
- }
- else {
- #should we use iconThumb here to get a symbolic thumbnail?
- #or should we fail with an internal error?
- return NULL; //can't make bitmap
- }
+ list( $thumbExt, $thumbMime ) = self::getThumbType( $this->extension, $this->mime );
+ $thumbName = $handler->makeParamString( $params ) . '-' . $this->name;
+ if ( $thumbExt != $this->extension ) {
+ $thumbName .= ".$thumbExt";
}
- return $thumb;
+ return $thumbName;
}
/**
@@ -887,9 +859,13 @@ class Image
* @param integer $height maximum height of the image (optional)
* @public
*/
- function createThumb( $width, $height=-1 ) {
- $thumb = $this->getThumbnail( $width, $height );
- if( is_null( $thumb ) ) return '';
+ function createThumb( $width, $height = -1 ) {
+ $params = array( 'width' => $width );
+ if ( $height != -1 ) {
+ $params['height'] = $height;
+ }
+ $thumb = $this->transform( $params );
+ if( is_null( $thumb ) || $thumb->isError() ) return '';
return $thumb->getUrl();
}
@@ -907,149 +883,90 @@ class Image
*
* @return ThumbnailImage or null on failure
* @public
+ *
+ * @deprecated use transform()
*/
function getThumbnail( $width, $height=-1, $render = true ) {
- wfProfileIn( __METHOD__ );
- if ($this->canRender()) {
- if ( $height > 0 ) {
- $this->load();
- if ( $width > $this->width * $height / $this->height ) {
- $width = wfFitBoxWidth( $this->width, $this->height, $height );
- }
- }
- if ( $render ) {
- $thumb = $this->renderThumb( $width );
- } else {
- // Don't render, just return the URL
- if ( $this->validateThumbParams( $width, $height ) ) {
- if ( !$this->mustRender() && $width == $this->width && $height == $this->height ) {
- $url = $this->getURL();
- } else {
- list( /* $isScriptUrl */, $url ) = $this->thumbUrl( $width );
- }
- $thumb = new ThumbnailImage( $url, $width, $height );
- } else {
- $thumb = null;
- }
- }
- } else {
- // not a bitmap or renderable image, don't try.
- $thumb = $this->iconThumb();
+ $params = array( 'width' => $width );
+ if ( $height != -1 ) {
+ $params['height'] = $height;
}
- wfProfileOut( __METHOD__ );
- return $thumb;
+ $flags = $render ? self::RENDER_NOW : 0;
+ return $this->transform( $params, $flags );
}
-
+
/**
- * @return ThumbnailImage
+ * Transform a media file
+ *
+ * @param array $params An associative array of handler-specific parameters. Typical
+ * keys are width, height and page.
+ * @param integer $flags A bitfield, may contain self::RENDER_NOW to force rendering
+ * @return MediaTransformOutput
*/
- function iconThumb() {
- global $wgStylePath, $wgStyleDirectory;
+ function transform( $params, $flags = 0 ) {
+ global $wgGenerateThumbnailOnParse, $wgUseSquid, $wgIgnoreImageErrors;
- $try = array( 'fileicon-' . $this->extension . '.png', 'fileicon.png' );
- foreach( $try as $icon ) {
- $path = '/common/images/icons/' . $icon;
- $filepath = $wgStyleDirectory . $path;
- if( file_exists( $filepath ) ) {
- return new ThumbnailImage( $wgStylePath . $path, 120, 120 );
+ wfProfileIn( __METHOD__ );
+ do {
+ $handler = $this->getHandler();
+ if ( !$handler || !$handler->canRender() ) {
+ // not a bitmap or renderable image, don't try.
+ $thumb = $this->iconThumb();
+ break;
}
- }
- return null;
- }
- /**
- * Validate thumbnail parameters and fill in the correct height
- *
- * @param integer &$width Specified width (input/output)
- * @param integer &$height Height (output only)
- * @return false to indicate that an error should be returned to the user.
- */
- function validateThumbParams( &$width, &$height ) {
- global $wgSVGMaxSize, $wgMaxImageArea;
-
- $this->load();
+ $script = $this->getTransformScript();
+ if ( $script && !($flags & self::RENDER_NOW) ) {
+ // Use a script to transform on client request
+ $thumb = $handler->getScriptedTransform( $this, $script, $params );
+ break;
+ }
- if ( ! $this->exists() )
- {
- # If there is no image, there will be no thumbnail
- return false;
- }
-
- $width = intval( $width );
-
- # Sanity check $width
- if( $width <= 0 || $this->width <= 0) {
- # BZZZT
- return false;
- }
+ $normalisedParams = $params;
+ $handler->normaliseParams( $this, $normalisedParams );
+ list( $thumbExt, $thumbMime ) = self::getThumbType( $this->extension, $this->mime );
+ $thumbName = $this->thumbName( $normalisedParams );
+ $thumbPath = wfImageThumbDir( $this->name, $this->fromSharedDirectory ) . "/$thumbName";
+ $thumbUrl = $this->thumbUrlFromName( $thumbName );
- # Don't thumbnail an image so big that it will fill hard drives and send servers into swap
- # JPEG has the handy property of allowing thumbnailing without full decompression, so we make
- # an exception for it.
- if ( $this->getMediaType() == MEDIATYPE_BITMAP &&
- $this->getMimeType() !== 'image/jpeg' &&
- $this->width * $this->height > $wgMaxImageArea )
- {
- return false;
- }
+ $this->migrateThumbFile( $thumbName );
- # Don't make an image bigger than the source, or wgMaxSVGSize for SVGs
- if ( $this->mustRender() ) {
- $width = min( $width, $wgSVGMaxSize );
- } elseif ( $width > $this->width - 1 ) {
- $width = $this->width;
- $height = $this->height;
- return true;
- }
+ if ( file_exists( $thumbPath ) ) {
+ $thumb = $handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
+ break;
+ }
- $height = round( $this->height * $width / $this->width );
- return true;
+ if ( !$wgGenerateThumbnailOnParse && !($flags & self::RENDER_NOW ) ) {
+ $thumb = $handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
+ break;
+ }
+ $thumb = $handler->doTransform( $this, $thumbPath, $thumbUrl, $params );
+
+ // Ignore errors if requested
+ if ( !$thumb ) {
+ $thumb = null;
+ } elseif ( $thumb->isError() ) {
+ $this->lastError = $thumb->toText();
+ if ( $wgIgnoreImageErrors && !($flags & self::RENDER_NOW) ) {
+ $thumb = $handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
+ }
+ }
+
+ if ( $wgUseSquid ) {
+ wfPurgeSquidServers( array( $thumbUrl ) );
+ }
+ } while (false);
+
+ wfProfileOut( __METHOD__ );
+ return $thumb;
}
-
+
/**
- * Create a thumbnail of the image having the specified width.
- * The thumbnail will not be created if the width is larger than the
- * image's width. Let the browser do the scaling in this case.
- * The thumbnail is stored on disk and is only computed if the thumbnail
- * file does not exist OR if it is older than the image.
- * Returns an object which can return the pathname, URL, and physical
- * pixel size of the thumbnail -- or null on failure.
- *
- * @return ThumbnailImage or null on failure
- * @private
+ * Fix thumbnail files from 1.4 or before, with extreme prejudice
*/
- function renderThumb( $width, $useScript = true ) {
- global $wgUseSquid, $wgThumbnailEpoch;
-
- wfProfileIn( __METHOD__ );
-
- $this->load();
- $height = -1;
- if ( !$this->validateThumbParams( $width, $height ) ) {
- # Validation error
- wfProfileOut( __METHOD__ );
- return null;
- }
-
- if ( !$this->mustRender() && $width == $this->width && $height == $this->height ) {
- # validateThumbParams (or the user) wants us to return the unscaled image
- $thumb = new ThumbnailImage( $this->getURL(), $width, $height );
- wfProfileOut( __METHOD__ );
- return $thumb;
- }
-
- list( $isScriptUrl, $url ) = $this->thumbUrl( $width );
- if ( $isScriptUrl && $useScript ) {
- // Use thumb.php to render the image
- $thumb = new ThumbnailImage( $url, $width, $height );
- wfProfileOut( __METHOD__ );
- return $thumb;
- }
-
- $thumbName = $this->thumbName( $width, $this->fromSharedDirectory );
+ function migrateThumbFile( $thumbName ) {
$thumbDir = wfImageThumbDir( $this->name, $this->fromSharedDirectory );
- $thumbPath = $thumbDir.'/'.$thumbName;
-
+ $thumbPath = "$thumbDir/$thumbName";
if ( is_dir( $thumbPath ) ) {
// Directory where file should be
// This happened occasionally due to broken migration code in 1.5
@@ -1062,254 +979,50 @@ class Image
break;
}
}
- // Code below will ask if it exists, and the answer is now no
+ // Doesn't exist anymore
clearstatcache();
}
-
- $done = true;
- if ( !file_exists( $thumbPath ) ||
- filemtime( $thumbPath ) < wfTimestamp( TS_UNIX, $wgThumbnailEpoch ) )
- {
- // Create the directory if it doesn't exist
- if ( is_file( $thumbDir ) ) {
- // File where thumb directory should be, destroy if possible
- @unlink( $thumbDir );
- }
- wfMkdirParents( $thumbDir );
-
- $oldThumbPath = wfDeprecatedThumbDir( $thumbName, 'thumb', $this->fromSharedDirectory ).
- '/'.$thumbName;
- $done = false;
-
- // Migration from old directory structure
- if ( is_file( $oldThumbPath ) ) {
- if ( filemtime($oldThumbPath) >= filemtime($this->imagePath) ) {
- if ( file_exists( $thumbPath ) ) {
- if ( !is_dir( $thumbPath ) ) {
- // Old image in the way of rename
- unlink( $thumbPath );
- } else {
- // This should have been dealt with already
- throw new MWException( "Directory where image should be: $thumbPath" );
- }
- }
- // Rename the old image into the new location
- rename( $oldThumbPath, $thumbPath );
- $done = true;
- } else {
- unlink( $oldThumbPath );
- }
- }
- if ( !$done ) {
- $this->lastError = $this->reallyRenderThumb( $thumbPath, $width, $height );
- if ( $this->lastError === true ) {
- $done = true;
- } elseif( $GLOBALS['wgIgnoreImageErrors'] ) {
- // Log the error but output anyway.
- // With luck it's a transitory error...
- $done = true;
- }
-
- # Purge squid
- # This has to be done after the image is updated and present for all machines on NFS,
- # or else the old version might be stored into the squid again
- if ( $wgUseSquid ) {
- $urlArr = array( $url );
- wfPurgeSquidServers($urlArr);
- }
- }
- }
-
- if ( $done ) {
- $thumb = new ThumbnailImage( $url, $width, $height, $thumbPath );
- } else {
- $thumb = null;
+ if ( is_file( $thumbDir ) ) {
+ // File where directory should be
+ unlink( $thumbDir );
+ // Doesn't exist anymore
+ clearstatcache();
}
- wfProfileOut( __METHOD__ );
- return $thumb;
- } // END OF function renderThumb
+ }
/**
- * Really render a thumbnail
- * Call this only for images for which canRender() returns true.
- *
- * @param string $thumbPath Path to thumbnail
- * @param int $width Desired width in pixels
- * @param int $height Desired height in pixels
- * @return bool True on error, false or error string on failure.
- * @private
+ * Get a MediaHandler instance for this image
*/
- function reallyRenderThumb( $thumbPath, $width, $height ) {
- global $wgSVGConverters, $wgSVGConverter;
- global $wgUseImageMagick, $wgImageMagickConvertCommand;
- global $wgCustomConvertCommand;
- global $wgDjvuRenderer, $wgDjvuPostProcessor;
-
- $this->load();
-
- $err = false;
- $cmd = "";
- $retval = 0;
-
- if( $this->mime === "image/svg" ) {
- #Right now we have only SVG
-
- global $wgSVGConverters, $wgSVGConverter;
- if( isset( $wgSVGConverters[$wgSVGConverter] ) ) {
- global $wgSVGConverterPath;
- $cmd = str_replace(
- array( '$path/', '$width', '$height', '$input', '$output' ),
- array( $wgSVGConverterPath ? "$wgSVGConverterPath/" : "",
- intval( $width ),
- intval( $height ),
- wfEscapeShellArg( $this->imagePath ),
- wfEscapeShellArg( $thumbPath ) ),
- $wgSVGConverters[$wgSVGConverter] );
- wfProfileIn( 'rsvg' );
- wfDebug( "reallyRenderThumb SVG: $cmd\n" );
- $err = wfShellExec( $cmd, $retval );
- wfProfileOut( 'rsvg' );
- }
- } else {
- if ( $this->mime === "image/vnd.djvu" && $wgDjvuRenderer ) {
- // DJVU image
- // The file contains several images. First, extract the
- // page in hi-res, if it doesn't yet exist. Then, thumbnail
- // it.
-
- $cmd = "{$wgDjvuRenderer} -page={$this->page} -size=${width}x${height} " .
- wfEscapeShellArg( $this->imagePath ) .
- " | {$wgDjvuPostProcessor} > " . wfEscapeShellArg($thumbPath);
- wfProfileIn( 'ddjvu' );
- wfDebug( "reallyRenderThumb DJVU: $cmd\n" );
- $err = wfShellExec( $cmd, $retval );
- wfProfileOut( 'ddjvu' );
-
- } elseif ( $wgUseImageMagick ) {
- # use ImageMagick
-
- if ( $this->mime == 'image/jpeg' ) {
- $quality = "-quality 80"; // 80%
- } elseif ( $this->mime == 'image/png' ) {
- $quality = "-quality 95"; // zlib 9, adaptive filtering
- } else {
- $quality = ''; // default
- }
-
- # Specify white background color, will be used for transparent images
- # in Internet Explorer/Windows instead of default black.
-
- # Note, we specify "-size {$width}" and NOT "-size {$width}x{$height}".
- # It seems that ImageMagick has a bug wherein it produces thumbnails of
- # the wrong size in the second case.
-
- $cmd = wfEscapeShellArg($wgImageMagickConvertCommand) .
- " {$quality} -background white -size {$width} ".
- wfEscapeShellArg($this->imagePath) .
- // Coalesce is needed to scale animated GIFs properly (bug 1017).
- ' -coalesce ' .
- // For the -resize option a "!" is needed to force exact size,
- // or ImageMagick may decide your ratio is wrong and slice off
- // a pixel.
- " -thumbnail " . wfEscapeShellArg( "{$width}x{$height}!" ) .
- " -depth 8 " .
- wfEscapeShellArg($thumbPath) . " 2>&1";
- wfDebug("reallyRenderThumb: running ImageMagick: $cmd\n");
- wfProfileIn( 'convert' );
- $err = wfShellExec( $cmd, $retval );
- wfProfileOut( 'convert' );
- } elseif( $wgCustomConvertCommand ) {
- # Use a custom convert command
- # Variables: %s %d %w %h
- $src = wfEscapeShellArg( $this->imagePath );
- $dst = wfEscapeShellArg( $thumbPath );
- $cmd = $wgCustomConvertCommand;
- $cmd = str_replace( '%s', $src, str_replace( '%d', $dst, $cmd ) ); # Filenames
- $cmd = str_replace( '%h', $height, str_replace( '%w', $width, $cmd ) ); # Size
- wfDebug( "reallyRenderThumb: Running custom convert command $cmd\n" );
- wfProfileIn( 'convert' );
- $err = wfShellExec( $cmd, $retval );
- wfProfileOut( 'convert' );
- } else {
- # Use PHP's builtin GD library functions.
- #
- # First find out what kind of file this is, and select the correct
- # input routine for this.
-
- $typemap = array(
- 'image/gif' => array( 'imagecreatefromgif', 'palette', 'imagegif' ),
- 'image/jpeg' => array( 'imagecreatefromjpeg', 'truecolor', array( &$this, 'imageJpegWrapper' ) ),
- 'image/png' => array( 'imagecreatefrompng', 'bits', 'imagepng' ),
- 'image/vnd.wap.wmbp' => array( 'imagecreatefromwbmp', 'palette', 'imagewbmp' ),
- 'image/xbm' => array( 'imagecreatefromxbm', 'palette', 'imagexbm' ),
- );
- if( !isset( $typemap[$this->mime] ) ) {
- $err = 'Image type not supported';
- wfDebug( "$err\n" );
- return $err;
- }
- list( $loader, $colorStyle, $saveType ) = $typemap[$this->mime];
-
- if( !function_exists( $loader ) ) {
- $err = "Incomplete GD library configuration: missing function $loader";
- wfDebug( "$err\n" );
- return $err;
- }
- if( $colorStyle == 'palette' ) {
- $truecolor = false;
- } elseif( $colorStyle == 'truecolor' ) {
- $truecolor = true;
- } elseif( $colorStyle == 'bits' ) {
- $truecolor = ( $this->bits > 8 );
- }
+ function getHandler() {
+ return MediaHandler::getHandler( $this->getMimeType() );
+ }
- $src_image = call_user_func( $loader, $this->imagePath );
- if ( $truecolor ) {
- $dst_image = imagecreatetruecolor( $width, $height );
- } else {
- $dst_image = imagecreate( $width, $height );
- }
- imagecopyresampled( $dst_image, $src_image,
- 0,0,0,0,
- $width, $height, $this->width, $this->height );
- call_user_func( $saveType, $dst_image, $thumbPath );
- imagedestroy( $dst_image );
- imagedestroy( $src_image );
- }
- }
+ /**
+ * Get a ThumbnailImage representing a file type icon
+ * @return ThumbnailImage
+ */
+ function iconThumb() {
+ global $wgStylePath, $wgStyleDirectory;
- #
- # Check for zero-sized thumbnails. Those can be generated when
- # no disk space is available or some other error occurs
- #
- if( file_exists( $thumbPath ) ) {
- $thumbstat = stat( $thumbPath );
- if( $thumbstat['size'] == 0 || $retval != 0 ) {
- wfDebugLog( 'thumbnail',
- sprintf( 'Removing bad %d-byte thumbnail "%s"',
- $thumbstat['size'], $thumbPath ) );
- unlink( $thumbPath );
+ $try = array( 'fileicon-' . $this->extension . '.png', 'fileicon.png' );
+ foreach( $try as $icon ) {
+ $path = '/common/images/icons/' . $icon;
+ $filepath = $wgStyleDirectory . $path;
+ if( file_exists( $filepath ) ) {
+ return new ThumbnailImage( $wgStylePath . $path, 120, 120 );
}
}
- if ( $retval != 0 ) {
- wfDebugLog( 'thumbnail',
- sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
- wfHostname(), $retval, trim($err), $cmd ) );
- return wfMsg( 'thumbnail_error', $err );
- } else {
- return true;
- }
+ return null;
}
+ /**
+ * Get last thumbnailing error.
+ * Largely obsolete.
+ */
function getLastError() {
return $this->lastError;
}
- function imageJpegWrapper( $dst_image, $thumbPath ) {
- imageinterlace( $dst_image );
- imagejpeg( $dst_image, $thumbPath, 95 );
- }
-
/**
* Get all thumbnail names previously generated for this image
*/
@@ -1319,16 +1032,17 @@ class Image
$files = array();
$dir = wfImageThumbDir( $this->name, $shared );
- // This generates an error on failure, hence the @
- $handle = @opendir( $dir );
+ if ( is_dir( $dir ) ) {
+ $handle = opendir( $dir );
- if ( $handle ) {
- while ( false !== ( $file = readdir($handle) ) ) {
- if ( $file{0} != '.' ) {
- $files[] = $file;
+ if ( $handle ) {
+ while ( false !== ( $file = readdir($handle) ) ) {
+ if ( $file{0} != '.' ) {
+ $files[] = $file;
+ }
}
+ closedir( $handle );
}
- closedir( $handle );
}
} else {
$files = array();
@@ -1361,8 +1075,10 @@ class Image
$urls = array();
foreach ( $files as $file ) {
$m = array();
- if ( preg_match( '/^(\d+)px/', $file, $m ) ) {
- list( /* $isScriptUrl */, $url ) = $this->thumbUrl( $m[1] );
+ # Check that the base image name is part of the thumb name
+ # This is a basic sanity check to avoid erasing unrelated directories
+ if ( strpos( $file, $this->name ) !== false ) {
+ $url = $this->thumbUrlFromName( $file );
$urls[] = $url;
@unlink( "$dir/$file" );
}
@@ -1377,7 +1093,7 @@ class Image
wfPurgeSquidServers( $urls );
}
}
-
+
/**
* Purge the image description page, but don't go after
* pages using the image. Use when modifying file history
@@ -1388,7 +1104,7 @@ class Image
$page->invalidateCache();
$page->purgeSquid();
}
-
+
/**
* Purge metadata and all affected pages when the image is created,
* deleted, or majorly updated. A set of additional URLs may be
@@ -1399,12 +1115,15 @@ class Image
// Delete thumbnails and refresh image metadata cache
$this->purgeCache();
$this->purgeDescription();
-
+
// Purge cache of all pages using this image
$update = new HTMLCacheUpdate( $this->getTitle(), 'imagelinks' );
$update->doUpdate();
}
+ /**
+ * Check the image table schema on the given connection for subtle problems
+ */
function checkDBSchema(&$db) {
static $checkDone = false;
global $wgCheckDBSchema;
@@ -1445,7 +1164,7 @@ class Image
* @public
*/
function nextHistoryLine() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$this->checkDBSchema($dbr);
@@ -1541,7 +1260,7 @@ class Image
function recordUpload( $oldver, $desc, $license = '', $copyStatus = '', $source = '', $watch = false ) {
global $wgUser, $wgUseCopyrightUpload;
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$this->checkDBSchema($dbw);
@@ -1670,6 +1389,9 @@ class Image
$article->insertNewArticle( $textdesc, $desc, $minor, $watch, $suppressRC );
}
+ # Hooks, hooks, the magic of hooks...
+ wfRunHooks( 'FileUpload', array( $this ) );
+
# Add the log entry
$log = new LogPage( 'upload' );
$log->addEntry( 'upload', $descTitle, $desc );
@@ -1697,9 +1419,9 @@ class Image
wfProfileIn( __METHOD__ );
if ( $options ) {
- $db =& wfGetDB( DB_MASTER );
+ $db = wfGetDB( DB_MASTER );
} else {
- $db =& wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_SLAVE );
}
$linkCache =& LinkCache::singleton();
@@ -1721,75 +1443,24 @@ class Image
wfProfileOut( __METHOD__ );
return $retVal;
}
-
- /**
- * Retrive Exif data from the file and prune unrecognized tags
- * and/or tags with invalid contents
- *
- * @param $filename
- * @return array
- */
- private function retrieveExifData( $filename ) {
- global $wgShowEXIF;
-
- /*
- if ( $this->getMimeType() !== "image/jpeg" )
- return array();
- */
-
- if( $wgShowEXIF && file_exists( $filename ) ) {
- $exif = new Exif( $filename );
- return $exif->getFilteredData();
- }
-
- return array();
- }
function getExifData() {
global $wgRequest;
- if ( $this->metadata === '0' || $this->mime == 'image/vnd.djvu' )
+ $handler = $this->getHandler();
+ if ( !$handler || $handler->getMetadataType( $this ) != 'exif' ) {
return array();
-
- $purge = $wgRequest->getVal( 'action' ) == 'purge';
- $ret = unserialize( $this->metadata );
-
- $oldver = isset( $ret['MEDIAWIKI_EXIF_VERSION'] ) ? $ret['MEDIAWIKI_EXIF_VERSION'] : 0;
- $newver = Exif::version();
-
- if ( !count( $ret ) || $purge || $oldver != $newver ) {
- $this->purgeMetadataCache();
- $this->updateExifData( $newver );
}
- if ( isset( $ret['MEDIAWIKI_EXIF_VERSION'] ) )
- unset( $ret['MEDIAWIKI_EXIF_VERSION'] );
- $format = new FormatExif( $ret );
-
- return $format->getFormattedData();
- }
-
- function updateExifData( $version ) {
- if ( $this->getImagePath() === false ) # Not a local image
- return;
-
- # Get EXIF data from image
- $exif = $this->retrieveExifData( $this->imagePath );
- if ( count( $exif ) ) {
- $exif['MEDIAWIKI_EXIF_VERSION'] = $version;
- $this->metadata = serialize( $exif );
- } else {
- $this->metadata = '0';
+ if ( !$this->metadata ) {
+ return array();
}
+ $exif = unserialize( $this->metadata );
+ if ( !$exif ) {
+ return array();
+ }
+ unset( $exif['MEDIAWIKI_EXIF_VERSION'] );
+ $format = new FormatExif( $exif );
- # Update EXIF data in database
- $dbw =& wfGetDB( DB_MASTER );
-
- $this->checkDBSchema($dbw);
-
- $dbw->update( 'image',
- array( 'img_metadata' => $this->metadata ),
- array( 'img_name' => $this->name ),
- __METHOD__
- );
+ return $format->getFormattedData();
}
/**
@@ -1801,7 +1472,7 @@ class Image
function isLocal() {
return !$this->fromSharedDirectory;
}
-
+
/**
* Was this image ever deleted from the wiki?
*
@@ -1811,7 +1482,7 @@ class Image
$title = Title::makeTitle( NS_IMAGE, $this->name );
return ( $title->isDeleted() > 0 );
}
-
+
/**
* Delete all versions of the image.
*
@@ -1823,37 +1494,37 @@ class Image
* @param $reason
* @return true on success, false on some kind of failure
*/
- function delete( $reason ) {
+ function delete( $reason, $suppress=false ) {
$transaction = new FSTransaction();
$urlArr = array( $this->getURL() );
-
+
if( !FileStore::lock() ) {
wfDebug( __METHOD__.": failed to acquire file store lock, aborting\n" );
return false;
}
-
+
try {
$dbw = wfGetDB( DB_MASTER );
$dbw->begin();
-
+
// Delete old versions
$result = $dbw->select( 'oldimage',
array( 'oi_archive_name' ),
array( 'oi_name' => $this->name ) );
-
+
while( $row = $dbw->fetchObject( $result ) ) {
$oldName = $row->oi_archive_name;
-
- $transaction->add( $this->prepareDeleteOld( $oldName, $reason ) );
-
+
+ $transaction->add( $this->prepareDeleteOld( $oldName, $reason, $suppress ) );
+
// We'll need to purge this URL from caches...
$urlArr[] = wfImageArchiveUrl( $oldName );
}
$dbw->freeResult( $result );
-
+
// And the current version...
- $transaction->add( $this->prepareDeleteCurrent( $reason ) );
-
+ $transaction->add( $this->prepareDeleteCurrent( $reason, $suppress ) );
+
$dbw->immediateCommit();
} catch( MWException $e ) {
wfDebug( __METHOD__.": db error, rolling back file transactions\n" );
@@ -1861,22 +1532,22 @@ class Image
FileStore::unlock();
throw $e;
}
-
+
wfDebug( __METHOD__.": deleted db items, applying file transactions\n" );
$transaction->commit();
FileStore::unlock();
-
+
// Update site_stats
$site_stats = $dbw->tableName( 'site_stats' );
$dbw->query( "UPDATE $site_stats SET ss_images=ss_images-1", __METHOD__ );
-
+
$this->purgeEverything( $urlArr );
-
+
return true;
}
-
-
+
+
/**
* Delete an old version of the image.
*
@@ -1889,20 +1560,20 @@ class Image
* @throws MWException or FSException on database or filestore failure
* @return true on success, false on some kind of failure
*/
- function deleteOld( $archiveName, $reason ) {
+ function deleteOld( $archiveName, $reason, $suppress=false ) {
$transaction = new FSTransaction();
$urlArr = array();
-
+
if( !FileStore::lock() ) {
wfDebug( __METHOD__.": failed to acquire file store lock, aborting\n" );
return false;
}
-
+
$transaction = new FSTransaction();
try {
$dbw = wfGetDB( DB_MASTER );
$dbw->begin();
- $transaction->add( $this->prepareDeleteOld( $archiveName, $reason ) );
+ $transaction->add( $this->prepareDeleteOld( $archiveName, $reason, $suppress ) );
$dbw->immediateCommit();
} catch( MWException $e ) {
wfDebug( __METHOD__.": db error, rolling back file transaction\n" );
@@ -1910,11 +1581,11 @@ class Image
FileStore::unlock();
throw $e;
}
-
+
wfDebug( __METHOD__.": deleted db items, applying file transaction\n" );
$transaction->commit();
FileStore::unlock();
-
+
$this->purgeDescription();
// Squid purging
@@ -1927,13 +1598,13 @@ class Image
}
return true;
}
-
+
/**
* Delete the current version of a file.
* May throw a database error.
* @return true on success, false on failure
*/
- private function prepareDeleteCurrent( $reason ) {
+ private function prepareDeleteCurrent( $reason, $suppress=false ) {
return $this->prepareDeleteVersion(
$this->getFullPath(),
$reason,
@@ -1954,6 +1625,7 @@ class Image
'fa_user_text' => 'img_user_text',
'fa_timestamp' => 'img_timestamp' ),
array( 'img_name' => $this->name ),
+ $suppress,
__METHOD__ );
}
@@ -1962,7 +1634,7 @@ class Image
* May throw a database error.
* @return true on success, false on failure
*/
- private function prepareDeleteOld( $archiveName, $reason ) {
+ private function prepareDeleteOld( $archiveName, $reason, $suppress=false ) {
$oldpath = wfImageArchiveDir( $this->name ) .
DIRECTORY_SEPARATOR . $archiveName;
return $this->prepareDeleteVersion(
@@ -1987,6 +1659,7 @@ class Image
array(
'oi_name' => $this->name,
'oi_archive_name' => $archiveName ),
+ $suppress,
__METHOD__ );
}
@@ -1999,14 +1672,14 @@ class Image
*
* @return FSTransaction
*/
- private function prepareDeleteVersion( $path, $reason, $table, $fieldMap, $where, $fname ) {
+ private function prepareDeleteVersion( $path, $reason, $table, $fieldMap, $where, $suppress=false, $fname ) {
global $wgUser, $wgSaveDeletedFiles;
-
+
// Dupe the file into the file store
if( file_exists( $path ) ) {
if( $wgSaveDeletedFiles ) {
$group = 'deleted';
-
+
$store = FileStore::get( $group );
$key = FileStore::calculateKey( $path, $this->extension );
$transaction = $store->insert( $key, $path,
@@ -2022,7 +1695,7 @@ class Image
$key = null;
$transaction = new FSTransaction(); // empty
}
-
+
if( $transaction === false ) {
// Fail to restore?
wfDebug( __METHOD__.": import to file store failed, aborting\n" );
@@ -2030,16 +1703,28 @@ class Image
return false;
}
+ // Bitfields to further supress the image content
+ // Note that currently, live images are stored elsewhere
+ // and cannot be partially deleted
+ $bitfield = 0;
+ if ( $suppress ) {
+ $bitfield |= self::DELETED_FILE;
+ $bitfield |= self::DELETED_COMMENT;
+ $bitfield |= self::DELETED_USER;
+ $bitfield |= self::DELETED_RESTRICTED;
+ }
+
$dbw = wfGetDB( DB_MASTER );
$storageMap = array(
'fa_storage_group' => $dbw->addQuotes( $group ),
'fa_storage_key' => $dbw->addQuotes( $key ),
-
+
'fa_deleted_user' => $dbw->addQuotes( $wgUser->getId() ),
'fa_deleted_timestamp' => $dbw->timestamp(),
- 'fa_deleted_reason' => $dbw->addQuotes( $reason ) );
+ 'fa_deleted_reason' => $dbw->addQuotes( $reason ),
+ 'fa_deleted' => $bitfield);
$allFields = array_merge( $storageMap, $fieldMap );
-
+
try {
if( $wgSaveDeletedFiles ) {
$dbw->insertSelect( 'filearchive', $table, $allFields, $where, $fname );
@@ -2052,10 +1737,10 @@ class Image
$transaction->rollback();
throw $e;
}
-
+
return $transaction;
}
-
+
/**
* Restore all or specified deleted revisions to the given file.
* Permissions and logging are left to the caller.
@@ -2067,36 +1752,38 @@ class Image
* @return the number of file revisions restored if successful,
* or false on failure
*/
- function restore( $versions=array() ) {
+ function restore( $versions=array(), $Unsuppress=false ) {
+ global $wgUser;
+
if( !FileStore::lock() ) {
wfDebug( __METHOD__." could not acquire filestore lock\n" );
return false;
}
-
+
$transaction = new FSTransaction();
try {
$dbw = wfGetDB( DB_MASTER );
$dbw->begin();
-
+
// Re-confirm whether this image presently exists;
// if no we'll need to create an image record for the
// first item we restore.
$exists = $dbw->selectField( 'image', '1',
array( 'img_name' => $this->name ),
__METHOD__ );
-
+
// Fetch all or selected archived revisions for the file,
// sorted from the most recent to the oldest.
$conditions = array( 'fa_name' => $this->name );
if( $versions ) {
$conditions['fa_id'] = $versions;
}
-
+
$result = $dbw->select( 'filearchive', '*',
$conditions,
__METHOD__,
array( 'ORDER BY' => 'fa_timestamp DESC' ) );
-
+
if( $dbw->numRows( $result ) < count( $versions ) ) {
// There's some kind of conflict or confusion;
// we can't restore everything we were asked to.
@@ -2113,40 +1800,51 @@ class Image
FileStore::unlock();
return true;
}
-
+
$revisions = 0;
while( $row = $dbw->fetchObject( $result ) ) {
+ if ( $Unsuppress ) {
+ // Currently, fa_deleted flags fall off upon restore, lets be careful about this
+ } else if ( ($row->fa_deleted & Revision::DELETED_RESTRICTED) && !$wgUser->isAllowed('hiderevision') ) {
+ // Skip restoring file revisions that the user cannot restore
+ continue;
+ }
$revisions++;
$store = FileStore::get( $row->fa_storage_group );
if( !$store ) {
wfDebug( __METHOD__.": skipping row with no file.\n" );
continue;
}
-
+
if( $revisions == 1 && !$exists ) {
$destDir = wfImageDir( $row->fa_name );
if ( !is_dir( $destDir ) ) {
wfMkdirParents( $destDir );
}
$destPath = $destDir . DIRECTORY_SEPARATOR . $row->fa_name;
-
+
// We may have to fill in data if this was originally
// an archived file revision.
if( is_null( $row->fa_metadata ) ) {
$tempFile = $store->filePath( $row->fa_storage_key );
- $metadata = serialize( $this->retrieveExifData( $tempFile ) );
-
+
$magic = MimeMagic::singleton();
$mime = $magic->guessMimeType( $tempFile, true );
$media_type = $magic->getMediaType( $tempFile, $mime );
list( $major_mime, $minor_mime ) = self::splitMime( $mime );
+ $handler = MediaHandler::getHandler( $mime );
+ if ( $handler ) {
+ $metadata = $handler->getMetadata( false, $tempFile );
+ } else {
+ $metadata = '';
+ }
} else {
$metadata = $row->fa_metadata;
$major_mime = $row->fa_major_mime;
$minor_mime = $row->fa_minor_mime;
$media_type = $row->fa_media_type;
}
-
+
$table = 'image';
$fields = array(
'img_name' => $row->fa_name,
@@ -2177,7 +1875,7 @@ class Image
wfMkdirParents( $destDir );
}
$destPath = $destDir . DIRECTORY_SEPARATOR . $archiveName;
-
+
$table = 'oldimage';
$fields = array(
'oi_name' => $row->fa_name,
@@ -2191,13 +1889,13 @@ class Image
'oi_user_text' => $row->fa_user_text,
'oi_timestamp' => $row->fa_timestamp );
}
-
+
$dbw->insert( $table, $fields, __METHOD__ );
- /// @fixme this delete is not totally safe, potentially
+ // @todo this delete is not totally safe, potentially
$dbw->delete( 'filearchive',
array( 'fa_id' => $row->fa_id ),
__METHOD__ );
-
+
// Check if any other stored revisions use this file;
// if so, we shouldn't remove the file from the deletion
// archives so they will still work.
@@ -2213,161 +1911,230 @@ class Image
} else {
$flags = 0;
}
-
+
$transaction->add( $store->export( $row->fa_storage_key,
$destPath, $flags ) );
}
-
+
$dbw->immediateCommit();
} catch( MWException $e ) {
wfDebug( __METHOD__." caught error, aborting\n" );
$transaction->rollback();
throw $e;
}
-
+
$transaction->commit();
FileStore::unlock();
-
+
if( $revisions > 0 ) {
if( !$exists ) {
wfDebug( __METHOD__." restored $revisions items, creating a new current\n" );
-
+
// Update site_stats
$site_stats = $dbw->tableName( 'site_stats' );
$dbw->query( "UPDATE $site_stats SET ss_images=ss_images+1", __METHOD__ );
-
+
$this->purgeEverything();
} else {
wfDebug( __METHOD__." restored $revisions as archived versions\n" );
$this->purgeDescription();
}
}
-
+
return $revisions;
}
/**
- * Select a page from a multipage document. Determines the page used for
- * rendering thumbnails.
+ * Returns 'true' if this image is a multipage document, e.g. a DJVU
+ * document.
*
- * @param $page Integer: page number, starting with 1
+ * @return Bool
*/
- function selectPage( $page ) {
- wfDebug( __METHOD__." selecting page $page \n" );
- $this->page = $page;
- if ( ! $this->dataLoaded ) {
- $this->load();
- }
- if ( ! isset( $this->multiPageXML ) ) {
- $this->initializeMultiPageXML();
+ function isMultipage() {
+ $handler = $this->getHandler();
+ return $handler && $handler->isMultiPage();
+ }
+
+ /**
+ * Returns the number of pages of a multipage document, or NULL for
+ * documents which aren't multipage documents
+ */
+ function pageCount() {
+ $handler = $this->getHandler();
+ if ( $handler && $handler->isMultiPage() ) {
+ return $handler->pageCount( $this );
+ } else {
+ return null;
}
- $o = $this->multiPageXML->BODY[0]->OBJECT[$page-1];
- $this->height = intval( $o['height'] );
- $this->width = intval( $o['width'] );
}
- function initializeMultiPageXML() {
- #
- # Check for files uploaded prior to DJVU support activation
- # They have a '0' in their metadata field.
- #
- if ( $this->metadata == '0' || $this->metadata == '' ) {
- $deja = new DjVuImage( $this->imagePath );
- $this->metadata = $deja->retrieveMetaData();
- $this->purgeMetadataCache();
+ static function getCommonsDB() {
+ static $dbc;
+ global $wgLoadBalancer, $wgSharedUploadDBname;
+ if ( !isset( $dbc ) ) {
+ $i = $wgLoadBalancer->getGroupIndex( 'commons' );
+ $dbinfo = $wgLoadBalancer->mServers[$i];
+ $dbc = new Database( $dbinfo['host'], $dbinfo['user'],
+ $dbinfo['password'], $wgSharedUploadDBname );
+ }
+ return $dbc;
+ }
- # Update metadata in the database
- $dbw =& wfGetDB( DB_MASTER );
- $dbw->update( 'image',
- array( 'img_metadata' => $this->metadata ),
- array( 'img_name' => $this->name ),
- __METHOD__
- );
+ /**
+ * Calculate the height of a thumbnail using the source and destination width
+ */
+ static function scaleHeight( $srcWidth, $srcHeight, $dstWidth ) {
+ // Exact integer multiply followed by division
+ if ( $srcWidth == 0 ) {
+ return 0;
+ } else {
+ return round( $srcHeight * $dstWidth / $srcWidth );
}
- wfSuppressWarnings();
- $this->multiPageXML = new SimpleXMLElement( $this->metadata );
- wfRestoreWarnings();
}
/**
- * Returns 'true' if this image is a multipage document, e.g. a DJVU
- * document.
+ * Get an image size array like that returned by getimagesize(), or false if it
+ * can't be determined.
*
- * @return Bool
+ * @param string $fileName The filename
+ * @return array
*/
- function isMultipage() {
- return ( $this->mime == 'image/vnd.djvu' );
+ function getImageSize( $fileName ) {
+ $handler = $this->getHandler();
+ return $handler->getImageSize( $this, $fileName );
}
/**
- * Returns the number of pages of a multipage document, or NULL for
- * documents which aren't multipage documents
+ * Get the thumbnail extension and MIME type for a given source MIME type
+ * @return array thumbnail extension and MIME type
*/
- function pageCount() {
- if ( ! $this->isMultipage() ) {
- return null;
- }
- if ( ! isset( $this->multiPageXML ) ) {
- $this->initializeMultiPageXML();
+ static function getThumbType( $ext, $mime ) {
+ $handler = MediaHandler::getHandler( $mime );
+ if ( $handler ) {
+ return $handler->getThumbType( $ext, $mime );
+ } else {
+ return array( $ext, $mime );
}
- return count( $this->multiPageXML->xpath( '//OBJECT' ) );
}
-
+
} //class
+
/**
- * Wrapper class for thumbnail images
- * @package MediaWiki
+ * @addtogroup Media
*/
-class ThumbnailImage {
+class ArchivedFile
+{
/**
- * @param string $path Filesystem path to the thumb
- * @param string $url URL path to the thumb
- * @private
+ * Returns a file object from the filearchive table
+ * In the future, all current and old image storage
+ * may use FileStore. There will be a "old" storage
+ * for current and previous file revisions as well as
+ * the "deleted" group for archived revisions
+ * @param $title, the corresponding image page title
+ * @param $id, the image id, a unique key
+ * @param $key, optional storage key
+ * @return ResultWrapper
*/
- function ThumbnailImage( $url, $width, $height, $path = false ) {
- $this->url = $url;
- $this->width = round( $width );
- $this->height = round( $height );
- # These should be integers when they get here.
- # If not, there's a bug somewhere. But let's at
- # least produce valid HTML code regardless.
- $this->path = $path;
+ function ArchivedFile( $title, $id=0, $key='' ) {
+ if( !is_object( $title ) ) {
+ throw new MWException( 'Image constructor given bogus title.' );
+ }
+ $conds = ($id) ? "fa_id = $id" : "fa_storage_key = '$key'";
+ if( $title->getNamespace() == NS_IMAGE ) {
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select( 'filearchive',
+ array(
+ 'fa_id',
+ 'fa_name',
+ 'fa_storage_key',
+ 'fa_storage_group',
+ 'fa_size',
+ 'fa_bits',
+ 'fa_width',
+ 'fa_height',
+ 'fa_metadata',
+ 'fa_media_type',
+ 'fa_major_mime',
+ 'fa_minor_mime',
+ 'fa_description',
+ 'fa_user',
+ 'fa_user_text',
+ 'fa_timestamp',
+ 'fa_deleted' ),
+ array(
+ 'fa_name' => $title->getDbKey(),
+ $conds ),
+ __METHOD__,
+ array( 'ORDER BY' => 'fa_timestamp DESC' ) );
+
+ if ( $dbr->numRows( $res ) == 0 ) {
+ // this revision does not exist?
+ return;
+ }
+ $ret = $dbr->resultObject( $res );
+ $row = $ret->fetchObject();
+
+ // initialize fields for filestore image object
+ $this->mId = intval($row->fa_id);
+ $this->mName = $row->fa_name;
+ $this->mGroup = $row->fa_storage_group;
+ $this->mKey = $row->fa_storage_key;
+ $this->mSize = $row->fa_size;
+ $this->mBits = $row->fa_bits;
+ $this->mWidth = $row->fa_width;
+ $this->mHeight = $row->fa_height;
+ $this->mMetaData = $row->fa_metadata;
+ $this->mMime = "$row->fa_major_mime/$row->fa_minor_mime";
+ $this->mType = $row->fa_media_type;
+ $this->mDescription = $row->fa_description;
+ $this->mUser = $row->fa_user;
+ $this->mUserText = $row->fa_user_text;
+ $this->mTimestamp = $row->fa_timestamp;
+ $this->mDeleted = $row->fa_deleted;
+ } else {
+ throw new MWException( 'This title does not correspond to an image page.' );
+ return;
+ }
+ return true;
}
/**
- * @return string The thumbnail URL
+ * int $field one of DELETED_* bitfield constants
+ * for file or revision rows
+ * @return bool
*/
- function getUrl() {
- return $this->url;
+ function isDeleted( $field ) {
+ return ($this->mDeleted & $field) == $field;
}
-
+
/**
- * Return HTML <img ... /> tag for the thumbnail, will include
- * width and height attributes and a blank alt text (as required).
- *
- * You can set or override additional attributes by passing an
- * associative array of name => data pairs. The data will be escaped
- * for HTML output, so should be in plaintext.
- *
- * @param array $attribs
- * @return string
- * @public
+ * Determine if the current user is allowed to view a particular
+ * field of this FileStore image file, if it's marked as deleted.
+ * @param int $field
+ * @return bool
*/
- function toHtml( $attribs = array() ) {
- $attribs['src'] = $this->url;
- $attribs['width'] = $this->width;
- $attribs['height'] = $this->height;
- if( !isset( $attribs['alt'] ) ) $attribs['alt'] = '';
-
- $html = '<img ';
- foreach( $attribs as $name => $data ) {
- $html .= $name . '="' . htmlspecialchars( $data ) . '" ';
+ function userCan( $field ) {
+ if( isset($this->mDeleted) && ($this->mDeleted & $field) == $field ) {
+ // images
+ global $wgUser;
+ $permission = ( $this->mDeleted & Revision::DELETED_RESTRICTED ) == Revision::DELETED_RESTRICTED
+ ? 'hiderevision'
+ : 'deleterevision';
+ wfDebug( "Checking for $permission due to $field match on $this->mDeleted\n" );
+ return $wgUser->isAllowed( $permission );
+ } else {
+ return true;
}
- $html .= '/>';
- return $html;
}
-
}
+/**
+ * Aliases for backwards compatibility with 1.6
+ */
+define( 'MW_IMG_DELETED_FILE', Image::DELETED_FILE );
+define( 'MW_IMG_DELETED_COMMENT', Image::DELETED_COMMENT );
+define( 'MW_IMG_DELETED_USER', Image::DELETED_USER );
+define( 'MW_IMG_DELETED_RESTRICTED', Image::DELETED_RESTRICTED );
+
?>
diff --git a/includes/ImageFunctions.php b/includes/ImageFunctions.php
index 931fdff1..d04110d4 100644
--- a/includes/ImageFunctions.php
+++ b/includes/ImageFunctions.php
@@ -21,7 +21,7 @@ function wfImageDir( $fname ) {
}
/**
- * Returns the image directory of an image's thubnail
+ * Returns the image directory of an image's thumbnail
* The result is an absolute path.
*
* This function is called from thumb.php before Setup.php is included
diff --git a/includes/ImageGallery.php b/includes/ImageGallery.php
index 9d58b7f6..fba7714c 100644
--- a/includes/ImageGallery.php
+++ b/includes/ImageGallery.php
@@ -3,7 +3,6 @@ if ( ! defined( 'MEDIAWIKI' ) )
die( 1 );
/**
- * @package MediaWiki
*/
/**
@@ -11,23 +10,32 @@ if ( ! defined( 'MEDIAWIKI' ) )
*
* Add images to the gallery using add(), then render that list to HTML using toHTML().
*
- * @package MediaWiki
+ * @addtogroup Media
*/
class ImageGallery
{
var $mImages, $mShowBytes, $mShowFilename;
var $mCaption = false;
var $mSkin = false;
-
+
/**
* Is the gallery on a wiki page (i.e. not a special page)
*/
var $mParsing;
/**
+ * Contextual title, used when images are being screened
+ * against the bad image list
+ */
+ private $contextTitle = false;
+
+ private $mPerRow = 4; // How many images wide should the gallery be?
+ private $mWidths = 120, $mHeights = 120; // How wide/tall each thumbnail should be
+
+ /**
* Create a new image gallery object.
*/
- function ImageGallery( ) {
+ function __construct( ) {
$this->mImages = array();
$this->mShowBytes = true;
$this->mShowFilename = true;
@@ -40,7 +48,7 @@ class ImageGallery
function setParsing( $val = true ) {
$this->mParsing = $val;
}
-
+
/**
* Set the caption (as plain text)
*
@@ -49,25 +57,58 @@ class ImageGallery
function setCaption( $caption ) {
$this->mCaption = htmlspecialchars( $caption );
}
-
+
/**
* Set the caption (as HTML)
*
* @param $caption Caption
*/
- function setCaptionHtml( $caption ) {
+ public function setCaptionHtml( $caption ) {
$this->mCaption = $caption;
}
/**
+ * Set how many images will be displayed per row.
+ *
+ * @param int $num > 0; invalid numbers will be rejected
+ */
+ public function setPerRow( $num ) {
+ if ($num > 0) {
+ $this->mPerRow = (int)$num;
+ }
+ }
+
+ /**
+ * Set how wide each image will be, in pixels.
+ *
+ * @param int $num > 0; invalid numbers will be ignored
+ */
+ public function setWidths( $num ) {
+ if ($num > 0) {
+ $this->mWidths = (int)$num;
+ }
+ }
+
+ /**
+ * Set how high each image will be, in pixels.
+ *
+ * @param int $num > 0; invalid numbers will be ignored
+ */
+ public function setHeights( $num ) {
+ if ($num > 0) {
+ $this->mHeights = (int)$num;
+ }
+ }
+
+ /**
* Instruct the class to use a specific skin for rendering
*
* @param $skin Skin object
*/
function useSkin( $skin ) {
- $this->mSkin =& $skin;
+ $this->mSkin = $skin;
}
-
+
/**
* Return the skin that should be used
*
@@ -76,9 +117,9 @@ class ImageGallery
function getSkin() {
if( !$this->mSkin ) {
global $wgUser;
- $skin =& $wgUser->getSkin();
+ $skin = $wgUser->getSkin();
} else {
- $skin =& $this->mSkin;
+ $skin = $this->mSkin;
}
return $skin;
}
@@ -143,14 +184,15 @@ class ImageGallery
*
*/
function toHTML() {
- global $wgLang, $wgGenerateThumbnailOnParse;
+ global $wgLang;
$sk = $this->getSkin();
$s = '<table class="gallery" cellspacing="0" cellpadding="0">';
if( $this->mCaption )
- $s .= '<td class="galleryheader" colspan="4"><big>' . $this->mCaption . '</big></td>';
-
+ $s .= "\n\t<caption>{$this->mCaption}</caption>";
+
+ $params = array( 'width' => $this->mWidths, 'height' => $this->mHeights );
$i = 0;
foreach ( $this->mImages as $pair ) {
$img =& $pair[0];
@@ -160,20 +202,19 @@ class ImageGallery
if( $nt->getNamespace() != NS_IMAGE ) {
# We're dealing with a non-image, spit out the name and be done with it.
- $thumbhtml = '<div style="height: 152px;">' . htmlspecialchars( $nt->getText() ) . '</div>';
- }
- else if( $this->mParsing && wfIsBadImage( $nt->getDBkey() ) ) {
+ $thumbhtml = "\n\t\t\t".'<div style="height: '.($this->mHeights*1.25+2).'px;">'
+ . htmlspecialchars( $nt->getText() ) . '</div>';
+ } elseif( $this->mParsing && wfIsBadImage( $nt->getDBkey(), $this->getContextTitle() ) ) {
# The image is blacklisted, just show it as a text link.
- $thumbhtml = '<div style="height: 152px;">'
+ $thumbhtml = "\n\t\t\t".'<div style="height: '.($this->mHeights*1.25+2).'px;">'
. $sk->makeKnownLinkObj( $nt, htmlspecialchars( $nt->getText() ) ) . '</div>';
- } else if( !( $thumb = $img->getThumbnail( 120, 120, $wgGenerateThumbnailOnParse ) ) ) {
+ } elseif( !( $thumb = $img->transform( $params ) ) ) {
# Error generating thumbnail.
- $thumbhtml = '<div style="height: 152px;">'
+ $thumbhtml = "\n\t\t\t".'<div style="height: '.($this->mHeights*1.25+2).'px;">'
. htmlspecialchars( $img->getLastError() ) . '</div>';
- }
- else {
- $vpad = floor( ( 150 - $thumb->height ) /2 ) - 2;
- $thumbhtml = '<div class="thumb" style="padding: ' . $vpad . 'px 0;">'
+ } else {
+ $vpad = floor( ( 1.25*$this->mHeights - $thumb->height ) /2 ) - 2;
+ $thumbhtml = "\n\t\t\t".'<div class="thumb" style="padding: ' . $vpad . 'px 0; width: '.($this->mWidths+30).'px;">'
. $sk->makeKnownLinkObj( $nt, $thumb->toHtml() ) . '</div>';
}
@@ -200,27 +241,55 @@ class ImageGallery
# in version 4.8.6 generated crackpot html in its absence, see:
# http://bugzilla.wikimedia.org/show_bug.cgi?id=1765 -Ævar
- $s .= ($i%4==0) ? '<tr>' : '';
- $s .= '<td><div class="gallerybox">' . $thumbhtml
- . '<div class="gallerytext">' . "\n" . $textlink . $text . $nb
- . "</div></div></td>\n";
- $s .= ($i%4==3) ? '</tr>' : '';
- $i++;
+ if ( $i % $this->mPerRow == 0 ) {
+ $s .= "\n\t<tr>";
+ }
+ $s .=
+ "\n\t\t" . '<td><div class="gallerybox" style="width: '.($this->mWidths*1.25).'px;">'
+ . $thumbhtml
+ . "\n\t\t\t" . '<div class="gallerytext">' . "\n"
+ . $textlink . $text . $nb
+ . "\n\t\t\t</div>"
+ . "\n\t\t</div></td>";
+ if ( $i % $this->mPerRow == $this->mPerRow - 1 ) {
+ $s .= "\n\t</tr>";
+ }
+ ++$i;
}
- if( $i %4 != 0 ) {
- $s .= "</tr>\n";
+ if( $i % $this->mPerRow != 0 ) {
+ $s .= "\n\t</tr>";
}
- $s .= '</table>';
+ $s .= "\n</table>";
return $s;
}
-
+
/**
* @return int Number of images in the gallery
*/
public function count() {
return count( $this->mImages );
}
+
+ /**
+ * Set the contextual title
+ *
+ * @param Title $title Contextual title
+ */
+ public function setContextTitle( $title ) {
+ $this->contextTitle = $title;
+ }
+
+ /**
+ * Get the contextual title, if applicable
+ *
+ * @return mixed Title or false
+ */
+ public function getContextTitle() {
+ return is_object( $this->contextTitle ) && $this->contextTitle instanceof Title
+ ? $this->contextTitle
+ : false;
+ }
} //class
?>
diff --git a/includes/ImagePage.php b/includes/ImagePage.php
index 43b99130..13f8e46a 100644
--- a/includes/ImagePage.php
+++ b/includes/ImagePage.php
@@ -1,6 +1,5 @@
<?php
/**
- * @package MediaWiki
*/
/**
@@ -11,7 +10,8 @@ if( !defined( 'MEDIAWIKI' ) )
/**
* Special handling for image description pages
- * @package MediaWiki
+ *
+ * @addtogroup Media
*/
class ImagePage extends Article {
@@ -29,59 +29,62 @@ class ImagePage extends Article {
}
function view() {
- global $wgOut, $wgShowEXIF;
+ global $wgOut, $wgShowEXIF, $wgRequest, $wgUser;
$this->img = new Image( $this->mTitle );
- if( $this->mTitle->getNamespace() == NS_IMAGE ) {
- if ($wgShowEXIF && $this->img->exists()) {
- $exif = $this->img->getExifData();
- $showmeta = count($exif) ? true : false;
- } else {
- $exif = false;
- $showmeta = false;
- }
+ $diff = $wgRequest->getVal( 'diff' );
+ $diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) );
- if ($this->img->exists())
- $wgOut->addHTML($this->showTOC($showmeta));
+ if ( $this->mTitle->getNamespace() != NS_IMAGE || ( isset( $diff ) && $diffOnly ) )
+ return Article::view();
- $this->openShowImage();
+ if ($wgShowEXIF && $this->img->exists()) {
+ $exif = $this->img->getExifData();
+ $showmeta = count($exif) ? true : false;
+ } else {
+ $exif = false;
+ $showmeta = false;
+ }
- # No need to display noarticletext, we use our own message, output in openShowImage()
- if( $this->getID() ) {
- Article::view();
- } else {
- # Just need to set the right headers
- $wgOut->setArticleFlag( true );
- $wgOut->setRobotpolicy( 'index,follow' );
- $wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
- $this->viewUpdates();
- }
+ if ($this->img->exists())
+ $wgOut->addHTML($this->showTOC($showmeta));
- # Show shared description, if needed
- if( $this->mExtraDescription ) {
- $fol = wfMsg( 'shareddescriptionfollows' );
- if( $fol != '-' ) {
- $wgOut->addWikiText( $fol );
- }
- $wgOut->addHTML( '<div id="shared-image-desc">' . $this->mExtraDescription . '</div>' );
- }
+ $this->openShowImage();
- $this->closeShowImage();
- $this->imageHistory();
- $this->imageLinks();
- if( $exif ) {
- global $wgStylePath, $wgStyleVersion;
- $expand = htmlspecialchars( wfEscapeJsString( wfMsg( 'metadata-expand' ) ) );
- $collapse = htmlspecialchars( wfEscapeJsString( wfMsg( 'metadata-collapse' ) ) );
- $wgOut->addHTML( "<h2 id=\"metadata\">" . wfMsgHtml( 'metadata' ) . "</h2>\n" );
- $wgOut->addWikiText( $this->makeMetadataTable( $exif ) );
- $wgOut->addHTML(
- "<script type=\"text/javascript\" src=\"$wgStylePath/common/metadata.js?$wgStyleVersion\"></script>\n" .
- "<script type=\"text/javascript\">attachMetadataToggle('mw_metadata', '$expand', '$collapse');</script>\n" );
- }
- } else {
+ # No need to display noarticletext, we use our own message, output in openShowImage()
+ if ( $this->getID() ) {
Article::view();
+ } else {
+ # Just need to set the right headers
+ $wgOut->setArticleFlag( true );
+ $wgOut->setRobotpolicy( 'index,follow' );
+ $wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
+ $this->viewUpdates();
+ }
+
+ # Show shared description, if needed
+ if ( $this->mExtraDescription ) {
+ $fol = wfMsg( 'shareddescriptionfollows' );
+ if( $fol != '-' && !wfEmptyMsg( 'shareddescriptionfollows', $fol ) ) {
+ $wgOut->addWikiText( $fol );
+ }
+ $wgOut->addHTML( '<div id="shared-image-desc">' . $this->mExtraDescription . '</div>' );
+ }
+
+ $this->closeShowImage();
+ $this->imageHistory();
+ $this->imageLinks();
+
+ if ( $exif ) {
+ global $wgStylePath, $wgStyleVersion;
+ $expand = htmlspecialchars( wfEscapeJsString( wfMsg( 'metadata-expand' ) ) );
+ $collapse = htmlspecialchars( wfEscapeJsString( wfMsg( 'metadata-collapse' ) ) );
+ $wgOut->addHTML( Xml::element( 'h2', array( 'id' => 'metadata' ), wfMsg( 'metadata' ) ). "\n" );
+ $wgOut->addWikiText( $this->makeMetadataTable( $exif ) );
+ $wgOut->addHTML(
+ "<script type=\"text/javascript\" src=\"$wgStylePath/common/metadata.js?$wgStyleVersion\"></script>\n" .
+ "<script type=\"text/javascript\">attachMetadataToggle('mw_metadata', '$expand', '$collapse');</script>\n" );
}
}
@@ -165,15 +168,19 @@ class ImagePage extends Article {
function openShowImage() {
global $wgOut, $wgUser, $wgImageLimits, $wgRequest, $wgLang;
- global $wgUseImageResize, $wgGenerateThumbnailOnParse;
$full_url = $this->img->getURL();
- $anchoropen = '';
- $anchorclose = '';
+ $linkAttribs = false;
$sizeSel = intval( $wgUser->getOption( 'imagesize') );
-
if( !isset( $wgImageLimits[$sizeSel] ) ) {
$sizeSel = User::getDefaultOption( 'imagesize' );
+
+ // The user offset might still be incorrect, specially if
+ // $wgImageLimits got changed (see bug #8858).
+ if( !isset( $wgImageLimits[$sizeSel] ) ) {
+ // Default to the first offset in $wgImageLimits
+ $sizeSel = 0;
+ }
}
$max = $wgImageLimits[$sizeSel];
$maxWidth = $max[0];
@@ -183,21 +190,25 @@ class ImagePage extends Article {
if ( $this->img->exists() ) {
# image
$page = $wgRequest->getIntOrNull( 'page' );
- if ( ! is_null( $page ) ) {
- $this->img->selectPage( $page );
- } else {
+ if ( is_null( $page ) ) {
+ $params = array();
$page = 1;
+ } else {
+ $params = array( 'page' => $page );
}
- $width = $this->img->getWidth();
- $height = $this->img->getHeight();
+ $width_orig = $this->img->getWidth();
+ $width = $width_orig;
+ $height_orig = $this->img->getHeight();
+ $height = $height_orig;
+ $mime = $this->img->getMimeType();
$showLink = false;
+ $linkAttribs = array( 'href' => $full_url );
if ( $this->img->allowInlineDisplay() and $width and $height) {
# image
# "Download high res version" link below the image
- $msg = wfMsgHtml('showbigimage', $width, $height, intval( $this->img->getSize()/1024 ) );
-
+ $msgsize = wfMsgHtml('file-info-size', $width_orig, $height_orig, $sk->formatSize( $this->img->getSize() ), $mime );
# We'll show a thumbnail of this image
if ( $width > $maxWidth || $height > $maxHeight ) {
# Calculate the thumbnail size.
@@ -213,38 +224,41 @@ class ImagePage extends Article {
# Note that $height <= $maxHeight now, but might not be identical
# because of rounding.
}
-
- if( $wgUseImageResize ) {
- $thumbnail = $this->img->getThumbnail( $width, -1, $wgGenerateThumbnailOnParse );
- if ( $thumbnail == null ) {
- $url = $this->img->getViewURL();
- } else {
- $url = $thumbnail->getURL();
- }
- } else {
- # No resize ability? Show the full image, but scale
- # it down in the browser so it fits on the page.
- $url = $this->img->getViewURL();
- }
- $anchoropen = "<a href=\"{$full_url}\">";
- $anchorclose = "</a><br />";
- if( $this->img->mustRender() ) {
- $showLink = true;
- } else {
- $anchorclose .= "\n$anchoropen{$msg}</a>";
- }
+ $msgbig = wfMsgHtml( 'show-big-image' );
+ $msgsmall = wfMsgExt( 'show-big-image-thumb',
+ array( 'parseinline' ), $width, $height );
} else {
- $url = $this->img->getViewURL();
+ # Image is small enough to show full size on image page
+ $msgbig = htmlspecialchars( $this->img->getName() );
+ $msgsmall = wfMsgExt( 'file-nohires', array( 'parseinline' ) );
+ }
+
+ $params['width'] = $width;
+ $thumbnail = $this->img->transform( $params );
+
+ $anchorclose = "<br />";
+ if( $this->img->mustRender() ) {
$showLink = true;
+ } else {
+ $anchorclose .=
+ $msgsmall .
+ '<br />' . Xml::tags( 'a', $linkAttribs, $msgbig ) . ' ' . $msgsize;
}
if ( $this->img->isMultipage() ) {
$wgOut->addHTML( '<table class="multipageimage"><tr><td>' );
}
- $wgOut->addHTML( '<div class="fullImageLink" id="file">' . $anchoropen .
- "<img border=\"0\" src=\"{$url}\" width=\"{$width}\" height=\"{$height}\" alt=\"" .
- htmlspecialchars( $this->img->getTitle()->getPrefixedText() ).'" />' . $anchorclose . '</div>' );
+ $imgAttribs = array(
+ 'border' => 0,
+ 'alt' => $this->img->getTitle()->getPrefixedText()
+ );
+
+ if ( $thumbnail ) {
+ $wgOut->addHTML( '<div class="fullImageLink" id="file">' .
+ $thumbnail->toHtml( $imgAttribs, $linkAttribs ) .
+ $anchorclose . '</div>' );
+ }
if ( $this->img->isMultipage() ) {
$count = $this->img->pageCount();
@@ -252,22 +266,26 @@ class ImagePage extends Article {
if ( $page > 1 ) {
$label = $wgOut->parse( wfMsg( 'imgmultipageprev' ), false );
$link = $sk->makeLinkObj( $this->mTitle, $label, 'page='. ($page-1) );
- $this->img->selectPage( $page - 1 );
- $thumb1 = $sk->makeThumbLinkObj( $this->img, $link, $label, 'none' );
+ $thumb1 = $sk->makeThumbLinkObj( $this->img, $link, $label, 'none',
+ array( 'page' => $page - 1 ) );
} else {
$thumb1 = '';
}
if ( $page < $count ) {
$label = wfMsg( 'imgmultipagenext' );
- $this->img->selectPage( $page + 1 );
$link = $sk->makeLinkObj( $this->mTitle, $label, 'page='. ($page+1) );
- $thumb2 = $sk->makeThumbLinkObj( $this->img, $link, $label, 'none' );
+ $thumb2 = $sk->makeThumbLinkObj( $this->img, $link, $label, 'none',
+ array( 'page' => $page + 1 ) );
} else {
$thumb2 = '';
}
- $select = '<form name="pageselector" action="' . $this->img->getEscapeLocalUrl( '' ) . '" method="GET" onchange="document.pageselector.submit();">' ;
+ global $wgScript;
+ $select = '<form name="pageselector" action="' .
+ htmlspecialchars( $wgScript ) .
+ '" method="get" onchange="document.pageselector.submit();">' .
+ Xml::hidden( 'title', $this->getTitle()->getPrefixedDbKey() );
$select .= $wgOut->parse( wfMsg( 'imgmultigotopre' ), false ) .
' <select id="pageselector" name="page">';
for ( $i=1; $i <= $count; $i++ ) {
@@ -279,7 +297,7 @@ class ImagePage extends Article {
htmlspecialchars( wfMsg( 'imgmultigo' ) ) . '"></form>';
$wgOut->addHTML( '</td><td><div class="multipageimagenavbox">' .
- "$select<hr />$thumb1\n$thumb2<br clear=\"all\" /></div></td></tr></table>" );
+ "$select<hr />$thumb1\n$thumb2<br clear=\"all\" /></div></td></tr></table>" );
}
} else {
#if direct link is allowed but it's not a renderable image, show an icon.
@@ -296,25 +314,26 @@ class ImagePage extends Article {
if ($showLink) {
- $filename = wfEscapeWikiText( $this->img->getName() );
- // Hacky workaround: for some reason we use the incorrect MIME type
- // image/svg for SVG. This should be fixed internally, but at least
- // make the displayed type right.
- $mime = $this->img->getMimeType();
+ // Workaround for incorrect MIME type on SVGs uploaded in previous versions
if ($mime == 'image/svg') $mime = 'image/svg+xml';
- $info = wfMsg( 'fileinfo',
- ceil($this->img->getSize()/1024.0),
- $mime );
+ $filename = wfEscapeWikiText( $this->img->getName() );
+ $info = wfMsg( 'file-info', $sk->formatSize( $this->img->getSize() ), $mime );
+ $infores = '';
+
+ // Check for MIME type. Other types may have more information in the future.
+ if (substr($mime,0,9) == 'image/svg' ) {
+ $infores = wfMsg('file-svg', $width_orig, $height_orig ) . '<br />';
+ }
global $wgContLang;
$dirmark = $wgContLang->getDirMark();
if (!$this->img->isSafeFile()) {
$warning = wfMsg( 'mediawarning' );
$wgOut->addWikiText( <<<END
-<div class="fullMedia">
+<div class="fullMedia">$infores
<span class="dangerousLink">[[Media:$filename|$filename]]</span>$dirmark
-<span class="fileInfo"> ($info)</span>
+<span class="fileInfo"> $info</span>
</div>
<div class="mediaWarning">$warning</div>
@@ -322,8 +341,8 @@ END
);
} else {
$wgOut->addWikiText( <<<END
-<div class="fullMedia">
-[[Media:$filename|$filename]]$dirmark <span class="fileInfo"> ($info)</span>
+<div class="fullMedia">$infores
+[[Media:$filename|$filename]]$dirmark <span class="fileInfo"> $info</span>
</div>
END
);
@@ -360,7 +379,9 @@ END
$wgOut->addHTML($sharedtext);
if ($wgRepositoryBaseUrl && $wgFetchCommonsDescriptions) {
- $text = Http::get($url . '?action=render');
+ $renderUrl = wfAppendQuery( $url, 'action=render' );
+ wfDebug( "Fetching shared description from $renderUrl\n" );
+ $text = Http::get( $renderUrl );
if ($text)
$this->mExtraDescription = $text;
}
@@ -389,11 +410,11 @@ END
# "Upload a new version of this file" link
if( $wgUser->isAllowed( 'reupload' ) ) {
$ulink = $sk->makeExternalLink( $this->getUploadUrl(), wfMsg( 'uploadnewversion-linktext' ) );
- $wgOut->addHtml( "<li><div>{$ulink}</div></li>" );
+ $wgOut->addHtml( "<li><div class='plainlinks'>{$ulink}</div></li>" );
}
# External editing link
- $elink = $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'edit-externally' ), 'action=edit&externaledit=true&mode=file' );
+ $elink = $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml( 'edit-externally' ), 'action=edit&externaledit=true&mode=file' );
$wgOut->addHtml( '<li>' . $elink . '<div>' . wfMsgWikiHtml( 'edit-externally-help' ) . '</div></li>' );
$wgOut->addHtml( '</ul>' );
@@ -449,9 +470,9 @@ END
{
global $wgUser, $wgOut;
- $wgOut->addHTML( '<h2 id="filelinks">' . wfMsg( 'imagelinks' ) . "</h2>\n" );
+ $wgOut->addHTML( Xml::element( 'h2', array( 'id' => 'filelinks' ), wfMsg( 'imagelinks' ) ) . "\n" );
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$page = $dbr->tableName( 'page' );
$imagelinks = $dbr->tableName( 'imagelinks' );
@@ -619,7 +640,7 @@ END
$wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' );
return;
}
- if ( ! $this->mTitle->userCanEdit() ) {
+ if ( ! $this->mTitle->userCan( 'edit' ) ) {
$wgOut->readOnlyPage( $this->getContent(), true );
return;
}
@@ -645,9 +666,6 @@ END
}
$oldver = wfTimestampNow() . "!{$name}";
- $dbr =& wfGetDB( DB_SLAVE );
- $size = $dbr->selectField( 'oldimage', 'oi_size', array( 'oi_archive_name' => $oldimage ) );
-
if ( ! rename( $curfile, "${archive}/{$oldver}" ) ) {
$wgOut->showFileRenameError( $curfile, "${archive}/{$oldver}" );
return;
@@ -683,6 +701,7 @@ END
wfDebug( "ImagePage::doPurge purging " . $this->img->getName() . "\n" );
$update = new HTMLCacheUpdate( $this->mTitle, 'imagelinks' );
$update->doUpdate();
+ $this->img->upgradeRow();
$this->img->purgeCache();
} else {
wfDebug( "ImagePage::doPurge no image\n" );
@@ -694,7 +713,7 @@ END
/**
* @todo document
- * @package MediaWiki
+ * @addtogroup Media
*/
class ImageHistoryList {
function ImageHistoryList( &$skin ) {
@@ -702,8 +721,9 @@ class ImageHistoryList {
}
function beginImageHistoryList() {
- $s = "\n<h2 id=\"filehistory\">" . wfMsg( 'imghistory' ) . "</h2>\n" .
- "<p>" . wfMsg( 'imghistlegend' ) . "</p>\n".'<ul class="special">';
+ $s = "\n" .
+ Xml::element( 'h2', array( 'id' => 'filehistory' ), wfMsg( 'imghistory' ) ) .
+ "\n<p>" . wfMsg( 'imghistlegend' ) . "</p>\n".'<ul class="special">';
return $s;
}
@@ -716,9 +736,9 @@ class ImageHistoryList {
global $wgUser, $wgLang, $wgTitle, $wgContLang;
$datetime = $wgLang->timeanddate( $timestamp, true );
- $del = wfMsg( 'deleteimg' );
- $delall = wfMsg( 'deleteimgcompletely' );
- $cur = wfMsg( 'cur' );
+ $del = wfMsgHtml( 'deleteimg' );
+ $delall = wfMsgHtml( 'deleteimgcompletely' );
+ $cur = wfMsgHtml( 'cur' );
if ( $iscur ) {
$url = Image::imageUrl( $img );
@@ -734,10 +754,10 @@ class ImageHistoryList {
}
} else {
$url = htmlspecialchars( wfImageArchiveUrl( $img ) );
- if( $wgUser->getID() != 0 && $wgTitle->userCanEdit() ) {
+ if( $wgUser->getID() != 0 && $wgTitle->userCan( 'edit' ) ) {
$token = urlencode( $wgUser->editToken( $img ) );
$rlink = $this->skin->makeKnownLinkObj( $wgTitle,
- wfMsg( 'revertimg' ), 'action=revert&oldimage=' .
+ wfMsgHtml( 'revertimg' ), 'action=revert&oldimage=' .
urlencode( $img ) . "&wpEditToken=$token" );
$dlink = $this->skin->makeKnownLinkObj( $wgTitle,
$del, 'action=delete&oldimage=' . urlencode( $img ) .
@@ -746,7 +766,7 @@ class ImageHistoryList {
# Having live active links for non-logged in users
# means that bots and spiders crawling our site can
# inadvertently change content. Baaaad idea.
- $rlink = wfMsg( 'revertimg' );
+ $rlink = wfMsgHtml( 'revertimg' );
$dlink = $del;
}
}
@@ -754,7 +774,7 @@ class ImageHistoryList {
$userlink = $this->skin->userLink( $user, $usertext ) . $this->skin->userToolLinks( $user, $usertext );
$nbytes = wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
$wgLang->formatNum( $size ) );
- $widthheight = wfMsg( 'widthheight', $width, $height );
+ $widthheight = wfMsgHtml( 'widthheight', $width, $height );
$style = $this->skin->getInternalLinkAttributes( $url, $datetime );
$s = "<li> ({$dlink}) ({$rlink}) <a href=\"{$url}\"{$style}>{$datetime}</a> . . {$userlink} . . {$widthheight} ({$nbytes})";
diff --git a/includes/ImageQueryPage.php b/includes/ImageQueryPage.php
new file mode 100644
index 00000000..93f090a1
--- /dev/null
+++ b/includes/ImageQueryPage.php
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * Variant of QueryPage which uses a gallery to output results, thus
+ * suited for reports generating images
+ *
+ * @package MediaWiki
+ * @addtogroup SpecialPage
+ * @author Rob Church <robchur@gmail.com>
+ */
+class ImageQueryPage extends QueryPage {
+
+ /**
+ * Format and output report results using the given information plus
+ * OutputPage
+ *
+ * @param OutputPage $out OutputPage to print to
+ * @param Skin $skin User skin to use
+ * @param Database $dbr Database (read) connection to use
+ * @param int $res Result pointer
+ * @param int $num Number of available result rows
+ * @param int $offset Paging offset
+ */
+ protected function outputResults( $out, $skin, $dbr, $res, $num, $offset ) {
+ if( $num > 0 ) {
+ $gallery = new ImageGallery();
+ $gallery->useSkin( $skin );
+
+ # $res might contain the whole 1,000 rows, so we read up to
+ # $num [should update this to use a Pager]
+ for( $i = 0; $i < $num && $row = $dbr->fetchObject( $res ); $i++ ) {
+ $image = $this->prepareImage( $row );
+ if( $image instanceof Image ) {
+ $gallery->add( $image, $this->getCellHtml( $row ) );
+ }
+ }
+
+ $out->addHtml( $gallery->toHtml() );
+ }
+ }
+
+ /**
+ * Prepare an image object given a result row
+ *
+ * @param object $row Result row
+ * @return Image
+ */
+ private function prepareImage( $row ) {
+ $namespace = isset( $row->namespace ) ? $row->namespace : NS_IMAGE;
+ $title = Title::makeTitleSafe( $namespace, $row->title );
+ return ( $title instanceof Title && $title->getNamespace() == NS_IMAGE )
+ ? new Image( $title )
+ : null;
+ }
+
+ /**
+ * Get additional HTML to be shown in a results' cell
+ *
+ * @param object $row Result row
+ * @return string
+ */
+ protected function getCellHtml( $row ) {
+ return '';
+ }
+
+}
+
+?>
diff --git a/includes/JobQueue.php b/includes/JobQueue.php
index 746cf5de..140130fa 100644
--- a/includes/JobQueue.php
+++ b/includes/JobQueue.php
@@ -4,6 +4,9 @@ if ( !defined( 'MEDIAWIKI' ) ) {
die( "This file is part of MediaWiki, it is not a valid entry point\n" );
}
+/**
+ * Class to both describe a background job and handle jobs.
+ */
abstract class Job {
var $command,
$title,
@@ -13,10 +16,20 @@ abstract class Job {
$error;
/*-------------------------------------------------------------------------
+ * Abstract functions
+ *------------------------------------------------------------------------*/
+
+ /**
+ * Run the job
+ * @return boolean success
+ */
+ abstract function run();
+
+ /*-------------------------------------------------------------------------
* Static functions
*------------------------------------------------------------------------*/
- /**
+ /**
* @deprecated use LinksUpdate::queueRecursiveJobs()
*/
/**
@@ -26,25 +39,41 @@ abstract class Job {
/**
* Pop a job off the front of the queue
* @static
+ * @param $offset Number of jobs to skip
* @return Job or false if there's no jobs
*/
- static function pop() {
+ static function pop($offset=0) {
wfProfileIn( __METHOD__ );
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
- // Get a job from the slave
- $row = $dbr->selectRow( 'job', '*', '', __METHOD__,
- array( 'ORDER BY' => 'job_id', 'LIMIT' => 1 )
- );
+ /* Get a job from the slave, start with an offset,
+ scan full set afterwards, avoid hitting purged rows
- if ( $row === false ) {
- wfProfileOut( __METHOD__ );
- return false;
+ NB: If random fetch previously was used, offset
+ will always be ahead of few entries
+ */
+
+ $row = $dbr->selectRow( 'job', '*', "job_id >= ${offset}", __METHOD__,
+ array( 'ORDER BY' => 'job_id', 'LIMIT' => 1 ));
+
+ // Refetching without offset is needed as some of job IDs could have had delayed commits
+ // and have lower IDs than jobs already executed, blame concurrency :)
+ //
+ if ( $row === false) {
+ if ($offset!=0)
+ $row = $dbr->selectRow( 'job', '*', '', __METHOD__,
+ array( 'ORDER BY' => 'job_id', 'LIMIT' => 1 ));
+
+ if ($row === false ) {
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
}
+ $offset = $row->job_id;
// Try to delete it from the master
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'job', array( 'job_id' => $row->job_id ), __METHOD__ );
$affected = $dbw->affectedRows();
$dbw->immediateCommit();
@@ -53,7 +82,7 @@ abstract class Job {
// Failed, someone else beat us to it
// Try getting a random row
$row = $dbw->selectRow( 'job', array( 'MIN(job_id) as minjob',
- 'MAX(job_id) as maxjob' ), '', __METHOD__ );
+ 'MAX(job_id) as maxjob' ), "job_id >= $offset", __METHOD__ );
if ( $row === false || is_null( $row->minjob ) || is_null( $row->maxjob ) ) {
// No jobs to get
wfProfileOut( __METHOD__ );
@@ -61,7 +90,7 @@ abstract class Job {
}
// Get the random row
$row = $dbw->selectRow( 'job', '*',
- array( 'job_id' => mt_rand( $row->minjob, $row->maxjob ) ), __METHOD__ );
+ 'job_id >= ' . mt_rand( $row->minjob, $row->maxjob ), __METHOD__ );
if ( $row === false ) {
// Random job gone before we got the chance to select it
// Give up
@@ -72,7 +101,7 @@ abstract class Job {
$dbw->delete( 'job', array( 'job_id' => $row->job_id ), __METHOD__ );
$affected = $dbw->affectedRows();
$dbw->immediateCommit();
-
+
if ( !$affected ) {
// Random job gone before we exclusively deleted it
// Give up
@@ -80,22 +109,22 @@ abstract class Job {
return false;
}
}
-
+
// If execution got to here, there's a row in $row that has been deleted from the database
// by this thread. Hence the concurrent pop was successful.
$namespace = $row->job_namespace;
$dbkey = $row->job_title;
$title = Title::makeTitleSafe( $namespace, $dbkey );
$job = Job::factory( $row->job_cmd, $title, Job::extractBlob( $row->job_params ), $row->job_id );
-
+
// Remove any duplicates it may have later in the queue
$dbw->delete( 'job', $job->insertFields(), __METHOD__ );
-
+
wfProfileOut( __METHOD__ );
return $job;
}
- /**
+ /**
* Create an object of a subclass
*/
static function factory( $command, $title, $params = false, $id = 0 ) {
@@ -126,6 +155,27 @@ abstract class Job {
}
}
+ /**
+ * Batch-insert a group of jobs into the queue.
+ * This will be wrapped in a transaction with a forced commit.
+ *
+ * This may add duplicate at insert time, but they will be
+ * removed later on, when the first one is popped.
+ *
+ * @param $jobs array of Job objects
+ */
+ static function batchInsert( $jobs ) {
+ if( count( $jobs ) ) {
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->begin();
+ foreach( $jobs as $job ) {
+ $rows[] = $job->insertFields();
+ }
+ $dbw->insert( 'job', $rows, __METHOD__, 'IGNORE' );
+ $dbw->commit();
+ }
+ }
+
/*-------------------------------------------------------------------------
* Non-static functions
*------------------------------------------------------------------------*/
@@ -147,8 +197,8 @@ abstract class Job {
function insert() {
$fields = $this->insertFields();
- $dbw =& wfGetDB( DB_MASTER );
-
+ $dbw = wfGetDB( DB_MASTER );
+
if ( $this->removeDuplicates ) {
$res = $dbw->select( 'job', array( '1' ), $fields, __METHOD__ );
if ( $dbw->numRows( $res ) ) {
@@ -158,7 +208,7 @@ abstract class Job {
$fields['job_id'] = $dbw->nextSequenceValue( 'job_job_id_seq' );
$dbw->insert( 'job', $fields, __METHOD__ );
}
-
+
protected function insertFields() {
return array(
'job_cmd' => $this->command,
@@ -167,34 +217,7 @@ abstract class Job {
'job_params' => Job::makeBlob( $this->params )
);
}
-
- /**
- * Batch-insert a group of jobs into the queue.
- * This will be wrapped in a transaction with a forced commit.
- *
- * This may add duplicate at insert time, but they will be
- * removed later on, when the first one is popped.
- *
- * @param $jobs array of Job objects
- */
- static function batchInsert( $jobs ) {
- if( count( $jobs ) ) {
- $dbw = wfGetDB( DB_MASTER );
- $dbw->begin();
- foreach( $jobs as $job ) {
- $rows[] = $job->insertFields();
- }
- $dbw->insert( 'job', $rows, __METHOD__, 'IGNORE' );
- $dbw->commit();
- }
- }
- /**
- * Run the job
- * @return boolean success
- */
- abstract function run();
-
function toString() {
$paramString = '';
if ( $this->params ) {
@@ -222,6 +245,10 @@ abstract class Job {
}
}
+
+/**
+ * Background job to update links for a given title.
+ */
class RefreshLinksJob extends Job {
function __construct( $title, $params = '', $id = 0 ) {
parent::__construct( 'refreshLinks', $title, $params, $id );
@@ -237,7 +264,7 @@ class RefreshLinksJob extends Job {
$linkCache =& LinkCache::singleton();
$linkCache->clear();
-
+
if ( is_null( $this->title ) ) {
$this->error = "refreshLinks: Invalid title";
wfProfileOut( __METHOD__ );
diff --git a/includes/Licenses.php b/includes/Licenses.php
index dd1308b4..f4586ae5 100644
--- a/includes/Licenses.php
+++ b/includes/Licenses.php
@@ -1,9 +1,8 @@
<?php
/**
* A License class for use on Special:Upload
- *
- * @package MediaWiki
- * @subpackage SpecialPage
+ *
+ * @addtogroup SpecialPage
*
* @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
* @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason
@@ -31,12 +30,12 @@ class Licenses {
/**#@-*/
/**
- * Constrictor
+ * Constructor
*
* @param $str String: the string to build the licenses member from, will use
* wfMsgForContent( 'licenses' ) if null (default: null)
*/
- function Licenses( $str = null ) {
+ function __construct( $str = null ) {
// PHP sucks, this should be possible in the constructor
$this->msg = is_null( $str ) ? wfMsgForContent( 'licenses' ) : $str;
$this->html = '';
@@ -147,6 +146,9 @@ class Licenses {
function getHtml() { return $this->html; }
}
+/**
+ * A License class for use on Special:Upload (represents a single type of license).
+ */
class License {
/**
* @var string
diff --git a/includes/LinkBatch.php b/includes/LinkBatch.php
index 61e1c040..065c540a 100644
--- a/includes/LinkBatch.php
+++ b/includes/LinkBatch.php
@@ -4,8 +4,7 @@
* Class representing a list of titles
* The execute() method checks them all for existence and adds them to a LinkCache object
+
- * @package MediaWiki
- * @subpackage Cache
+ * @addtogroup Cache
*/
class LinkBatch {
/**
@@ -13,7 +12,7 @@ class LinkBatch {
*/
var $data = array();
- function LinkBatch( $arr = array() ) {
+ function __construct( $arr = array() ) {
foreach( $arr as $item ) {
$this->addObj( $item );
}
@@ -120,7 +119,7 @@ class LinkBatch {
// Construct query
// This is very similar to Parser::replaceLinkHolders
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$page = $dbr->tableName( 'page' );
$set = $this->constructSet( 'page', $dbr );
if ( $set === false ) {
diff --git a/includes/LinkCache.php b/includes/LinkCache.php
index 8e56225b..53fb640a 100644
--- a/includes/LinkCache.php
+++ b/includes/LinkCache.php
@@ -1,13 +1,8 @@
<?php
/**
* Cache for article titles (prefixed DB keys) and ids linked from one source
- * @package MediaWiki
- * @subpackage Cache
- */
-
-/**
- * @package MediaWiki
- * @subpackage Cache
+ *
+ * @addtogroup Cache
*/
class LinkCache {
// Increment $mClassVer whenever old serialized versions of this class
@@ -29,7 +24,7 @@ class LinkCache {
return $instance;
}
- function LinkCache() {
+ function __construct() {
$this->mForUpdate = false;
$this->mPageLinks = array();
$this->mGoodLinks = array();
@@ -135,14 +130,14 @@ class LinkCache {
$id = $wgMemc->get( $key = $this->getKey( $title ) );
if( ! is_integer( $id ) ) {
if ( $this->mForUpdate ) {
- $db =& wfGetDB( DB_MASTER );
+ $db = wfGetDB( DB_MASTER );
if ( !( $wgAntiLockFlags & ALF_NO_LINK_LOCK ) ) {
$options = array( 'FOR UPDATE' );
} else {
$options = array();
}
} else {
- $db =& wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_SLAVE );
$options = array();
}
diff --git a/includes/LinkFilter.php b/includes/LinkFilter.php
index e03b59dd..39341d5d 100644
--- a/includes/LinkFilter.php
+++ b/includes/LinkFilter.php
@@ -14,7 +14,7 @@ class LinkFilter {
/**
* @static
*/
- function matchEntry( $text, $filterEntry ) {
+ static function matchEntry( $text, $filterEntry ) {
$regex = LinkFilter::makeRegex( $filterEntry );
return preg_match( $regex, $text );
}
@@ -22,10 +22,10 @@ class LinkFilter {
/**
* @static
*/
- function makeRegex( $filterEntry ) {
+ private static function makeRegex( $filterEntry ) {
$regex = '!http://';
if ( substr( $filterEntry, 0, 2 ) == '*.' ) {
- $regex .= '([A-Za-z0-9.-]+\.|)';
+ $regex .= '(?:[A-Za-z0-9.-]+\.|)';
$filterEntry = substr( $filterEntry, 2 );
}
$regex .= preg_quote( $filterEntry, '!' ) . '!Si';
@@ -47,8 +47,10 @@ class LinkFilter {
* Asterisks in any other location are considered invalid.
*
* @static
+ * @param $filterEntry String: domainparts
+ * @param $prot String: protocol
*/
- function makeLike( $filterEntry ) {
+ public static function makeLike( $filterEntry , $prot = 'http://' ) {
if ( substr( $filterEntry, 0, 2 ) == '*.' ) {
$subdomains = true;
$filterEntry = substr( $filterEntry, 2 );
@@ -74,17 +76,31 @@ class LinkFilter {
$path = '/';
$host = $filterEntry;
}
- $host = strtolower( implode( '.', array_reverse( explode( '.', $host ) ) ) );
- if ( substr( $host, -1, 1 ) !== '.' ) {
- $host .= '.';
- }
- $like = "http://$host";
-
- if ( $subdomains ) {
- $like .= '%';
- }
- if ( !$subdomains || $path !== '/' ) {
- $like .= $path . '%';
+ // Reverse the labels in the hostname, convert to lower case
+ // For emails reverse domainpart only
+ if ( $prot == 'mailto:' && strpos($host, '@') ) {
+ // complete email adress
+ $mailparts = explode( '@', $host );
+ $domainpart = strtolower( implode( '.', array_reverse( explode( '.', $mailparts[1] ) ) ) );
+ $host = $domainpart . '@' . $mailparts[0];
+ $like = "$prot$host%";
+ } elseif ( $prot == 'mailto:' ) {
+ // domainpart of email adress only. do not add '.'
+ $host = strtolower( implode( '.', array_reverse( explode( '.', $host ) ) ) );
+ $like = "$prot$host%";
+ } else {
+ $host = strtolower( implode( '.', array_reverse( explode( '.', $host ) ) ) );
+ if ( substr( $host, -1, 1 ) !== '.' ) {
+ $host .= '.';
+ }
+ $like = "$prot$host";
+
+ if ( $subdomains ) {
+ $like .= '%';
+ }
+ if ( !$subdomains || $path !== '/' ) {
+ $like .= $path . '%';
+ }
}
return $like;
}
diff --git a/includes/Linker.php b/includes/Linker.php
index 0eabab2f..b12e2ad0 100644
--- a/includes/Linker.php
+++ b/includes/Linker.php
@@ -4,19 +4,15 @@
* These functions are used for primarily page content:
* links, embedded images, table of contents. Links are
* also used in the skin.
- * @package MediaWiki
- */
-
-/**
* For the moment, Skin is a descendent class of Linker.
* In the future, it should probably be further split
* so that ever other bit of the wiki doesn't have to
* go loading up Skin to get at it.
*
- * @package MediaWiki
+ * @addtogroup Skins
*/
class Linker {
- function Linker() {}
+ function __construct() {}
/**
* @deprecated
@@ -229,7 +225,7 @@ class Linker {
} else {
$threshold = $wgUser->getOption('stubthreshold') ;
if ( $threshold > 0 ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$s = $dbr->selectRow(
array( 'page' ),
array( 'page_len',
@@ -358,16 +354,8 @@ class Linker {
* the end of the link.
*/
function makeStubLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
- $u = $nt->escapeLocalURL( $query );
-
- if ( '' == $text ) {
- $text = htmlspecialchars( $nt->getPrefixedText() );
- }
$style = $this->getInternalLinkAttributesObj( $nt, $text, 'stub' );
-
- list( $inside, $trail ) = Linker::splitTrail( $trail );
- $s = "<a href=\"{$u}\"{$style}>{$prefix}{$text}{$inside}</a>{$trail}";
- return $s;
+ return $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix, '', $style );
}
/**
@@ -431,25 +419,19 @@ class Linker {
}
/** @todo document */
- function makeImageLinkObj( $nt, $label, $alt, $align = '', $width = false, $height = false, $framed = false,
- $thumb = false, $manual_thumb = '', $page = null )
+ function makeImageLinkObj( $nt, $label, $alt, $align = '', $params = array(), $framed = false,
+ $thumb = false, $manual_thumb = '', $valign = '' )
{
- global $wgContLang, $wgUser, $wgThumbLimits, $wgGenerateThumbnailOnParse;
+ global $wgContLang, $wgUser, $wgThumbLimits;
$img = new Image( $nt );
- if ( ! is_null( $page ) ) {
- $img->selectPage( $page );
- }
-
if ( !$img->allowInlineDisplay() && $img->exists() ) {
return $this->makeKnownLinkObj( $nt );
}
- $url = $img->getViewURL();
$error = $prefix = $postfix = '';
-
- wfDebug( "makeImageLinkObj: '$width'x'$height', \"$label\"\n" );
+ $page = isset( $params['page'] ) ? $params['page'] : false;
if ( 'center' == $align )
{
@@ -458,6 +440,19 @@ class Linker {
$align = 'none';
}
+ if ( !isset( $params['width'] ) ) {
+ $params['width'] = $img->getWidth( $page );
+ if( $thumb || $framed ) {
+ $wopt = $wgUser->getOption( 'thumbsize' );
+
+ if( !isset( $wgThumbLimits[$wopt] ) ) {
+ $wopt = User::getDefaultOption( 'thumbsize' );
+ }
+
+ $params['width'] = min( $params['width'], $wgThumbLimits[$wopt] );
+ }
+ }
+
if ( $thumb || $framed ) {
# Create a thumbnail. Alignment depends on language
@@ -470,70 +465,39 @@ class Linker {
if ( $align == '' ) {
$align = $wgContLang->isRTL() ? 'left' : 'right';
}
-
-
- if ( $width === false ) {
- $wopt = $wgUser->getOption( 'thumbsize' );
-
- if( !isset( $wgThumbLimits[$wopt] ) ) {
- $wopt = User::getDefaultOption( 'thumbsize' );
- }
-
- $width = min( $img->getWidth(), $wgThumbLimits[$wopt] );
- }
-
- return $prefix.$this->makeThumbLinkObj( $img, $label, $alt, $align, $width, $height, $framed, $manual_thumb ).$postfix;
+ return $prefix.$this->makeThumbLinkObj( $img, $label, $alt, $align, $params, $framed, $manual_thumb ).$postfix;
}
- if ( $width && $img->exists() ) {
-
- # Create a resized image, without the additional thumbnail
- # features
-
- if ( $height == false )
- $height = -1;
- if ( $manual_thumb == '') {
- $thumb = $img->getThumbnail( $width, $height, $wgGenerateThumbnailOnParse );
- if ( $thumb ) {
- // In most cases, $width = $thumb->width or $height = $thumb->height.
- // If not, we're scaling the image larger than it can be scaled,
- // so we send to the browser a smaller thumbnail, and let the client do the scaling.
-
- if ($height != -1 && $width > $thumb->width * $height / $thumb->height) {
- // $height is the limiting factor, not $width
- // set $width to the largest it can be, such that the resulting
- // scaled height is at most $height
- $width = floor($thumb->width * $height / $thumb->height);
- }
- $height = round($thumb->height * $width / $thumb->width);
+ if ( $params['width'] && $img->exists() ) {
+ # Create a resized image, without the additional thumbnail features
+ $thumb = $img->transform( $params );
+ } else {
+ $thumb = false;
+ }
- wfDebug( "makeImageLinkObj: client-size set to '$width x $height'\n" );
- $url = $thumb->getUrl();
- } else {
- $error = htmlspecialchars( $img->getLastError() );
- // Do client-side scaling...
- $height = intval( $img->getHeight() * $width / $img->getWidth() );
- }
- }
+ if ( $page ) {
+ $query = 'page=' . urlencode( $page );
} else {
- $width = $img->width;
- $height = $img->height;
+ $query = '';
+ }
+ $u = $nt->getLocalURL( $query );
+ $imgAttribs = array(
+ 'alt' => $alt,
+ 'longdesc' => $u
+ );
+ if ( $valign ) {
+ $imgAttribs['style'] = "vertical-align: $valign";
}
+ $linkAttribs = array(
+ 'href' => $u,
+ 'class' => 'image',
+ 'title' => $alt
+ );
- wfDebug( "makeImageLinkObj2: '$width'x'$height'\n" );
- $u = $nt->escapeLocalURL();
- if ( $error ) {
- $s = $error;
- } elseif ( $url == '' ) {
+ if ( !$thumb ) {
$s = $this->makeBrokenImageLinkObj( $img->getTitle() );
- //$s .= "<br />{$alt}<br />{$url}<br />\n";
} else {
- $s = '<a href="'.$u.'" class="image" title="'.$alt.'">' .
- '<img src="'.$url.'" alt="'.$alt.'" ' .
- ( $width
- ? ( 'width="'.$width.'" height="'.$height.'" ' )
- : '' ) .
- 'longdesc="'.$u.'" /></a>';
+ $s = $thumb->toHtml( $imgAttribs, $linkAttribs );
}
if ( '' != $align ) {
$s = "<div class=\"float{$align}\"><span>{$s}</span></div>";
@@ -545,86 +509,64 @@ class Linker {
* Make HTML for a thumbnail including image, border and caption
* $img is an Image object
*/
- function makeThumbLinkObj( $img, $label = '', $alt, $align = 'right', $boxwidth = 180, $boxheight=false, $framed=false , $manual_thumb = "" ) {
- global $wgStylePath, $wgContLang, $wgGenerateThumbnailOnParse;
+ function makeThumbLinkObj( $img, $label = '', $alt, $align = 'right', $params = array(), $framed=false , $manual_thumb = "" ) {
+ global $wgStylePath, $wgContLang;
$thumbUrl = '';
$error = '';
- $width = $height = 0;
- if ( $img->exists() ) {
- $width = $img->getWidth();
- $height = $img->getHeight();
- }
- if ( 0 == $width || 0 == $height ) {
- $width = $height = 180;
- }
- if ( $boxwidth == 0 ) {
- $boxwidth = 180;
+ $page = isset( $params['page'] ) ? $params['page'] : false;
+
+ if ( empty( $params['width'] ) ) {
+ $params['width'] = 180;
}
- if ( $framed ) {
+ $thumb = false;
+ if ( $manual_thumb != '' ) {
+ # Use manually specified thumbnail
+ $manual_title = Title::makeTitleSafe( NS_IMAGE, $manual_thumb );
+ if( $manual_title ) {
+ $manual_img = new Image( $manual_title );
+ $thumb = $manual_img->getUnscaledThumb();
+ }
+ } elseif ( $framed ) {
// Use image dimensions, don't scale
- $boxwidth = $width;
- $boxheight = $height;
- $thumbUrl = $img->getViewURL();
+ $thumb = $img->getUnscaledThumb( $page );
} else {
- if ( $boxheight === false )
- $boxheight = -1;
- if ( '' == $manual_thumb ) {
- $thumb = $img->getThumbnail( $boxwidth, $boxheight, $wgGenerateThumbnailOnParse );
- if ( $thumb ) {
- $thumbUrl = $thumb->getUrl();
- $boxwidth = $thumb->width;
- $boxheight = $thumb->height;
- } else {
- $error = $img->getLastError();
- }
- }
+ $thumb = $img->transform( $params );
}
- $oboxwidth = $boxwidth + 2;
- if ( $manual_thumb != '' ) # Use manually specified thumbnail
- {
- $manual_title = Title::makeTitleSafe( NS_IMAGE, $manual_thumb ); #new Title ( $manual_thumb ) ;
- if( $manual_title ) {
- $manual_img = new Image( $manual_title );
- $thumbUrl = $manual_img->getViewURL();
- if ( $manual_img->exists() )
- {
- $width = $manual_img->getWidth();
- $height = $manual_img->getHeight();
- $boxwidth = $width ;
- $boxheight = $height ;
- $oboxwidth = $boxwidth + 2 ;
- }
- }
+ if ( $thumb ) {
+ $outerWidth = $thumb->getWidth() + 2;
+ } else {
+ $outerWidth = $params['width'] + 2;
}
- $u = $img->getEscapeLocalURL();
+ $query = $page ? 'page=' . urlencode( $page ) : '';
+ $u = $img->getTitle()->getLocalURL( $query );
$more = htmlspecialchars( wfMsg( 'thumbnail-more' ) );
$magnifyalign = $wgContLang->isRTL() ? 'left' : 'right';
$textalign = $wgContLang->isRTL() ? ' style="text-align:right"' : '';
- $s = "<div class=\"thumb t{$align}\"><div class=\"thumbinner\" style=\"width:{$oboxwidth}px;\">";
- if( $thumbUrl == '' ) {
- // Couldn't generate thumbnail? Scale the image client-side.
- $thumbUrl = $img->getViewURL();
- if( $boxheight == -1 ) {
- // Approximate...
- $boxheight = intval( $height * $boxwidth / $width );
- }
- }
- if ( $error ) {
- $s .= htmlspecialchars( $error );
+ $s = "<div class=\"thumb t{$align}\"><div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
+ if ( !$thumb ) {
+ $s .= htmlspecialchars( wfMsg( 'thumbnail_error', '' ) );
$zoomicon = '';
} elseif( !$img->exists() ) {
$s .= $this->makeBrokenImageLinkObj( $img->getTitle() );
$zoomicon = '';
} else {
- $s .= '<a href="'.$u.'" class="internal" title="'.$alt.'">'.
- '<img src="'.$thumbUrl.'" alt="'.$alt.'" ' .
- 'width="'.$boxwidth.'" height="'.$boxheight.'" ' .
- 'longdesc="'.$u.'" class="thumbimage" /></a>';
+ $imgAttribs = array(
+ 'alt' => $alt,
+ 'longdesc' => $u,
+ 'class' => 'thumbimage'
+ );
+ $linkAttribs = array(
+ 'href' => $u,
+ 'class' => 'internal',
+ 'title' => $alt
+ );
+
+ $s .= $thumb->toHtml( $imgAttribs, $linkAttribs );
if ( $framed ) {
$zoomicon="";
} else {
@@ -680,8 +622,6 @@ class Linker {
*
* @param $title Title object.
* @param $text String: pre-sanitized HTML
- * @param $nourl Boolean: Mask absolute URLs, so the parser doesn't
- * linkify them (it is currently not context-aware)
* @return string HTML
*
* @public
@@ -756,10 +696,10 @@ class Linker {
/**
* @param $userId Integer: user id in database.
* @param $userText String: user name in database.
+ * @param $redContribsWhenNoEdits Bool: return a red contribs link when the user had no edits and this is true.
* @return string HTML fragment with talk and/or block links
- * @private
*/
- function userToolLinks( $userId, $userText ) {
+ public function userToolLinks( $userId, $userText, $redContribsWhenNoEdits = false ) {
global $wgUser, $wgDisableAnonTalk, $wgSysopUserBans;
$talkable = !( $wgDisableAnonTalk && 0 == $userId );
$blockable = ( $wgSysopUserBans || 0 == $userId );
@@ -769,9 +709,15 @@ class Linker {
$items[] = $this->userTalkLink( $userId, $userText );
}
if( $userId ) {
+ // check if the user has an edit
+ if( $redContribsWhenNoEdits && User::edits( $userId ) == 0 ) {
+ $style = "class='new'";
+ } else {
+ $style = '';
+ }
$contribsPage = SpecialPage::getTitleFor( 'Contributions', $userText );
- $items[] = $this->makeKnownLinkObj( $contribsPage ,
- wfMsgHtml( 'contribslink' ) );
+
+ $items[] = $this->makeKnownLinkObj( $contribsPage, wfMsgHtml( 'contribslink' ), '', '', '', '', $style );
}
if( $blockable && $wgUser->isAllowed( 'block' ) ) {
$items[] = $this->blockLink( $userId, $userText );
@@ -785,17 +731,22 @@ class Linker {
}
/**
+ * Alias for userToolLinks( $userId, $userText, true );
+ */
+ public function userToolLinksRedContribs( $userId, $userText ) {
+ return $this->userToolLinks( $userId, $userText, true );
+ }
+
+
+ /**
* @param $userId Integer: user id in database.
* @param $userText String: user name in database.
* @return string HTML fragment with user talk link
* @private
*/
function userTalkLink( $userId, $userText ) {
- global $wgLang;
- $talkname = $wgLang->getNsText( NS_TALK ); # use the shorter name
-
$userTalkPage = Title::makeTitle( NS_USER_TALK, $userText );
- $userTalkLink = $this->makeLinkObj( $userTalkPage, $talkname );
+ $userTalkLink = $this->makeLinkObj( $userTalkPage, wfMsgHtml( 'talkpagelinktext' ) );
return $userTalkLink;
}
@@ -860,7 +811,7 @@ class Linker {
* Since you can't set a default parameter for a reference, I've turned it
* temporarily to a value pass. Should be adjusted further. --brion
*
- * $param string $comment
+ * @param string $comment
* @param mixed $title Title object (to generate link to the section in autocomment) or null
* @param bool $local Whether section links should refer to local page
*/
@@ -1013,7 +964,7 @@ class Linker {
/** @todo document */
function tocList($toc) {
global $wgJsMimeType;
- $title = wfMsgForContent('toc') ;
+ $title = wfMsgHtml('toc') ;
return
'<table id="toc" class="toc" summary="' . $title .'"><tr><td>'
. '<div id="toctitle"><h2>' . $title . "</h2></div>\n"
@@ -1023,8 +974,8 @@ class Linker {
. "</ul>\n</td></tr></table>"
. '<script type="' . $wgJsMimeType . '">'
. ' if (window.showTocToggle) {'
- . ' var tocShowText = "' . wfEscapeJsString( wfMsgForContent('showtoc') ) . '";'
- . ' var tocHideText = "' . wfEscapeJsString( wfMsgForContent('hidetoc') ) . '";'
+ . ' var tocShowText = "' . wfEscapeJsString( wfMsg('showtoc') ) . '";'
+ . ' var tocHideText = "' . wfEscapeJsString( wfMsg('hidetoc') ) . '";'
. ' showTocToggle();'
. ' } '
. "</script>\n";
@@ -1134,7 +1085,7 @@ class Linker {
global $wgUser;
wfProfileIn( __METHOD__ );
- $sk =& $wgUser->getSkin();
+ $sk = $wgUser->getSkin();
$outText = '';
if ( count( $templates ) > 0 ) {
@@ -1182,10 +1133,14 @@ class Linker {
*/
public function formatSize( $size ) {
global $wgLang;
+ // For small sizes no decimal places necessary
+ $round = 0;
if( $size > 1024 ) {
$size = $size / 1024;
if( $size > 1024 ) {
$size = $size / 1024;
+ // For MB and bigger two decimal places are smarter
+ $round = 2;
if( $size > 1024 ) {
$size = $size / 1024;
$msg = 'size-gigabytes';
@@ -1198,10 +1153,59 @@ class Linker {
} else {
$msg = 'size-bytes';
}
- $size = round( $size, 0 );
+ $size = round( $size, $round );
return wfMsgHtml( $msg, $wgLang->formatNum( $size ) );
}
-
+
+ /**
+ * Given the id of an interface element, constructs the appropriate title
+ * and accesskey attributes from the system messages. (Note, this is usu-
+ * ally the id but isn't always, because sometimes the accesskey needs to
+ * go on a different element than the id, for reverse-compatibility, etc.)
+ *
+ * @param string $name Id of the element, minus prefixes.
+ * @return string title and accesskey attributes, ready to drop in an
+ * element (e.g., ' title="This does something [x]" accesskey="x"').
+ */
+ public function tooltipAndAccesskey($name) {
+ $out = '';
+
+ $tooltip = wfMsg('tooltip-'.$name);
+ if (!wfEmptyMsg('tooltip-'.$name, $tooltip) && $tooltip != '-') {
+ // Compatibility: formerly some tooltips had [alt-.] hardcoded
+ $tooltip = preg_replace( "/ ?\[alt-.\]$/", '', $tooltip );
+ $out .= ' title="'.htmlspecialchars($tooltip);
+ }
+ $accesskey = wfMsg('accesskey-'.$name);
+ if ($accesskey && $accesskey != '-' && !wfEmptyMsg('accesskey-'.$name, $accesskey)) {
+ if ($out) $out .= " [$accesskey]\" accesskey=\"$accesskey\"";
+ else $out .= " title=\"[$accesskey]\" accesskey=\"$accesskey\"";
+ } elseif ($out) {
+ $out .= '"';
+ }
+ return $out;
+ }
+
+ /**
+ * Given the id of an interface element, constructs the appropriate title
+ * attribute from the system messages. (Note, this is usually the id but
+ * isn't always, because sometimes the accesskey needs to go on a different
+ * element than the id, for reverse-compatibility, etc.)
+ *
+ * @param string $name Id of the element, minus prefixes.
+ * @return string title attribute, ready to drop in an element
+ * (e.g., ' title="This does something"').
+ */
+ public function tooltip($name) {
+ $out = '';
+
+ $tooltip = wfMsg('tooltip-'.$name);
+ if (!wfEmptyMsg('tooltip-'.$name, $tooltip) && $tooltip != '-') {
+ $out = ' title="'.htmlspecialchars($tooltip).'"';
+ }
+
+ return $out;
+ }
}
?>
diff --git a/includes/LinksUpdate.php b/includes/LinksUpdate.php
index 9e25bf07..856c665d 100644
--- a/includes/LinksUpdate.php
+++ b/includes/LinksUpdate.php
@@ -1,19 +1,15 @@
<?php
/**
- * See deferred.txt
- * @package MediaWiki
- */
-
-/**
- * @todo document
- * @package MediaWiki
+ * See docs/deferred.txt
+ *
+ * @todo document (e.g. one-sentence top-level class description).
*/
class LinksUpdate {
/**@{{
* @private
*/
- var $mId, //!< Page ID of the article linked from
+ var $mId, //!< Page ID of the article linked from
$mTitle, //!< Title object of the article linked from
$mLinks, //!< Map of title strings to IDs for the links in the document
$mImages, //!< DB keys of the images used, in the array key only
@@ -41,7 +37,7 @@ class LinksUpdate {
} else {
$this->mOptions = array( 'FOR UPDATE' );
}
- $this->mDb =& wfGetDB( DB_MASTER );
+ $this->mDb = wfGetDB( DB_MASTER );
if ( !is_object( $title ) ) {
throw new MWException( "The calling convention to LinksUpdate::LinksUpdate() has changed. " .
@@ -172,7 +168,7 @@ class LinksUpdate {
wfProfileIn( __METHOD__ );
$batchSize = 100;
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( array( 'templatelinks', 'page' ),
array( 'page_namespace', 'page_title' ),
array(
diff --git a/includes/LoadBalancer.php b/includes/LoadBalancer.php
index 396ef865..4ebe26c7 100644
--- a/includes/LoadBalancer.php
+++ b/includes/LoadBalancer.php
@@ -1,7 +1,6 @@
<?php
/**
*
- * @package MediaWiki
*/
@@ -9,7 +8,6 @@
* Database load balancing object
*
* @todo document
- * @package MediaWiki
*/
class LoadBalancer {
/* private */ var $mServers, $mConnections, $mLoads, $mGroupLoads;
@@ -24,7 +22,7 @@ class LoadBalancer {
*/
const AVG_STATUS_POLL = 2000;
- function LoadBalancer( $servers, $failFunction = false, $waitTimeout = 10, $waitForMasterNow = false )
+ function __construct( $servers, $failFunction = false, $waitTimeout = 10, $waitForMasterNow = false )
{
$this->mServers = $servers;
$this->mFailFunction = $failFunction;
@@ -32,7 +30,7 @@ class LoadBalancer {
$this->mWriteIndex = -1;
$this->mForce = -1;
$this->mConnections = array();
- $this->mLastIndex = 1;
+ $this->mLastIndex = -1;
$this->mLoads = array();
$this->mWaitForFile = false;
$this->mWaitForPos = false;
@@ -97,7 +95,9 @@ class LoadBalancer {
# Unset excessively lagged servers
$lags = $this->getLagTimes();
foreach ( $lags as $i => $lag ) {
- if ( isset( $this->mServers[$i]['max lag'] ) && $lag > $this->mServers[$i]['max lag'] ) {
+ if ( $i != 0 && isset( $this->mServers[$i]['max lag'] ) &&
+ ( $lag === false || $lag > $this->mServers[$i]['max lag'] ) )
+ {
unset( $loads[$i] );
}
}
@@ -504,8 +504,7 @@ class LoadBalancer {
* Save master pos to the session and to memcached, if the session exists
*/
function saveMasterPos() {
- global $wgSessionStarted;
- if ( $wgSessionStarted && count( $this->mServers ) > 1 ) {
+ if ( session_id() != '' && count( $this->mServers ) > 1 ) {
# If this entire request was served from a slave without opening a connection to the
# master (however unlikely that may be), then we can fetch the position from the slave.
if ( empty( $this->mConnections[0] ) ) {
diff --git a/includes/LogPage.php b/includes/LogPage.php
index dd395126..af03bbba 100644
--- a/includes/LogPage.php
+++ b/includes/LogPage.php
@@ -21,7 +21,6 @@
/**
* Contain log classes
*
- * @package MediaWiki
*/
/**
@@ -29,7 +28,6 @@
* The logs are now kept in a table which is easier to manage and trim
* than ever-growing wiki pages.
*
- * @package MediaWiki
*/
class LogPage {
/* @access private */
@@ -44,7 +42,7 @@ class LogPage {
* 'upload', 'move'
* @param bool $rc Whether to update recent changes as well as the logging table
*/
- function LogPage( $type, $rc = true ) {
+ function __construct( $type, $rc = true ) {
$this->type = $type;
$this->updateRecentChanges = $rc;
}
@@ -55,7 +53,7 @@ class LogPage {
global $wgUser;
$fname = 'LogPage::saveContent';
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$uid = $wgUser->getID();
$this->timestamp = $now = wfTimestampNow();
@@ -119,7 +117,7 @@ class LogPage {
}
/**
- * @fixme: handle missing log types
+ * @todo handle missing log types
* @static
*/
function logHeader( $type ) {
@@ -134,6 +132,10 @@ class LogPage {
global $wgLang, $wgContLang, $wgLogActions;
$key = "$type/$action";
+
+ if( $key == 'patrol/patrol' )
+ return PatrolLog::makeActionText( $title, $params, $skin );
+
if( isset( $wgLogActions[$key] ) ) {
if( is_null( $title ) ) {
$rv=wfMsg( $wgLogActions[$key] );
@@ -183,8 +185,13 @@ class LogPage {
}
} else {
array_unshift( $params, $titleLink );
- if ( $translate && $key == 'block/block' ) {
- $params[1] = $wgLang->translateBlockExpiry($params[1]);
+ if ( $key == 'block/block' ) {
+ if ( $translate ) {
+ $params[1] = $wgLang->translateBlockExpiry( $params[1] );
+ }
+ $params[2] = isset( $params[2] )
+ ? self::formatBlockFlags( $params[2] )
+ : '';
}
$rv = wfMsgReal( $wgLogActions[$key], $params, true, !$skin );
}
@@ -241,6 +248,41 @@ class LogPage {
return explode( "\n", $blob );
}
}
+
+ /**
+ * Convert a comma-delimited list of block log flags
+ * into a more readable (and translated) form
+ *
+ * @param $flags Flags to format
+ * @return string
+ */
+ public static function formatBlockFlags( $flags ) {
+ $flags = explode( ',', trim( $flags ) );
+ if( count( $flags ) > 0 ) {
+ for( $i = 0; $i < count( $flags ); $i++ )
+ $flags[$i] = self::formatBlockFlag( $flags[$i] );
+ return '(' . implode( ', ', $flags ) . ')';
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * Translate a block log flag if possible
+ *
+ * @param $flag Flag to translate
+ * @return string
+ */
+ public static function formatBlockFlag( $flag ) {
+ static $messages = array();
+ if( !isset( $messages[$flag] ) ) {
+ $k = 'block-log-flags-' . $flag;
+ $msg = wfMsg( $k );
+ $messages[$flag] = htmlspecialchars( wfEmptyMsg( $k, $msg ) ? $flag : $msg );
+ }
+ return $messages[$flag];
+ }
+
}
?>
diff --git a/includes/MacBinary.php b/includes/MacBinary.php
index 05c3ce5c..2f6ad4f4 100644
--- a/includes/MacBinary.php
+++ b/includes/MacBinary.php
@@ -22,12 +22,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
class MacBinary {
- function MacBinary( $filename ) {
+ function __construct( $filename ) {
$this->open( $filename );
$this->loadHeader();
}
@@ -269,4 +268,4 @@ class MacBinary {
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/MagicWord.php b/includes/MagicWord.php
index 60bfd0f4..bf72a0c8 100644
--- a/includes/MagicWord.php
+++ b/includes/MagicWord.php
@@ -1,8 +1,7 @@
<?php
/**
* File for magic words
- * @package MediaWiki
- * @subpackage Parser
+ * @addtogroup Parser
*/
/**
@@ -21,7 +20,6 @@
* magic words which are also Parser variables, add a MagicWordwgVariableIDs
* hook. Use string keys.
*
- * @package MediaWiki
*/
class MagicWord {
/**#@+
@@ -55,6 +53,7 @@ class MagicWord {
'localhour',
'numberofarticles',
'numberoffiles',
+ 'numberofedits',
'sitename',
'server',
'servername',
@@ -108,7 +107,7 @@ class MagicWord {
/**#@-*/
- function MagicWord($id = 0, $syn = '', $cs = false) {
+ function __construct($id = 0, $syn = '', $cs = false) {
$this->mId = $id;
$this->mSynonyms = (array)$syn;
$this->mCaseSensitive = $cs;
diff --git a/includes/Math.php b/includes/Math.php
index 9fa631f7..88934e5f 100644
--- a/includes/Math.php
+++ b/includes/Math.php
@@ -1,7 +1,6 @@
<?php
/**
* Contain everything related to <math> </math> parsing
- * @package MediaWiki
*/
/**
@@ -11,7 +10,6 @@
*
* by Tomasz Wegrzanowski, with additions by Brion Vibber (2003, 2004)
*
- * @package MediaWiki
*/
class MathRenderer {
var $mode = MW_MATH_MODERN;
@@ -22,7 +20,7 @@ class MathRenderer {
var $mathml = '';
var $conservativeness = 0;
- function MathRenderer( $tex ) {
+ function __construct( $tex ) {
$this->tex = $tex;
}
@@ -156,7 +154,7 @@ class MathRenderer {
$md5_sql = pack('H32', $this->md5); # Binary packed, not hex
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->replace( 'math', array( 'math_inputhash' ),
array(
'math_inputhash' => $md5_sql,
@@ -185,7 +183,7 @@ class MathRenderer {
$fname = 'MathRenderer::_recall';
$this->md5 = md5( $this->tex );
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$rpage = $dbr->selectRow( 'math',
array( 'math_outputhash','math_html_conservativeness','math_html','math_mathml' ),
array( 'math_inputhash' => pack("H32", $this->md5)), # Binary packed, not hex
diff --git a/includes/MediaTransformOutput.php b/includes/MediaTransformOutput.php
new file mode 100644
index 00000000..60057e3a
--- /dev/null
+++ b/includes/MediaTransformOutput.php
@@ -0,0 +1,166 @@
+<?php
+
+/**
+ * Base class for the output of MediaHandler::doTransform() and Image::transform().
+ *
+ * @addtogroup Media
+ */
+abstract class MediaTransformOutput {
+ /**
+ * Get the width of the output box
+ */
+ function getWidth() {
+ return $this->width;
+ }
+
+ /**
+ * Get the height of the output box
+ */
+ function getHeight() {
+ return $this->height;
+ }
+
+ /**
+ * @return string The thumbnail URL
+ */
+ function getUrl() {
+ return $this->url;
+ }
+
+ /**
+ * @return string Destination file path (local filesystem)
+ */
+ function getPath() {
+ return $this->path;
+ }
+
+ /**
+ * Fetch HTML for this transform output
+ * @param array $attribs Advisory associative array of HTML attributes supplied
+ * by the linker. These can be incorporated into the output in any way.
+ * @param array $linkAttribs Attributes of a suggested enclosing <a> tag.
+ * May be ignored.
+ */
+ abstract function toHtml( $attribs = array() , $linkAttribs = false );
+
+ /**
+ * This will be overridden to return true in error classes
+ */
+ function isError() {
+ return false;
+ }
+
+ /**
+ * Wrap some XHTML text in an anchor tag with the given attributes
+ */
+ protected function linkWrap( $linkAttribs, $contents ) {
+ if ( $linkAttribs ) {
+ return Xml::tags( 'a', $linkAttribs, $contents );
+ } else {
+ return $contents;
+ }
+ }
+}
+
+
+/**
+ * Media transform output for images
+ *
+ * @addtogroup Media
+ */
+class ThumbnailImage extends MediaTransformOutput {
+ /**
+ * @param string $path Filesystem path to the thumb
+ * @param string $url URL path to the thumb
+ * @private
+ */
+ function ThumbnailImage( $url, $width, $height, $path = false ) {
+ $this->url = $url;
+ # These should be integers when they get here.
+ # If not, there's a bug somewhere. But let's at
+ # least produce valid HTML code regardless.
+ $this->width = round( $width );
+ $this->height = round( $height );
+ $this->path = $path;
+ }
+
+ /**
+ * Return HTML <img ... /> tag for the thumbnail, will include
+ * width and height attributes and a blank alt text (as required).
+ *
+ * You can set or override additional attributes by passing an
+ * associative array of name => data pairs. The data will be escaped
+ * for HTML output, so should be in plaintext.
+ *
+ * If $linkAttribs is given, the image will be enclosed in an <a> tag.
+ *
+ * @param array $attribs
+ * @param array $linkAttribs
+ * @return string
+ * @public
+ */
+ function toHtml( $attribs = array(), $linkAttribs = false ) {
+ $attribs['src'] = $this->url;
+ $attribs['width'] = $this->width;
+ $attribs['height'] = $this->height;
+ if( !isset( $attribs['alt'] ) ) $attribs['alt'] = '';
+ return $this->linkWrap( $linkAttribs, Xml::element( 'img', $attribs ) );
+ }
+
+}
+
+/**
+ * Basic media transform error class
+ *
+ * @addtogroup Media
+ */
+class MediaTransformError extends MediaTransformOutput {
+ var $htmlMsg, $textMsg, $width, $height, $url, $path;
+
+ function __construct( $msg, $width, $height /*, ... */ ) {
+ $args = array_slice( func_get_args(), 3 );
+ $htmlArgs = array_map( 'htmlspecialchars', $args );
+ $htmlArgs = array_map( 'nl2br', $htmlArgs );
+
+ $this->htmlMsg = wfMsgReplaceArgs( htmlspecialchars( wfMsgGetKey( $msg, true ) ), $htmlArgs );
+ $this->textMsg = wfMsgReal( $msg, $args );
+ $this->width = intval( $width );
+ $this->height = intval( $height );
+ $this->url = false;
+ $this->path = false;
+ }
+
+ function toHtml( $attribs = array(), $linkAttribs = false ) {
+ return "<table class=\"MediaTransformError\" style=\"" .
+ "width: {$this->width}px; height: {$this->height}px;\"><tr><td>" .
+ $this->htmlMsg .
+ "</td></tr></table>";
+ }
+
+ function toText() {
+ return $this->textMsg;
+ }
+
+ function getHtmlMsg() {
+ return $this->htmlMsg;
+ }
+
+ function isError() {
+ return true;
+ }
+}
+
+/**
+ * Shortcut class for parameter validation errors
+ *
+ * @addtogroup Media
+ */
+class TransformParameterError extends MediaTransformError {
+ function __construct( $params ) {
+ parent::__construct( 'thumbnail_error',
+ max( @$params['width'], 180 ), max( @$params['height'], 180 ),
+ wfMsg( 'thumbnail_invalid_params' ) );
+ }
+}
+
+?>
diff --git a/includes/MemcachedSessions.php b/includes/MemcachedSessions.php
index e2dc52ca..3bcf5535 100644
--- a/includes/MemcachedSessions.php
+++ b/includes/MemcachedSessions.php
@@ -6,7 +6,6 @@
* be necessary to change the cookie settings to work across hostnames.
* See: http://www.php.net/manual/en/function.session-set-save-handler.php
*
- * @package MediaWiki
*/
/**
diff --git a/includes/MessageCache.php b/includes/MessageCache.php
index a269c620..e2cbf5f6 100644
--- a/includes/MessageCache.php
+++ b/includes/MessageCache.php
@@ -1,8 +1,7 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage Cache
+ * @addtogroup Cache
*/
/**
@@ -17,7 +16,6 @@ define( 'MSG_CACHE_VERSION', 1 );
* Message cache
* Performs various MediaWiki namespace-related functions
*
- * @package MediaWiki
*/
class MessageCache {
var $mCache, $mUseCache, $mDisable, $mExpiry;
@@ -298,10 +296,10 @@ class MessageCache {
* Loads all or main part of cacheable messages from the database
*/
function loadFromDB() {
- global $wgLang, $wgMaxMsgCacheEntrySize;
+ global $wgMaxMsgCacheEntrySize;
wfProfileIn( __METHOD__ );
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$this->mCache = array();
# Load titles for all oversized pages in the MediaWiki namespace
@@ -547,7 +545,7 @@ class MessageCache {
if ( $type == ' ' ) {
$message = substr( $entry, 1 );
- $this->mCache[$title] = $message;
+ $this->mCache[$title] = $entry;
return $message;
} elseif ( $entry == '!NONEXISTENT' ) {
return false;
diff --git a/includes/Metadata.php b/includes/Metadata.php
index 4e0d91b7..b995b223 100644
--- a/includes/Metadata.php
+++ b/includes/Metadata.php
@@ -18,7 +18,6 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
* @author Evan Prodromou <evan@wikitravel.org>
- * @package MediaWiki
*/
/**
@@ -74,7 +73,9 @@ function wfCreativeCommonsRdf($article) {
function rdfSetup() {
global $wgOut, $_SERVER;
- $rdftype = wfNegotiateType(wfAcceptToPrefs($_SERVER['HTTP_ACCEPT']), wfAcceptToPrefs(RDF_TYPE_PREFS));
+ $httpaccept = isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : null;
+
+ $rdftype = wfNegotiateType(wfAcceptToPrefs($httpaccept), wfAcceptToPrefs(RDF_TYPE_PREFS));
if (!$rdftype) {
wfHttpError(406, "Not Acceptable", wfMsg("notacceptable"));
diff --git a/includes/MimeMagic.php b/includes/MimeMagic.php
index ca05dbb3..db35535d 100644
--- a/includes/MimeMagic.php
+++ b/includes/MimeMagic.php
@@ -1,7 +1,6 @@
<?php
/** Module defining helper functions for detecting and dealing with mime types.
*
- * @package MediaWiki
*/
/** Defines a set of well known mime types
@@ -23,9 +22,10 @@ image/x-bmp bmp
image/gif gif
image/jpeg jpeg jpg jpe
image/png png
-image/svg+xml svg
+image/svg+xml image/svg svg
image/tiff tiff tif
image/vnd.djvu djvu
+image/x-portable-pixmap ppm
text/plain txt
text/html html htm
video/ogg ogm ogg
@@ -51,9 +51,10 @@ image/x-bmp image/bmp [BITMAP]
image/gif [BITMAP]
image/jpeg [BITMAP]
image/png [BITMAP]
-image/svg image/svg+xml [DRAWING]
+image/svg+xml [DRAWING]
image/tiff [BITMAP]
image/vnd.djvu [BITMAP]
+image/x-portable-pixmap [BITMAP]
text/plain [TEXT]
text/html [TEXT]
video/ogg [VIDEO]
@@ -70,13 +71,13 @@ if ($wgLoadFileinfoExtension) {
if(!extension_loaded('fileinfo')) dl('fileinfo.' . PHP_SHLIB_SUFFIX);
}
-/** Implements functions related to mime types such as detection and mapping to
-* file extension,
-*
-* Instances of this class are stateles, there only needs to be one global instance
-* of MimeMagic. Please use MimeMagic::singleton() to get that instance.
-* @package MediaWiki
-*/
+/**
+ * Implements functions related to mime types such as detection and mapping to
+ * file extension.
+ *
+ * Instances of this class are stateles, there only needs to be one global instance
+ * of MimeMagic. Please use MimeMagic::singleton() to get that instance.
+ */
class MimeMagic {
/**
@@ -105,7 +106,7 @@ class MimeMagic {
*
* This constructor parses the mime.types and mime.info files and build internal mappings.
*/
- function MimeMagic() {
+ function __construct() {
/*
* --- load mime.types ---
*/
@@ -149,7 +150,7 @@ class MimeMagic {
if (empty($ext)) continue;
- if (@$this->mMimeToExt[$mime]) $this->mMimeToExt[$mime] .= ' '.$ext;
+ if ( !empty($this->mMimeToExt[$mime])) $this->mMimeToExt[$mime] .= ' '.$ext;
else $this->mMimeToExt[$mime]= $ext;
$extensions= explode(' ',$ext);
@@ -158,7 +159,7 @@ class MimeMagic {
$e= trim($e);
if (empty($e)) continue;
- if (@$this->mExtToMime[$e]) $this->mExtToMime[$e] .= ' '.$mime;
+ if ( !empty($this->mExtToMime[$e])) $this->mExtToMime[$e] .= ' '.$mime;
else $this->mExtToMime[$e]= $mime;
}
}
@@ -262,7 +263,7 @@ class MimeMagic {
function getTypesForExtension($ext) {
$ext= strtolower($ext);
- $r= @$this->mExtToMime[$ext];
+ $r= isset( $this->mExtToMime[$ext] ) ? $this->mExtToMime[$ext] : null;
return $r;
}
@@ -341,7 +342,7 @@ class MimeMagic {
}
- /** mime type detection. This uses detectMimeType to detect the mim type of the file,
+ /** mime type detection. This uses detectMimeType to detect the mime type of the file,
* but applies additional checks to determine some well known file formats that may be missed
* or misinterpreter by the default mime detection (namely xml based formats like XHTML or SVG).
*
@@ -399,8 +400,8 @@ class MimeMagic {
#print "<br>ANALYSING $file ($mime): doctype= $doctype; tag= $tag<br>";
- if (strpos($doctype,"-//W3C//DTD SVG")===0) $mime= "image/svg";
- elseif ($tag==="svg") $mime= "image/svg";
+ if (strpos($doctype,"-//W3C//DTD SVG")===0) $mime= "image/svg+xml";
+ elseif ($tag==="svg") $mime= "image/svg+xml";
elseif (strpos($doctype,"-//W3C//DTD XHTML")===0) $mime= "text/html";
elseif ($tag==="html") $mime= "text/html";
}
@@ -424,7 +425,9 @@ class MimeMagic {
$match= array();
$prog= "";
- if (preg_match('%/?([^\s]+/)(w+)%sim',$head,$match)) $script= $match[2];
+ if (preg_match('%/?([^\s]+/)(w+)%sim',$head,$match)) {
+ $script= $match[2]; // FIXME: $script variable not used; should this be "$prog = $match[2];" instead?
+ }
$mime= "application/x-$prog";
}
diff --git a/includes/Namespace.php b/includes/Namespace.php
index 78493902..dd67b55a 100644
--- a/includes/Namespace.php
+++ b/includes/Namespace.php
@@ -1,7 +1,6 @@
<?php
/**
* Provide things related to namespaces
- * @package MediaWiki
*/
/**
@@ -41,7 +40,6 @@ if( is_array( $wgExtraNamespaces ) ) {
* These are synonyms for the names given in the language file
* Users and translators should not change them
*
- * @package MediaWiki
*/
class Namespace {
@@ -125,5 +123,19 @@ class Namespace {
static function canTalk( $index ) {
return( $index >= NS_MAIN );
}
+
+ /**
+ * Does this namespace contain content, for the purposes
+ * of calculating statistics, etc?
+ *
+ * @param $index Index to check
+ * @return bool
+ */
+ public static function isContent( $index ) {
+ global $wgContentNamespaces;
+ return $index == NS_MAIN || in_array( $index, $wgContentNamespaces );
+ }
+
}
+
?>
diff --git a/includes/ObjectCache.php b/includes/ObjectCache.php
index 2b26cf4e..a493a75c 100644
--- a/includes/ObjectCache.php
+++ b/includes/ObjectCache.php
@@ -1,7 +1,6 @@
<?php
/**
- * @package MediaWiki
- * @subpackage Cache
+ * @addtogroup Cache
*/
/**
@@ -9,8 +8,7 @@
* It acts as a memcached server with no RAM, that is, all objects are
* cleared the moment they are set. All set operations succeed and all
* get operations return null.
- * @package MediaWiki
- * @subpackage Cache
+ * @addtogroup Cache
*/
class FakeMemCachedClient {
function add ($key, $val, $exp = 0) { return true; }
diff --git a/includes/OutputHandler.php b/includes/OutputHandler.php
new file mode 100644
index 00000000..d7e7c90f
--- /dev/null
+++ b/includes/OutputHandler.php
@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * Standard output handler for use with ob_start
+ */
+function wfOutputHandler( $s ) {
+ global $wgDisableOutputCompression;
+ $s = wfMangleFlashPolicy( $s );
+ if ( !$wgDisableOutputCompression && !ini_get( 'zlib.output_compression' ) ) {
+ if ( !defined( 'MW_NO_OUTPUT_COMPRESSION' ) ) {
+ $s = wfGzipHandler( $s );
+ }
+ if ( !ini_get( 'output_handler' ) ) {
+ wfDoContentLength( strlen( $s ) );
+ }
+ }
+ return $s;
+}
+
+/**
+ * Handler that compresses data with gzip if allowed by the Accept header.
+ * Unlike ob_gzhandler, it works for HEAD requests too.
+ */
+function wfGzipHandler( $s ) {
+ if ( function_exists( 'gzencode' ) && !headers_sent() ) {
+ $tokens = preg_split( '/[,; ]/', $_SERVER['HTTP_ACCEPT_ENCODING'] );
+ if ( in_array( 'gzip', $tokens ) ) {
+ header( 'Content-Encoding: gzip' );
+ $s = gzencode( $s, 3 );
+
+ # Set vary header if it hasn't been set already
+ $headers = headers_list();
+ $foundVary = false;
+ foreach ( $headers as $header ) {
+ if ( substr( $header, 0, 5 ) == 'Vary:' ) {
+ $foundVary = true;
+ break;
+ }
+ }
+ if ( !$foundVary ) {
+ header( 'Vary: Accept-Encoding' );
+ }
+ }
+ }
+ return $s;
+}
+
+/**
+ * Mangle flash policy tags which open up the site to XSS attacks.
+ */
+function wfMangleFlashPolicy( $s ) {
+ return preg_replace( '/\<\s*cross-domain-policy\s*\>/i', '<NOT-cross-domain-policy>', $s );
+}
+
+/**
+ * Add a Content-Length header if possible. This makes it cooperate with squid better.
+ */
+function wfDoContentLength( $length ) {
+ if ( !headers_sent() && $_SERVER['SERVER_PROTOCOL'] == 'HTTP/1.0' ) {
+ header( "Content-Length: $length" );
+ }
+}
+
+?>
diff --git a/includes/OutputPage.php b/includes/OutputPage.php
index 6d3cc0ac..03e832a4 100644
--- a/includes/OutputPage.php
+++ b/includes/OutputPage.php
@@ -2,12 +2,10 @@
if ( ! defined( 'MEDIAWIKI' ) )
die( 1 );
/**
- * @package MediaWiki
*/
/**
* @todo document
- * @package MediaWiki
*/
class OutputPage {
var $mMetatags, $mKeywords;
@@ -34,7 +32,7 @@ class OutputPage {
* Constructor
* Initialise private variables
*/
- function OutputPage() {
+ function __construct() {
$this->mMetatags = $this->mKeywords = $this->mLinktags = array();
$this->mHTMLtitle = $this->mPagetitle = $this->mBodytext =
$this->mRedirect = $this->mLastModified =
@@ -49,6 +47,7 @@ class OutputPage {
$this->mParserOptions = null;
$this->mSquidMaxage = 0;
$this->mScripts = '';
+ $this->mHeadItems = array();
$this->mETag = false;
$this->mRevisionId = null;
$this->mNewSectionLink = false;
@@ -71,7 +70,7 @@ class OutputPage {
# To add an http-equiv meta tag, precede the name with "http:"
function addMeta( $name, $val ) { array_push( $this->mMetatags, array( $name, $val ) ); }
function addKeyword( $text ) { array_push( $this->mKeywords, $text ); }
- function addScript( $script ) { $this->mScripts .= $script; }
+ function addScript( $script ) { $this->mScripts .= "\t\t".$script; }
/**
* Add a self-contained script tag with the given contents
@@ -79,10 +78,24 @@ class OutputPage {
*/
function addInlineScript( $script ) {
global $wgJsMimeType;
- $this->mScripts .= "<script type=\"$wgJsMimeType\"><!--\n$script\n--></script>";
+ $this->mScripts .= "<script type=\"$wgJsMimeType\">/*<![CDATA[*/\n$script\n/*]]>*/</script>";
}
- function getScript() { return $this->mScripts; }
+ function getScript() {
+ return $this->mScripts . $this->getHeadItems();
+ }
+
+ function getHeadItems() {
+ $s = '';
+ foreach ( $this->mHeadItems as $item ) {
+ $s .= $item;
+ }
+ return $s;
+ }
+
+ function addHeadItem( $name, $value ) {
+ $this->mHeadItems[$name] = $value;
+ }
function setETag($tag) { $this->mETag = $tag; }
function setArticleBodyOnly($only) { $this->mArticleBodyOnly = $only; }
@@ -254,7 +267,7 @@ class OutputPage {
$lb->setArray( $arr );
$lb->execute();
- $sk =& $wgUser->getSkin();
+ $sk = $wgUser->getSkin();
foreach ( $categories as $category => $unused ) {
$title = Title::makeTitleSafe( NS_CATEGORY, $category );
$text = $wgContLang->convertHtml( $title->getText() );
@@ -315,14 +328,26 @@ class OutputPage {
$this->addWikiTextTitle($text, $title, $linestart);
}
- private function addWikiTextTitle($text, &$title, $linestart) {
+ function addWikiTextTitleTidy($text, &$title, $linestart = true) {
+ $this->addWikiTextTitle( $text, $title, $linestart, true );
+ }
+
+ public function addWikiTextTitle($text, &$title, $linestart, $tidy = false) {
global $wgParser;
+
$fname = 'OutputPage:addWikiTextTitle';
wfProfileIn($fname);
+
wfIncrStats('pcache_not_possible');
- $parserOutput = $wgParser->parse( $text, $title, $this->parserOptions(),
+
+ $popts = $this->parserOptions();
+ $popts->setTidy($tidy);
+
+ $parserOutput = $wgParser->parse( $text, $title, $popts,
$linestart, true, $this->mRevisionId );
+
$this->addParserOutput( $parserOutput );
+
wfProfileOut($fname);
}
@@ -345,6 +370,7 @@ class OutputPage {
$this->mSubtitle .= $parserOutput->mSubtitle ;
}
$this->mNoGallery = $parserOutput->getNoGallery();
+ $this->mHeadItems = array_merge( $this->mHeadItems, (array)$parserOutput->mHeadItems );
wfRunHooks( 'OutputPageParserOutput', array( &$this, $parserOutput ) );
}
@@ -366,6 +392,7 @@ class OutputPage {
* @param string $text
* @param Article $article
* @param bool $cache
+ * @deprecated Use Article::outputWikitext
*/
public function addPrimaryWikiText( $text, $article, $cache = true ) {
global $wgParser, $wgUser;
@@ -384,17 +411,19 @@ class OutputPage {
}
/**
- * For anything that isn't primary text or interface message
- *
- * @param string $text
- * @param bool $linestart Is this the start of a line?
+ * @deprecated use addWikiTextTidy()
*/
public function addSecondaryWikiText( $text, $linestart = true ) {
global $wgTitle;
- $popts = $this->parserOptions();
- $popts->setTidy(true);
- $this->addWikiTextTitle($text, $wgTitle, $linestart);
- $popts->setTidy(false);
+ $this->addWikiTextTitleTidy($text, $wgTitle, $linestart);
+ }
+
+ /**
+ * Add wikitext with tidy enabled
+ */
+ public function addWikiTextTidy( $text, $linestart = true ) {
+ global $wgTitle;
+ $this->addWikiTextTitleTidy($text, $wgTitle, $linestart);
}
@@ -476,7 +505,7 @@ class OutputPage {
# maintain different caches for logged-in users and non-logged in ones
$wgRequest->response()->header( 'Vary: Accept-Encoding, Cookie' );
if( !$this->uncacheableBecauseRequestvars() && $this->mEnableClientCache ) {
- if( $wgUseSquid && ! isset( $_COOKIE[ini_get( 'session.name') ] ) &&
+ if( $wgUseSquid && session_id() == '' &&
! $this->isPrintable() && $this->mSquidMaxage != 0 )
{
if ( $wgUseESI ) {
@@ -536,13 +565,16 @@ class OutputPage {
if ( $wgUseAjax ) {
$this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajax.js?$wgStyleVersion\"></script>\n" );
+
+ wfRunHooks( 'AjaxAddScript', array( &$this ) );
+
if( $wgAjaxSearch ) {
- $this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajaxsearch.js\"></script>\n" );
+ $this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajaxsearch.js?$wgStyleVersion\"></script>\n" );
$this->addScript( "<script type=\"{$wgJsMimeType}\">hookEvent(\"load\", sajax_onload);</script>\n" );
}
if( $wgAjaxWatch && $wgUser->isLoggedIn() ) {
- $this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajaxwatch.js\"></script>\n" );
+ $this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajaxwatch.js?$wgStyleVersion\"></script>\n" );
}
}
@@ -750,7 +782,7 @@ class OutputPage {
$this->returnToMain( false );
}
- /** @obsolete */
+ /** @deprecated */
public function errorpage( $title, $msg ) {
throw new ErrorPageError( $title, $msg );
}
@@ -792,10 +824,10 @@ class OutputPage {
$groupName = User::getGroupName( $key );
$groupPage = User::getGroupPage( $key );
if( $groupPage ) {
- $skin =& $wgUser->getSkin();
- $groups[] = '"'.$skin->makeLinkObj( $groupPage, $groupName ).'"';
+ $skin = $wgUser->getSkin();
+ $groups[] = $skin->makeLinkObj( $groupPage, $groupName );
} else {
- $groups[] = '"'.$groupName.'"';
+ $groups[] = $groupName;
}
}
}
@@ -860,7 +892,7 @@ class OutputPage {
$this->returnToMain( true, $mainPage );
}
- /** @obsolete */
+ /** @deprecated */
public function databaseError( $fname, $sql, $error, $errno ) {
throw new MWException( "OutputPage::databaseError is obsolete\n" );
}
@@ -881,10 +913,22 @@ class OutputPage {
$this->setPageTitle( wfMsg( 'viewsource' ) );
$this->setSubtitle( wfMsg( 'viewsourcefor', $skin->makeKnownLinkObj( $wgTitle ) ) );
+ list( $cascadeSources, $restrictions ) = $wgTitle->getCascadeProtectionSources();
+
# Determine if protection is due to the page being a system message
# and show an appropriate explanation
- if( $wgTitle->getNamespace() == NS_MEDIAWIKI && !$wgUser->isAllowed( 'editinterface' ) ) {
+ if( $wgTitle->getNamespace() == NS_MEDIAWIKI ) {
$this->addWikiText( wfMsg( 'protectedinterface' ) );
+ } if ( $cascadeSources && count($cascadeSources) > 0 ) {
+ $titles = '';
+
+ foreach ( $cascadeSources as $title ) {
+ $titles .= '* [[:' . $title->getPrefixedText() . "]]\n";
+ }
+
+ $notice = wfMsgExt( 'cascadeprotected', array('parsemag'), count($cascadeSources) ) . "\n$titles";
+
+ $this->addWikiText( $notice );
} else {
$this->addWikiText( wfMsg( 'protectedpagetext' ) );
}
@@ -900,17 +944,8 @@ class OutputPage {
if( is_string( $source ) ) {
$this->addWikiText( wfMsg( 'viewsourcetext' ) );
- if( $source === '' ) {
- global $wgTitle;
- if ( $wgTitle->getNamespace() == NS_MEDIAWIKI ) {
- $source = wfMsgWeirdKey ( $wgTitle->getText() );
- } else {
- $source = '';
- }
- }
$rows = $wgUser->getIntOption( 'rows' );
$cols = $wgUser->getIntOption( 'cols' );
-
$text = "\n<textarea name='wpTextbox1' id='wpTextbox1' cols='$cols' rows='$rows' readonly='readonly'>" .
htmlspecialchars( $source ) . "\n</textarea>";
$this->addHTML( $text );
@@ -921,32 +956,32 @@ class OutputPage {
$this->returnToMain( false );
}
- /** @obsolete */
+ /** @deprecated */
public function fatalError( $message ) {
throw new FatalError( $message );
}
- /** @obsolete */
+ /** @deprecated */
public function unexpectedValueError( $name, $val ) {
throw new FatalError( wfMsg( 'unexpected', $name, $val ) );
}
- /** @obsolete */
+ /** @deprecated */
public function fileCopyError( $old, $new ) {
throw new FatalError( wfMsg( 'filecopyerror', $old, $new ) );
}
- /** @obsolete */
+ /** @deprecated */
public function fileRenameError( $old, $new ) {
throw new FatalError( wfMsg( 'filerenameerror', $old, $new ) );
}
- /** @obsolete */
+ /** @deprecated */
public function fileDeleteError( $name ) {
throw new FatalError( wfMsg( 'filedeleteerror', $name ) );
}
- /** @obsolete */
+ /** @deprecated */
public function fileNotFoundError( $name ) {
throw new FatalError( wfMsg( 'filenotfound', $name ) );
}
@@ -1082,6 +1117,7 @@ class OutputPage {
$ret .= $sk->getHeadScripts();
$ret .= $this->mScripts;
$ret .= $sk->getUserStyles();
+ $ret .= $this->getHeadItems();
if ($wgUseTrackbacks && $this->isArticleRelated())
$ret .= $wgTitle->trackbackRDF();
@@ -1118,11 +1154,11 @@ class OutputPage {
"/<.*?>/" => '',
"/_/" => ' '
);
- $ret .= "<meta name=\"keywords\" content=\"" .
+ $ret .= "\t\t<meta name=\"keywords\" content=\"" .
htmlspecialchars(preg_replace(array_keys($strip), array_values($strip),implode( ",", $this->mKeywords ))) . "\" />\n";
}
foreach ( $this->mLinktags as $tag ) {
- $ret .= '<link';
+ $ret .= "\t\t<link";
foreach( $tag as $attr => $val ) {
$ret .= " $attr=\"" . htmlspecialchars( $val ) . "\"";
}
diff --git a/includes/PageHistory.php b/includes/PageHistory.php
index aea0f0ed..b1cf41f0 100644
--- a/includes/PageHistory.php
+++ b/includes/PageHistory.php
@@ -3,7 +3,6 @@
* Page history
*
* Split off from Article.php and Skin.php, 2003-12-22
- * @package MediaWiki
*/
/**
@@ -14,9 +13,7 @@
* Construct it by passing in an Article, and call $h->history() to print the
* history.
*
- * @package MediaWiki
*/
-
class PageHistory {
const DIR_PREV = 0;
const DIR_NEXT = 1;
@@ -33,7 +30,7 @@ class PageHistory {
* @param Article $article
* @returns nothing
*/
- function PageHistory($article) {
+ function __construct($article) {
global $wgUser;
$this->mArticle =& $article;
@@ -101,6 +98,8 @@ class PageHistory {
$wgOut->redirect( $wgTitle->getLocalURL( "action=history&limit={$limit}&dir=prev" ) );
return;
}
+
+ wfRunHooks( 'PageHistoryBeforeList', array( &$this->mArticle ) );
/**
* Do the list
@@ -159,7 +158,7 @@ class PageHistory {
'class' => 'historysubmit',
'type' => 'submit',
'accesskey' => wfMsg( 'accesskey-compareselectedversions' ),
- 'title' => wfMsg( 'tooltip-compareselectedversions' ),
+ 'title' => wfMsg( 'tooltip-compareselectedversions' ).' ['.wfMsg( 'accesskey-compareselectedversions' ).']',
'value' => wfMsg( 'compareselectedversions' ),
) ) )
: '';
@@ -179,7 +178,7 @@ class PageHistory {
* @return string HTML output for the row
*/
function historyLine( $row, $next, $counter = '', $notificationtimestamp = false, $latest = false, $firstInList = false ) {
- global $wgUser;
+ global $wgUser, $wgLang;
$rev = new Revision( $row );
$rev->setTitle( $this->mTitle );
@@ -199,31 +198,58 @@ class PageHistory {
if( $firstInList ) {
// We don't currently handle well changing the top revision's settings
$del = wfMsgHtml( 'rev-delundel' );
+ } else if( !$rev->userCan( Revision::DELETED_RESTRICTED ) ) {
+ // If revision was hidden from sysops
+ $del = wfMsgHtml( 'rev-delundel' );
} else {
$del = $this->mSkin->makeKnownLinkObj( $revdel,
wfMsg( 'rev-delundel' ),
'target=' . urlencode( $this->mTitle->getPrefixedDbkey() ) .
'&oldid=' . urlencode( $rev->getId() ) );
}
- $s .= "(<small>$del</small>) ";
+ $s .= " (<small>$del</small>) ";
}
- $s .= " $link <span class='history-user'>$user</span>";
+ $s .= " $link";
+ #getUser is safe, but this avoids making the invalid untargeted contribs links
+ if( $row->rev_deleted & Revision::DELETED_USER ) {
+ $user = '<span class="history-deleted">' . wfMsg('rev-deleted-user') . '</span>';
+ }
+ $s .= " <span class='history-user'>$user</span>";
if( $row->rev_minor_edit ) {
$s .= ' ' . wfElement( 'span', array( 'class' => 'minor' ), wfMsg( 'minoreditletter') );
}
- $s .= $this->mSkin->revComment( $rev );
+ if (!is_null($size = $rev->getSize())) {
+ if ($size == 0)
+ $stxt = wfMsgHtml('historyempty');
+ else
+ $stxt = wfMsgHtml('historysize', $wgLang->formatNum( $size ) );
+ $s .= " <span class=\"history-size\">$stxt</span>";
+ }
+
+ #getComment is safe, but this is better formatted
+ if( $rev->isDeleted( Revision::DELETED_COMMENT ) ) {
+ $s .= " <span class=\"history-deleted\"><span class=\"comment\">" .
+ wfMsgHtml( 'rev-deleted-comment' ) . "</span></span>";
+ } else {
+ $s .= $this->mSkin->revComment( $rev );
+ }
+
if ($notificationtimestamp && ($row->rev_timestamp >= $notificationtimestamp)) {
$s .= ' <span class="updatedmarker">' . wfMsgHtml( 'updatedmarker' ) . '</span>';
}
+ #add blurb about text having been deleted
if( $row->rev_deleted & Revision::DELETED_TEXT ) {
$s .= ' ' . wfMsgHtml( 'deletedrev' );
}
if( $wgUser->isAllowed( 'rollback' ) && $latest ) {
$s .= ' '.$this->mSkin->generateRollback( $rev );
}
+
+ wfRunHooks( 'PageHistoryLineEnding', array( &$row , &$s ) );
+
$s .= "</li>\n";
return $s;
@@ -332,7 +358,7 @@ class PageHistory {
function getLatestId() {
if( is_null( $this->mLatestId ) ) {
$id = $this->mTitle->getArticleID();
- $db =& wfGetDB(DB_SLAVE);
+ $db = wfGetDB(DB_SLAVE);
$this->mLatestId = $db->selectField( 'page',
"page_latest",
array( 'page_id' => $id ),
@@ -349,7 +375,7 @@ class PageHistory {
function fetchRevisions($limit, $offset, $direction) {
$fname = 'PageHistory::fetchRevisions';
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
if ($direction == PageHistory::DIR_PREV)
list($dirs, $oper) = array("ASC", ">=");
@@ -365,8 +391,7 @@ class PageHistory {
$res = $dbr->select(
'revision',
- array('rev_id', 'rev_page', 'rev_text_id', 'rev_user', 'rev_comment', 'rev_user_text',
- 'rev_timestamp', 'rev_minor_edit', 'rev_deleted'),
+ Revision::selectFields(),
array_merge(array("rev_page=$page_id"), $offsets),
$fname,
array('ORDER BY' => "rev_timestamp $dirs",
@@ -391,7 +416,7 @@ class PageHistory {
if ($wgUser->isAnon() || !$wgShowUpdatedMarker)
return $this->mNotificationTimestamp = false;
- $dbr =& wfGetDB(DB_SLAVE);
+ $dbr = wfGetDB(DB_SLAVE);
$this->mNotificationTimestamp = $dbr->selectField(
'watchlist',
@@ -497,6 +522,9 @@ class PageHistory {
}
+/**
+ * @addtogroup Pager
+ */
class PageHistoryPager extends ReverseChronologicalPager {
public $mLastRow = false, $mPageHistory;
@@ -508,8 +536,7 @@ class PageHistoryPager extends ReverseChronologicalPager {
function getQueryInfo() {
return array(
'tables' => 'revision',
- 'fields' => array('rev_id', 'rev_page', 'rev_text_id', 'rev_user', 'rev_comment', 'rev_user_text',
- 'rev_timestamp', 'rev_minor_edit', 'rev_deleted'),
+ 'fields' => Revision::selectFields(),
'conds' => array('rev_page' => $this->mPageHistory->mTitle->getArticleID() ),
'options' => array( 'USE INDEX' => 'page_timestamp' )
);
diff --git a/includes/PageQueryPage.php b/includes/PageQueryPage.php
new file mode 100644
index 00000000..5b82ebf6
--- /dev/null
+++ b/includes/PageQueryPage.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * Variant of QueryPage which formats the result as a simple link to the page
+ *
+ * @package MediaWiki
+ * @addtogroup SpecialPage
+ */
+class PageQueryPage extends QueryPage {
+
+ /**
+ * Format the result as a simple link to the page
+ *
+ * @param Skin $skin
+ * @param object $row Result row
+ * @return string
+ */
+ public function formatResult( $skin, $row ) {
+ global $wgContLang;
+ $title = Title::makeTitleSafe( $row->namespace, $row->title );
+ return $skin->makeKnownLinkObj( $title,
+ htmlspecialchars( $wgContLang->convert( $title->getPrefixedText() ) ) );
+ }
+}
+
+?>
diff --git a/includes/Pager.php b/includes/Pager.php
index 0987cc06..a475dc16 100644
--- a/includes/Pager.php
+++ b/includes/Pager.php
@@ -2,6 +2,7 @@
/**
* Basic pager interface.
+ * @addtogroup Pager
*/
interface Pager {
function getNavigationBar();
@@ -46,6 +47,8 @@ interface Pager {
* please see the examples in PageHistory.php and SpecialIpblocklist.php. You just need
* to override formatRow(), getQueryInfo() and getIndexField(). Don't forget to call the
* parent constructor if you override it.
+ *
+ * @addtogroup Pager
*/
abstract class IndexPager implements Pager {
public $mRequest;
@@ -69,17 +72,18 @@ abstract class IndexPager implements Pager {
public $mResult;
function __construct() {
- global $wgRequest;
+ global $wgRequest, $wgUser;
$this->mRequest = $wgRequest;
-
+
# NB: the offset is quoted, not validated. It is treated as an arbitrary string
# to support the widest variety of index types. Be careful outputting it into
# HTML!
$this->mOffset = $this->mRequest->getText( 'offset' );
- $this->mLimit = $this->mRequest->getInt( 'limit', $this->mDefaultLimit );
- if ( $this->mLimit <= 0 || $this->mLimit > 50000 ) {
- $this->mLimit = $this->mDefaultLimit;
- }
+
+ # Use consistent behavior for the limit options
+ $this->mDefaultLimit = intval( $wgUser->getOption( 'rclimit' ) );
+ list( $this->mLimit, /* $offset */ ) = $this->mRequest->getLimitOffset();
+
$this->mIsBackwards = ( $this->mRequest->getVal( 'dir' ) == 'prev' );
$this->mIndexField = $this->getIndexField();
$this->mDb = wfGetDB( DB_SLAVE );
@@ -386,8 +390,45 @@ abstract class IndexPager implements Pager {
abstract function getIndexField();
}
+
+/**
+ * IndexPager with an alphabetic list and a formatted navigation bar
+ * @addtogroup Pager
+ */
+abstract class AlphabeticPager extends IndexPager {
+ public $mDefaultDirection = false;
+
+ function __construct() {
+ parent::__construct();
+ }
+
+ /**
+ * Shamelessly stolen bits from ReverseChronologicalPager, d
+ * didn't want to do class magic as may be still revamped
+ */
+ function getNavigationBar() {
+ global $wgLang;
+
+ $linkTexts = array(
+ 'prev' => wfMsgHtml( "prevn", $this->mLimit ),
+ 'next' => wfMsgHtml( 'nextn', $this->mLimit ),
+ 'first' => wfMsgHtml('page_first'), /* Introduced the message */
+ 'last' => wfMsgHtml( 'page_last' ) /* Introduced the message */
+ );
+
+ $pagingLinks = $this->getPagingLinks( $linkTexts );
+ $limitLinks = $this->getLimitLinks();
+ $limits = implode( ' | ', $limitLinks );
+
+ $this->mNavigationBar = "({$pagingLinks['first']} | {$pagingLinks['last']}) " . wfMsgHtml("viewprevnext", $pagingLinks['prev'], $pagingLinks['next'], $limits);
+ return $this->mNavigationBar;
+
+ }
+}
+
/**
* IndexPager with a formatted navigation bar
+ * @addtogroup Pager
*/
abstract class ReverseChronologicalPager extends IndexPager {
public $mDefaultDirection = true;
@@ -413,13 +454,15 @@ abstract class ReverseChronologicalPager extends IndexPager {
$limitLinks = $this->getLimitLinks();
$limits = implode( ' | ', $limitLinks );
- $this->mNavigationBar = "({$pagingLinks['first']} | {$pagingLinks['last']}) " . wfMsgHtml("viewprevnext", $pagingLinks['prev'], $pagingLinks['next'], $limits);
+ $this->mNavigationBar = "({$pagingLinks['first']} | {$pagingLinks['last']}) " .
+ wfMsgHtml("viewprevnext", $pagingLinks['prev'], $pagingLinks['next'], $limits);
return $this->mNavigationBar;
}
}
/**
* Table-based display with a user-selectable sort order
+ * @addtogroup Pager
*/
abstract class TablePager extends IndexPager {
var $mSort;
diff --git a/includes/Parser.php b/includes/Parser.php
index 8d67279d..8e36e170 100644
--- a/includes/Parser.php
+++ b/includes/Parser.php
@@ -2,8 +2,7 @@
/**
* File for Parser and related classes
*
- * @package MediaWiki
- * @subpackage Parser
+ * @addtogroup Parser
*/
/**
@@ -57,9 +56,10 @@ define( 'MW_COLON_STATE_COMMENTDASH', 6 );
define( 'MW_COLON_STATE_COMMENTDASHDASH', 7 );
/**
- * PHP Parser
- *
- * Processes wiki markup
+ * PHP Parser - Processes wiki markup (which uses a more user-friendly
+ * syntax, such as "[[link]]" for making links), and provides a one-way
+ * transformation of that wiki markup it into XHTML output / markup
+ * (which in turn the browser understands, and can display).
*
* <pre>
* There are four main entry points into the Parser class:
@@ -86,10 +86,11 @@ define( 'MW_COLON_STATE_COMMENTDASHDASH', 7 );
* * only within ParserOptions
* </pre>
*
- * @package MediaWiki
+ * @addtogroup Parser
*/
class Parser
{
+ const VERSION = MW_PARSER_VERSION;
/**#@+
* @private
*/
@@ -114,7 +115,7 @@ class Parser
$ot, // Shortcut alias, see setOutputType()
$mRevisionId, // ID to display in {{REVISIONID}} tags
$mRevisionTimestamp, // The timestamp of the specified revision ID
- $mRevIdForTs; // The revision ID which was used to fetch the timestamp
+ $mRevIdForTs; // The revision ID which was used to fetch the timestamp
/**#@-*/
@@ -162,6 +163,7 @@ class Parser
$this->setFunctionHook( 'numberofarticles', array( 'CoreParserFunctions', 'numberofarticles' ), SFH_NO_HASH );
$this->setFunctionHook( 'numberoffiles', array( 'CoreParserFunctions', 'numberoffiles' ), SFH_NO_HASH );
$this->setFunctionHook( 'numberofadmins', array( 'CoreParserFunctions', 'numberofadmins' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'numberofedits', array( 'CoreParserFunctions', 'numberofedits' ), SFH_NO_HASH );
$this->setFunctionHook( 'language', array( 'CoreParserFunctions', 'language' ), SFH_NO_HASH );
$this->setFunctionHook( 'padleft', array( 'CoreParserFunctions', 'padleft' ), SFH_NO_HASH );
$this->setFunctionHook( 'padright', array( 'CoreParserFunctions', 'padright' ), SFH_NO_HASH );
@@ -211,7 +213,7 @@ class Parser
'titles' => array()
);
$this->mRevisionTimestamp = $this->mRevisionId = null;
-
+
/**
* Prefix for temporary replacement strings for the multipass parser.
* \x07 should never appear in input as it's disallowed in XML.
@@ -262,7 +264,6 @@ class Parser
* Convert wikitext to HTML
* Do not call this function recursively.
*
- * @private
* @param string $text Text we want to parse
* @param Title &$title A title object
* @param array $options
@@ -271,7 +272,7 @@ class Parser
* @param int $revid number to pass in {{REVISIONID}}
* @return ParserOutput a ParserOutput
*/
- function parse( $text, &$title, $options, $linestart = true, $clearState = true, $revid = null ) {
+ public function parse( $text, &$title, $options, $linestart = true, $clearState = true, $revid = null ) {
/**
* First pass--just handle <nowiki> sections, pass the rest off
* to internalParse() which does all the real work.
@@ -724,7 +725,7 @@ class Parser
$descriptorspec = array(
0 => array('pipe', 'r'),
1 => array('pipe', 'w'),
- 2 => array('file', '/dev/null', 'a')
+ 2 => array('file', '/dev/null', 'a') // FIXME: this line in UNIX-specific, it generates a warning on Windows, because /dev/null is not a valid Windows file.
);
$pipes = array();
$process = proc_open("$wgTidyBin -config $wgTidyConf $wgTidyOpts$opts", $descriptorspec, $pipes);
@@ -872,7 +873,7 @@ class Parser
array_push ( $td_history , false );
array_push ( $last_tag_history , '' );
}
- else if ( $first_character == '|' || $first_character == '!' || substr ( $line , 0 , 2 ) == '|+' ) {
+ else if ( $first_character == '|' || $first_character == '!' || substr ( $line , 0 , 2 ) == '|+' ) {
// This might be cell elements, td, th or captions
if ( substr ( $line , 0 , 2 ) == '|+' ) {
$first_character = '+';
@@ -1002,6 +1003,7 @@ class Parser
$text = Sanitizer::removeHTMLtags( $text, array( &$this, 'attributeStripCallback' ) );
$text = $this->replaceVariables( $text, $args );
+ wfRunHooks( 'InternalParseBeforeLinks', array( &$this, &$text, &$this->mStripState ) );
// Tables need to come after variable replacement for things to work
// properly; putting them before other transformations should keep
@@ -1086,7 +1088,7 @@ class Parser
}
$url = wfMsg( $urlmsg, $id);
- $sk =& $this->mOptions->getSkin();
+ $sk = $this->mOptions->getSkin();
$la = $sk->getExternalLinkAttributes( $url, $keyword.$id );
$text = "<a href=\"{$url}\"{$la}>{$keyword} {$id}</a>";
}
@@ -1287,7 +1289,8 @@ class Parser
$output .= '</i>';
if ($state == 'bi')
$output .= '</b>';
- if ($state == 'both')
+ # There might be lonely ''''', so make sure we have a buffer
+ if ($state == 'both' && $buffer)
$output .= '<b><i>'.$buffer.'</i></b>';
return $output;
}
@@ -1306,7 +1309,7 @@ class Parser
$fname = 'Parser::replaceExternalLinks';
wfProfileIn( $fname );
- $sk =& $this->mOptions->getSkin();
+ $sk = $this->mOptions->getSkin();
$bits = preg_split( EXT_LINK_BRACKETED, $text, -1, PREG_SPLIT_DELIM_CAPTURE );
@@ -1395,7 +1398,7 @@ class Parser
$s = array_shift( $bits );
$i = 0;
- $sk =& $this->mOptions->getSkin();
+ $sk = $this->mOptions->getSkin();
while ( $i < count( $bits ) ){
$protocol = $bits[$i++];
@@ -1468,7 +1471,7 @@ class Parser
* @param string
* @return string
* @static
- * @fixme This can merge genuinely required bits in the path or query string,
+ * @todo This can merge genuinely required bits in the path or query string,
* breaking legit URLs. A proper fix would treat the various parts of
* the URL differently; as a workaround, just use the output for
* statistical records, not for actual linking/output.
@@ -1503,7 +1506,7 @@ class Parser
* @private
*/
function maybeMakeExternalImage( $url ) {
- $sk =& $this->mOptions->getSkin();
+ $sk = $this->mOptions->getSkin();
$imagesfrom = $this->mOptions->getAllowExternalImagesFrom();
$imagesexception = !empty($imagesfrom);
$text = false;
@@ -1533,7 +1536,7 @@ class Parser
# the % is needed to support urlencoded titles as well
if ( !$tc ) { $tc = Title::legalChars() . '#%'; }
- $sk =& $this->mOptions->getSkin();
+ $sk = $this->mOptions->getSkin();
#split the entire text string on occurences of [[
$a = explode( '[[', ' ' . $s );
@@ -1552,7 +1555,6 @@ class Parser
$e2 = wfMsgForContent( 'linkprefix' );
$useLinkPrefixExtension = $wgContLang->linkPrefixExtension();
-
if( is_null( $this->mTitle ) ) {
throw new MWException( __METHOD__.": \$this->mTitle is null\n" );
}
@@ -1569,10 +1571,11 @@ class Parser
$prefix = '';
}
- if($wgContLang->hasVariants())
+ if($wgContLang->hasVariants()) {
$selflink = $wgContLang->convertLinkToAllVariants($this->mTitle->getPrefixedText());
- else
+ } else {
$selflink = array($this->mTitle->getPrefixedText());
+ }
$useSubpages = $this->areSubpagesAllowed();
wfProfileOut( $fname.'-setup' );
@@ -1626,7 +1629,7 @@ class Parser
$might_be_img = true;
$text = $m[2];
if ( strpos( $m[1], '%' ) !== false ) {
- $m[1] = urldecode($m[1]);
+ $m[1] = urldecode($m[1]);
}
$trail = "";
} else { # Invalid form; output directly
@@ -1640,7 +1643,7 @@ class Parser
# Don't allow internal links to pages containing
# PROTO: where PROTO is a valid URL protocol; these
# should be external links.
- if (preg_match('/^(\b(?:' . wfUrlProtocols() . '))/', $m[1])) {
+ if (preg_match('/^\b(?:' . wfUrlProtocols() . ')/', $m[1])) {
$s .= $prefix . '[[' . $line ;
continue;
}
@@ -1723,8 +1726,8 @@ class Parser
wfProfileIn( "$fname-interwiki" );
if( $iw && $this->mOptions->getInterwikiMagic() && $nottalk && $wgContLang->getLanguageName( $iw ) ) {
$this->mOutput->addLanguageLink( $nt->getFullText() );
- $s = rtrim($s . "\n");
- $s .= trim($prefix . $trail, "\n") == '' ? '': $prefix . $trail;
+ $s = rtrim($s . $prefix);
+ $s .= trim($trail, "\n") == '' ? '': $prefix . $trail;
wfProfileOut( "$fname-interwiki" );
continue;
}
@@ -1778,11 +1781,12 @@ class Parser
}
}
- if( ( in_array( $nt->getPrefixedText(), $selflink ) ) &&
- ( $nt->getFragment() === '' ) ) {
- # Self-links are handled specially; generally de-link and change to bold.
- $s .= $prefix . $sk->makeSelfLinkObj( $nt, $text, '', $trail );
- continue;
+ # Self-link checking
+ if( $nt->getFragment() === '' ) {
+ if( in_array( $nt->getPrefixedText(), $selflink, true ) ) {
+ $s .= $prefix . $sk->makeSelfLinkObj( $nt, $text, '', $trail );
+ continue;
+ }
}
# Special and Media are pseudo-namespaces; no pages actually exist in them
@@ -1862,7 +1866,7 @@ class Parser
*/
function makeKnownLinkHolder( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
list( $inside, $trail ) = Linker::splitTrail( $trail );
- $sk =& $this->mOptions->getSkin();
+ $sk = $this->mOptions->getSkin();
$link = $sk->makeKnownLinkObj( $nt, $text, $query, $inside, $prefix );
return $this->armorLinks( $link ) . $trail;
}
@@ -1923,9 +1927,9 @@ class Parser
# Look at the first character
if( $target != '' && $target{0} == '/' ) {
# / at end means we don't want the slash to be shown
- if( substr( $target, -1, 1 ) == '/' ) {
- $target = substr( $target, 1, -1 );
- $noslash = $target;
+ $trailingSlashes = preg_match_all( '%(/+)$%', $target, $m );
+ if( $trailingSlashes ) {
+ $noslash = $target = substr( $target, 1, -strlen($m[0][0]) );
} else {
$noslash = substr( $target, 1 );
}
@@ -2134,9 +2138,9 @@ class Parser
wfProfileIn( "$fname-paragraph" );
# No prefix (not in list)--go to paragraph mode
// XXX: use a stack for nestable elements like span, table and div
- $openmatch = preg_match('/(<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6|<pre|<tr|<p|<ul|<ol|<li|<\\/tr|<\\/td|<\\/th)/iS', $t );
+ $openmatch = preg_match('/(?:<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6|<pre|<tr|<p|<ul|<ol|<li|<\\/tr|<\\/td|<\\/th)/iS', $t );
$closematch = preg_match(
- '/(<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6|'.
+ '/(?:<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6|'.
'<td|<th|<\\/?div|<hr|<\\/pre|<\\/p|'.$this->mUniqPrefix.'-pre|<\\/li|<\\/ul|<\\/ol|<\\/?center)/iS', $t );
if ( $openmatch or $closematch ) {
$paragraphStack = false;
@@ -2538,6 +2542,8 @@ class Parser
return $varCache[$index] = $wgContLang->formatNum( SiteStats::pages() );
case 'numberofadmins':
return $varCache[$index] = $wgContLang->formatNum( SiteStats::admins() );
+ case 'numberofedits':
+ return $varCache[$index] = $wgContLang->formatNum( SiteStats::edits() );
case 'currenttimestamp':
return $varCache[$index] = wfTimestampNow();
case 'localtimestamp':
@@ -2852,7 +2858,7 @@ class Parser
return $text;
}
-
+
/// Clean up argument array - refactored in 1.9 so parserfunctions can use it, too.
static function createAssocArgs( $args ) {
$assocArgs = array();
@@ -2872,10 +2878,10 @@ class Parser
}
}
}
-
+
return $assocArgs;
}
-
+
/**
* Return the text of a template, after recursively
* replacing any variables or templates within the template.
@@ -2888,7 +2894,7 @@ class Parser
* @private
*/
function braceSubstitution( $piece ) {
- global $wgContLang, $wgLang, $wgAllowDisplayTitle;
+ global $wgContLang, $wgLang, $wgAllowDisplayTitle, $wgNonincludableNamespaces;
$fname = __METHOD__ /*. '-L' . count( $this->mArgStack )*/;
wfProfileIn( $fname );
wfProfileIn( __METHOD__.'-setup' );
@@ -3031,6 +3037,19 @@ class Parser
} else {
# set $text to cached message.
$text = $linestart . $this->mTemplates[$piece['title']];
+ #treat title for cached page the same as others
+ $ns = NS_TEMPLATE;
+ $subpage = '';
+ $part1 = $this->maybeDoSubpageLink( $part1, $subpage );
+ if ($subpage !== '') {
+ $ns = $this->mTitle->getNamespace();
+ }
+ $title = Title::newFromText( $part1, $ns );
+ //used by include size checking
+ $titleText = $title->getPrefixedText();
+ //used by edit section links
+ $replaceHeadings = true;
+
}
}
@@ -3066,6 +3085,9 @@ class Parser
$isHTML = true;
$this->disableCache();
}
+ } else if ( $wgNonincludableNamespaces && in_array( $title->getNamespace(), $wgNonincludableNamespaces ) ) {
+ $found = false; //access denied
+ wfDebug( "$fname: template inclusion denied for " . $title->getPrefixedDBkey() );
} else {
$articleContent = $this->fetchTemplate( $title );
if ( $articleContent !== false ) {
@@ -3155,7 +3177,7 @@ class Parser
# If the template begins with a table or block-level
# element, it should be treated as beginning a new line.
- if (!$piece['lineStart'] && preg_match('/^({\\||:|;|#|\*)/', $text)) /*}*/{
+ if (!$piece['lineStart'] && preg_match('/^(?:{\\||:|;|#|\*)/', $text)) /*}*/{
$text = "\n" . $text;
}
} elseif ( !$noargs ) {
@@ -3271,7 +3293,7 @@ class Parser
return wfMsg('scarytranscludedisabled');
$url = $title->getFullUrl( "action=$action" );
-
+
if (strlen($url) > 255)
return wfMsg('scarytranscludetoolong');
return $this->fetchScaryTemplateMaybeFromCache($url);
@@ -3279,7 +3301,7 @@ class Parser
function fetchScaryTemplateMaybeFromCache($url) {
global $wgTranscludeCacheExpiry;
- $dbr =& wfGetDB(DB_SLAVE);
+ $dbr = wfGetDB(DB_SLAVE);
$obj = $dbr->selectRow('transcache', array('tc_time', 'tc_contents'),
array('tc_url' => $url));
if ($obj) {
@@ -3294,7 +3316,7 @@ class Parser
if (!$text)
return wfMsg('scarytranscludefailed', $url);
- $dbw =& wfGetDB(DB_MASTER);
+ $dbw = wfGetDB(DB_MASTER);
$dbw->replace('transcache', array('tc_url'), array(
'tc_url' => $url,
'tc_time' => time(),
@@ -3395,7 +3417,7 @@ class Parser
global $wgMaxTocLevel, $wgContLang;
$doNumberHeadings = $this->mOptions->getNumberHeadings();
- if( !$this->mTitle->userCanEdit() ) {
+ if( !$this->mTitle->quickUserCan( 'edit' ) ) {
$showEditLink = 0;
} else {
$showEditLink = $this->mOptions->getEditSection();
@@ -3437,7 +3459,7 @@ class Parser
}
# We need this to perform operations on the HTML
- $sk =& $this->mOptions->getSkin();
+ $sk = $this->mOptions->getSkin();
# headline counter
$headlineCount = 0;
@@ -3723,11 +3745,7 @@ class Parser
}
# Trim trailing whitespace
- # __END__ tag allows for trailing
- # whitespace to be deliberately included
$text = rtrim( $text );
- $mw =& MagicWord::get( 'end' );
- $mw->matchAndRemove( $text );
return $text;
}
@@ -3847,7 +3865,7 @@ class Parser
wfProfileIn($fname);
- if ( $wgTitle ) {
+ if ( $wgTitle && !( $wgTitle instanceof FakeTitle ) ) {
$this->mTitle = $wgTitle;
} else {
$this->mTitle = Title::newFromText('msg');
@@ -3966,12 +3984,12 @@ class Parser
$pdbks = array();
$colours = array();
- $sk =& $this->mOptions->getSkin();
+ $sk = $this->mOptions->getSkin();
$linkCache =& LinkCache::singleton();
if ( !empty( $this->mLinkHolders['namespaces'] ) ) {
wfProfileIn( $fname.'-check' );
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$page = $dbr->tableName( 'page' );
$threshold = $wgUser->getOption('stubthreshold');
@@ -4161,8 +4179,8 @@ class Parser
if(isset($categoryMap[$vardbk])){
$oldkey = $categoryMap[$vardbk];
if($oldkey != $vardbk)
- $varCategories[$oldkey]=$vardbk;
- }
+ $varCategories[$oldkey]=$vardbk;
+ }
}
// rebuild the categories in original order (if there are replacements)
@@ -4303,6 +4321,7 @@ class Parser
*/
function renderImageGallery( $text, $params ) {
$ig = new ImageGallery();
+ $ig->setContextTitle( $this->mTitle );
$ig->setShowBytes( false );
$ig->setShowFilename( false );
$ig->setParsing();
@@ -4314,6 +4333,15 @@ class Parser
$caption = $this->replaceInternalLinks( $caption );
$ig->setCaptionHtml( $caption );
}
+ if( isset( $params['perrow'] ) ) {
+ $ig->setPerRow( $params['perrow'] );
+ }
+ if( isset( $params['widths'] ) ) {
+ $ig->setWidths( $params['widths'] );
+ }
+ if( isset( $params['heights'] ) ) {
+ $ig->setHeights( $params['heights'] );
+ }
$lines = explode( "\n", $text );
foreach ( $lines as $line ) {
@@ -4359,10 +4387,8 @@ class Parser
* Parse image options text and use it to make an image
*/
function makeImage( $nt, $options ) {
- global $wgUseImageResize, $wgDjvuRenderer;
-
- $align = '';
-
+ # @TODO: let the MediaHandler specify its transform parameters
+ #
# Check if the options text is of the form "options|alt text"
# Options are:
# * thumbnail make a thumbnail with enlarge-icon and caption, alignment depends on lang
@@ -4372,61 +4398,74 @@ class Parser
# * ___px scale to ___ pixels width, no aligning. e.g. use in taxobox
# * center center the image
# * framed Keep original image size, no magnify-button.
-
- $part = explode( '|', $options);
-
+ # vertical-align values (no % or length right now):
+ # * baseline
+ # * sub
+ # * super
+ # * top
+ # * text-top
+ # * middle
+ # * bottom
+ # * text-bottom
+
+
+ $part = array_map( 'trim', explode( '|', $options) );
+
+ $mwAlign = array();
+ $alignments = array( 'left', 'right', 'center', 'none', 'baseline', 'sub', 'super', 'top', 'text-top', 'middle', 'bottom', 'text-bottom' );
+ foreach ( $alignments as $alignment ) {
+ $mwAlign[$alignment] =& MagicWord::get( 'img_'.$alignment );
+ }
$mwThumb =& MagicWord::get( 'img_thumbnail' );
$mwManualThumb =& MagicWord::get( 'img_manualthumb' );
- $mwLeft =& MagicWord::get( 'img_left' );
- $mwRight =& MagicWord::get( 'img_right' );
- $mwNone =& MagicWord::get( 'img_none' );
$mwWidth =& MagicWord::get( 'img_width' );
- $mwCenter =& MagicWord::get( 'img_center' );
$mwFramed =& MagicWord::get( 'img_framed' );
$mwPage =& MagicWord::get( 'img_page' );
$caption = '';
- $width = $height = $framed = $thumb = false;
- $page = null;
+ $params = array();
+ $framed = $thumb = false;
$manual_thumb = '' ;
+ $align = $valign = '';
+ $sk = $this->mOptions->getSkin();
foreach( $part as $val ) {
- if ( $wgUseImageResize && ! is_null( $mwThumb->matchVariableStartToEnd($val) ) ) {
+ if ( !is_null( $mwThumb->matchVariableStartToEnd($val) ) ) {
$thumb=true;
} elseif ( ! is_null( $match = $mwManualThumb->matchVariableStartToEnd($val) ) ) {
# use manually specified thumbnail
$thumb=true;
$manual_thumb = $match;
- } elseif ( ! is_null( $mwRight->matchVariableStartToEnd($val) ) ) {
- # remember to set an alignment, don't render immediately
- $align = 'right';
- } elseif ( ! is_null( $mwLeft->matchVariableStartToEnd($val) ) ) {
- # remember to set an alignment, don't render immediately
- $align = 'left';
- } elseif ( ! is_null( $mwCenter->matchVariableStartToEnd($val) ) ) {
- # remember to set an alignment, don't render immediately
- $align = 'center';
- } elseif ( ! is_null( $mwNone->matchVariableStartToEnd($val) ) ) {
- # remember to set an alignment, don't render immediately
- $align = 'none';
- } elseif ( isset( $wgDjvuRenderer ) && $wgDjvuRenderer
- && ! is_null( $match = $mwPage->matchVariableStartToEnd($val) ) ) {
- # Select a page in a multipage document
- $page = $match;
- } elseif ( $wgUseImageResize && !$width && ! is_null( $match = $mwWidth->matchVariableStartToEnd($val) ) ) {
- wfDebug( "img_width match: $match\n" );
- # $match is the image width in pixels
- $m = array();
- if ( preg_match( '/^([0-9]*)x([0-9]*)$/', $match, $m ) ) {
- $width = intval( $m[1] );
- $height = intval( $m[2] );
+ } else {
+ foreach( $alignments as $alignment ) {
+ if ( ! is_null( $mwAlign[$alignment]->matchVariableStartToEnd($val) ) ) {
+ switch ( $alignment ) {
+ case 'left': case 'right': case 'center': case 'none':
+ $align = $alignment; break;
+ default:
+ $valign = $alignment;
+ }
+ continue 2;
+ }
+ }
+ if ( ! is_null( $match = $mwPage->matchVariableStartToEnd($val) ) ) {
+ # Select a page in a multipage document
+ $params['page'] = $match;
+ } elseif ( !isset( $params['width'] ) && ! is_null( $match = $mwWidth->matchVariableStartToEnd($val) ) ) {
+ wfDebug( "img_width match: $match\n" );
+ # $match is the image width in pixels
+ $m = array();
+ if ( preg_match( '/^([0-9]*)x([0-9]*)$/', $match, $m ) ) {
+ $params['width'] = intval( $m[1] );
+ $params['height'] = intval( $m[2] );
+ } else {
+ $params['width'] = intval($match);
+ }
+ } elseif ( ! is_null( $mwFramed->matchVariableStartToEnd($val) ) ) {
+ $framed=true;
} else {
- $width = intval($match);
+ $caption = $val;
}
- } elseif ( ! is_null( $mwFramed->matchVariableStartToEnd($val) ) ) {
- $framed=true;
- } else {
- $caption = $val;
}
}
# Strip bad stuff out of the alt text
@@ -4439,8 +4478,7 @@ class Parser
$alt = Sanitizer::stripAllTags( $alt );
# Linker does the rest
- $sk =& $this->mOptions->getSkin();
- return $sk->makeImageLinkObj( $nt, $caption, $alt, $align, $width, $height, $framed, $thumb, $manual_thumb, $page );
+ return $sk->makeImageLinkObj( $nt, $caption, $alt, $align, $params, $framed, $thumb, $manual_thumb, $valign );
}
/**
@@ -4518,24 +4556,6 @@ class Parser
$uniq = preg_quote( $this->uniqPrefix(), '/' );
$comment = "(?:$uniq-!--.*?QINU)";
$secs = preg_split(
- /*
- "/
- ^(
- (?:$comment|<\/?noinclude>)* # Initial comments will be stripped
- (?:
- (=+) # Should this be limited to 6?
- .+? # Section title...
- \\2 # Ending = count must match start
- |
- ^
- <h([1-6])\b.*?>
- .*?
- <\/h\\3\s*>
- )
- (?:$comment|<\/?noinclude>|\s+)* # Trailing whitespace ok
- )$
- /mix",
- */
"/
(
^
@@ -4559,7 +4579,8 @@ class Parser
// "Section 0" returns the content before any other section.
$rv = $secs[0];
} else {
- $rv = "";
+ //track missing section, will replace if found.
+ $rv = $newtext;
}
} elseif( $mode == "replace" ) {
if( $section == 0 ) {
@@ -4614,8 +4635,10 @@ class Parser
}
}
}
- # reinsert stripped tags
- $rv = trim( $stripState->unstripBoth( $rv ) );
+ if (is_string($rv))
+ # reinsert stripped tags
+ $rv = trim( $stripState->unstripBoth( $rv ) );
+
return $rv;
}
@@ -4628,34 +4651,35 @@ class Parser
*
* @param $text String: text to look in
* @param $section Integer: section number
+ * @param $deftext: default to return if section is not found
* @return string text of the requested section
*/
- function getSection( $text, $section ) {
- return $this->extractSections( $text, $section, "get" );
+ public function getSection( $text, $section, $deftext='' ) {
+ return $this->extractSections( $text, $section, "get", $deftext );
}
- function replaceSection( $oldtext, $section, $text ) {
+ public function replaceSection( $oldtext, $section, $text ) {
return $this->extractSections( $oldtext, $section, "replace", $text );
}
/**
- * Get the timestamp associated with the current revision, adjusted for
+ * Get the timestamp associated with the current revision, adjusted for
* the default server-local timestamp
*/
function getRevisionTimestamp() {
if ( is_null( $this->mRevisionTimestamp ) ) {
wfProfileIn( __METHOD__ );
global $wgContLang;
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$timestamp = $dbr->selectField( 'revision', 'rev_timestamp',
array( 'rev_id' => $this->mRevisionId ), __METHOD__ );
-
+
// Normalize timestamp to internal MW format for timezone processing.
// This has the added side-effect of replacing a null value with
// the current time, which gives us more sensible behavior for
// previews.
$timestamp = wfTimestamp( TS_MW, $timestamp );
-
+
// The cryptic '' timezone parameter tells to use the site-default
// timezone offset instead of the user settings.
//
@@ -4663,12 +4687,12 @@ class Parser
// to other users, and potentially even used inside links and such,
// it needs to be consistent for all visitors.
$this->mRevisionTimestamp = $wgContLang->userAdjust( $timestamp, '' );
-
+
wfProfileOut( __METHOD__ );
}
return $this->mRevisionTimestamp;
}
-
+
/**
* Mutator for $mDefaultSort
*
@@ -4677,7 +4701,7 @@ class Parser
public function setDefaultSort( $sort ) {
$this->mDefaultSort = $sort;
}
-
+
/**
* Accessor for $mDefaultSort
* Will use the title/prefixed title if none is set
@@ -4693,241 +4717,13 @@ class Parser
: $this->mTitle->getPrefixedText();
}
}
-
-}
-
-/**
- * @todo document
- * @package MediaWiki
- */
-class ParserOutput
-{
- var $mText, # The output text
- $mLanguageLinks, # List of the full text of language links, in the order they appear
- $mCategories, # Map of category names to sort keys
- $mContainsOldMagic, # Boolean variable indicating if the input contained variables like {{CURRENTDAY}}
- $mCacheTime, # Time when this object was generated, or -1 for uncacheable. Used in ParserCache.
- $mVersion, # Compatibility check
- $mTitleText, # title text of the chosen language variant
- $mLinks, # 2-D map of NS/DBK to ID for the links in the document. ID=zero for broken.
- $mTemplates, # 2-D map of NS/DBK to ID for the template references. ID=zero for broken.
- $mImages, # DB keys of the images used, in the array key only
- $mExternalLinks, # External link URLs, in the key only
- $mHTMLtitle, # Display HTML title
- $mSubtitle, # Additional subtitle
- $mNewSection, # Show a new section link?
- $mNoGallery; # No gallery on category page? (__NOGALLERY__)
-
- function ParserOutput( $text = '', $languageLinks = array(), $categoryLinks = array(),
- $containsOldMagic = false, $titletext = '' )
- {
- $this->mText = $text;
- $this->mLanguageLinks = $languageLinks;
- $this->mCategories = $categoryLinks;
- $this->mContainsOldMagic = $containsOldMagic;
- $this->mCacheTime = '';
- $this->mVersion = MW_PARSER_VERSION;
- $this->mTitleText = $titletext;
- $this->mLinks = array();
- $this->mTemplates = array();
- $this->mImages = array();
- $this->mExternalLinks = array();
- $this->mHTMLtitle = "" ;
- $this->mSubtitle = "" ;
- $this->mNewSection = false;
- $this->mNoGallery = false;
- }
- function getText() { return $this->mText; }
- function &getLanguageLinks() { return $this->mLanguageLinks; }
- function getCategoryLinks() { return array_keys( $this->mCategories ); }
- function &getCategories() { return $this->mCategories; }
- function getCacheTime() { return $this->mCacheTime; }
- function getTitleText() { return $this->mTitleText; }
- function &getLinks() { return $this->mLinks; }
- function &getTemplates() { return $this->mTemplates; }
- function &getImages() { return $this->mImages; }
- function &getExternalLinks() { return $this->mExternalLinks; }
- function getNoGallery() { return $this->mNoGallery; }
- function getSubtitle() { return $this->mSubtitle; }
-
- function containsOldMagic() { return $this->mContainsOldMagic; }
- function setText( $text ) { return wfSetVar( $this->mText, $text ); }
- function setLanguageLinks( $ll ) { return wfSetVar( $this->mLanguageLinks, $ll ); }
- function setCategoryLinks( $cl ) { return wfSetVar( $this->mCategories, $cl ); }
- function setContainsOldMagic( $com ) { return wfSetVar( $this->mContainsOldMagic, $com ); }
- function setCacheTime( $t ) { return wfSetVar( $this->mCacheTime, $t ); }
- function setTitleText( $t ) { return wfSetVar($this->mTitleText, $t); }
- function setSubtitle( $st ) { return wfSetVar( $this->mSubtitle, $st ); }
-
- function addCategory( $c, $sort ) { $this->mCategories[$c] = $sort; }
- function addImage( $name ) { $this->mImages[$name] = 1; }
- function addLanguageLink( $t ) { $this->mLanguageLinks[] = $t; }
- function addExternalLink( $url ) { $this->mExternalLinks[$url] = 1; }
-
- function setNewSection( $value ) {
- $this->mNewSection = (bool)$value;
- }
- function getNewSection() {
- return (bool)$this->mNewSection;
- }
-
- function addLink( $title, $id = null ) {
- $ns = $title->getNamespace();
- $dbk = $title->getDBkey();
- if ( !isset( $this->mLinks[$ns] ) ) {
- $this->mLinks[$ns] = array();
- }
- if ( is_null( $id ) ) {
- $id = $title->getArticleID();
- }
- $this->mLinks[$ns][$dbk] = $id;
- }
-
- function addTemplate( $title, $id ) {
- $ns = $title->getNamespace();
- $dbk = $title->getDBkey();
- if ( !isset( $this->mTemplates[$ns] ) ) {
- $this->mTemplates[$ns] = array();
- }
- $this->mTemplates[$ns][$dbk] = $id;
- }
-
- /**
- * Return true if this cached output object predates the global or
- * per-article cache invalidation timestamps, or if it comes from
- * an incompatible older version.
- *
- * @param string $touched the affected article's last touched timestamp
- * @return bool
- * @public
- */
- function expired( $touched ) {
- global $wgCacheEpoch;
- return $this->getCacheTime() == -1 || // parser says it's uncacheable
- $this->getCacheTime() < $touched ||
- $this->getCacheTime() <= $wgCacheEpoch ||
- !isset( $this->mVersion ) ||
- version_compare( $this->mVersion, MW_PARSER_VERSION, "lt" );
- }
}
/**
- * Set options of the Parser
- * @todo document
- * @package MediaWiki
+ * @todo document, briefly.
+ * @addtogroup Parser
*/
-class ParserOptions
-{
- # All variables are supposed to be private in theory, although in practise this is not the case.
- var $mUseTeX; # Use texvc to expand <math> tags
- var $mUseDynamicDates; # Use DateFormatter to format dates
- var $mInterwikiMagic; # Interlanguage links are removed and returned in an array
- var $mAllowExternalImages; # Allow external images inline
- var $mAllowExternalImagesFrom; # If not, any exception?
- var $mSkin; # Reference to the preferred skin
- var $mDateFormat; # Date format index
- var $mEditSection; # Create "edit section" links
- var $mNumberHeadings; # Automatically number headings
- var $mAllowSpecialInclusion; # Allow inclusion of special pages
- var $mTidy; # Ask for tidy cleanup
- var $mInterfaceMessage; # Which lang to call for PLURAL and GRAMMAR
- var $mMaxIncludeSize; # Maximum size of template expansions, in bytes
- var $mRemoveComments; # Remove HTML comments. ONLY APPLIES TO PREPROCESS OPERATIONS
-
- var $mUser; # Stored user object, just used to initialise the skin
-
- function getUseTeX() { return $this->mUseTeX; }
- function getUseDynamicDates() { return $this->mUseDynamicDates; }
- function getInterwikiMagic() { return $this->mInterwikiMagic; }
- function getAllowExternalImages() { return $this->mAllowExternalImages; }
- function getAllowExternalImagesFrom() { return $this->mAllowExternalImagesFrom; }
- function getEditSection() { return $this->mEditSection; }
- function getNumberHeadings() { return $this->mNumberHeadings; }
- function getAllowSpecialInclusion() { return $this->mAllowSpecialInclusion; }
- function getTidy() { return $this->mTidy; }
- function getInterfaceMessage() { return $this->mInterfaceMessage; }
- function getMaxIncludeSize() { return $this->mMaxIncludeSize; }
- function getRemoveComments() { return $this->mRemoveComments; }
-
- function &getSkin() {
- if ( !isset( $this->mSkin ) ) {
- $this->mSkin = $this->mUser->getSkin();
- }
- return $this->mSkin;
- }
-
- function getDateFormat() {
- if ( !isset( $this->mDateFormat ) ) {
- $this->mDateFormat = $this->mUser->getDatePreference();
- }
- return $this->mDateFormat;
- }
-
- function setUseTeX( $x ) { return wfSetVar( $this->mUseTeX, $x ); }
- function setUseDynamicDates( $x ) { return wfSetVar( $this->mUseDynamicDates, $x ); }
- function setInterwikiMagic( $x ) { return wfSetVar( $this->mInterwikiMagic, $x ); }
- function setAllowExternalImages( $x ) { return wfSetVar( $this->mAllowExternalImages, $x ); }
- function setAllowExternalImagesFrom( $x ) { return wfSetVar( $this->mAllowExternalImagesFrom, $x ); }
- function setDateFormat( $x ) { return wfSetVar( $this->mDateFormat, $x ); }
- function setEditSection( $x ) { return wfSetVar( $this->mEditSection, $x ); }
- function setNumberHeadings( $x ) { return wfSetVar( $this->mNumberHeadings, $x ); }
- function setAllowSpecialInclusion( $x ) { return wfSetVar( $this->mAllowSpecialInclusion, $x ); }
- function setTidy( $x ) { return wfSetVar( $this->mTidy, $x); }
- function setSkin( $x ) { $this->mSkin = $x; }
- function setInterfaceMessage( $x ) { return wfSetVar( $this->mInterfaceMessage, $x); }
- function setMaxIncludeSize( $x ) { return wfSetVar( $this->mMaxIncludeSize, $x ); }
- function setRemoveComments( $x ) { return wfSetVar( $this->mRemoveComments, $x ); }
-
- function ParserOptions( $user = null ) {
- $this->initialiseFromUser( $user );
- }
-
- /**
- * Get parser options
- * @static
- */
- static function newFromUser( $user ) {
- return new ParserOptions( $user );
- }
-
- /** Get user options */
- function initialiseFromUser( $userInput ) {
- global $wgUseTeX, $wgUseDynamicDates, $wgInterwikiMagic, $wgAllowExternalImages;
- global $wgAllowExternalImagesFrom, $wgAllowSpecialInclusion, $wgMaxArticleSize;
- $fname = 'ParserOptions::initialiseFromUser';
- wfProfileIn( $fname );
- if ( !$userInput ) {
- global $wgUser;
- if ( isset( $wgUser ) ) {
- $user = $wgUser;
- } else {
- $user = new User;
- }
- } else {
- $user =& $userInput;
- }
-
- $this->mUser = $user;
-
- $this->mUseTeX = $wgUseTeX;
- $this->mUseDynamicDates = $wgUseDynamicDates;
- $this->mInterwikiMagic = $wgInterwikiMagic;
- $this->mAllowExternalImages = $wgAllowExternalImages;
- $this->mAllowExternalImagesFrom = $wgAllowExternalImagesFrom;
- $this->mSkin = null; # Deferred
- $this->mDateFormat = null; # Deferred
- $this->mEditSection = true;
- $this->mNumberHeadings = $user->getOption( 'numberheadings' );
- $this->mAllowSpecialInclusion = $wgAllowSpecialInclusion;
- $this->mTidy = false;
- $this->mInterfaceMessage = false;
- $this->mMaxIncludeSize = $wgMaxArticleSize * 1024;
- $this->mRemoveComments = true;
- wfProfileOut( $fname );
- }
-}
-
class OnlyIncludeReplacer {
var $output = '';
@@ -4940,6 +4736,10 @@ class OnlyIncludeReplacer {
}
}
+/**
+ * @todo document, briefly.
+ * @addtogroup Parser
+ */
class StripState {
var $general, $nowiki;
diff --git a/includes/ParserCache.php b/includes/ParserCache.php
index 37a42b7f..1489fcf9 100644
--- a/includes/ParserCache.php
+++ b/includes/ParserCache.php
@@ -1,13 +1,8 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage Cache
- */
-
-/**
- *
- * @package MediaWiki
+ * @addtogroup Cache
+ * @todo document
*/
class ParserCache {
/**
@@ -28,14 +23,14 @@ class ParserCache {
*
* @param object $memCached
*/
- function ParserCache( &$memCached ) {
+ function __construct( &$memCached ) {
$this->mMemc =& $memCached;
}
function getKey( &$article, &$user ) {
global $action;
$hash = $user->getPageRenderingHash();
- if( !$article->mTitle->userCanEdit() ) {
+ if( !$article->mTitle->quickUserCan( 'edit' ) ) {
// section edit links are suppressed even if the user has them on
$edit = '!edit=0';
} else {
@@ -95,31 +90,30 @@ class ParserCache {
function save( $parserOutput, &$article, &$user ){
global $wgParserCacheExpireTime;
$key = $this->getKey( $article, $user );
-
+
if( $parserOutput->getCacheTime() != -1 ) {
-
+
$now = wfTimestampNow();
$parserOutput->setCacheTime( $now );
-
+
// Save the timestamp so that we don't have to load the revision row on view
$parserOutput->mTimestamp = $article->getTimestamp();
-
+
$parserOutput->mText .= "\n<!-- Saved in parser cache with key $key and timestamp $now -->\n";
wfDebug( "Saved in parser cache with key $key and timestamp $now\n" );
-
+
if( $parserOutput->containsOldMagic() ){
$expire = 3600; # 1 hour
} else {
$expire = $wgParserCacheExpireTime;
}
$this->mMemc->set( $key, $parserOutput, $expire );
-
+
} else {
wfDebug( "Parser output was marked as uncacheable and has not been saved.\n" );
}
-
}
-
+
}
?>
diff --git a/includes/ParserOptions.php b/includes/ParserOptions.php
new file mode 100644
index 00000000..e335720f
--- /dev/null
+++ b/includes/ParserOptions.php
@@ -0,0 +1,119 @@
+<?php
+
+/**
+ * Set options of the Parser
+ * @todo document
+ * @addtogroup Parser
+ */
+class ParserOptions
+{
+ # All variables are supposed to be private in theory, although in practise this is not the case.
+ var $mUseTeX; # Use texvc to expand <math> tags
+ var $mUseDynamicDates; # Use DateFormatter to format dates
+ var $mInterwikiMagic; # Interlanguage links are removed and returned in an array
+ var $mAllowExternalImages; # Allow external images inline
+ var $mAllowExternalImagesFrom; # If not, any exception?
+ var $mSkin; # Reference to the preferred skin
+ var $mDateFormat; # Date format index
+ var $mEditSection; # Create "edit section" links
+ var $mNumberHeadings; # Automatically number headings
+ var $mAllowSpecialInclusion; # Allow inclusion of special pages
+ var $mTidy; # Ask for tidy cleanup
+ var $mInterfaceMessage; # Which lang to call for PLURAL and GRAMMAR
+ var $mMaxIncludeSize; # Maximum size of template expansions, in bytes
+ var $mRemoveComments; # Remove HTML comments. ONLY APPLIES TO PREPROCESS OPERATIONS
+
+ var $mUser; # Stored user object, just used to initialise the skin
+
+ function getUseTeX() { return $this->mUseTeX; }
+ function getUseDynamicDates() { return $this->mUseDynamicDates; }
+ function getInterwikiMagic() { return $this->mInterwikiMagic; }
+ function getAllowExternalImages() { return $this->mAllowExternalImages; }
+ function getAllowExternalImagesFrom() { return $this->mAllowExternalImagesFrom; }
+ function getEditSection() { return $this->mEditSection; }
+ function getNumberHeadings() { return $this->mNumberHeadings; }
+ function getAllowSpecialInclusion() { return $this->mAllowSpecialInclusion; }
+ function getTidy() { return $this->mTidy; }
+ function getInterfaceMessage() { return $this->mInterfaceMessage; }
+ function getMaxIncludeSize() { return $this->mMaxIncludeSize; }
+ function getRemoveComments() { return $this->mRemoveComments; }
+
+ function getSkin() {
+ if ( !isset( $this->mSkin ) ) {
+ $this->mSkin = $this->mUser->getSkin();
+ }
+ return $this->mSkin;
+ }
+
+ function getDateFormat() {
+ if ( !isset( $this->mDateFormat ) ) {
+ $this->mDateFormat = $this->mUser->getDatePreference();
+ }
+ return $this->mDateFormat;
+ }
+
+ function setUseTeX( $x ) { return wfSetVar( $this->mUseTeX, $x ); }
+ function setUseDynamicDates( $x ) { return wfSetVar( $this->mUseDynamicDates, $x ); }
+ function setInterwikiMagic( $x ) { return wfSetVar( $this->mInterwikiMagic, $x ); }
+ function setAllowExternalImages( $x ) { return wfSetVar( $this->mAllowExternalImages, $x ); }
+ function setAllowExternalImagesFrom( $x ) { return wfSetVar( $this->mAllowExternalImagesFrom, $x ); }
+ function setDateFormat( $x ) { return wfSetVar( $this->mDateFormat, $x ); }
+ function setEditSection( $x ) { return wfSetVar( $this->mEditSection, $x ); }
+ function setNumberHeadings( $x ) { return wfSetVar( $this->mNumberHeadings, $x ); }
+ function setAllowSpecialInclusion( $x ) { return wfSetVar( $this->mAllowSpecialInclusion, $x ); }
+ function setTidy( $x ) { return wfSetVar( $this->mTidy, $x); }
+ function setSkin( $x ) { $this->mSkin = $x; }
+ function setInterfaceMessage( $x ) { return wfSetVar( $this->mInterfaceMessage, $x); }
+ function setMaxIncludeSize( $x ) { return wfSetVar( $this->mMaxIncludeSize, $x ); }
+ function setRemoveComments( $x ) { return wfSetVar( $this->mRemoveComments, $x ); }
+
+ function __construct( $user = null ) {
+ $this->initialiseFromUser( $user );
+ }
+
+ /**
+ * Get parser options
+ * @static
+ */
+ static function newFromUser( $user ) {
+ return new ParserOptions( $user );
+ }
+
+ /** Get user options */
+ function initialiseFromUser( $userInput ) {
+ global $wgUseTeX, $wgUseDynamicDates, $wgInterwikiMagic, $wgAllowExternalImages;
+ global $wgAllowExternalImagesFrom, $wgAllowSpecialInclusion, $wgMaxArticleSize;
+ $fname = 'ParserOptions::initialiseFromUser';
+ wfProfileIn( $fname );
+ if ( !$userInput ) {
+ global $wgUser;
+ if ( isset( $wgUser ) ) {
+ $user = $wgUser;
+ } else {
+ $user = new User;
+ }
+ } else {
+ $user =& $userInput;
+ }
+
+ $this->mUser = $user;
+
+ $this->mUseTeX = $wgUseTeX;
+ $this->mUseDynamicDates = $wgUseDynamicDates;
+ $this->mInterwikiMagic = $wgInterwikiMagic;
+ $this->mAllowExternalImages = $wgAllowExternalImages;
+ $this->mAllowExternalImagesFrom = $wgAllowExternalImagesFrom;
+ $this->mSkin = null; # Deferred
+ $this->mDateFormat = null; # Deferred
+ $this->mEditSection = true;
+ $this->mNumberHeadings = $user->getOption( 'numberheadings' );
+ $this->mAllowSpecialInclusion = $wgAllowSpecialInclusion;
+ $this->mTidy = false;
+ $this->mInterfaceMessage = false;
+ $this->mMaxIncludeSize = $wgMaxArticleSize * 1024;
+ $this->mRemoveComments = true;
+ wfProfileOut( $fname );
+ }
+}
+
+?>
diff --git a/includes/ParserOutput.php b/includes/ParserOutput.php
new file mode 100644
index 00000000..03f1819c
--- /dev/null
+++ b/includes/ParserOutput.php
@@ -0,0 +1,133 @@
+<?php
+/**
+ * @todo document
+ * @addtogroup Parser
+ */
+class ParserOutput
+{
+ var $mText, # The output text
+ $mLanguageLinks, # List of the full text of language links, in the order they appear
+ $mCategories, # Map of category names to sort keys
+ $mContainsOldMagic, # Boolean variable indicating if the input contained variables like {{CURRENTDAY}}
+ $mCacheTime, # Time when this object was generated, or -1 for uncacheable. Used in ParserCache.
+ $mVersion, # Compatibility check
+ $mTitleText, # title text of the chosen language variant
+ $mLinks, # 2-D map of NS/DBK to ID for the links in the document. ID=zero for broken.
+ $mTemplates, # 2-D map of NS/DBK to ID for the template references. ID=zero for broken.
+ $mImages, # DB keys of the images used, in the array key only
+ $mExternalLinks, # External link URLs, in the key only
+ $mHTMLtitle, # Display HTML title
+ $mSubtitle, # Additional subtitle
+ $mNewSection, # Show a new section link?
+ $mNoGallery, # No gallery on category page? (__NOGALLERY__)
+ $mHeadItems; # Items to put in the <head> section
+
+ function ParserOutput( $text = '', $languageLinks = array(), $categoryLinks = array(),
+ $containsOldMagic = false, $titletext = '' )
+ {
+ $this->mText = $text;
+ $this->mLanguageLinks = $languageLinks;
+ $this->mCategories = $categoryLinks;
+ $this->mContainsOldMagic = $containsOldMagic;
+ $this->mCacheTime = '';
+ $this->mVersion = Parser::VERSION;
+ $this->mTitleText = $titletext;
+ $this->mLinks = array();
+ $this->mTemplates = array();
+ $this->mImages = array();
+ $this->mExternalLinks = array();
+ $this->mHTMLtitle = "" ;
+ $this->mSubtitle = "" ;
+ $this->mNewSection = false;
+ $this->mNoGallery = false;
+ $this->mHeadItems = array();
+ }
+
+ function getText() { return $this->mText; }
+ function &getLanguageLinks() { return $this->mLanguageLinks; }
+ function getCategoryLinks() { return array_keys( $this->mCategories ); }
+ function &getCategories() { return $this->mCategories; }
+ function getCacheTime() { return $this->mCacheTime; }
+ function getTitleText() { return $this->mTitleText; }
+ function &getLinks() { return $this->mLinks; }
+ function &getTemplates() { return $this->mTemplates; }
+ function &getImages() { return $this->mImages; }
+ function &getExternalLinks() { return $this->mExternalLinks; }
+ function getNoGallery() { return $this->mNoGallery; }
+ function getSubtitle() { return $this->mSubtitle; }
+
+ function containsOldMagic() { return $this->mContainsOldMagic; }
+ function setText( $text ) { return wfSetVar( $this->mText, $text ); }
+ function setLanguageLinks( $ll ) { return wfSetVar( $this->mLanguageLinks, $ll ); }
+ function setCategoryLinks( $cl ) { return wfSetVar( $this->mCategories, $cl ); }
+ function setContainsOldMagic( $com ) { return wfSetVar( $this->mContainsOldMagic, $com ); }
+ function setCacheTime( $t ) { return wfSetVar( $this->mCacheTime, $t ); }
+ function setTitleText( $t ) { return wfSetVar($this->mTitleText, $t); }
+ function setSubtitle( $st ) { return wfSetVar( $this->mSubtitle, $st ); }
+
+ function addCategory( $c, $sort ) { $this->mCategories[$c] = $sort; }
+ function addImage( $name ) { $this->mImages[$name] = 1; }
+ function addLanguageLink( $t ) { $this->mLanguageLinks[] = $t; }
+ function addExternalLink( $url ) { $this->mExternalLinks[$url] = 1; }
+
+ function setNewSection( $value ) {
+ $this->mNewSection = (bool)$value;
+ }
+ function getNewSection() {
+ return (bool)$this->mNewSection;
+ }
+
+ function addLink( $title, $id = null ) {
+ $ns = $title->getNamespace();
+ $dbk = $title->getDBkey();
+ if ( !isset( $this->mLinks[$ns] ) ) {
+ $this->mLinks[$ns] = array();
+ }
+ if ( is_null( $id ) ) {
+ $id = $title->getArticleID();
+ }
+ $this->mLinks[$ns][$dbk] = $id;
+ }
+
+ function addTemplate( $title, $id ) {
+ $ns = $title->getNamespace();
+ $dbk = $title->getDBkey();
+ if ( !isset( $this->mTemplates[$ns] ) ) {
+ $this->mTemplates[$ns] = array();
+ }
+ $this->mTemplates[$ns][$dbk] = $id;
+ }
+
+ /**
+ * Return true if this cached output object predates the global or
+ * per-article cache invalidation timestamps, or if it comes from
+ * an incompatible older version.
+ *
+ * @param string $touched the affected article's last touched timestamp
+ * @return bool
+ * @public
+ */
+ function expired( $touched ) {
+ global $wgCacheEpoch;
+ return $this->getCacheTime() == -1 || // parser says it's uncacheable
+ $this->getCacheTime() < $touched ||
+ $this->getCacheTime() <= $wgCacheEpoch ||
+ !isset( $this->mVersion ) ||
+ version_compare( $this->mVersion, Parser::VERSION, "lt" );
+ }
+
+ /**
+ * Add some text to the <head>.
+ * If $tag is set, the section with that tag will only be included once
+ * in a given page.
+ */
+ function addHeadItem( $section, $tag = false ) {
+ if ( $tag !== false ) {
+ $this->mHeadItems[$tag] = $section;
+ } else {
+ $this->mHeadItems[] = $section;
+ }
+ }
+}
+
+?>
diff --git a/includes/PatrolLog.php b/includes/PatrolLog.php
new file mode 100644
index 00000000..a22839ff
--- /dev/null
+++ b/includes/PatrolLog.php
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * Class containing static functions for working with
+ * logs of patrol events
+ *
+ * @author Rob Church <robchur@gmail.com>
+ */
+class PatrolLog {
+
+ /**
+ * Record a log event for a change being patrolled
+ *
+ * @param mixed $change Change identifier or RecentChange object
+ * @param bool $auto Was this patrol event automatic?
+ */
+ public static function record( $change, $auto = false ) {
+ if( !( is_object( $change ) && $change instanceof RecentChange ) ) {
+ $change = RecentChange::newFromId( $change );
+ if( !is_object( $change ) )
+ return false;
+ }
+ $title = Title::makeTitleSafe( $change->getAttribute( 'rc_namespace' ),
+ $change->getAttribute( 'rc_title' ) );
+ if( is_object( $title ) ) {
+ $params = self::buildParams( $change, $auto );
+ $log = new LogPage( 'patrol', false ); # False suppresses RC entries
+ $log->addEntry( 'patrol', $title, '', $params );
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Generate the log action text corresponding to a patrol log item
+ *
+ * @param Title $title Title of the page that was patrolled
+ * @param array $params Log parameters (from logging.log_params)
+ * @param Skin $skin Skin to use for building links, etc.
+ * @return string
+ */
+ public static function makeActionText( $title, $params, $skin ) {
+ # This is a bit of a hack, but...if $skin is not a Skin, then *do nothing*
+ # -- this is fine, because the action text we would be queried for under
+ # these conditions would have gone into recentchanges, which we aren't
+ # supposed to be updating
+ if( is_object( $skin ) ) {
+ list( $cur, $prev, $auto ) = $params;
+ # Standard link to the page in question
+ $link = $skin->makeLinkObj( $title );
+ # Generate a diff link
+ $bits[] = 'oldid=' . urlencode( $cur );
+ $bits[] = 'diff=prev';
+ $bits = implode( '&', $bits );
+ $diff = $skin->makeLinkObj( $title, htmlspecialchars( wfMsg( 'patrol-log-diff', $cur ) ), $bits );
+ # Indicate whether or not the patrolling was automatic
+ $auto = $auto ? wfMsgHtml( 'patrol-log-auto' ) : '';
+ # Put it all together
+ return wfMsgHtml( 'patrol-log-line', $diff, $link, $auto );
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * Prepare log parameters for a patrolled change
+ *
+ * @param RecentChange $change RecentChange to represent
+ * @param bool $auto Whether the patrol event was automatic
+ * @return array
+ */
+ private static function buildParams( $change, $auto ) {
+ return array(
+ $change->getAttribute( 'rc_this_oldid' ),
+ $change->getAttribute( 'rc_last_oldid' ),
+ (int)$auto
+ );
+ }
+
+}
+
+?> \ No newline at end of file
diff --git a/includes/Profiler.php b/includes/Profiler.php
index 30cda63f..da3a82ed 100644
--- a/includes/Profiler.php
+++ b/includes/Profiler.php
@@ -1,7 +1,6 @@
<?php
/**
* This file is only included if profiling is enabled
- * @package MediaWiki
*/
$wgProfiling = true;
@@ -41,14 +40,13 @@ if (!function_exists('memory_get_usage')) {
/**
* @todo document
- * @package MediaWiki
+ * @addtogroup Profiler
*/
class Profiler {
var $mStack = array (), $mWorkStack = array (), $mCollated = array ();
var $mCalls = array (), $mTotals = array ();
- function Profiler()
- {
+ function __construct() {
// Push an entry for the pre-profile setup time onto the stack
global $wgRequestTime;
if ( !empty( $wgRequestTime ) ) {
@@ -57,7 +55,6 @@ class Profiler {
} else {
$this->profileIn( '-total' );
}
-
}
function profileIn($functionname) {
@@ -291,7 +288,7 @@ class Profiler {
* @return Integer
* @private
*/
- function calltreeCount(& $stack, $start) {
+ function calltreeCount($stack, $start) {
$level = $stack[$start][1];
$count = 0;
for ($i = $start -1; $i >= 0 && $stack[$i][1] > $level; $i --) {
@@ -308,7 +305,7 @@ class Profiler {
global $wguname, $wgProfilePerHost;
$fname = 'Profiler::logToDB';
- $dbw = & wfGetDB(DB_MASTER);
+ $dbw = wfGetDB(DB_MASTER);
if (!is_object($dbw))
return false;
$errorState = $dbw->ignoreErrors( true );
diff --git a/includes/ProfilerSimple.php b/includes/ProfilerSimple.php
index e69bfc47..f43c7dfc 100644
--- a/includes/ProfilerSimple.php
+++ b/includes/ProfilerSimple.php
@@ -1,20 +1,17 @@
<?php
-/**
- * Simple profiler base class
- * @package MediaWiki
- */
-/**
- * @todo document
- * @package MediaWiki
- */
require_once(dirname(__FILE__).'/Profiler.php');
+/**
+ * Simple profiler base class.
+ * @todo document methods (?)
+ * @addtogroup Profiler
+ */
class ProfilerSimple extends Profiler {
var $mMinimumTime = 0;
var $mProfileID = false;
- function ProfilerSimple() {
+ function __construct() {
global $wgRequestTime,$wgRUstart;
if (!empty($wgRequestTime) && !empty($wgRUstart)) {
$this->mWorkStack[] = array( '-total', 0, $wgRequestTime,$this->getCpuTime($wgRUstart));
@@ -26,7 +23,6 @@ class ProfilerSimple extends Profiler {
if (!is_array($entry)) {
$entry = array('cpu'=> 0.0, 'cpu_sq' => 0.0, 'real' => 0.0, 'real_sq' => 0.0, 'count' => 0);
$this->mCollated["-setup"] =& $entry;
-
}
$entry['cpu'] += $elapsedcpu;
$entry['cpu_sq'] += $elapsedcpu*$elapsedcpu;
@@ -57,7 +53,7 @@ class ProfilerSimple extends Profiler {
if ($wgDebugFunctionEntry) {
$this->debug(str_repeat(' ', count($this->mWorkStack)).'Entering '.$functionname."\n");
}
- $this->mWorkStack[] = array($functionname, count( $this->mWorkStack ), microtime(true), $this->getCpuTime());
+ $this->mWorkStack[] = array($functionname, count( $this->mWorkStack ), microtime(true), $this->getCpuTime());
}
function profileOut($functionname) {
@@ -87,7 +83,6 @@ class ProfilerSimple extends Profiler {
if (!is_array($entry)) {
$entry = array('cpu'=> 0.0, 'cpu_sq' => 0.0, 'real' => 0.0, 'real_sq' => 0.0, 'count' => 0);
$this->mCollated[$functionname] =& $entry;
-
}
$entry['cpu'] += $elapsedcpu;
$entry['cpu_sq'] += $elapsedcpu*$elapsedcpu;
diff --git a/includes/ProfilerSimpleUDP.php b/includes/ProfilerSimpleUDP.php
index a8527c38..500f1cbd 100644
--- a/includes/ProfilerSimpleUDP.php
+++ b/includes/ProfilerSimpleUDP.php
@@ -1,11 +1,13 @@
<?php
-/* ProfilerSimpleUDP class, that sends out messages for 'udpprofile' daemon
- (the one from wikipedia/udpprofile CVS )
-*/
require_once(dirname(__FILE__).'/Profiler.php');
require_once(dirname(__FILE__).'/ProfilerSimple.php');
+/**
+ * ProfilerSimpleUDP class, that sends out messages for 'udpprofile' daemon
+ * (the one from mediawiki/trunk/udpprofile SVN )
+ * @addtogroup Profiler
+ */
class ProfilerSimpleUDP extends ProfilerSimple {
function getFunctionReport() {
global $wgUDPProfilerHost;
@@ -15,8 +17,7 @@ class ProfilerSimpleUDP extends ProfilerSimple {
# Less than minimum, ignore
return;
}
-
-
+
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$plength=0;
$packet="";
diff --git a/includes/ProtectionForm.php b/includes/ProtectionForm.php
index f96262fe..3cafbd55 100644
--- a/includes/ProtectionForm.php
+++ b/includes/ProtectionForm.php
@@ -17,27 +17,42 @@
* 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
- *
- * @package MediaWiki
- * @subpackage SpecialPage
*/
+/**
+ * @todo document, briefly.
+ * @addtogroup SpecialPage
+ */
class ProtectionForm {
var $mRestrictions = array();
var $mReason = '';
+ var $mCascade = false;
+ var $mExpiry = null;
- function ProtectionForm( &$article ) {
+ function __construct( &$article ) {
global $wgRequest, $wgUser;
global $wgRestrictionTypes, $wgRestrictionLevels;
$this->mArticle =& $article;
$this->mTitle =& $article->mTitle;
if( $this->mTitle ) {
+ $this->mTitle->loadRestrictions();
+
foreach( $wgRestrictionTypes as $action ) {
// Fixme: this form currently requires individual selections,
// but the db allows multiples separated by commas.
$this->mRestrictions[$action] = implode( '', $this->mTitle->getRestrictions( $action ) );
}
+
+ $this->mCascade = $this->mTitle->areRestrictionsCascading();
+
+ if ( $this->mTitle->mRestrictionsExpiry == 'infinity' ) {
+ $this->mExpiry = 'infinite';
+ } else if ( strlen($this->mTitle->mRestrictionsExpiry) == 0 ) {
+ $this->mExpiry = '';
+ } else {
+ $this->mExpiry = wfTimestamp( TS_RFC2822, $this->mTitle->mRestrictionsExpiry );
+ }
}
// The form will be available in read-only to show levels.
@@ -48,6 +63,9 @@ class ProtectionForm {
if( $wgRequest->wasPosted() ) {
$this->mReason = $wgRequest->getText( 'mwProtect-reason' );
+ $this->mCascade = $wgRequest->getBool( 'mwProtect-cascade' );
+ $this->mExpiry = $wgRequest->getText( 'mwProtect-expiry' );
+
foreach( $wgRestrictionTypes as $action ) {
$val = $wgRequest->getVal( "mwProtect-level-$action" );
if( isset( $val ) && in_array( $val, $wgRestrictionLevels ) ) {
@@ -56,9 +74,21 @@ class ProtectionForm {
}
}
}
+
+ function execute() {
+ global $wgRequest;
+ if( $wgRequest->wasPosted() ) {
+ if( $this->save() ) {
+ global $wgOut;
+ $wgOut->redirect( $this->mTitle->getFullUrl() );
+ }
+ } else {
+ $this->show();
+ }
+ }
- function show() {
- global $wgOut;
+ function show( $err = null ) {
+ global $wgOut, $wgUser;
$wgOut->setRobotpolicy( 'noindex,nofollow' );
@@ -69,17 +99,47 @@ class ProtectionForm {
return;
}
- if( $this->save() ) {
- $wgOut->redirect( $this->mTitle->getFullUrl() );
- return;
+ list( $cascadeSources, $restrictions ) = $this->mTitle->getCascadeProtectionSources();
+
+ if ( "" != $err ) {
+ $wgOut->setSubtitle( wfMsgHtml( 'formerror' ) );
+ $wgOut->addHTML( "<p class='error'>{$err}</p>\n" );
+ }
+
+ if ( $cascadeSources && count($cascadeSources) > 0 ) {
+ $titles = '';
+
+ foreach ( $cascadeSources as $title ) {
+ $titles .= '* [[:' . $title->getPrefixedText() . "]]\n";
+ }
+
+ $notice = wfMsgExt( 'protect-cascadeon', array('parsemag'), count($cascadeSources) ) . "\r\n$titles";
+
+ $wgOut->addWikiText( $notice );
}
$wgOut->setPageTitle( wfMsg( 'confirmprotect' ) );
$wgOut->setSubtitle( wfMsg( 'protectsub', $this->mTitle->getPrefixedText() ) );
- $wgOut->addWikiText(
- wfMsg( $this->disabled ? "protect-viewtext" : "protect-text",
- wfEscapeWikiText( $this->mTitle->getPrefixedText() ) ) );
+ # Show an appropriate message if the user isn't allowed or able to change
+ # the protection settings at this time
+ if( $this->disabled ) {
+ if( $wgUser->isAllowed( 'protect' ) ) {
+ if( $wgUser->isBlocked() ) {
+ # Blocked
+ $message = 'protect-locked-blocked';
+ } else {
+ # Database lock
+ $message = 'protect-locked-dblock';
+ }
+ } else {
+ # Permission error
+ $message = 'protect-locked-access';
+ }
+ } else {
+ $message = 'protect-text';
+ }
+ $wgOut->addWikiText( wfMsg( $message, wfEscapeWikiText( $this->mTitle->getPrefixedText() ) ) );
$wgOut->addHTML( $this->buildForm() );
@@ -88,20 +148,43 @@ class ProtectionForm {
function save() {
global $wgRequest, $wgUser, $wgOut;
- if( !$wgRequest->wasPosted() ) {
- return false;
- }
-
+
if( $this->disabled ) {
+ $this->show();
return false;
}
$token = $wgRequest->getVal( 'wpEditToken' );
if( !$wgUser->matchEditToken( $token ) ) {
- throw new FatalError( wfMsg( 'sessionfailure' ) );
+ $this->show( wfMsg( 'sessionfailure' ) );
+ return false;
}
- $ok = $this->mArticle->updateRestrictions( $this->mRestrictions, $this->mReason );
+ if ( strlen( $this->mExpiry ) == 0 ) {
+ $this->mExpiry = 'infinite';
+ }
+
+ if ( $this->mExpiry == 'infinite' || $this->mExpiry == 'indefinite' ) {
+ $expiry = Block::infinity();
+ } else {
+ # Convert GNU-style date, on error returns -1 for PHP <5.1 and false for PHP >=5.1
+ $expiry = strtotime( $this->mExpiry );
+
+ if ( $expiry < 0 || $expiry === false ) {
+ $this->show( wfMsg( 'protect_expiry_invalid' ) );
+ return false;
+ }
+
+ $expiry = wfTimestamp( TS_MW, $expiry );
+
+ if ( $expiry < wfTimestampNow() ) {
+ $this->show( wfMsg( 'protect_expiry_old' ) );
+ return false;
+ }
+
+ }
+
+ $ok = $this->mArticle->updateRestrictions( $this->mRestrictions, $this->mReason, $this->mCascade, $expiry );
if( !$ok ) {
throw new FatalError( "Unknown error at restriction save time." );
}
@@ -117,6 +200,7 @@ class ProtectionForm {
// The submission needs to reenable the move permission selector
// if it's in locked mode, or some browsers won't submit the data.
$out .= wfOpenElement( 'form', array(
+ 'id' => 'mw-Protect-Form',
'action' => $this->mTitle->getLocalUrl( 'action=protect' ),
'method' => 'post',
'onsubmit' => 'protectEnable(true)' ) );
@@ -148,13 +232,25 @@ class ProtectionForm {
$out .= "</tbody>\n";
$out .= "</table>\n";
+ global $wgEnableCascadingProtection;
+
+ if ($wgEnableCascadingProtection)
+ $out .= $this->buildCascadeInput();
+
+ $out .= "<table>\n";
+ $out .= "<tbody>\n";
+
+ $out .= $this->buildExpiryInput();
+
if( !$this->disabled ) {
- $out .= "<table>\n";
- $out .= "<tbody>\n";
$out .= "<tr><td>" . $this->buildReasonInput() . "</td></tr>\n";
$out .= "<tr><td></td><td>" . $this->buildSubmit() . "</td></tr>\n";
- $out .= "</tbody>\n";
- $out .= "</table>\n";
+ }
+
+ $out .= "</tbody>\n";
+ $out .= "</table>\n";
+
+ if ( !$this->disabled ) {
$out .= "</form>\n";
$out .= $this->buildCleanupScript();
}
@@ -202,11 +298,38 @@ class ProtectionForm {
wfElement( 'input', array(
'size' => 60,
'name' => $id,
- 'id' => $id ) );
+ 'id' => $id,
+ 'value' => $this->mReason ) );
+ }
+
+ function buildCascadeInput() {
+ $id = 'mwProtect-cascade';
+ $ci = wfCheckLabel( wfMsg( 'protect-cascade' ), $id, $id, $this->mCascade, $this->disabledAttrib);
+ return $ci;
+ }
+
+ function buildExpiryInput() {
+ $id = 'mwProtect-expiry';
+
+ $ci = "<tr> <td align=\"right\">";
+ $ci .= wfElement( 'label', array (
+ 'id' => "$id-label",
+ 'for' => $id ),
+ wfMsg( 'protectexpiry' ) );
+ $ci .= "</td> <td align=\"left\">";
+ $ci .= wfElement( 'input', array(
+ 'size' => 60,
+ 'name' => $id,
+ 'id' => $id,
+ 'value' => $this->mExpiry ) + $this->disabledAttrib );
+ $ci .= "</td></tr>";
+
+ return $ci;
}
function buildSubmit() {
return wfElement( 'input', array(
+ 'id' => 'mw-Protect-submit',
'type' => 'submit',
'value' => wfMsg( 'confirm' ) ) );
}
@@ -219,8 +342,17 @@ class ProtectionForm {
}
function buildCleanupScript() {
- return '<script type="text/javascript">protectInitialize("mwProtectSet","' .
- wfEscapeJsString( wfMsg( 'protect-unchain' ) ) . '")</script>';
+ global $wgRestrictionLevels, $wgGroupPermissions;
+ $script = 'var wgCascadeableLevels=';
+ $CascadeableLevels = array();
+ foreach( $wgRestrictionLevels as $key ) {
+ if ( isset($wgGroupPermissions[$key]['protect']) && $wgGroupPermissions[$key]['protect'] ) {
+ $CascadeableLevels[]="'" . wfEscapeJsString($key) . "'";
+ }
+ }
+ $script .= "[" . implode(',',$CascadeableLevels) . "];\n";
+ $script .= 'protectInitialize("mwProtectSet","' . wfEscapeJsString( wfMsg( 'protect-unchain' ) ) . '")';
+ return '<script type="text/javascript">' . $script . '</script>';
}
/**
@@ -239,5 +371,4 @@ class ProtectionForm {
}
}
-
?>
diff --git a/includes/ProxyTools.php b/includes/ProxyTools.php
index 22ea4947..f72b640f 100644
--- a/includes/ProxyTools.php
+++ b/includes/ProxyTools.php
@@ -1,27 +1,63 @@
<?php
/**
* Functions for dealing with proxies
- * @package MediaWiki
*/
+/**
+ * Extracts the XFF string from the request header
+ * Checks first for "X-Forwarded-For", then "Client-ip"
+ * Note: headers are spoofable
+ * @return string
+ */
function wfGetForwardedFor() {
if( function_exists( 'apache_request_headers' ) ) {
// More reliable than $_SERVER due to case and -/_ folding
$set = apache_request_headers();
$index = 'X-Forwarded-For';
+ $index2 = 'Client-ip';
} else {
// Subject to spoofing with headers like X_Forwarded_For
$set = $_SERVER;
$index = 'HTTP_X_FORWARDED_FOR';
+ $index2 = 'CLIENT-IP';
}
+ #Try a couple of headers
if( isset( $set[$index] ) ) {
return $set[$index];
+ } else if( isset( $set[$index2] ) ) {
+ return $set[$index2];
} else {
return null;
}
}
-/** Work out the IP address based on various globals */
+/**
+ * Returns the browser/OS data from the request header
+ * Note: headers are spoofable
+ * @return string
+ */
+function wfGetAgent() {
+ if( function_exists( 'apache_request_headers' ) ) {
+ // More reliable than $_SERVER due to case and -/_ folding
+ $set = apache_request_headers();
+ $index = 'User-Agent';
+ } else {
+ // Subject to spoofing with headers like X_Forwarded_For
+ $set = $_SERVER;
+ $index = 'HTTP_USER_AGENT';
+ }
+ if( isset( $set[$index] ) ) {
+ return $set[$index];
+ } else {
+ return '';
+ }
+}
+
+/**
+ * Work out the IP address based on various globals
+ * For trusted proxies, use the XFF client IP (first of the chain)
+ * @return string
+ */
function wfGetIP() {
global $wgIP;
@@ -66,6 +102,13 @@ function wfGetIP() {
return $ip;
}
+/**
+ * Checks if an IP is a trusted proxy providor
+ * Useful to tell if X-Fowarded-For data is possibly bogus
+ * Squid cache servers for the site and AOL are whitelisted
+ * @param string $ip
+ * @return bool
+ */
function wfIsTrustedProxy( $ip ) {
global $wgSquidServers, $wgSquidServersNoPurge;
@@ -130,6 +173,7 @@ function wfProxyCheck() {
/**
* Convert a network specification in CIDR notation to an integer network and a number of bits
+ * @return array(string, int)
*/
function wfParseCIDR( $range ) {
return IP::parseCIDR( $range );
@@ -137,6 +181,7 @@ function wfParseCIDR( $range ) {
/**
* Check if an IP address is in the local proxy list
+ * @return bool
*/
function wfIsLocallyBlockedProxy( $ip ) {
global $wgProxyList;
@@ -169,6 +214,7 @@ function wfIsLocallyBlockedProxy( $ip ) {
/**
* TODO: move this list to the database in a global IP info table incorporating
* trusted ISP proxies, blocked IP addresses and open proxies.
+ * @return bool
*/
function wfIsAOLProxy( $ip ) {
$ranges = array(
diff --git a/includes/QueryPage.php b/includes/QueryPage.php
index ff6355e7..143c8be6 100644
--- a/includes/QueryPage.php
+++ b/includes/QueryPage.php
@@ -1,42 +1,45 @@
<?php
/**
* Contain a class for special pages
- * @package MediaWiki
*/
/**
- * List of query page classes and their associated special pages, for periodic update purposes
+ * List of query page classes and their associated special pages,
+ * for periodic updates.
+ *
+ * DO NOT CHANGE THIS LIST without testing that
+ * maintenance/updateSpecialPages.php still works.
*/
global $wgQueryPages; // not redundant
$wgQueryPages = array(
// QueryPage subclass Special page name Limit (false for none, none for the default)
//----------------------------------------------------------------------------
- array( 'AncientPagesPage', 'Ancientpages' ),
- array( 'BrokenRedirectsPage', 'BrokenRedirects' ),
- array( 'CategoriesPage', 'Categories' ),
- array( 'DeadendPagesPage', 'Deadendpages' ),
- array( 'DisambiguationsPage', 'Disambiguations' ),
- array( 'DoubleRedirectsPage', 'DoubleRedirects' ),
- array( 'ListUsersPage', 'Listusers' ),
- array( 'ListredirectsPage', 'Listredirects' ),
- array( 'LonelyPagesPage', 'Lonelypages' ),
- array( 'LongPagesPage', 'Longpages' ),
- array( 'MostcategoriesPage', 'Mostcategories' ),
- array( 'MostimagesPage', 'Mostimages' ),
- array( 'MostlinkedCategoriesPage', 'Mostlinkedcategories' ),
- array( 'MostlinkedPage', 'Mostlinked' ),
- array( 'MostrevisionsPage', 'Mostrevisions' ),
- array( 'NewPagesPage', 'Newpages' ),
- array( 'ShortPagesPage', 'Shortpages' ),
- array( 'UncategorizedCategoriesPage', 'Uncategorizedcategories' ),
- array( 'UncategorizedPagesPage', 'Uncategorizedpages' ),
- array( 'UncategorizedImagesPage', 'Uncategorizedimages' ),
- array( 'UnusedCategoriesPage', 'Unusedcategories' ),
- array( 'UnusedimagesPage', 'Unusedimages' ),
- array( 'WantedCategoriesPage', 'Wantedcategories' ),
- array( 'WantedPagesPage', 'Wantedpages' ),
- array( 'UnwatchedPagesPage', 'Unwatchedpages' ),
- array( 'UnusedtemplatesPage', 'Unusedtemplates' ),
+ array( 'AncientPagesPage', 'Ancientpages' ),
+ array( 'BrokenRedirectsPage', 'BrokenRedirects' ),
+ array( 'DeadendPagesPage', 'Deadendpages' ),
+ array( 'DisambiguationsPage', 'Disambiguations' ),
+ array( 'DoubleRedirectsPage', 'DoubleRedirects' ),
+ array( 'ListredirectsPage', 'Listredirects' ),
+ array( 'LonelyPagesPage', 'Lonelypages' ),
+ array( 'LongPagesPage', 'Longpages' ),
+ array( 'MostcategoriesPage', 'Mostcategories' ),
+ array( 'MostimagesPage', 'Mostimages' ),
+ array( 'MostlinkedCategoriesPage', 'Mostlinkedcategories' ),
+ array( 'MostlinkedPage', 'Mostlinked' ),
+ array( 'MostrevisionsPage', 'Mostrevisions' ),
+ array( 'FewestrevisionsPage', 'Fewestrevisions' ),
+ array( 'NewPagesPage', 'Newpages' ),
+ array( 'ShortPagesPage', 'Shortpages' ),
+ array( 'UncategorizedCategoriesPage', 'Uncategorizedcategories' ),
+ array( 'UncategorizedPagesPage', 'Uncategorizedpages' ),
+ array( 'UncategorizedImagesPage', 'Uncategorizedimages' ),
+ array( 'UnusedCategoriesPage', 'Unusedcategories' ),
+ array( 'UnusedimagesPage', 'Unusedimages' ),
+ array( 'WantedCategoriesPage', 'Wantedcategories' ),
+ array( 'WantedPagesPage', 'Wantedpages' ),
+ array( 'UnwatchedPagesPage', 'Unwatchedpages' ),
+ array( 'UnusedtemplatesPage', 'Unusedtemplates' ),
+ array( 'WithoutInterwikiPage', 'Withoutinterwiki' ),
);
wfRunHooks( 'wgQueryPages', array( &$wgQueryPages ) );
@@ -49,8 +52,7 @@ if ( !$wgDisableCounters )
* This is a class for doing query pages; since they're almost all the same,
* we factor out some of the functionality into a superclass, and let
* subclasses derive from it.
- *
- * @package MediaWiki
+ * @addtogroup SpecialPage
*/
class QueryPage {
/**
@@ -59,7 +61,7 @@ class QueryPage {
* @var bool
*/
var $listoutput = false;
-
+
/**
* The offset and limit in use, as passed to the query() function
*
@@ -197,8 +199,8 @@ class QueryPage {
*/
function recache( $limit, $ignoreErrors = true ) {
$fname = get_class($this) . '::recache';
- $dbw =& wfGetDB( DB_MASTER );
- $dbr =& wfGetDB( DB_SLAVE, array( $this->getName(), 'QueryPage::recache', 'vslow' ) );
+ $dbw = wfGetDB( DB_MASTER );
+ $dbr = wfGetDB( DB_SLAVE, array( $this->getName(), 'QueryPage::recache', 'vslow' ) );
if ( !$dbw || !$dbr ) {
return false;
}
@@ -282,7 +284,7 @@ class QueryPage {
$sname = $this->getName();
$fname = get_class($this) . '::doQuery';
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$wgOut->setSyndicated( $this->isSyndicated() );
@@ -306,7 +308,7 @@ class QueryPage {
$updated = $wgLang->timeAndDate( $tRow->qci_timestamp, true, true );
$cacheNotice = wfMsg( 'perfcachedts', $updated );
$wgOut->addMeta( 'Data-Cache-Time', $tRow->qci_timestamp );
- $wgOut->addScript( '<script language="JavaScript">var dataCacheTime = \'' . $tRow->qci_timestamp . '\';</script>' );
+ $wgOut->addInlineScript( "var dataCacheTime = '{$tRow->qci_timestamp}';" );
} else {
$cacheNotice = wfMsg( 'perfcached' );
}
@@ -330,58 +332,99 @@ class QueryPage {
$num = $dbr->numRows($res);
$this->preprocessResults( $dbr, $res );
-
- $sk = $wgUser->getSkin( );
-
- if($shownavigation) {
- $wgOut->addHTML( $this->getPageHeader() );
- $top = wfShowingResults( $offset, $num);
- $wgOut->addHTML( "<p>{$top}\n" );
-
- # often disable 'next' link when we reach the end
- $atend = $num < $limit;
-
- $sl = wfViewPrevNext( $offset, $limit ,
- $wgContLang->specialPage( $sname ),
- wfArrayToCGI( $this->linkParameters() ), $atend );
- $wgOut->addHTML( "<br />{$sl}</p>\n" );
+ $sk = $wgUser->getSkin();
+
+ # Top header and navigation
+ if( $shownavigation ) {
+ $wgOut->addHtml( $this->getPageHeader() );
+ if( $num > 0 ) {
+ $wgOut->addHtml( '<p>' . wfShowingResults( $offset, $num ) . '</p>' );
+ # Disable the "next" link when we reach the end
+ $paging = wfViewPrevNext( $offset, $limit, $wgContLang->specialPage( $sname ),
+ wfArrayToCGI( $this->linkParameters() ), ( $num < $limit ) );
+ $wgOut->addHtml( '<p>' . $paging . '</p>' );
+ } else {
+ # No results to show, so don't bother with "showing X of Y" etc.
+ # -- just let the user know and give up now
+ $wgOut->addHtml( '<p>' . wfMsgHtml( 'specialpage-empty' ) . '</p>' );
+ return;
+ }
+ }
+
+ # The actual results; specialist subclasses will want to handle this
+ # with more than a straight list, so we hand them the info, plus
+ # an OutputPage, and let them get on with it
+ $this->outputResults( $wgOut,
+ $wgUser->getSkin(),
+ $dbr, # Should use a ResultWrapper for this
+ $res,
+ $dbr->numRows( $res ),
+ $offset );
+
+ # Repeat the paging links at the bottom
+ if( $shownavigation ) {
+ $wgOut->addHtml( '<p>' . $paging . '</p>' );
}
- if ( $num > 0 ) {
- $s = array();
- if ( ! $this->listoutput )
- $s[] = $this->openList( $offset );
-
- # Only read at most $num rows, because $res may contain the whole 1000
- for ( $i = 0; $i < $num && $obj = $dbr->fetchObject( $res ); $i++ ) {
- $format = $this->formatResult( $sk, $obj );
- if ( $format ) {
- $attr = ( isset ( $obj->usepatrol ) && $obj->usepatrol &&
- $obj->patrolled == 0 ) ? ' class="not-patrolled"' : '';
- $s[] = $this->listoutput ? $format : "<li{$attr}>{$format}</li>\n";
+
+ return $num;
+ }
+
+ /**
+ * Format and output report results using the given information plus
+ * OutputPage
+ *
+ * @param OutputPage $out OutputPage to print to
+ * @param Skin $skin User skin to use
+ * @param Database $dbr Database (read) connection to use
+ * @param int $res Result pointer
+ * @param int $num Number of available result rows
+ * @param int $offset Paging offset
+ */
+ protected function outputResults( $out, $skin, $dbr, $res, $num, $offset ) {
+ global $wgContLang;
+
+ if( $num > 0 ) {
+ $html = array();
+ if( !$this->listoutput )
+ $html[] = $this->openList( $offset );
+
+ # $res might contain the whole 1,000 rows, so we read up to
+ # $num [should update this to use a Pager]
+ for( $i = 0; $i < $num && $row = $dbr->fetchObject( $res ); $i++ ) {
+ $line = $this->formatResult( $skin, $row );
+ if( $line ) {
+ $attr = ( isset( $row->usepatrol ) && $row->usepatrol && $row->patrolled == 0 )
+ ? ' class="not-patrolled"'
+ : '';
+ $html[] = $this->listoutput
+ ? $format
+ : "<li{$attr}>{$line}</li>\n";
}
}
-
- if($this->tryLastResult()) {
- // flush the very last result
- $obj = null;
- $format = $this->formatResult( $sk, $obj );
- if( $format ) {
- $attr = ( isset ( $obj->usepatrol ) && $obj->usepatrol &&
- $obj->patrolled == 0 ) ? ' class="not-patrolled"' : '';
- $s[] = "<li{$attr}>{$format}</li>\n";
+
+ # Flush the final result
+ if( $this->tryLastResult() ) {
+ $row = null;
+ $line = $this->formatResult( $skin, $row );
+ if( $line ) {
+ $attr = ( isset( $row->usepatrol ) && $row->usepatrol && $row->patrolled == 0 )
+ ? ' class="not-patrolled"'
+ : '';
+ $html[] = $this->listoutput
+ ? $format
+ : "<li{$attr}>{$line}</li>\n";
}
}
-
- $dbr->freeResult( $res );
- if ( ! $this->listoutput )
- $s[] = $this->closeList();
- $str = $this->listoutput ? $wgContLang->listToText( $s ) : implode( '', $s );
- $wgOut->addHTML( $str );
- }
- if($shownavigation) {
- $wgOut->addHTML( "<p>{$sl}</p>\n" );
+
+ if( !$this->listoutput )
+ $html[] = $this->closeList();
+
+ $html = $this->listoutput
+ ? $wgContLang->listToText( $html )
+ : implode( '', $html );
+
+ $out->addHtml( $html );
}
- return $num;
}
function openList( $offset ) {
@@ -411,7 +454,7 @@ class QueryPage {
$this->feedUrl() );
$feed->outHeader();
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$sql = $this->getSQL() . $this->getOrder();
$sql = $dbr->limitResult( $sql, $limit, 0 );
$res = $dbr->query( $sql, 'QueryPage::doFeed' );
@@ -482,20 +525,4 @@ class QueryPage {
}
}
-/**
- * This is a subclass for very simple queries that are just looking for page
- * titles that match some criteria. It formats each result item as a link to
- * that page.
- *
- * @package MediaWiki
- */
-class PageQueryPage extends QueryPage {
-
- function formatResult( $skin, $result ) {
- global $wgContLang;
- $nt = Title::makeTitle( $result->namespace, $result->title );
- return $skin->makeKnownLinkObj( $nt, htmlspecialchars( $wgContLang->convert( $nt->getPrefixedText() ) ) );
- }
-}
-
?>
diff --git a/includes/RawPage.php b/includes/RawPage.php
index a0b76886..93484829 100644
--- a/includes/RawPage.php
+++ b/includes/RawPage.php
@@ -7,12 +7,11 @@
* License: GPL (http://www.gnu.org/copyleft/gpl.html)
*
* @author Gabriel Wicke <wicke@wikidev.net>
- * @package MediaWiki
*/
/**
- * @todo document
- * @package MediaWiki
+ * A simple method to retrieve the plain source of an article,
+ * using "action=raw" in the GET request string.
*/
class RawPage {
var $mArticle, $mTitle, $mRequest;
@@ -20,9 +19,8 @@ class RawPage {
var $mSmaxage, $mMaxage;
var $mContentType, $mExpandTemplates;
- function RawPage( &$article, $request = false ) {
+ function __construct( &$article, $request = false ) {
global $wgRequest, $wgInputEncoding, $wgSquidMaxage, $wgJsMimeType;
- global $wgUser;
$allowedCTypes = array('text/x-wiki', $wgJsMimeType, 'text/css', 'application/x-zope-edit');
$this->mArticle =& $article;
@@ -39,7 +37,7 @@ class RawPage {
$maxage = $this->mRequest->getInt( 'maxage', $wgSquidMaxage );
$this->mExpandTemplates = $this->mRequest->getVal( 'templates' ) === 'expand';
$this->mUseMessageCache = $this->mRequest->getBool( 'usemsgcache' );
-
+
$oldid = $this->mRequest->getInt( 'oldid' );
switch ( $wgRequest->getText( 'direction' ) ) {
case 'next':
@@ -85,8 +83,7 @@ class RawPage {
// Output may contain user-specific data; vary for open sessions
$this->mPrivateCache = ( $this->mSmaxage == 0 ) ||
- ( isset( $_COOKIE[ini_get( 'session.name' )] ) ||
- $wgUser->isLoggedIn() );
+ ( session_id() != '' );
if ( $ctype == '' or ! in_array( $ctype, $allowedCTypes ) ) {
$this->mContentType = 'text/x-wiki';
@@ -137,7 +134,13 @@ class RawPage {
# allow the client to cache this for 24 hours
$mode = $this->mPrivateCache ? 'private' : 'public';
header( 'Cache-Control: '.$mode.', s-maxage='.$this->mSmaxage.', max-age='.$this->mMaxage );
- echo $this->getRawText();
+ $text = $this->getRawText();
+
+ if( !wfRunHooks( 'RawPageViewBeforeOutput', array( &$this, &$text ) ) ) {
+ wfDebug( __METHOD__ . ': RawPageViewBeforeOutput hook broke raw page output.' );
+ }
+
+ echo $text;
$wgOut->disable();
}
@@ -189,6 +192,20 @@ class RawPage {
header( "HTTP/1.0 404 Not Found" );
}
+ // Special-case for empty CSS/JS
+ //
+ // Internet Explorer for Mac handles empty files badly;
+ // particularly so when keep-alive is active. It can lead
+ // to long timeouts as it seems to sit there waiting for
+ // more data that never comes.
+ //
+ // Give it a comment...
+ if( strlen( $text ) == 0 &&
+ ($this->mContentType == 'text/css' ||
+ $this->mContentType == 'text/javascript' ) ) {
+ return "/* Empty */";
+ }
+
return $this->parseArticleText( $text );
}
diff --git a/includes/RecentChange.php b/includes/RecentChange.php
index 1c7791c2..fced4343 100644
--- a/includes/RecentChange.php
+++ b/includes/RecentChange.php
@@ -1,7 +1,6 @@
<?php
/**
*
- * @package MediaWiki
*/
/**
@@ -39,7 +38,6 @@
* numberofWatchingusers
*
* @todo document functions and variables
- * @package MediaWiki
*/
class RecentChange
{
@@ -49,7 +47,7 @@ class RecentChange
# Factory methods
- /* static */ function newFromRow( $row )
+ public static function newFromRow( $row )
{
$rc = new RecentChange;
$rc->loadFromRow( $row );
@@ -72,7 +70,7 @@ class RecentChange
* @return RecentChange
*/
public static function newFromId( $rcid ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'recentchanges', '*', array( 'rc_id' => $rcid ), __METHOD__ );
if( $res && $dbr->numRows( $res ) > 0 ) {
$row = $dbr->fetchObject( $res );
@@ -118,7 +116,7 @@ class RecentChange
global $wgLocalInterwiki, $wgPutIPinRC, $wgRC2UDPAddress, $wgRC2UDPPort, $wgRC2UDPPrefix;
$fname = 'RecentChange::save';
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
if ( !is_array($this->mExtra) ) {
$this->mExtra = array();
}
@@ -216,7 +214,7 @@ class RecentChange
{
$fname = 'RecentChange::markPatrolled';
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->update( 'recentchanges',
array( /* SET */
@@ -504,6 +502,8 @@ class RecentChange
function getIRCLine() {
global $wgUseRCPatrol;
+ // FIXME: Would be good to replace these 2 extract() calls with something more explicit
+ // e.g. list ($rc_type, $rc_id) = array_values ($this->mAttribs); [or something like that]
extract($this->mAttribs);
extract($this->mExtra);
diff --git a/includes/Revision.php b/includes/Revision.php
index c5235e22..71f214e3 100644
--- a/includes/Revision.php
+++ b/includes/Revision.php
@@ -1,19 +1,17 @@
<?php
/**
- * @package MediaWiki
* @todo document
*/
/**
- * @package MediaWiki
* @todo document
*/
class Revision {
- const DELETED_TEXT = 1;
- const DELETED_COMMENT = 2;
- const DELETED_USER = 4;
+ const DELETED_TEXT = 1;
+ const DELETED_COMMENT = 2;
+ const DELETED_USER = 4;
const DELETED_RESTRICTED = 8;
-
+
/**
* Load a page revision from a given revision ID number.
* Returns null if no such revision can be found.
@@ -79,7 +77,7 @@ class Revision {
* @access public
* @static
*/
- public static function loadFromPageId( &$db, $pageid, $id = 0 ) {
+ public static function loadFromPageId( $db, $pageid, $id = 0 ) {
$conds=array('page_id=rev_page','rev_page'=>intval( $pageid ), 'page_id'=>intval( $pageid ));
if( $id ) {
$conds['rev_id']=intval($id);
@@ -145,10 +143,10 @@ class Revision {
* @static
*/
private static function newFromConds( $conditions ) {
- $db =& wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_SLAVE );
$row = Revision::loadFromConds( $db, $conditions );
if( is_null( $row ) ) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$row = Revision::loadFromConds( $dbw, $conditions );
}
return $row;
@@ -164,7 +162,7 @@ class Revision {
* @access private
* @static
*/
- private static function loadFromConds( &$db, $conditions ) {
+ private static function loadFromConds( $db, $conditions ) {
$res = Revision::fetchFromConds( $db, $conditions );
if( $res ) {
$row = $res->fetchObject();
@@ -226,7 +224,7 @@ class Revision {
* @access private
* @static
*/
- private static function fetchFromConds( &$db, $conditions ) {
+ private static function fetchFromConds( $db, $conditions ) {
$res = $db->select(
array( 'page', 'revision' ),
array( 'page_namespace',
@@ -240,7 +238,8 @@ class Revision {
'rev_user',
'rev_minor_edit',
'rev_timestamp',
- 'rev_deleted' ),
+ 'rev_deleted',
+ 'rev_len' ),
$conditions,
'Revision::fetchRow',
array( 'LIMIT' => 1 ) );
@@ -249,6 +248,25 @@ class Revision {
}
/**
+ * Return the list of revision fields that should be selected to create
+ * a new revision.
+ */
+ static function selectFields() {
+ return array(
+ 'rev_id',
+ 'rev_page',
+ 'rev_text_id',
+ 'rev_timestamp',
+ 'rev_comment',
+ 'rev_minor_edit',
+ 'rev_user',
+ 'rev_user_text,'.
+ 'rev_deleted',
+ 'rev_len'
+ );
+ }
+
+ /**
* @param object $row
* @access private
*/
@@ -263,6 +281,11 @@ class Revision {
$this->mMinorEdit = intval( $row->rev_minor_edit );
$this->mTimestamp = $row->rev_timestamp;
$this->mDeleted = intval( $row->rev_deleted );
+
+ if( !isset( $row->rev_len ) || is_null( $row->rev_len ) )
+ $this->mSize = null;
+ else
+ $this->mSize = intval( $row->rev_len );
if( isset( $row->page_latest ) ) {
$this->mCurrent = ( $row->rev_id == $row->page_latest );
@@ -293,7 +316,8 @@ class Revision {
$this->mMinorEdit = isset( $row['minor_edit'] ) ? intval( $row['minor_edit'] ) : 0;
$this->mTimestamp = isset( $row['timestamp'] ) ? strval( $row['timestamp'] ) : wfTimestamp( TS_MW );
$this->mDeleted = isset( $row['deleted'] ) ? intval( $row['deleted'] ) : 0;
-
+ $this->mSize = isset( $row['len'] ) ? intval( $row['len'] ) : null;
+
// Enforce spacing trimming on supplied text
$this->mComment = isset( $row['comment'] ) ? trim( strval( $row['comment'] ) ) : null;
$this->mText = isset( $row['text'] ) ? rtrim( strval( $row['text'] ) ) : null;
@@ -301,6 +325,9 @@ class Revision {
$this->mTitle = null; # Load on demand if needed
$this->mCurrent = false;
+ # If we still have no len_size, see it we have the text to figure it out
+ if ( !$this->mSize )
+ $this->mSize = is_null($this->mText) ? null : strlen($this->mText);
} else {
throw new MWException( 'Revision constructor passed invalid row format.' );
}
@@ -325,6 +352,13 @@ class Revision {
}
/**
+ * Returns the length of the text in this revision, or null if unknown.
+ */
+ function getSize() {
+ return $this->mSize;
+ }
+
+ /**
* Returns the title of the page associated with this entry.
* @return Title
*/
@@ -332,7 +366,7 @@ class Revision {
if( isset( $this->mTitle ) ) {
return $this->mTitle;
}
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow(
array( 'page', 'revision' ),
array( 'page_namespace', 'page_title' ),
@@ -459,6 +493,18 @@ class Revision {
}
return $this->mText;
}
+
+ /**
+ * Fetch revision text if it's available to THIS user
+ * @return string
+ */
+ function revText() {
+ if( !$this->userCan( self::DELETED_TEXT ) ) {
+ return "";
+ } else {
+ return $this->getRawText();
+ }
+ }
/**
* @return string
@@ -565,7 +611,7 @@ class Revision {
# Old revisions kept around in a legacy encoding?
# Upconvert on demand.
global $wgInputEncoding, $wgContLang;
- $text = $wgContLang->iconv( $wgLegacyEncoding, $wgInputEncoding . '//IGNORE', $text );
+ $text = $wgContLang->iconv( $wgLegacyEncoding, $wgInputEncoding, $text );
}
}
wfProfileOut( $fname );
@@ -666,6 +712,7 @@ class Revision {
'rev_user_text' => $this->mUserText,
'rev_timestamp' => $dbw->timestamp( $this->mTimestamp ),
'rev_deleted' => $this->mDeleted,
+ 'rev_len' => $this->mSize,
), $fname
);
@@ -706,7 +753,7 @@ class Revision {
if( !$row ) {
// Text data is immutable; check slaves first.
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow( 'text',
array( 'old_text', 'old_flags' ),
array( 'old_id' => $this->getTextId() ),
@@ -715,7 +762,7 @@ class Revision {
if( !$row ) {
// Possible slave lag!
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$row = $dbw->selectRow( 'text',
array( 'old_text', 'old_flags' ),
array( 'old_id' => $this->getTextId() ),
@@ -802,12 +849,12 @@ class Revision {
* @param integer $id
*/
static function getTimestampFromID( $id ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$timestamp = $dbr->selectField( 'revision', 'rev_timestamp',
array( 'rev_id' => $id ), __METHOD__ );
if ( $timestamp === false ) {
# Not in slave, try master
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$timestamp = $dbw->selectField( 'revision', 'rev_timestamp',
array( 'rev_id' => $id ), __METHOD__ );
}
diff --git a/includes/Sanitizer.php b/includes/Sanitizer.php
index 0c0f7244..fa5416dc 100644
--- a/includes/Sanitizer.php
+++ b/includes/Sanitizer.php
@@ -20,8 +20,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
- * @package MediaWiki
- * @subpackage Parser
+ * @addtogroup Parser
*/
/**
@@ -29,7 +28,7 @@
* Sanitizer::normalizeCharReferences and Sanitizer::decodeCharReferences
*/
define( 'MW_CHAR_REFS_REGEX',
- '/&([A-Za-z0-9]+);
+ '/&([A-Za-z0-9\x80-\xff]+);
|&\#([0-9]+);
|&\#x([0-9A-Za-z]+);
|&\#X([0-9A-Za-z]+);
@@ -316,7 +315,20 @@ $wgHtmlEntities = array(
'zwj' => 8205,
'zwnj' => 8204 );
-/** @package MediaWiki */
+/**
+ * Character entity aliases accepted by MediaWiki
+ */
+global $wgHtmlEntityAliases;
+$wgHtmlEntityAliases = array(
+ 'רלמ' => 'rlm',
+ 'رلم' => 'rlm',
+);
+
+
+/**
+ * XHTML sanitizer for MediaWiki
+ * @addtogroup Parser
+ */
class Sanitizer {
/**
* Cleans up HTML, removes dangerous tags and attributes, and
@@ -328,48 +340,41 @@ class Sanitizer {
* @return string
*/
static function removeHTMLtags( $text, $processCallback = null, $args = array() ) {
- global $wgUseTidy, $wgUserHtml;
+ global $wgUseTidy;
- static $htmlpairs, $htmlsingle, $htmlsingleonly, $htmlnest, $tabletags,
+ static $htmlpairs, $htmlsingle, $htmlsingleonly, $htmlnest, $tabletags,
$htmllist, $listtags, $htmlsingleallowed, $htmlelements, $staticInitialised;
-
+
wfProfileIn( __METHOD__ );
-
+
if ( !$staticInitialised ) {
- if( $wgUserHtml ) {
- $htmlpairs = array( # Tags that must be closed
- 'b', 'del', 'i', 'ins', 'u', 'font', 'big', 'small', 'sub', 'sup', 'h1',
- 'h2', 'h3', 'h4', 'h5', 'h6', 'cite', 'code', 'em', 's',
- 'strike', 'strong', 'tt', 'var', 'div', 'center',
- 'blockquote', 'ol', 'ul', 'dl', 'table', 'caption', 'pre',
- 'ruby', 'rt' , 'rb' , 'rp', 'p', 'span', 'u'
- );
- $htmlsingle = array(
- 'br', 'hr', 'li', 'dt', 'dd'
- );
- $htmlsingleonly = array( # Elements that cannot have close tags
- 'br', 'hr'
- );
- $htmlnest = array( # Tags that can be nested--??
- 'table', 'tr', 'td', 'th', 'div', 'blockquote', 'ol', 'ul',
- 'dl', 'font', 'big', 'small', 'sub', 'sup', 'span'
- );
- $tabletags = array( # Can only appear inside table
- 'td', 'th', 'tr',
- );
- $htmllist = array( # Tags used by list
- 'ul','ol',
- );
- $listtags = array( # Tags that can appear in a list
- 'li',
- );
-
- } else {
- $htmlpairs = array();
- $htmlsingle = array();
- $htmlnest = array();
- $tabletags = array();
- }
+
+ $htmlpairs = array( # Tags that must be closed
+ 'b', 'del', 'i', 'ins', 'u', 'font', 'big', 'small', 'sub', 'sup', 'h1',
+ 'h2', 'h3', 'h4', 'h5', 'h6', 'cite', 'code', 'em', 's',
+ 'strike', 'strong', 'tt', 'var', 'div', 'center',
+ 'blockquote', 'ol', 'ul', 'dl', 'table', 'caption', 'pre',
+ 'ruby', 'rt' , 'rb' , 'rp', 'p', 'span', 'u'
+ );
+ $htmlsingle = array(
+ 'br', 'hr', 'li', 'dt', 'dd'
+ );
+ $htmlsingleonly = array( # Elements that cannot have close tags
+ 'br', 'hr'
+ );
+ $htmlnest = array( # Tags that can be nested--??
+ 'table', 'tr', 'td', 'th', 'div', 'blockquote', 'ol', 'ul',
+ 'dl', 'font', 'big', 'small', 'sub', 'sup', 'span'
+ );
+ $tabletags = array( # Can only appear inside table, we will close them
+ 'td', 'th', 'tr',
+ );
+ $htmllist = array( # Tags used by list
+ 'ul','ol',
+ );
+ $listtags = array( # Tags that can appear in a list
+ 'li',
+ );
$htmlsingleallowed = array_merge( $htmlsingle, $tabletags );
$htmlelements = array_merge( $htmlsingle, $htmlpairs, $htmlnest );
@@ -386,7 +391,7 @@ class Sanitizer {
# Remove HTML comments
$text = Sanitizer::removeHTMLcomments( $text );
$bits = explode( '<', $text );
- $text = array_shift( $bits );
+ $text = str_replace( '>', '&gt;', array_shift( $bits ) );
if(!$wgUseTidy) {
$tagstack = $tablestack = array();
foreach ( $bits as $x ) {
@@ -396,7 +401,7 @@ class Sanitizer {
} else {
$slash = $t = $params = $brace = $rest = null;
}
-
+
$badtag = 0 ;
if ( isset( $htmlelements[$t = strtolower( $t )] ) ) {
# Check our stack
@@ -453,6 +458,10 @@ class Sanitizer {
} else if( isset( $htmlsingle[$t] ) ) {
# Hack to not close $htmlsingle tags
$brace = NULL;
+ } else if( isset( $tabletags[$t] )
+ && in_array($t ,$tagstack) ) {
+ // New table tag but forgot to close the previous one
+ $text .= "</$t>";
} else {
if ( $t == 'table' ) {
array_push( $tablestack, $tagstack );
@@ -472,7 +481,7 @@ class Sanitizer {
}
if ( ! $badtag ) {
$rest = str_replace( '>', '&gt;', $rest );
- $close = ( $brace == '/>' ) ? ' /' : '';
+ $close = ( $brace == '/>' && !$slash ) ? ' /' : '';
$text .= "<$slash$t$newparams$close>$rest";
continue;
}
@@ -613,7 +622,7 @@ class Sanitizer {
$stripped = preg_replace( '!\\\\([0-9A-Fa-f]{1,6})[ \\n\\r\\t\\f]?!e',
'codepointToUtf8(hexdec("$1"))', $stripped );
$stripped = str_replace( '\\', '', $stripped );
- if( preg_match( '/(expression|tps*:\/\/|url\\s*\().*/is',
+ if( preg_match( '/(?:expression|tps*:\/\/|url\\s*\().*/is',
$stripped ) ) {
# haxx0r
return false;
@@ -645,15 +654,15 @@ class Sanitizer {
if( trim( $text ) == '' ) {
return '';
}
-
+
$stripped = Sanitizer::validateTagAttributes(
Sanitizer::decodeTagAttributes( $text ), $element );
-
+
$attribs = array();
foreach( $stripped as $attribute => $value ) {
$encAttribute = htmlspecialchars( $attribute );
$encValue = Sanitizer::safeEncodeAttribute( $value );
-
+
$attribs[] = "$encAttribute=\"$encValue\"";
}
return count( $attribs ) ? ' ' . implode( ' ', $attribs ) : '';
@@ -666,7 +675,7 @@ class Sanitizer {
*/
static function encodeAttribute( $text ) {
$encValue = htmlspecialchars( $text );
-
+
// Whitespace is normalized during attribute decoding,
// so if we've been passed non-spaces we must encode them
// ahead of time or they won't be preserved.
@@ -675,10 +684,10 @@ class Sanitizer {
"\r" => '&#13;',
"\t" => '&#9;',
) );
-
+
return $encValue;
}
-
+
/**
* Encode an attribute value for HTML tags, with extra armoring
* against further wiki processing.
@@ -687,7 +696,7 @@ class Sanitizer {
*/
static function safeEncodeAttribute( $text ) {
$encValue = Sanitizer::encodeAttribute( $text );
-
+
# Templates and links may be expanded in later parsing,
# creating invalid or dangerous output. Suppress this.
$encValue = strtr( $encValue, array(
@@ -716,12 +725,10 @@ class Sanitizer {
* Given a value escape it so that it can be used in an id attribute and
* return it, this does not validate the value however (see first link)
*
- * @link http://www.w3.org/TR/html401/types.html#type-name Valid characters
+ * @see http://www.w3.org/TR/html401/types.html#type-name Valid characters
* in the id and
* name attributes
- * @link http://www.w3.org/TR/html401/struct/links.html#h-12.2.3 Anchors with the id attribute
- *
- * @bug 4461
+ * @see http://www.w3.org/TR/html401/struct/links.html#h-12.2.3 Anchors with the id attribute
*
* @static
*
@@ -743,9 +750,9 @@ class Sanitizer {
* Given a value, escape it so that it can be used as a CSS class and
* return it.
*
- * TODO: For extra validity, input should be validated UTF-8.
+ * @todo For extra validity, input should be validated UTF-8.
*
- * @link http://www.w3.org/TR/CSS21/syndata.html Valid characters/format
+ * @see http://www.w3.org/TR/CSS21/syndata.html Valid characters/format
*
* @param string $class
* @return string
@@ -795,11 +802,11 @@ class Sanitizer {
foreach( $pairs as $set ) {
$attribute = strtolower( $set[1] );
$value = Sanitizer::getTagAttributeCallback( $set );
-
+
// Normalize whitespace
$value = preg_replace( '/[\t\r\n ]+/', ' ', $value );
$value = trim( $value );
-
+
// Decode character references
$attribs[$attribute] = Sanitizer::decodeCharReferences( $value );
}
@@ -850,11 +857,16 @@ class Sanitizer {
*/
private static function normalizeAttributeValue( $text ) {
return str_replace( '"', '&quot;',
- preg_replace(
- '/\r\n|[\x20\x0d\x0a\x09]/',
- ' ',
+ self::normalizeWhitespace(
Sanitizer::normalizeCharReferences( $text ) ) );
}
+
+ private static function normalizeWhitespace( $text ) {
+ return preg_replace(
+ '/\r\n|[\x20\x0d\x0a\x09]/',
+ ' ',
+ $text );
+ }
/**
* Ensure that any entities and character references are legal
@@ -900,16 +912,19 @@ class Sanitizer {
/**
* If the named entity is defined in the HTML 4.0/XHTML 1.0 DTD,
- * return the named entity reference as is. Otherwise, returns
- * HTML-escaped text of pseudo-entity source (eg &amp;foo;)
+ * return the named entity reference as is. If the entity is a
+ * MediaWiki-specific alias, returns the HTML equivalent. Otherwise,
+ * returns HTML-escaped text of pseudo-entity source (eg &amp;foo;)
*
* @param string $name
* @return string
* @static
*/
static function normalizeEntity( $name ) {
- global $wgHtmlEntities;
- if( isset( $wgHtmlEntities[$name] ) ) {
+ global $wgHtmlEntities, $wgHtmlEntityAliases;
+ if ( isset( $wgHtmlEntityAliases[$name] ) ) {
+ return "&{$wgHtmlEntityAliases[$name]};";
+ } elseif( isset( $wgHtmlEntities[$name] ) ) {
return "&$name;";
} else {
return "&amp;$name;";
@@ -1006,7 +1021,10 @@ class Sanitizer {
* @return string
*/
static function decodeEntity( $name ) {
- global $wgHtmlEntities;
+ global $wgHtmlEntities, $wgHtmlEntityAliases;
+ if ( isset( $wgHtmlEntityAliases[$name] ) ) {
+ $name = $wgHtmlEntityAliases[$name];
+ }
if( isset( $wgHtmlEntities[$name] ) ) {
return codepointToUtf8( $wgHtmlEntities[$name] );
} else {
@@ -1173,8 +1191,10 @@ class Sanitizer {
/**
* Take a fragment of (potentially invalid) HTML and return
- * a version with any tags removed, encoded suitably for literal
- * inclusion in an attribute value.
+ * a version with any tags removed, encoded as plain text.
+ *
+ * Warning: this return value must be further escaped for literal
+ * inclusion in HTML output as of 1.10!
*
* @param string $text HTML fragment
* @return string
@@ -1184,14 +1204,8 @@ class Sanitizer {
$text = StringUtils::delimiterReplace( '<', '>', '', $text );
# Normalize &entities and whitespace
- $text = Sanitizer::normalizeAttributeValue( $text );
-
- # Will be placed into "double-quoted" attributes,
- # make sure remaining bits are safe.
- $text = str_replace(
- array('<', '>', '"'),
- array('&lt;', '&gt;', '&quot;'),
- $text );
+ $text = self::decodeCharReferences( $text );
+ $text = self::normalizeWhitespace( $text );
return $text;
}
@@ -1215,7 +1229,7 @@ class Sanitizer {
$out .= "]>\n";
return $out;
}
-
+
static function cleanUrl( $url, $hostname=true ) {
# Normalize any HTML entities in input. They will be
# re-escaped by makeExternalLink().
@@ -1223,12 +1237,12 @@ class Sanitizer {
# Escape any control characters introduced by the above step
$url = preg_replace( '/[\][<>"\\x00-\\x20\\x7F]/e', "urlencode('\\0')", $url );
-
+
# Validate hostname portion
$matches = array();
if( preg_match( '!^([^:]+:)(//[^/]+)?(.*)$!iD', $url, $matches ) ) {
list( /* $whole */, $protocol, $host, $rest ) = $matches;
-
+
// Characters that will be ignored in IDNs.
// http://tools.ietf.org/html/3454#section-3.1
// Strip them before further processing so blacklists and such work.
@@ -1247,11 +1261,11 @@ class Sanitizer {
\xe2\x80\x8d| # 200d ZERO WIDTH JOINER
[\xef\xb8\x80-\xef\xb8\x8f] # fe00-fe00f VARIATION SELECTOR-1-16
/xuD";
-
+
$host = preg_replace( $strip, '', $host );
-
+
// @fixme: validate hostnames here
-
+
return $protocol . $host . $rest;
} else {
return $url;
diff --git a/includes/SearchEngine.php b/includes/SearchEngine.php
index cec40c91..24795ba9 100644
--- a/includes/SearchEngine.php
+++ b/includes/SearchEngine.php
@@ -1,12 +1,7 @@
<?php
/**
* Contain a class for special pages
- * @package MediaWiki
- * @subpackage Search
- */
-
-/**
- * @package MediaWiki
+ * @addtogroup Search
*/
class SearchEngine {
var $limit = 10;
@@ -124,17 +119,33 @@ class SearchEngine {
if ( $title->getNamespace() == NS_USER ) {
return $title;
}
+
+ # Go to images that exist even if there's no local page.
+ # There may have been a funny upload, or it may be on a shared
+ # file repository such as Wikimedia Commons.
+ if( $title->getNamespace() == NS_IMAGE ) {
+ $image = new Image( $title );
+ if( $image->exists() ) {
+ return $title;
+ }
+ }
+
+ # MediaWiki namespace? Page may be "implied" if not customized.
+ # Just return it, with caps forced as the message system likes it.
+ if( $title->getNamespace() == NS_MEDIAWIKI ) {
+ return Title::makeTitle( NS_MEDIAWIKI, $wgContLang->ucfirst( $title->getText() ) );
+ }
# Quoted term? Try without the quotes...
$matches = array();
if( preg_match( '/^"([^"]+)"$/', $searchterm, $matches ) ) {
return SearchEngine::getNearMatch( $matches[1] );
}
-
+
return NULL;
}
- function legalSearchChars() {
+ public static function legalSearchChars() {
return "A-Za-z_'0-9\\x80-\\xFF\\-";
}
@@ -193,9 +204,8 @@ class SearchEngine {
* active database backend, and return a configured instance.
*
* @return SearchEngine
- * @private
*/
- function create() {
+ public static function create() {
global $wgDBtype, $wgSearchType;
if( $wgSearchType ) {
$class = $wgSearchType;
@@ -203,6 +213,8 @@ class SearchEngine {
$class = 'SearchMySQL4';
} else if ( $wgDBtype == 'postgres' ) {
$class = 'SearchPostgres';
+ } else if ( $wgDBtype == 'oracle' ) {
+ $class = 'SearchOracle';
} else {
$class = 'SearchEngineDummy';
}
@@ -232,12 +244,15 @@ class SearchEngine {
* @param string $title
* @abstract
*/
- function updateTitle( $id, $title ) {
+ function updateTitle( $id, $title ) {
// no-op
- }
+ }
}
-/** @package MediaWiki */
+
+/**
+ * @addtogroup Search
+ */
class SearchResultSet {
/**
* Fetch an array of regular expression fragments for matching
@@ -312,7 +327,10 @@ class SearchResultSet {
}
}
-/** @package MediaWiki */
+
+/**
+ * @addtogroup Search
+ */
class SearchResult {
function SearchResult( $row ) {
$this->mTitle = Title::makeTitle( $row->page_namespace, $row->page_title );
@@ -335,7 +353,7 @@ class SearchResult {
}
/**
- * @package MediaWiki
+ * @addtogroup Search
*/
class SearchEngineDummy {
function search( $term ) {
diff --git a/includes/SearchMySQL.php b/includes/SearchMySQL.php
index 15515952..0e02a684 100644
--- a/includes/SearchMySQL.php
+++ b/includes/SearchMySQL.php
@@ -20,11 +20,8 @@
/**
* Search engine hook base class for MySQL.
* Specific bits for MySQL 3 and 4 variants are in child classes.
- * @package MediaWiki
- * @subpackage Search
+ * @addtogroup Search
*/
-
-/** @package MediaWiki */
class SearchMySQL extends SearchEngine {
/**
* Perform a full text search query and return a result set.
@@ -150,7 +147,7 @@ class SearchMySQL extends SearchEngine {
* @param string $text
*/
function update( $id, $title, $text ) {
- $dbw=& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->replace( 'searchindex',
array( 'si_page' ),
array(
@@ -168,7 +165,7 @@ class SearchMySQL extends SearchEngine {
* @param string $title
*/
function updateTitle( $id, $title ) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->update( 'searchindex',
array( 'si_title' => $title ),
@@ -178,7 +175,9 @@ class SearchMySQL extends SearchEngine {
}
}
-/** @package MediaWiki */
+/**
+ * @addtogroup Search
+ */
class MySQLSearchResultSet extends SearchResultSet {
function MySQLSearchResultSet( $resultSet, $terms ) {
$this->mResultSet = $resultSet;
diff --git a/includes/SearchMySQL4.php b/includes/SearchMySQL4.php
index c20e3f8e..97ce3850 100644
--- a/includes/SearchMySQL4.php
+++ b/includes/SearchMySQL4.php
@@ -19,20 +19,14 @@
/**
* Search engine hook for MySQL 4+
- * @package MediaWiki
- * @subpackage Search
- */
-
-/**
- * @package MediaWiki
- * @subpackage Search
+ * @addtogroup Search
*/
class SearchMySQL4 extends SearchMySQL {
var $strictMatching = true;
/** @todo document */
- function SearchMySQL4( &$db ) {
- $this->db =& $db;
+ function SearchMySQL4( $db ) {
+ $this->db = $db;
}
/** @todo document */
diff --git a/includes/SearchOracle.php b/includes/SearchOracle.php
new file mode 100644
index 00000000..c9a675e6
--- /dev/null
+++ b/includes/SearchOracle.php
@@ -0,0 +1,235 @@
+<?php
+# Copyright (C) 2004 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
+
+/**
+ * Search engine hook base class for Oracle (ConText).
+ * @addtogroup Search
+ */
+class SearchOracle extends SearchEngine {
+ function __construct($db) {
+ $this->db = $db;
+ }
+
+ /**
+ * Perform a full text search query and return a result set.
+ *
+ * @param string $term - Raw search term
+ * @return OracleSearchResultSet
+ * @access public
+ */
+ function searchText( $term ) {
+ $resultSet = $this->db->resultObject($this->db->query($this->getQuery($this->filter($term), true)));
+ return new OracleSearchResultSet($resultSet, $this->searchTerms);
+ }
+
+ /**
+ * Perform a title-only search query and return a result set.
+ *
+ * @param string $term - Raw search term
+ * @return ORacleSearchResultSet
+ * @access public
+ */
+ function searchTitle($term) {
+ $resultSet = $this->db->resultObject($this->db->query($this->getQuery($this->filter($term), false)));
+ return new MySQLSearchResultSet($resultSet, $this->searchTerms);
+ }
+
+
+ /**
+ * Return a partial WHERE clause to exclude redirects, if so set
+ * @return string
+ * @private
+ */
+ function queryRedirect() {
+ if ($this->showRedirects) {
+ return '';
+ } else {
+ return 'AND page_is_redirect=0';
+ }
+ }
+
+ /**
+ * Return a partial WHERE clause to limit the search to the given namespaces
+ * @return string
+ * @private
+ */
+ function queryNamespaces() {
+ $namespaces = implode(',', $this->namespaces);
+ if ($namespaces == '') {
+ $namespaces = '0';
+ }
+ return 'AND page_namespace IN (' . $namespaces . ')';
+ }
+
+ /**
+ * Return a LIMIT clause to limit results on the query.
+ * @return string
+ * @private
+ */
+ function queryLimit($sql) {
+ return $this->db->limitResult($sql, $this->limit, $this->offset);
+ }
+
+ /**
+ * Does not do anything for generic search engine
+ * subclasses may define this though
+ * @return string
+ * @private
+ */
+ function queryRanking($filteredTerm, $fulltext) {
+ return ' ORDER BY score(1)';
+ }
+
+ /**
+ * Construct the full SQL query to do the search.
+ * The guts shoulds be constructed in queryMain()
+ * @param string $filteredTerm
+ * @param bool $fulltext
+ * @private
+ */
+ function getQuery( $filteredTerm, $fulltext ) {
+ return $this->queryLimit($this->queryMain($filteredTerm, $fulltext) . ' ' .
+ $this->queryRedirect() . ' ' .
+ $this->queryNamespaces() . ' ' .
+ $this->queryRanking( $filteredTerm, $fulltext ) . ' ');
+ }
+
+
+ /**
+ * Picks which field to index on, depending on what type of query.
+ * @param bool $fulltext
+ * @return string
+ */
+ function getIndexField($fulltext) {
+ return $fulltext ? 'si_text' : 'si_title';
+ }
+
+ /**
+ * Get the base part of the search query.
+ *
+ * @param string $filteredTerm
+ * @param bool $fulltext
+ * @return string
+ * @private
+ */
+ function queryMain( $filteredTerm, $fulltext ) {
+ $match = $this->parseQuery($filteredTerm, $fulltext);
+ $page = $this->db->tableName('page');
+ $searchindex = $this->db->tableName('searchindex');
+ return 'SELECT page_id, page_namespace, page_title ' .
+ "FROM $page,$searchindex " .
+ 'WHERE page_id=si_page AND ' . $match;
+ }
+
+ /** @todo document */
+ function parseQuery($filteredText, $fulltext) {
+ global $wgContLang;
+ $lc = SearchEngine::legalSearchChars();
+ $this->searchTerms = array();
+
+ # FIXME: This doesn't handle parenthetical expressions.
+ $m = array();
+ $q = array();
+
+ if (preg_match_all('/([-+<>~]?)(([' . $lc . ']+)(\*?)|"[^"]*")/',
+ $filteredText, $m, PREG_SET_ORDER)) {
+ foreach($m as $terms) {
+ $q[] = $terms[1] . $wgContLang->stripForSearch($terms[2]);
+
+ if (!empty($terms[3])) {
+ $regexp = preg_quote( $terms[3], '/' );
+ if ($terms[4])
+ $regexp .= "[0-9A-Za-z_]+";
+ } else {
+ $regexp = preg_quote(str_replace('"', '', $terms[2]), '/');
+ }
+ $this->searchTerms[] = $regexp;
+ }
+ }
+
+ $searchon = $this->db->strencode(join(',', $q));
+ $field = $this->getIndexField($fulltext);
+ return " CONTAINS($field, '$searchon', 1) > 0 ";
+ }
+
+ /**
+ * Create or update the search index record for the given page.
+ * Title and text should be pre-processed.
+ *
+ * @param int $id
+ * @param string $title
+ * @param string $text
+ */
+ function update($id, $title, $text) {
+ $dbw = wfGetDB(DB_MASTER);
+ $dbw->replace('searchindex',
+ array('si_page'),
+ array(
+ 'si_page' => $id,
+ 'si_title' => $title,
+ 'si_text' => $text
+ ), 'SearchOracle::update' );
+ $dbw->query("CALL ctx_ddl.sync_index('si_text_idx')");
+ $dbw->query("CALL ctx_ddl.sync_index('si_title_idx')");
+ }
+
+ /**
+ * Update a search index record's title only.
+ * Title should be pre-processed.
+ *
+ * @param int $id
+ * @param string $title
+ */
+ function updateTitle($id, $title) {
+ $dbw = wfGetDB(DB_MASTER);
+
+ $dbw->update('searchindex',
+ array('si_title' => $title),
+ array('si_page' => $id),
+ 'SearchOracle::updateTitle',
+ array());
+ }
+}
+
+/**
+ * @addtogroup Search
+ */
+class OracleSearchResultSet extends SearchResultSet {
+ function __construct($resultSet, $terms) {
+ $this->mResultSet = $resultSet;
+ $this->mTerms = $terms;
+ }
+
+ function termMatches() {
+ return $this->mTerms;
+ }
+
+ function numRows() {
+ return $this->mResultSet->numRows();
+ }
+
+ function next() {
+ $row = $this->mResultSet->fetchObject();
+ if ($row === false)
+ return false;
+ return new SearchResult($row);
+ }
+}
+
+?>
diff --git a/includes/SearchPostgres.php b/includes/SearchPostgres.php
index 457636b4..3a624ced 100644
--- a/includes/SearchPostgres.php
+++ b/includes/SearchPostgres.php
@@ -1,5 +1,5 @@
<?php
-# Copyright (C) 2006 Greg Sabino Mullane <greg@turnstep.com>
+# Copyright (C) 2006-2007 Greg Sabino Mullane <greg@turnstep.com>
# http://www.mediawiki.org/
#
# This program is free software; you can redistribute it and/or modify
@@ -17,35 +17,31 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# http://www.gnu.org/copyleft/gpl.html
-## XXX Better catching of SELECT to_tsquery('the')
-
/**
* Search engine hook base class for Postgres
- * @package MediaWiki
- * @subpackage Search
+ * @addtogroup Search
*/
-
-/** @package MediaWiki */
class SearchPostgres extends SearchEngine {
- function SearchPostgres( &$db ) {
- $this->db =& $db;
+ function SearchPostgres( $db ) {
+ $this->db = $db;
}
/**
* Perform a full text search query via tsearch2 and return a result set.
- * Currently searches a page's current title (p.page_title) and text (t.old_text)
+ * Currently searches a page's current title (page.page_title) and
+ * latest revision article text (pagecontent.old_text)
*
* @param string $term - Raw search term
* @return PostgresSearchResultSet
* @access public
*/
- function searchText( $term ) {
- $resultSet = $this->db->resultObject( $this->db->query( $this->searchQuery( $term, 'textvector' ) ) );
+ function searchTitle( $term ) {
+ $resultSet = $this->db->resultObject( $this->db->query( $this->searchQuery( $term , 'titlevector', 'page_title' )));
return new PostgresSearchResultSet( $resultSet, $this->searchTerms );
}
- function searchTitle( $term ) {
- $resultSet = $this->db->resultObject( $this->db->query( $this->searchQuery( $term , 'titlevector' ) ) );
+ function searchText( $term ) {
+ $resultSet = $this->db->resultObject( $this->db->query( $this->searchQuery( $term, 'textvector', 'old_text' )));
return new PostgresSearchResultSet( $resultSet, $this->searchTerms );
}
@@ -53,39 +49,63 @@ class SearchPostgres extends SearchEngine {
/*
* Transform the user's search string into a better form for tsearch2
*/
- function parseQuery( $filteredText, $fulltext ) {
- global $wgContLang;
- $lc = SearchEngine::legalSearchChars();
- $searchon = '';
- $this->searchTerms = array();
-
- # FIXME: This doesn't handle parenthetical expressions.
- $m = array();
- if( preg_match_all( '/([-+<>~]?)(([' . $lc . ']+)(\*?)|"[^"]*")/',
- $filteredText, $m, PREG_SET_ORDER ) ) {
+ function parseQuery( $term ) {
+
+ wfDebug( "parseQuery received: $term" );
+
+ ## No backslashes allowed
+ $term = preg_replace('/\\\/', '', $term);
+
+ ## Collapse parens into nearby words:
+ $term = preg_replace('/\s*\(\s*/', ' (', $term);
+ $term = preg_replace('/\s*\)\s*/', ') ', $term);
+
+ ## Treat colons as word separators:
+ $term = preg_replace('/:/', ' ', $term);
+
+ $searchstring = '';
+ if( preg_match_all('/([-!]?)(\S+)\s*/', $term, $m, PREG_SET_ORDER ) ) {
foreach( $m as $terms ) {
- if( $searchon !== '' ) $searchon .= ' ';
- if($terms[1] == '') {
- $terms[1] = '+';
+ if (strlen($terms[1])) {
+ $searchstring .= ' & !';
}
- $searchon .= $terms[1] . $wgContLang->stripForSearch( $terms[2] );
- if( !empty( $terms[3] ) ) {
- $regexp = preg_quote( $terms[3], '/' );
- if( $terms[4] ) $regexp .= "[0-9A-Za-z_]+";
- } else {
- $regexp = preg_quote( str_replace( '"', '', $terms[2] ), '/' );
+ if (strtolower($terms[2]) === 'and') {
+ $searchstring .= ' & ';
+ }
+ else if (strtolower($terms[2]) === 'or' or $terms[2] === '|') {
+ $searchstring .= ' | ';
+ }
+ else if (strtolower($terms[2]) === 'not') {
+ $searchstring .= ' & !';
+ }
+ else {
+ $searchstring .= " & $terms[2]";
}
- $this->searchTerms[] = $regexp;
}
- wfDebug( "Would search with '$searchon'\n" );
- wfDebug( 'Match with /\b' . implode( '\b|\b', $this->searchTerms ) . "\b/\n" );
- } else {
- wfDebug( "Can't understand search query '{$this->filteredText}'\n" );
}
- $searchon = preg_replace('/(\s+)/','&',$searchon);
- $searchon = $this->db->strencode( $searchon );
- return $searchon;
+ ## Strip out leading junk
+ $searchstring = preg_replace('/^[\s\&\|]+/', '', $searchstring);
+
+ ## Remove any doubled-up operators
+ $searchstring = preg_replace('/([\!\&\|]) +(?:[\&\|] +)+/', "$1 ", $searchstring);
+
+ ## Remove any non-spaced operators (e.g. "Zounds!")
+ $searchstring = preg_replace('/([^ ])[\!\&\|]/', "$1", $searchstring);
+
+ ## Remove any trailing whitespace or operators
+ $searchstring = preg_replace('/[\s\!\&\|]+$/', '', $searchstring);
+
+ ## Remove unnecessary quotes around everything
+ $searchstring = preg_replace('/^[\'"](.*)[\'"]$/', "$1", $searchstring);
+
+ ## Quote the whole thing
+ $searchstring = $this->db->addQuotes($searchstring);
+
+ wfDebug( "parseQuery returned: $searchstring" );
+
+ return $searchstring;
+
}
/**
@@ -94,14 +114,37 @@ class SearchPostgres extends SearchEngine {
* @param string $fulltext
* @private
*/
- function searchQuery( $filteredTerm, $fulltext ) {
+ function searchQuery( $term, $fulltext, $colname ) {
+
+ $searchstring = $this->parseQuery( $term );
- $match = $this->parseQuery( $filteredTerm, $fulltext );
+ ## We need a separate query here so gin does not complain about empty searches
+ $SQL = "SELECT to_tsquery('default',$searchstring)";
+ $res = $this->db->doQuery($SQL);
+ if (!$res) {
+ ## TODO: Better output (example to catch: one 'two)
+ die ("Sorry, that was not a valid search string. Please go back and try again");
+ }
+ $top = pg_fetch_result($res,0,0);
- $query = "SELECT page_id, page_namespace, page_title, old_text AS page_text, ".
- "rank(titlevector, to_tsquery('default','$match')) AS rnk ".
+ if ($top === "") { ## e.g. if only stopwords are used XXX return something better
+ $query = "SELECT page_id, page_namespace, page_title, 0 AS score ".
+ "FROM page p, revision r, pagecontent c WHERE p.page_latest = r.rev_id " .
+ "AND r.rev_text_id = c.old_id AND 1=0";
+ }
+ else {
+ $m = array();
+ if( preg_match_all("/'([^']+)'/", $top, $m, PREG_SET_ORDER ) ) {
+ foreach( $m as $terms ) {
+ $this->searchTerms[$terms[1]] = $terms[1];
+ }
+ }
+
+ $query = "SELECT page_id, page_namespace, page_title, ".
+ "rank($fulltext, to_tsquery('default',$searchstring),5) AS score ".
"FROM page p, revision r, pagecontent c WHERE p.page_latest = r.rev_id " .
- "AND r.rev_text_id = c.old_id AND $fulltext @@ to_tsquery('default','$match')";
+ "AND r.rev_text_id = c.old_id AND $fulltext @@ to_tsquery('default',$searchstring)";
+ }
## Redirects
if (! $this->showRedirects)
@@ -112,25 +155,51 @@ class SearchPostgres extends SearchEngine {
$query .= ' AND page_namespace = 0';
else {
$namespaces = implode( ',', $this->namespaces );
- $query .= " AND page_namespace IN ($namespaces)";
+ $query .= " AND page_namespace IN ($namespaces)";
}
- $query .= " ORDER BY rnk DESC, page_id DESC";
+ $query .= " ORDER BY score DESC, page_id DESC";
$query .= $this->db->limitResult( '', $this->limit, $this->offset );
+ wfDebug( "searchQuery returned: $query" );
+
return $query;
}
- ## These two functions are done automatically via triggers
+ ## Most of the work of these two functions are done automatically via triggers
- function update( $id, $title, $text ) { return true; }
- function updateTitle( $id, $title ) { return true; }
+ function update( $pageid, $title, $text ) {
+ ## We don't want to index older revisions
+ $SQL = "UPDATE pagecontent SET textvector = NULL WHERE old_id = ".
+ "(SELECT rev_text_id FROM revision WHERE rev_page = $pageid ".
+ "ORDER BY rev_text_id DESC LIMIT 1 OFFSET 1)";
+ $this->db->doQuery($SQL);
+ return true;
+ }
+
+ function updateTitle( $id, $title ) {
+ return true;
+ }
} ## end of the SearchPostgres class
+/**
+ * @addtogroup Search
+ */
+class PostgresSearchResult extends SearchResult {
+ function PostgresSearchResult( $row ) {
+ $this->mTitle = Title::makeTitle( $row->page_namespace, $row->page_title );
+ $this->score = $row->score;
+ }
+ function getScore() {
+ return $this->score;
+ }
+}
-/** @package MediaWiki */
+/**
+ * @addtogroup Search
+ */
class PostgresSearchResultSet extends SearchResultSet {
function PostgresSearchResultSet( $resultSet, $terms ) {
$this->mResultSet = $resultSet;
@@ -150,9 +219,10 @@ class PostgresSearchResultSet extends SearchResultSet {
if( $row === false ) {
return false;
} else {
- return new SearchResult( $row );
+ return new PostgresSearchResult( $row );
}
}
}
+
?>
diff --git a/includes/SearchTsearch2.php b/includes/SearchTsearch2.php
index 1fca9899..b504f034 100644
--- a/includes/SearchTsearch2.php
+++ b/includes/SearchTsearch2.php
@@ -19,14 +19,12 @@
/**
* Search engine hook for PostgreSQL / Tsearch2
- * @package MediaWiki
- * @subpackage Search
+ * @addtogroup Search
*/
/**
* @todo document
- * @package MediaWiki
- * @subpackage Search
+ * @addtogroup Search
*/
class SearchTsearch2 extends SearchEngine {
var $strictMatching = false;
@@ -97,7 +95,7 @@ class SearchTsearch2 extends SearchEngine {
}
function update( $id, $title, $text ) {
- $dbw=& wfGetDB(DB_MASTER);
+ $dbw = wfGetDB(DB_MASTER);
$searchindex = $dbw->tableName( 'searchindex' );
$sql = "DELETE FROM $searchindex WHERE si_page={$id}";
$dbw->query($sql,"SearchTsearch2:update");
@@ -110,13 +108,13 @@ class SearchTsearch2 extends SearchEngine {
}
function updateTitle($id,$title) {
- $dbw=& wfGetDB(DB_MASTER);
- $searchindex = $dbw->tableName( 'searchindex' );
- $sql = "UPDATE $searchindex SET si_title=to_tsvector('" .
- $dbw->strencode( $title ) .
- "') WHERE si_page={$id}";
+ $dbw = wfGetDB(DB_MASTER);
+ $searchindex = $dbw->tableName( 'searchindex' );
+ $sql = "UPDATE $searchindex SET si_title=to_tsvector('" .
+ $dbw->strencode( $title ) .
+ "') WHERE si_page={$id}";
- $dbw->query( $sql, "SearchMySQL4::updateTitle" );
+ $dbw->query( $sql, "SearchMySQL4::updateTitle" );
}
}
diff --git a/includes/SearchUpdate.php b/includes/SearchUpdate.php
index 37981a67..724197c1 100644
--- a/includes/SearchUpdate.php
+++ b/includes/SearchUpdate.php
@@ -1,12 +1,7 @@
<?php
/**
* See deferred.txt
- * @package MediaWiki
- */
-
-/**
- *
- * @package MediaWiki
+ * @addtogroup Search
*/
class SearchUpdate {
@@ -38,7 +33,7 @@ class SearchUpdate {
wfProfileIn( $fname );
$search = SearchEngine::create();
- $lc = $search->legalSearchChars() . '&#;';
+ $lc = SearchEngine::legalSearchChars() . '&#;';
if( $this->mText === false ) {
$search->updateTitle($this->mId,
@@ -98,15 +93,20 @@ class SearchUpdate {
# Strip wiki '' and '''
$text = preg_replace( "/''[']*/", " ", $text );
wfProfileOut( "$fname-regexps" );
+
+ wfRunHooks( 'SearchUpdate', array( $this->mId, $this->mNamespace, $this->mTitle, &$text ) );
+
+ # Perform the actual update
$search->update($this->mId, Title::indexTitle( $this->mNamespace, $this->mTitle ),
$text);
+
wfProfileOut( $fname );
}
}
/**
* Placeholder class
- * @package MediaWiki
+ * @addtogroup Search
*/
class SearchUpdateMyISAM extends SearchUpdate {
# Inherits everything
diff --git a/includes/Setup.php b/includes/Setup.php
index 80a5b48a..47ba494f 100644
--- a/includes/Setup.php
+++ b/includes/Setup.php
@@ -1,7 +1,6 @@
<?php
/**
* Include most things that's need to customize the site
- * @package MediaWiki
*/
/**
@@ -134,13 +133,14 @@ if ( $wgDBprefix ) {
} else {
$wgCookiePrefix = $wgDBname;
}
+$wgCookiePrefix = strtr($wgCookiePrefix, "=,; +.\"'\\[", "__________");
# If session.auto_start is there, we can't touch session name
#
if( !ini_get( 'session.auto_start' ) )
session_name( $wgSessionName ? $wgSessionName : $wgCookiePrefix . '_session' );
-if( !$wgCommandLineMode && ( isset( $_COOKIE[session_name()] ) || isset( $_COOKIE[$wgCookiePrefix.'Token'] ) ) ) {
+if( !$wgCommandLineMode && ( $wgRequest->checkSessionCookie() || isset( $_COOKIE[$wgCookiePrefix.'Token'] ) ) ) {
wfIncrStats( 'request_with_session' );
wfSetupSession();
$wgSessionStarted = true;
diff --git a/includes/SiteConfiguration.php b/includes/SiteConfiguration.php
index 8fd5d6b6..0968460c 100644
--- a/includes/SiteConfiguration.php
+++ b/includes/SiteConfiguration.php
@@ -1,9 +1,4 @@
<?php
-/**
- * This is a class used to hold configuration settings, particularly for multi-wiki sites.
- *
- * @package MediaWiki
- */
/**
* The include paths change after this file is included from commandLine.inc,
@@ -13,7 +8,10 @@
if (!defined('SITE_CONFIGURATION')) {
define('SITE_CONFIGURATION', 1);
-/** @package MediaWiki */
+/**
+ * This is a class used to hold configuration settings, particularly for multi-wiki sites.
+ *
+ */
class SiteConfiguration {
var $suffixes = array();
var $wikis = array();
diff --git a/includes/SiteStats.php b/includes/SiteStats.php
index e2774a14..e320a196 100644
--- a/includes/SiteStats.php
+++ b/includes/SiteStats.php
@@ -2,7 +2,6 @@
/**
* Static accessor class for site_stats and related things
- * @package MediaWiki
*/
class SiteStats {
static $row, $loaded = false;
@@ -18,17 +17,54 @@ class SiteStats {
return;
}
- $dbr =& wfGetDB( DB_SLAVE );
- self::$row = $dbr->selectRow( 'site_stats', '*', false, __METHOD__ );
+ self::$row = self::loadAndLazyInit();
# This code is somewhat schema-agnostic, because I'm changing it in a minor release -- TS
if ( !isset( self::$row->ss_total_pages ) && self::$row->ss_total_pages == -1 ) {
# Update schema
$u = new SiteStatsUpdate( 0, 0, 0 );
$u->doUpdate();
+ $dbr = wfGetDB( DB_SLAVE );
self::$row = $dbr->selectRow( 'site_stats', '*', false, __METHOD__ );
}
}
+
+ static function loadAndLazyInit() {
+ wfDebug( __METHOD__ . ": reading site_stats from slave\n" );
+ $row = self::doLoad( wfGetDB( DB_SLAVE ) );
+
+ if( $row === false ) {
+ // Might have just been initialzed during this request?
+ wfDebug( __METHOD__ . ": site_stats missing on slave\n" );
+ $row = self::doLoad( wfGetDB( DB_MASTER ) );
+ }
+
+ if( $row === false ) {
+ // Normally the site_stats table is initialized at install time.
+ // Some manual construction scenarios may leave the table empty,
+ // however, for instance when importing from a dump into a clean
+ // schema with mwdumper.
+ wfDebug( __METHOD__ . ": initializing empty site_stats\n" );
+
+ global $IP;
+ require_once "$IP/maintenance/initStats.inc";
+
+ ob_start();
+ wfInitStats();
+ ob_end_clean();
+
+ $row = self::doLoad( wfGetDB( DB_MASTER ) );
+ }
+
+ if( $row === false ) {
+ wfDebug( __METHOD__ . ": init of site_stats failed o_O\n" );
+ }
+ return $row;
+ }
+
+ static function doLoad( $db ) {
+ return $db->selectRow( 'site_stats', '*', false, __METHOD__ );
+ }
static function views() {
self::load();
@@ -62,7 +98,7 @@ class SiteStats {
static function admins() {
if ( !isset( self::$admins ) ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
self::$admins = $dbr->selectField( 'user_groups', 'COUNT(*)', array( 'ug_group' => 'sysop' ), __METHOD__ );
}
return self::$admins;
@@ -71,7 +107,7 @@ class SiteStats {
static function pagesInNs( $ns ) {
wfProfileIn( __METHOD__ );
if( !isset( self::$pageCount[$ns] ) ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$pageCount[$ns] = (int)$dbr->selectField( 'page', 'COUNT(*)', array( 'page_namespace' => $ns ), __METHOD__ );
}
wfProfileOut( __METHOD__ );
@@ -83,13 +119,12 @@ class SiteStats {
/**
*
- * @package MediaWiki
*/
class SiteStatsUpdate {
var $mViews, $mEdits, $mGood, $mPages, $mUsers;
- function SiteStatsUpdate( $views, $edits, $good, $pages = 0, $users = 0 ) {
+ function __construct( $views, $edits, $good, $pages = 0, $users = 0 ) {
$this->mViews = $views;
$this->mEdits = $edits;
$this->mGood = $good;
@@ -112,7 +147,7 @@ class SiteStatsUpdate {
function doUpdate() {
$fname = 'SiteStatsUpdate::doUpdate';
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
# First retrieve the row just to find out which schema we're in
$row = $dbw->selectRow( 'site_stats', '*', false, $fname );
@@ -126,7 +161,7 @@ class SiteStatsUpdate {
if ( isset( $row->ss_total_pages ) ) {
# Update schema if required
if ( $row->ss_total_pages == -1 && !$this->mViews ) {
- $dbr =& wfGetDB( DB_SLAVE, array( 'SpecialStatistics', 'vslow') );
+ $dbr = wfGetDB( DB_SLAVE, array( 'SpecialStatistics', 'vslow') );
list( $page, $user ) = $dbr->tableNamesN( 'page', 'user' );
$sql = "SELECT COUNT(page_namespace) AS total FROM $page";
diff --git a/includes/Skin.php b/includes/Skin.php
index f8e733ef..0ca95f7e 100644
--- a/includes/Skin.php
+++ b/includes/Skin.php
@@ -2,18 +2,15 @@
if ( ! defined( 'MEDIAWIKI' ) )
die( 1 );
-/**
- *
- * @package MediaWiki
- * @subpackage Skins
- */
-
# See skin.txt
/**
* The main skin class that provide methods and properties for all other skins.
* This base class is also the "Standard" skin.
- * @package MediaWiki
+ *
+ * See docs/skin.txt for more information.
+ *
+ * @addtogroup Skins
*/
class Skin extends Linker {
/**#@+
@@ -25,16 +22,17 @@ class Skin extends Linker {
var $rcMoveIndex;
var $mWatchLinkNum = 0; // Appended to end of watch link id's
/**#@-*/
+ protected $skinname = 'standard' ;
/** Constructor, call parent constructor */
- function Skin() { parent::Linker(); }
+ function Skin() { parent::__construct(); }
/**
* Fetch the set of available skins.
* @return array of strings
* @static
*/
- static function &getSkinNames() {
+ static function getSkinNames() {
global $wgValidSkinNames;
static $skinsInitialised = false;
if ( !$skinsInitialised ) {
@@ -144,8 +142,8 @@ class Skin extends Linker {
}
/** @return string skin name */
- function getSkinName() {
- return 'standard';
+ public function getSkinName() {
+ return $this->skinname;
}
function qbSetting() {
@@ -189,16 +187,19 @@ class Skin extends Linker {
function preloadExistence() {
global $wgUser, $wgTitle;
- if ( $wgTitle->isTalkPage() ) {
- $otherTab = $wgTitle->getSubjectPage();
+ // User/talk link
+ $titles = array( $wgUser->getUserPage(), $wgUser->getTalkPage() );
+
+ // Other tab link
+ if ( $wgTitle->getNamespace() == NS_SPECIAL ) {
+ // nothing
+ } elseif ( $wgTitle->isTalkPage() ) {
+ $titles[] = $wgTitle->getSubjectPage();
} else {
- $otherTab = $wgTitle->getTalkPage();
+ $titles[] = $wgTitle->getTalkPage();
}
- $lb = new LinkBatch( array(
- $wgUser->getUserPage(),
- $wgUser->getTalkPage(),
- $otherTab
- ));
+
+ $lb = new LinkBatch( $titles );
$lb->execute();
}
@@ -241,7 +242,7 @@ class Skin extends Linker {
function outputPage( &$out ) {
global $wgDebugComments;
- wfProfileIn( 'Skin::outputPage' );
+ wfProfileIn( __METHOD__ );
$this->initPage( $out );
$out->out( $out->headElement() );
@@ -268,6 +269,7 @@ class Skin extends Linker {
$out->out( $out->reportTime() );
$out->out( "\n</body></html>" );
+ wfProfileOut( __METHOD__ );
}
static function makeVariablesScript( $data ) {
@@ -293,7 +295,7 @@ class Skin extends Linker {
global $wgStylePath, $wgUser;
global $wgArticlePath, $wgScriptPath, $wgServer, $wgContLang, $wgLang;
global $wgTitle, $wgCanonicalNamespaceNames, $wgOut, $wgArticle;
- global $wgBreakFrames;
+ global $wgBreakFrames, $wgRequest;
$ns = $wgTitle->getNamespace();
$nsname = isset( $wgCanonicalNamespaceNames[ $ns ] ) ? $wgCanonicalNamespaceNames[ $ns ] : $wgTitle->getNsText();
@@ -309,15 +311,25 @@ class Skin extends Linker {
'wgNamespaceNumber' => $wgTitle->getNamespace(),
'wgPageName' => $wgTitle->getPrefixedDBKey(),
'wgTitle' => $wgTitle->getText(),
+ 'wgAction' => $wgRequest->getText( 'action', 'view' ),
'wgArticleId' => $wgTitle->getArticleId(),
'wgIsArticle' => $wgOut->isArticle(),
'wgUserName' => $wgUser->isAnon() ? NULL : $wgUser->getName(),
+ 'wgUserGroups' => $wgUser->isAnon() ? NULL : $wgUser->getEffectiveGroups(),
'wgUserLanguage' => $wgLang->getCode(),
'wgContentLanguage' => $wgContLang->getCode(),
'wgBreakFrames' => $wgBreakFrames,
'wgCurRevisionId' => isset( $wgArticle ) ? $wgArticle->getLatest() : 0,
);
+ global $wgLivePreview;
+ if ( $wgLivePreview && $wgUser->getOption( 'uselivepreview' ) ) {
+ $vars['wgLivepreviewMessageLoading'] = wfMsg( 'livepreview-loading' );
+ $vars['wgLivepreviewMessageReady'] = wfMsg( 'livepreview-ready' );
+ $vars['wgLivepreviewMessageFailed'] = wfMsg( 'livepreview-failed' );
+ $vars['wgLivepreviewMessageError'] = wfMsg( 'livepreview-error' );
+ }
+
return self::makeVariablesScript( $vars );
}
@@ -392,7 +404,6 @@ class Skin extends Linker {
* @return string
*/
function getUserJs() {
- $fname = 'Skin::getUserJs';
wfProfileIn( __METHOD__ );
global $wgStylePath;
@@ -419,7 +430,7 @@ var wgAjaxWatch = {
wfProfileOut( __METHOD__ );
return $s;
- }
+ }
/**
* Return html code that include User stylesheets
@@ -505,7 +516,7 @@ END;
}
else $a = array( 'bgcolor' => '#FFFFFF' );
if($wgOut->isArticle() && $wgUser->getOption('editondblclick') &&
- $wgTitle->userCanEdit() ) {
+ $wgTitle->userCan( 'edit' ) ) {
$s = $wgTitle->getFullURL( $this->editUrlOptions() );
$s = 'document.location = "' .wfEscapeJSString( $s ) .'";';
$a += array ('ondblclick' => $s);
@@ -519,7 +530,7 @@ END;
$a['onload'] .= 'setupRightClickEdit()';
}
$a['class'] = 'ns-'.$wgTitle->getNamespace().' '.($wgContLang->isRTL() ? "rtl" : "ltr").
- ' '.Sanitizer::escapeId( 'page-'.$wgTitle->getPrefixedText() );
+ ' '.Sanitizer::escapeClass( 'page-'.$wgTitle->getPrefixedText() );
return $a;
}
@@ -844,14 +855,22 @@ END;
return $subpages;
}
+ /**
+ * Returns true if the IP should be shown in the header
+ */
+ function showIPinHeader() {
+ global $wgShowIPinHeader;
+ return $wgShowIPinHeader && session_id() != '';
+ }
+
function nameAndLogin() {
- global $wgUser, $wgTitle, $wgLang, $wgContLang, $wgShowIPinHeader;
+ global $wgUser, $wgTitle, $wgLang, $wgContLang;
$lo = $wgContLang->specialPage( 'Userlogout' );
$s = '';
if ( $wgUser->isAnon() ) {
- if( $wgShowIPinHeader && isset( $_COOKIE[ini_get('session.name')] ) ) {
+ if( $this->showIPinHeader() ) {
$n = wfGetIP();
$tl = $this->makeKnownLinkObj( $wgUser->getTalkPage(),
@@ -1041,7 +1060,7 @@ END;
}
if ($wgPageShowWatchingUsers && $wgUser->getOption( 'shownumberswatching' )) {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$watchlist = $dbr->tableName( 'watchlist' );
$sql = "SELECT COUNT(*) AS n FROM $watchlist
WHERE wl_title='" . $dbr->strencode($wgTitle->getDBKey()) .
@@ -1185,29 +1204,29 @@ END;
return $s;
}
- function privacyLink() {
- $privacy = wfMsg( 'privacy' );
- if ($privacy == '-') {
+ private function footerLink ( $desc, $page ) {
+ // if the link description has been set to "-" in the default language,
+ if ( wfMsgForContent( $desc ) == '-') {
+ // then it is disabled, for all languages.
return '';
} else {
- return $this->makeKnownLink( wfMsgForContent( 'privacypage' ), $privacy);
+ // Otherwise, we display the link for the user, described in their
+ // language (which may or may not be the same as the default language),
+ // but we make the link target be the one site-wide page.
+ return $this->makeKnownLink( wfMsgForContent( $page ), wfMsg( $desc ) );
}
}
+ function privacyLink() {
+ return $this->footerLink( 'privacy', 'privacypage' );
+ }
+
function aboutLink() {
- $s = $this->makeKnownLink( wfMsgForContent( 'aboutpage' ),
- wfMsg( 'aboutsite' ) );
- return $s;
+ return $this->footerLink( 'aboutsite', 'aboutpage' );
}
function disclaimerLink() {
- $disclaimers = wfMsg( 'disclaimers' );
- if ($disclaimers == '-') {
- return '';
- } else {
- return $this->makeKnownLink( wfMsgForContent( 'disclaimerpage' ),
- $disclaimers );
- }
+ return $this->footerLink( 'disclaimers', 'disclaimerpage' );
}
function editThisPage() {
@@ -1216,7 +1235,7 @@ END;
if ( ! $wgOut->isArticleRelated() ) {
$s = wfMsg( 'protectedpage' );
} else {
- if ( $wgTitle->userCanEdit() ) {
+ if ( $wgTitle->userCan( 'edit' ) ) {
$t = wfMsg( 'editthispage' );
} else {
$t = wfMsg( 'viewsource' );
@@ -1301,7 +1320,7 @@ END;
function moveThisPage() {
global $wgTitle;
- if ( $wgTitle->userCanMove() ) {
+ if ( $wgTitle->userCan( 'move' ) ) {
return $this->makeKnownLinkObj( SpecialPage::getTitleFor( 'Movepage' ),
wfMsg( 'movethispage' ), 'target=' . $wgTitle->getPrefixedURL() );
} else {
@@ -1503,7 +1522,7 @@ END;
/* these are used extensively in SkinTemplate, but also some other places */
static function makeMainPageUrl( $urlaction = '' ) {
$title = Title::newMainPage();
- self::checkTitle( $title, $name );
+ self::checkTitle( $title, '' );
return $title->getLocalURL( $urlaction );
}
@@ -1569,7 +1588,7 @@ END;
}
# make sure we have some title to operate on
- static function checkTitle( &$title, &$name ) {
+ static function checkTitle( &$title, $name ) {
if( !is_object( $title ) ) {
$title = Title::newFromText( $name );
if( !is_object( $title ) ) {
diff --git a/includes/SkinTemplate.php b/includes/SkinTemplate.php
index ff095477..cddd2195 100644
--- a/includes/SkinTemplate.php
+++ b/includes/SkinTemplate.php
@@ -18,25 +18,11 @@ if ( ! defined( 'MEDIAWIKI' ) )
# http://www.gnu.org/copyleft/gpl.html
/**
- * Template-filler skin base class
- * Formerly generic PHPTal (http://phptal.sourceforge.net/) skin
- * Based on Brion's smarty skin
- * Copyright (C) Gabriel Wicke -- http://www.aulinx.de/
- *
- * Todo: Needs some serious refactoring into functions that correspond
- * to the computations individual esi snippets need. Most importantly no body
- * parsing for most of those of course.
- *
- * @package MediaWiki
- * @subpackage Skins
- */
-
-/**
* Wrapper object for MediaWiki's localization functions,
* to be passed to the template engine.
*
* @private
- * @package MediaWiki
+ * @addtogroup Skins
*/
class MediaWiki_I18N {
var $_context = array();
@@ -68,8 +54,16 @@ class MediaWiki_I18N {
}
/**
+ * Template-filler skin base class
+ * Formerly generic PHPTal (http://phptal.sourceforge.net/) skin
+ * Based on Brion's smarty skin
+ * @copyright Copyright © Gabriel Wicke -- http://www.aulinx.de/
+ *
+ * @todo Needs some serious refactoring into functions that correspond
+ * to the computations individual esi snippets need. Most importantly no body
+ * parsing for most of those of course.
*
- * @package MediaWiki
+ * @addtogroup Skins
*/
class SkinTemplate extends Skin {
/**#@+
@@ -193,9 +187,9 @@ class SkinTemplate extends Skin {
$tpl->set( 'title', $wgOut->getPageTitle() );
$tpl->set( 'pagetitle', $wgOut->getHTMLTitle() );
$tpl->set( 'displaytitle', $wgOut->mPageLinkTitle );
- $tpl->set( 'pageclass', Sanitizer::escapeClass( 'page-'.$wgTitle->getPrefixedText() ) );
+ $tpl->set( 'pageclass', Sanitizer::escapeClass( 'page-'.$this->mTitle->getPrefixedText() ) );
- $nsname = isset( $wgCanonicalNamespaceNames[ $this->mTitle->getNamespace() ] ) ?
+ $nsname = isset( $wgCanonicalNamespaceNames[ $this->mTitle->getNamespace() ] ) ?
$wgCanonicalNamespaceNames[ $this->mTitle->getNamespace() ] :
$this->mTitle->getNsText();
@@ -339,8 +333,8 @@ class SkinTemplate extends Skin {
$tpl->setRef( 'newtalk', $ntl );
$tpl->setRef( 'skin', $this);
$tpl->set( 'logo', $this->logoText() );
- if ( $wgOut->isArticle() and (!isset( $oldid ) or isset( $diff )) and
- $wgArticle and 0 != $wgArticle->getID() )
+ if ( $wgOut->isArticle() and (!isset( $oldid ) or isset( $diff )) and
+ $wgArticle and 0 != $wgArticle->getID() )
{
if ( !$wgDisableCounters ) {
$viewcount = $wgLang->formatNum( $wgArticle->getCount() );
@@ -354,7 +348,7 @@ class SkinTemplate extends Skin {
}
if ($wgPageShowWatchingUsers) {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$watchlist = $dbr->tableName( 'watchlist' );
$sql = "SELECT COUNT(*) AS n FROM $watchlist
WHERE wl_title='" . $dbr->strencode($this->mTitle->getDBKey()) .
@@ -458,6 +452,11 @@ class SkinTemplate extends Skin {
$tpl->set( 'sidebar', $this->buildSidebar() );
$tpl->set( 'nav_urls', $this->buildNavUrls() );
+ // original version by hansm
+ if( !wfRunHooks( 'SkinTemplateOutputPageBeforeExec', array( &$this, &$tpl ) ) ) {
+ wfDebug( __METHOD__ . ': Hook SkinTemplateOutputPageBeforeExec broke outputPage execution!' );
+ }
+
// execute template
wfProfileIn( "$fname-execute" );
$res = $tpl->execute();
@@ -476,7 +475,7 @@ class SkinTemplate extends Skin {
* @param mixed $str
* @private
*/
- function printOrError( &$str ) {
+ function printOrError( $str ) {
echo $str;
}
@@ -486,7 +485,7 @@ class SkinTemplate extends Skin {
* @private
*/
function buildPersonalUrls() {
- global $wgTitle, $wgShowIPinHeader;
+ global $wgTitle, $wgRequest;
$fname = 'SkinTemplate::buildPersonalUrls';
$pageurl = $wgTitle->getLocalURL();
@@ -511,22 +510,37 @@ class SkinTemplate extends Skin {
$href = self::makeSpecialUrl( 'Preferences' );
$personal_urls['preferences'] = array(
'text' => wfMsg( 'mypreferences' ),
- 'href' => self::makeSpecialUrl( 'Preferences' ),
+ 'href' => $href,
'active' => ( $href == $pageurl )
);
$href = self::makeSpecialUrl( 'Watchlist' );
$personal_urls['watchlist'] = array(
- 'text' => wfMsg( 'watchlist' ),
+ 'text' => wfMsg( 'mywatchlist' ),
'href' => $href,
'active' => ( $href == $pageurl )
);
+
+ # We need to do an explicit check for Special:Contributions, as we
+ # have to match both the title, and the target (which could come
+ # from request values or be specified in "sub page" form. The plot
+ # thickens, because $wgTitle is altered for special pages, so doesn't
+ # contain the original alias-with-subpage.
+ $title = Title::newFromText( $wgRequest->getText( 'title' ) );
+ if( $title instanceof Title && $title->getNamespace() == NS_SPECIAL ) {
+ list( $spName, $spPar ) =
+ SpecialPage::resolveAliasWithSubpage( $title->getText() );
+ $active = $spName == 'Contributions'
+ && ( ( $spPar && $spPar == $this->username )
+ || $wgRequest->getText( 'target' ) == $this->username );
+ } else {
+ $active = false;
+ }
+
$href = self::makeSpecialUrlSubpage( 'Contributions', $this->username );
$personal_urls['mycontris'] = array(
'text' => wfMsg( 'mycontris' ),
'href' => $href,
- // FIXME # 'active' was disabed in r11346 with message: "disable bold link to my contributions; link was bold on all
- // Special:Contributions, not just current user's (fix me please!)". Until resolved, explicitly setting active to false.
- 'active' => false # ( ( $href == $pageurl . '/' . $this->username )
+ 'active' => $active
);
$personal_urls['logout'] = array(
'text' => wfMsg( 'userlogout' ),
@@ -536,7 +550,7 @@ class SkinTemplate extends Skin {
'active' => false
);
} else {
- if( $wgShowIPinHeader && isset( $_COOKIE[ini_get("session.name")] ) ) {
+ if( $this->showIPinHeader() ) {
$href = &$this->userpageUrlDetails['href'];
$personal_urls['anonuserpage'] = array(
'text' => $this->username,
@@ -555,7 +569,7 @@ class SkinTemplate extends Skin {
$personal_urls['anonlogin'] = array(
'text' => wfMsg('userlogin'),
'href' => self::makeSpecialUrl( 'Userlogin', 'returnto=' . $this->thisurl ),
- 'active' => $wgTitle->isSpecial( 'Userlogin' )
+ 'active' => $wgTitle->isSpecial( 'Userlogin' )
);
} else {
@@ -567,19 +581,11 @@ class SkinTemplate extends Skin {
}
}
- wfRunHooks( 'PersonalUrls', array( &$personal_urls, &$wgTitle ) );
+ wfRunHooks( 'PersonalUrls', array( &$personal_urls, &$wgTitle ) );
wfProfileOut( $fname );
return $personal_urls;
}
- /**
- * Returns true if the IP should be shown in the header
- */
- function showIPinHeader() {
- global $wgShowIPinHeader;
- return $wgShowIPinHeader && isset( $_COOKIE[ini_get("session.name")] );
- }
-
function tabAction( $title, $message, $selected, $query='', $checkEdit=false ) {
$classes = array();
if( $selected ) {
@@ -604,6 +610,9 @@ class SkinTemplate extends Skin {
function makeTalkUrlDetails( $name, $urlaction = '' ) {
$title = Title::newFromText( $name );
+ if( !is_object($title) ) {
+ throw new MWException( __METHOD__." given invalid pagename $name" );
+ }
$title = $title->getTalkPage();
self::checkTitle( $title, $name );
return array(
@@ -659,7 +668,7 @@ class SkinTemplate extends Skin {
true);
wfProfileIn( "$fname-edit" );
- if ( $this->mTitle->userCanEdit() && ( $this->mTitle->exists() || $this->mTitle->userCanCreate() ) ) {
+ if ( $this->mTitle->quickUserCan( 'edit' ) && ( $this->mTitle->exists() || $this->mTitle->quickUserCan( 'create' ) ) ) {
$istalk = $this->mTitle->isTalkPage();
$istalkclass = $istalk?' istalk':'';
$content_actions['edit'] = array(
@@ -716,7 +725,7 @@ class SkinTemplate extends Skin {
'href' => $this->mTitle->getLocalUrl( 'action=delete' )
);
}
- if ( $this->mTitle->userCanMove()) {
+ if ( $this->mTitle->quickUserCan( 'move' ) ) {
$moveTitle = SpecialPage::getTitleFor( 'Movepage', $this->thispage );
$content_actions['move'] = array(
'class' => $this->mTitle->isSpecial( 'Movepage' ) ? 'selected' : false,
@@ -755,6 +764,7 @@ class SkinTemplate extends Skin {
);
}
}
+
wfRunHooks( 'SkinTemplateTabs', array( &$this , &$content_actions ) ) ;
} else {
@@ -762,7 +772,7 @@ class SkinTemplate extends Skin {
$content_actions[$this->mTitle->getNamespaceKey()] = array(
'class' => 'selected',
- 'text' => wfMsg('specialpage'),
+ 'text' => wfMsg('nstab-special'),
'href' => $wgRequest->getRequestURL(), // @bug 2457, 2510
);
@@ -832,16 +842,14 @@ class SkinTemplate extends Skin {
// default permalink to being off, will override it as required below.
$nav_urls['permalink'] = false;
-
+
// A print stylesheet is attached to all pages, but nobody ever
// figures that out. :) Add a link...
if( $this->iscontent && ($action == '' || $action == 'view' || $action == 'purge' ) ) {
- $revid = $wgArticle ? $wgArticle->getLatest() : 0;
- if ( !( $revid == 0 ) )
- $nav_urls['print'] = array(
- 'text' => wfMsg( 'printableversion' ),
- 'href' => $wgRequest->appendQuery( 'printable=yes' )
- );
+ $nav_urls['print'] = array(
+ 'text' => wfMsg( 'printableversion' ),
+ 'href' => $wgRequest->appendQuery( 'printable=yes' )
+ );
// Also add a "permalink" while we're at it
if ( (int)$oldid ) {
@@ -850,6 +858,7 @@ class SkinTemplate extends Skin {
'href' => ''
);
} else {
+ $revid = $wgArticle ? $wgArticle->getLatest() : 0;
if ( !( $revid == 0 ) )
$nav_urls['permalink'] = array(
'text' => wfMsg( 'permalink' ),
@@ -894,7 +903,7 @@ class SkinTemplate extends Skin {
if ( $wgUser->isAllowed( 'block' ) ) {
$nav_urls['blockip'] = array(
'href' => self::makeSpecialUrlSubpage( 'Blockip', $this->mTitle->getText() )
- );
+ );
} else {
$nav_urls['blockip'] = false;
}
@@ -1010,7 +1019,7 @@ class SkinTemplate extends Skin {
wfProfileIn( $fname );
$out = false;
wfRunHooks( 'SkinTemplateSetupPageCss', array( &$out ) );
-
+
wfProfileOut( $fname );
return $out;
}
@@ -1065,8 +1074,7 @@ class SkinTemplate extends Skin {
/**
* Generic wrapper for template functions, with interface
* compatible with what we use of PHPTAL 0.7.
- * @package MediaWiki
- * @subpackage Skins
+ * @addtogroup Skins
*/
class QuickTemplate {
/**
diff --git a/includes/SpecialAllmessages.php b/includes/SpecialAllmessages.php
index a28ab3c2..0862cd17 100644
--- a/includes/SpecialAllmessages.php
+++ b/includes/SpecialAllmessages.php
@@ -1,8 +1,7 @@
<?php
/**
* Use this special page to get a list of the MediaWiki system messages.
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
@@ -89,8 +88,8 @@ function makeHTMLText( $messages ) {
global $wgLang, $wgContLang, $wgUser;
wfProfileIn( __METHOD__ );
- $sk =& $wgUser->getSkin();
- $talk = $wgLang->getNsText( NS_TALK );
+ $sk = $wgUser->getSkin();
+ $talk = wfMsg( 'talkpagelinktext' );
$input = wfElement( 'input', array(
'type' => 'text',
@@ -124,7 +123,7 @@ function makeHTMLText( $messages ) {
NS_MEDIAWIKI => array(),
NS_MEDIAWIKI_TALK => array()
);
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$page = $dbr->tableName( 'page' );
$sql = "SELECT page_namespace,page_title FROM $page WHERE page_namespace IN (" . NS_MEDIAWIKI . ", " . NS_MEDIAWIKI_TALK . ")";
$res = $dbr->query( $sql );
diff --git a/includes/SpecialAllpages.php b/includes/SpecialAllpages.php
index 737e6834..03e164bd 100644
--- a/includes/SpecialAllpages.php
+++ b/includes/SpecialAllpages.php
@@ -1,13 +1,12 @@
<?php
/**
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
* Entry point : initialise variables and call subfunctions.
* @param $par String: becomes "FOO" when called like Special:Allpages/FOO (default NULL)
- * @param $specialPage @see SpecialPage object.
+ * @param $specialPage See the SpecialPage object.
*/
function wfSpecialAllpages( $par=NULL, $specialPage ) {
global $wgRequest, $wgOut, $wgContLang;
@@ -37,6 +36,10 @@ function wfSpecialAllpages( $par=NULL, $specialPage ) {
}
}
+/**
+ * Implements Special:Allpages
+ * @addtogroup SpecialPage
+ */
class SpecialAllpages {
var $maxPerPage=960;
var $topLevelMax=50;
@@ -89,7 +92,7 @@ function showToplevel ( $namespace = NS_MAIN, $including = false ) {
# TODO: Either make this *much* faster or cache the title index points
# in the querycache table.
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$out = "";
$where = array( 'page_namespace' => $namespace );
@@ -217,7 +220,7 @@ function showChunk( $namespace = NS_MAIN, $from, $including = false ) {
} else {
list( $namespace, $fromKey, $from ) = $fromList;
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'page',
array( 'page_namespace', 'page_title', 'page_is_redirect' ),
array(
@@ -261,31 +264,35 @@ function showChunk( $namespace = NS_MAIN, $from, $including = false ) {
if ( $including ) {
$out2 = '';
} else {
-
- # Get the last title from previous chunk
- $dbr =& wfGetDB( DB_SLAVE );
- $res_prev = $dbr->select(
- 'page',
- 'page_title',
- array( 'page_namespace' => $namespace, 'page_title < '.$dbr->addQuotes($from) ),
- $fname,
- array( 'ORDER BY' => 'page_title DESC', 'LIMIT' => $this->maxPerPage, 'OFFSET' => ($this->maxPerPage - 1 ) )
- );
-
- # Get first title of previous complete chunk
- if( $dbr->numrows( $res_prev ) >= $this->maxPerPage ) {
- $pt = $dbr->fetchObject( $res_prev );
- $prevTitle = Title::makeTitle( $namespace, $pt->page_title );
+ if( $from == '' ) {
+ // First chunk; no previous link.
+ $prevTitle = null;
} else {
- # The previous chunk is not complete, need to link to the very first title
- # available in the database
- $reallyFirstPage_title = $dbr->selectField( 'page', 'page_title', array( 'page_namespace' => $namespace ), $fname, array( 'LIMIT' => 1) );
+ # Get the last title from previous chunk
+ $dbr = wfGetDB( DB_SLAVE );
+ $res_prev = $dbr->select(
+ 'page',
+ 'page_title',
+ array( 'page_namespace' => $namespace, 'page_title < '.$dbr->addQuotes($from) ),
+ $fname,
+ array( 'ORDER BY' => 'page_title DESC', 'LIMIT' => $this->maxPerPage, 'OFFSET' => ($this->maxPerPage - 1 ) )
+ );
- # Show the previous link if it s not the current requested chunk
- if( $from != $reallyFirstPage_title ) {
- $prevTitle = Title::makeTitle( $namespace, $reallyFirstPage_title );
+ # Get first title of previous complete chunk
+ if( $dbr->numrows( $res_prev ) >= $this->maxPerPage ) {
+ $pt = $dbr->fetchObject( $res_prev );
+ $prevTitle = Title::makeTitle( $namespace, $pt->page_title );
} else {
- $prevTitle = null;
+ # The previous chunk is not complete, need to link to the very first title
+ # available in the database
+ $reallyFirstPage_title = $dbr->selectField( 'page', 'page_title', array( 'page_namespace' => $namespace ), $fname, array( 'LIMIT' => 1) );
+
+ # Show the previous link if it s not the current requested chunk
+ if( $from != $reallyFirstPage_title ) {
+ $prevTitle = Title::makeTitle( $namespace, $reallyFirstPage_title );
+ } else {
+ $prevTitle = null;
+ }
}
}
diff --git a/includes/SpecialAncientpages.php b/includes/SpecialAncientpages.php
index 39a3c8ea..c0bbb7ba 100644
--- a/includes/SpecialAncientpages.php
+++ b/includes/SpecialAncientpages.php
@@ -1,14 +1,12 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
+ * Implements Special:Ancientpages
+ * @addtogroup SpecialPage
*/
class AncientPagesPage extends QueryPage {
@@ -24,7 +22,7 @@ class AncientPagesPage extends QueryPage {
function getSQL() {
global $wgDBtype;
- $db =& wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_SLAVE );
$page = $db->tableName( 'page' );
$revision = $db->tableName( 'revision' );
#$use_index = $db->useIndexClause( 'cur_timestamp' ); # FIXME! this is gone
diff --git a/includes/SpecialBlockip.php b/includes/SpecialBlockip.php
index 626922bb..5f47fa13 100644
--- a/includes/SpecialBlockip.php
+++ b/includes/SpecialBlockip.php
@@ -2,8 +2,7 @@
/**
* Constructor for Special:Blockip page
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
@@ -12,6 +11,13 @@
function wfSpecialBlockip( $par ) {
global $wgUser, $wgOut, $wgRequest;
+ # Can't block when the database is locked
+ if( wfReadOnly() ) {
+ $wgOut->readOnlyPage();
+ return;
+ }
+
+ # Permission check
if( !$wgUser->isAllowed( 'block' ) ) {
$wgOut->permissionRequired( 'block' );
return;
@@ -31,28 +37,31 @@ function wfSpecialBlockip( $par ) {
}
/**
- * Form object
+ * Form object for the Special:Blockip page.
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
class IPBlockForm {
var $BlockAddress, $BlockExpiry, $BlockReason;
function IPBlockForm( $par ) {
- global $wgRequest;
+ global $wgRequest, $wgUser;
$this->BlockAddress = $wgRequest->getVal( 'wpBlockAddress', $wgRequest->getVal( 'ip', $par ) );
+ $this->BlockAddress = strtr( $this->BlockAddress, '_', ' ' );
$this->BlockReason = $wgRequest->getText( 'wpBlockReason' );
+ $this->BlockReasonList = $wgRequest->getText( 'wpBlockReasonList' );
$this->BlockExpiry = $wgRequest->getVal( 'wpBlockExpiry', wfMsg('ipbotheroption') );
$this->BlockOther = $wgRequest->getVal( 'wpBlockOther', '' );
- # Unchecked checkboxes are not included in the form data at all, so having one
+ # Unchecked checkboxes are not included in the form data at all, so having one
# that is true by default is a bit tricky
$byDefault = !$wgRequest->wasPosted();
$this->BlockAnonOnly = $wgRequest->getBool( 'wpAnonOnly', $byDefault );
$this->BlockCreateAccount = $wgRequest->getBool( 'wpCreateAccount', $byDefault );
$this->BlockEnableAutoblock = $wgRequest->getBool( 'wpEnableAutoblock', $byDefault );
+ # Re-check user's rights to hide names, very serious, defaults to 0
+ $this->BlockHideName = $wgRequest->getBool( 'wpHideName', 0 ) && $wgUser->isAllowed( 'hideuser' );
}
function showForm( $err ) {
@@ -62,15 +71,17 @@ class IPBlockForm {
$wgOut->addWikiText( wfMsg( 'blockiptext' ) );
if($wgSysopUserBans) {
- $mIpaddress = wfMsgHtml( 'ipadressorusername' );
+ $mIpaddress = Xml::label( wfMsg( 'ipadressorusername' ), 'mw-bi-target' );
} else {
- $mIpaddress = wfMsgHtml( 'ipaddress' );
+ $mIpaddress = Xml::label( wfMsg( 'ipadress' ), 'mw-bi-target' );
}
- $mIpbexpiry = wfMsgHtml( 'ipbexpiry' );
- $mIpbother = wfMsgHtml( 'ipbother' );
+ $mIpbexpiry = Xml::label( wfMsg( 'ipbexpiry' ), 'wpBlockExpiry' );
+ $mIpbother = Xml::label( wfMsg( 'ipbother' ), 'mw-bi-other' );
$mIpbothertime = wfMsgHtml( 'ipbotheroption' );
- $mIpbreason = wfMsgHtml( 'ipbreason' );
- $mIpbsubmit = wfMsgHtml( 'ipbsubmit' );
+ $mIpbreasonother = Xml::label( wfMsg( 'ipbreason' ), 'wpBlockReasonList' );
+ $mIpbreason = Xml::label( wfMsg( 'ipbotherreason' ), 'mw-bi-reason' );
+ $mIpbreasonotherlist = wfMsgHtml( 'ipbreasonotherlist' );
+
$titleObj = SpecialPage::getTitleFor( 'Blockip' );
$action = $titleObj->escapeLocalURL( "action=submit" );
@@ -79,10 +90,7 @@ class IPBlockForm {
$wgOut->addHTML( "<p class='error'>{$err}</p>\n" );
}
- $scBlockAddress = htmlspecialchars( $this->BlockAddress );
- $scBlockReason = htmlspecialchars( $this->BlockReason );
- $scBlockOtherTime = htmlspecialchars( $this->BlockOther );
- $scBlockExpiryOptions = htmlspecialchars( wfMsgForContent( 'ipboptions' ) );
+ $scBlockExpiryOptions = wfMsgForContent( 'ipboptions' );
$showblockoptions = $scBlockExpiryOptions != '-';
if (!$showblockoptions)
@@ -100,15 +108,55 @@ class IPBlockForm {
$blockExpiryFormOptions .= "<option value=\"$value\"$selected>$show</option>";
}
+ $scBlockReasonList = wfMsgForContent( 'ipbreason-dropdown' );
+ $blockReasonList = '';
+ if ( $scBlockReasonList != '' && $scBlockReasonList != '-' ) {
+ $blockReasonList = "<option value=\"other\">$mIpbreasonotherlist</option>";
+ $optgroup = "";
+ foreach ( explode( "\n", $scBlockReasonList ) as $option) {
+ $value = trim( htmlspecialchars($option) );
+ if ( $value == '' ) {
+ continue;
+ } elseif ( substr( $value, 0, 1) == '*' && substr( $value, 1, 1) != '*' ) {
+ // A new group is starting ...
+ $value = trim( substr( $value, 1 ) );
+ $blockReasonList .= "$optgroup<optgroup label=\"$value\">";
+ $optgroup = "</optgroup>";
+ } elseif ( substr( $value, 0, 2) == '**' ) {
+ // groupmember
+ $selected = "";
+ $value = trim( substr( $value, 2 ) );
+ if ( $this->BlockReasonList === $value)
+ $selected = ' selected="selected"';
+ $blockReasonList .= "<option value=\"$value\"$selected>$value</option>";
+ } else {
+ // groupless block reason
+ $selected = "";
+ if ( $this->BlockReasonList === $value)
+ $selected = ' selected="selected"';
+ $blockReasonList .= "$optgroup<option value=\"$value\"$selected>$value</option>";
+ $optgroup = "";
+ }
+ }
+ $blockReasonList .= $optgroup;
+ }
+
$token = htmlspecialchars( $wgUser->editToken() );
+ global $wgStylePath, $wgStyleVersion;
$wgOut->addHTML( "
+<script type=\"text/javascript\" src=\"$wgStylePath/common/block.js?$wgStyleVersion\">
+</script>
<form id=\"blockip\" method=\"post\" action=\"{$action}\">
<table border='0'>
<tr>
<td align=\"right\">{$mIpaddress}:</td>
<td align=\"left\">
- <input tabindex='1' type='text' size='40' name=\"wpBlockAddress\" value=\"{$scBlockAddress}\" />
+ " . Xml::input( 'wpBlockAddress', 45, $this->BlockAddress,
+ array(
+ 'tabindex' => '1',
+ 'id' => 'mw-bi-target',
+ 'onchange' => 'updateBlockOptions()' ) ) . "
</td>
</tr>
<tr>");
@@ -127,71 +175,124 @@ class IPBlockForm {
<tr id='wpBlockOther'>
<td align=\"right\">{$mIpbother}:</td>
<td align=\"left\">
- <input tabindex='3' type='text' size='40' name=\"wpBlockOther\" value=\"{$scBlockOtherTime}\" />
+ " . Xml::input( 'wpBlockOther', 45, $this->BlockOther,
+ array( 'tabindex' => '3', 'id' => 'mw-bi-other' ) ) . "
</td>
- </tr>
- <tr>
+ </tr>");
+ if ( $blockReasonList != '' ) {
+ $wgOut->addHTML("
+ <tr>
+ <td align=\"right\">{$mIpbreasonother}:</td>
+ <td align=\"left\">
+ <select tabindex='4' id=\"wpBlockReasonList\" name=\"wpBlockReasonList\">
+ $blockReasonList
+ </select>
+ </td>
+ </tr>");
+ }
+ $wgOut->addHTML("
+ <tr id=\"wpBlockReason\">
<td align=\"right\">{$mIpbreason}:</td>
<td align=\"left\">
- <input tabindex='3' type='text' size='40' name=\"wpBlockReason\" value=\"{$scBlockReason}\" />
+ " . Xml::input( 'wpBlockReason', 45, $this->BlockReason,
+ array( 'tabindex' => '5', 'id' => 'mw-bi-reason' ) ) . "
</td>
</tr>
- <tr>
+ <tr id='wpAnonOnlyRow'>
<td>&nbsp;</td>
<td align=\"left\">
- " . wfCheckLabel( wfMsg( 'ipbanononly' ),
+ " . wfCheckLabel( wfMsgHtml( 'ipbanononly' ),
'wpAnonOnly', 'wpAnonOnly', $this->BlockAnonOnly,
- array( 'tabindex' => 4 ) ) . "
+ array( 'tabindex' => '6' ) ) . "
</td>
</tr>
- <tr>
+ <tr id='wpCreateAccountRow'>
<td>&nbsp;</td>
<td align=\"left\">
- " . wfCheckLabel( wfMsg( 'ipbcreateaccount' ),
+ " . wfCheckLabel( wfMsgHtml( 'ipbcreateaccount' ),
'wpCreateAccount', 'wpCreateAccount', $this->BlockCreateAccount,
- array( 'tabindex' => 5 ) ) . "
+ array( 'tabindex' => '7' ) ) . "
</td>
</tr>
- <tr>
- <td>&nbsp;</td>
- <td align=\"left\">
- " . wfCheckLabel( wfMsg( 'ipbenableautoblock' ),
- 'wpEnableAutoblock', 'wpEnableAutoblock', $this->BlockEnableAutoblock,
- array( 'tabindex' => 6 ) ) . "
- </td>
- </tr>
+ <tr id='wpEnableAutoblockRow'>
+ <td>&nbsp;</td>
+ <td align=\"left\">
+ " . wfCheckLabel( wfMsgHtml( 'ipbenableautoblock' ),
+ 'wpEnableAutoblock', 'wpEnableAutoblock', $this->BlockEnableAutoblock,
+ array( 'tabindex' => '8' ) ) . "
+ </td>
+ </tr>
+ ");
+ // Allow some users to hide name from block log, blocklist and listusers
+ if ( $wgUser->isAllowed( 'hideuser' ) ) {
+ $wgOut->addHTML("
+ <tr>
+ <td>&nbsp;</td>
+ <td align=\"left\">
+ " . wfCheckLabel( wfMsgHtml( 'ipbhidename' ),
+ 'wpHideName', 'wpHideName', $this->BlockHideName,
+ array( 'tabindex' => '9' ) ) . "
+ </td>
+ </tr>
+ ");
+ }
+ $wgOut->addHTML("
<tr>
<td style='padding-top: 1em'>&nbsp;</td>
<td style='padding-top: 1em' align=\"left\">
- <input tabindex='7' type='submit' name=\"wpBlock\" value=\"{$mIpbsubmit}\" />
+ " . Xml::submitButton( wfMsgHtml( 'ipbsubmit' ),
+ array( 'name' => 'wpBlock', 'tabindex' => '10' ) ) . "
</td>
</tr>
- </table>
- <input type='hidden' name='wpEditToken' value=\"{$token}\" />
-</form>\n" );
+ </table>" .
+ Xml::hidden( 'wpEditToken', $token ) .
+"</form>
+<script type=\"text/javascript\">updateBlockOptions()</script>
+\n" );
+
+ $wgOut->addHtml( $this->getConvenienceLinks() );
$user = User::newFromName( $this->BlockAddress );
if( is_object( $user ) ) {
$this->showLogFragment( $wgOut, $user->getUserPage() );
} elseif( preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $this->BlockAddress ) ) {
$this->showLogFragment( $wgOut, Title::makeTitle( NS_USER, $this->BlockAddress ) );
+ } elseif( preg_match( '/^\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}/', $this->BlockAddress ) ) {
+ $this->showLogFragment( $wgOut, Title::makeTitle( NS_USER, $this->BlockAddress ) );
}
-
}
function doSubmit() {
global $wgOut, $wgUser, $wgSysopUserBans, $wgSysopRangeBans;
$userId = 0;
- $this->BlockAddress = trim( $this->BlockAddress );
- $rxIP = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';
-
+ # Expand valid IPv6 addresses, usernames are left as is
+ $this->BlockAddress = IP::sanitizeIP( $this->BlockAddress );
+ # isIPv4() and IPv6() are used for final validation
+ $rxIP4 = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';
+ $rxIP6 = '\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}';
+ $rxIP = "($rxIP4|$rxIP6)";
+
# Check for invalid specifications
- if ( ! preg_match( "/^$rxIP$/", $this->BlockAddress ) ) {
+ if ( !preg_match( "/^$rxIP$/", $this->BlockAddress ) ) {
$matches = array();
- if ( preg_match( "/^($rxIP)\\/(\\d{1,2})$/", $this->BlockAddress, $matches ) ) {
+ if ( preg_match( "/^($rxIP4)\\/(\\d{1,2})$/", $this->BlockAddress, $matches ) ) {
+ # IPv4
if ( $wgSysopRangeBans ) {
- if ( $matches[2] > 31 || $matches[2] < 16 ) {
+ if ( !IP::isIPv4( $this->BlockAddress ) || $matches[2] < 16 || $matches[2] > 32 ) {
+ $this->showForm( wfMsg( 'ip_range_invalid' ) );
+ return;
+ }
+ $this->BlockAddress = Block::normaliseRange( $this->BlockAddress );
+ } else {
+ # Range block illegal
+ $this->showForm( wfMsg( 'range_block_disabled' ) );
+ return;
+ }
+ } else if ( preg_match( "/^($rxIP6)\\/(\\d{1,3})$/", $this->BlockAddress, $matches ) ) {
+ # IPv6
+ if ( $wgSysopRangeBans ) {
+ if ( !IP::isIPv6( $this->BlockAddress ) || $matches[2] < 64 || $matches[2] > 128 ) {
$this->showForm( wfMsg( 'ip_range_invalid' ) );
return;
}
@@ -220,6 +321,14 @@ class IPBlockForm {
}
}
+ $reasonstr = $this->BlockReasonList;
+ if ( $reasonstr != 'other' && $this->BlockReason != '') {
+ // Entry from drop down menu + additional comment
+ $reasonstr .= ': ' . $this->BlockReason;
+ } elseif ( $reasonstr == 'other' ) {
+ $reasonstr = $this->BlockReason;
+ }
+
$expirestr = $this->BlockExpiry;
if( $expirestr == 'other' )
$expirestr = $this->BlockOther;
@@ -247,23 +356,29 @@ class IPBlockForm {
# Note: for a user block, ipb_address is only for display purposes
$block = new Block( $this->BlockAddress, $userId, $wgUser->getID(),
- $this->BlockReason, wfTimestampNow(), 0, $expiry, $this->BlockAnonOnly,
- $this->BlockCreateAccount, $this->BlockEnableAutoblock );
+ $reasonstr, wfTimestampNow(), 0, $expiry, $this->BlockAnonOnly,
+ $this->BlockCreateAccount, $this->BlockEnableAutoblock, $this->BlockHideName);
if (wfRunHooks('BlockIp', array(&$block, &$wgUser))) {
if ( !$block->insert() ) {
- $this->showForm( wfMsg( 'ipb_already_blocked',
+ $this->showForm( wfMsg( 'ipb_already_blocked',
htmlspecialchars( $this->BlockAddress ) ) );
return;
}
wfRunHooks('BlockIpComplete', array($block, $wgUser));
- # Make log entry
- $log = new LogPage( 'block' );
+ # Prepare log parameters
+ $logParams = array();
+ $logParams[] = $expirestr;
+ $logParams[] = $this->blockLogFlags();
+
+ # Make log entry, if the name is hidden, put it in the oversight log
+ $log_type = ($this->BlockHideName) ? 'oversight' : 'block';
+ $log = new LogPage( $log_type );
$log->addEntry( 'block', Title::makeTitle( NS_USER, $this->BlockAddress ),
- $this->BlockReason, $expirestr );
+ $reasonstr, $logParams );
# Report to the user
$titleObj = SpecialPage::getTitleFor( 'Blockip' );
@@ -280,14 +395,80 @@ class IPBlockForm {
$text = wfMsg( 'blockipsuccesstext', $this->BlockAddress );
$wgOut->addWikiText( $text );
}
-
+
function showLogFragment( $out, $title ) {
$out->addHtml( wfElement( 'h2', NULL, LogPage::logName( 'block' ) ) );
$request = new FauxRequest( array( 'page' => $title->getPrefixedText(), 'type' => 'block' ) );
$viewer = new LogViewer( new LogReader( $request ) );
$viewer->showList( $out );
}
-
-}
+ /**
+ * Return a comma-delimited list of "flags" to be passed to the log
+ * reader for this block, to provide more information in the logs
+ *
+ * @return array
+ */
+ private function blockLogFlags() {
+ $flags = array();
+ if( $this->BlockAnonOnly && IP::isIPAddress( $this->BlockAddress ) )
+ // when blocking a user the option 'anononly' is not available/has no effect -> do not write this into log
+ $flags[] = 'anononly';
+ if( $this->BlockCreateAccount )
+ $flags[] = 'nocreate';
+ if( !$this->BlockEnableAutoblock )
+ $flags[] = 'noautoblock';
+ return implode( ',', $flags );
+ }
+
+ /**
+ * Builds unblock and block list links
+ *
+ * @return string
+ */
+ private function getConvenienceLinks() {
+ global $wgUser;
+ $skin = $wgUser->getSkin();
+ $links[] = $skin->makeLink ( 'MediaWiki:ipbreason-dropdown', wfMsgHtml( 'ipb-edit-dropdown' ) );
+ $links[] = $this->getUnblockLink( $skin );
+ $links[] = $this->getBlockListLink( $skin );
+ return '<p class="mw-ipb-conveniencelinks">' . implode( ' | ', $links ) . '</p>';
+ }
+
+ /**
+ * Build a convenient link to unblock the given username or IP
+ * address, if available; otherwise link to a blank unblock
+ * form
+ *
+ * @param $skin Skin to use
+ * @return string
+ */
+ private function getUnblockLink( $skin ) {
+ $list = SpecialPage::getTitleFor( 'Ipblocklist' );
+ if( $this->BlockAddress ) {
+ $addr = htmlspecialchars( strtr( $this->BlockAddress, '_', ' ' ) );
+ return $skin->makeKnownLinkObj( $list, wfMsgHtml( 'ipb-unblock-addr', $addr ),
+ 'action=unblock&ip=' . urlencode( $this->BlockAddress ) );
+ } else {
+ return $skin->makeKnownLinkObj( $list, wfMsgHtml( 'ipb-unblock' ), 'action=unblock' );
+ }
+ }
+
+ /**
+ * Build a convenience link to the block list
+ *
+ * @param $skin Skin to use
+ * @return string
+ */
+ private function getBlockListLink( $skin ) {
+ $list = SpecialPage::getTitleFor( 'Ipblocklist' );
+ if( $this->BlockAddress ) {
+ $addr = htmlspecialchars( strtr( $this->BlockAddress, '_', ' ' ) );
+ return $skin->makeKnownLinkObj( $list, wfMsgHtml( 'ipb-blocklist-addr', $addr ),
+ 'ip=' . urlencode( $this->BlockAddress ) );
+ } else {
+ return $skin->makeKnownLinkObj( $list, wfMsgHtml( 'ipb-blocklist' ) );
+ }
+ }
+}
?>
diff --git a/includes/SpecialBlockme.php b/includes/SpecialBlockme.php
index 5bfce4ee..c2cb1a58 100644
--- a/includes/SpecialBlockme.php
+++ b/includes/SpecialBlockme.php
@@ -1,8 +1,7 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
diff --git a/includes/SpecialBooksources.php b/includes/SpecialBooksources.php
index 5c047fbe..d3136ea4 100644
--- a/includes/SpecialBooksources.php
+++ b/includes/SpecialBooksources.php
@@ -4,8 +4,7 @@
* Special page outputs information on sourcing a book with a particular ISBN
* The parser creates links to this page when dealing with ISBNs in wikitext
*
- * @package MediaWiki
- * @subpackage Special pages
+ * @addtogroup SpecialPage
* @author Rob Church <robchur@gmail.com>
* @todo Validate ISBNs using the standard check-digit method
*/
@@ -34,7 +33,7 @@ class SpecialBookSources extends SpecialPage {
$this->isbn = $this->cleanIsbn( $isbn ? $isbn : $wgRequest->getText( 'isbn' ) );
$wgOut->addWikiText( wfMsgNoTrans( 'booksources-summary' ) );
$wgOut->addHtml( $this->makeForm() );
- if( strlen( $this->isbn) > 0 )
+ if( strlen( $this->isbn ) > 0 )
$this->showList();
}
@@ -75,6 +74,10 @@ class SpecialBookSources extends SpecialPage {
private function showList() {
global $wgOut, $wgContLang;
+ # Hook to allow extensions to insert additional HTML,
+ # e.g. for API-interacting plugins and so on
+ wfRunHooks( 'BookInformation', array( $this->isbn, &$wgOut ) );
+
# Check for a local page such as Project:Book_sources and use that if available
$title = Title::makeTitleSafe( NS_PROJECT, wfMsg( 'booksources' ) ); # Should this be wfMsgForContent()? -- RC
if( is_object( $title ) && $title->exists() ) {
diff --git a/includes/SpecialBrokenRedirects.php b/includes/SpecialBrokenRedirects.php
index 50935654..208a7e1f 100644
--- a/includes/SpecialBrokenRedirects.php
+++ b/includes/SpecialBrokenRedirects.php
@@ -1,14 +1,13 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
+ * A special page listing redirects to non existent page. Those should be
+ * fixed to point to an existing page.
+ * @addtogroup SpecialPage
*/
class BrokenRedirectsPage extends PageQueryPage {
var $targets = array();
@@ -26,17 +25,17 @@ class BrokenRedirectsPage extends PageQueryPage {
}
function getSQL() {
- $dbr =& wfGetDB( DB_SLAVE );
- list( $page, $pagelinks ) = $dbr->tableNamesN( 'page', 'pagelinks' );
+ $dbr = wfGetDB( DB_SLAVE );
+ list( $page, $redirect ) = $dbr->tableNamesN( 'page', 'redirect' );
$sql = "SELECT 'BrokenRedirects' AS type,
p1.page_namespace AS namespace,
p1.page_title AS title,
- pl_namespace,
- pl_title
- FROM $pagelinks AS pl
- JOIN $page p1 ON (p1.page_is_redirect=1 AND pl.pl_from=p1.page_id)
- LEFT JOIN $page AS p2 ON (pl_namespace=p2.page_namespace AND pl_title=p2.page_title )
+ rd_namespace,
+ rd_title
+ FROM $redirect AS rd
+ JOIN $page p1 ON (rd.rd_from=p1.page_id)
+ LEFT JOIN $page AS p2 ON (rd_namespace=p2.page_namespace AND rd_title=p2.page_title )
WHERE p2.page_namespace IS NULL";
return $sql;
}
@@ -46,11 +45,11 @@ class BrokenRedirectsPage extends PageQueryPage {
}
function formatResult( $skin, $result ) {
- global $wgContLang;
+ global $wgUser, $wgContLang;
$fromObj = Title::makeTitle( $result->namespace, $result->title );
- if ( isset( $result->pl_title ) ) {
- $toObj = Title::makeTitle( $result->pl_namespace, $result->pl_title );
+ if ( isset( $result->rd_title ) ) {
+ $toObj = Title::makeTitle( $result->rd_namespace, $result->rd_title );
} else {
$blinks = $fromObj->getBrokenLinksFrom();
if ( $blinks ) {
@@ -66,11 +65,19 @@ class BrokenRedirectsPage extends PageQueryPage {
}
$from = $skin->makeKnownLinkObj( $fromObj ,'', 'redirect=no' );
- $edit = $skin->makeBrokenLinkObj( $fromObj , "(".wfMsg("qbedit").")" , 'redirect=no');
+ $edit = $skin->makeKnownLinkObj( $fromObj, wfMsgHtml( 'brokenredirects-edit' ), 'action=edit' );
$to = $skin->makeBrokenLinkObj( $toObj );
$arr = $wgContLang->getArrow();
-
- return "$from $edit $arr $to";
+
+ $out = "{$from} {$edit}";
+
+ if( $wgUser->isAllowed( 'delete' ) ) {
+ $delete = $skin->makeKnownLinkObj( $fromObj, wfMsgHtml( 'brokenredirects-delete' ), 'action=delete' );
+ $out .= " {$delete}";
+ }
+
+ $out .= " {$arr} {$to}";
+ return $out;
}
}
diff --git a/includes/SpecialCategories.php b/includes/SpecialCategories.php
index 346eac63..45e1ae6c 100644
--- a/includes/SpecialCategories.php
+++ b/includes/SpecialCategories.php
@@ -1,69 +1,66 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
-/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
- */
-class CategoriesPage extends QueryPage {
-
- function getName() {
- return "Categories";
- }
-
- function isExpensive() {
- return false;
- }
+function wfSpecialCategories() {
+ global $wgOut;
- function isSyndicated() { return false; }
+ $cap = new CategoryPager();
+ $wgOut->addHTML(
+ wfMsgWikiHtml( 'categoriespagetext' ) .
+ $cap->getNavigationBar()
+ . '<ul>' . $cap->getBody() . '</ul>' .
+ $cap->getNavigationBar()
+ );
+}
- function getPageHeader() {
- return wfMsgWikiHtml( 'categoriespagetext' );
+/**
+ * @addtogroup SpecialPage
+ * @addtogroup Pager
+ */
+class CategoryPager extends AlphabeticPager {
+ function getQueryInfo() {
+ return array(
+ 'tables' => array('categorylinks'),
+ 'fields' => array('cl_to','count(*) AS count'),
+ 'options' => array('GROUP BY' => 'cl_to')
+ );
}
- function getSQL() {
- $NScat = NS_CATEGORY;
- $dbr =& wfGetDB( DB_SLAVE );
- $categorylinks = $dbr->tableName( 'categorylinks' );
- $implicit_groupby = $dbr->implicitGroupby() ? '1' : 'cl_to';
- $s= "SELECT 'Categories' as type,
- {$NScat} as namespace,
- cl_to as title,
- $implicit_groupby as value,
- COUNT(*) as count
- FROM $categorylinks
- GROUP BY 1,2,3,4";
- return $s;
+ function getIndexField() {
+ return "cl_to";
}
-
- function sortDescending() {
- return false;
+
+ /* Override getBody to apply LinksBatch on resultset before actually outputting anything. */
+ function getBody() {
+ if (!$this->mQueryDone) {
+ $this->doQuery();
+ }
+ $batch = new LinkBatch;
+
+ $this->mResult->rewind();
+
+ while ( $row = $this->mResult->fetchObject() ) {
+ $batch->addObj( Title::makeTitleSafe( NS_CATEGORY, $row->cl_to ) );
+ }
+ $batch->execute();
+ $this->mResult->rewind();
+ return parent::getBody();
}
-
- function formatResult( $skin, $result ) {
+
+ function formatRow($result) {
global $wgLang;
- $title = Title::makeTitle( NS_CATEGORY, $result->title );
- $plink = $skin->makeLinkObj( $title, $title->getText() );
- $nlinks = wfMsgExt( 'nmembers', array( 'parsemag', 'escape'),
- $wgLang->formatNum( $result->count ) );
- return wfSpecialList($plink, $nlinks);
+ $title = Title::makeTitle( NS_CATEGORY, $result->cl_to );
+ return (
+ '<li>' .
+ $this->getSkin()->makeLinkObj( $title, $title->getText() )
+ . ' ' .
+ wfMsgExt( 'nmembers', array( 'parsemag', 'escape'),
+ $wgLang->formatNum( $result->count ) )
+ . "</li>\n" );
}
}
-/**
- *
- */
-function wfSpecialCategories() {
- list( $limit, $offset ) = wfCheckLimits();
-
- $cap = new CategoriesPage();
-
- return $cap->doQuery( $offset, $limit );
-}
-
?>
diff --git a/includes/SpecialConfirmemail.php b/includes/SpecialConfirmemail.php
index e64232aa..58e55899 100644
--- a/includes/SpecialConfirmemail.php
+++ b/includes/SpecialConfirmemail.php
@@ -1,15 +1,6 @@
<?php
/**
- * Special page allows users to request email confirmation message, and handles
- * processing of the confirmation code when the link in the email is followed
- *
- * @package MediaWiki
- * @subpackage Special pages
- * @author Rob Church <robchur@gmail.com>
- */
-
-/**
* Main execution point
*
* @param $par Parameters passed to the page
@@ -19,6 +10,13 @@ function wfSpecialConfirmemail( $par ) {
$form->execute( $par );
}
+/**
+ * Special page allows users to request email confirmation message, and handles
+ * processing of the confirmation code when the link in the email is followed
+ *
+ * @addtogroup SpecialPage
+ * @author Rob Church <robchur@gmail.com>
+ */
class EmailConfirmation extends SpecialPage {
/**
diff --git a/includes/SpecialContributions.php b/includes/SpecialContributions.php
index 0a1ef6ee..82c8d608 100644
--- a/includes/SpecialContributions.php
+++ b/includes/SpecialContributions.php
@@ -1,188 +1,165 @@
<?php
/**
- * @package MediaWiki
- * @subpackage SpecialPage
+ * Special:Contributions, show user contributions in a paged list
+ * @addtogroup SpecialPage
*/
-/** @package MediaWiki */
-class ContribsFinder {
- var $username, $offset, $limit, $namespace;
- var $dbr;
+class ContribsPager extends IndexPager {
+ public $mDefaultDirection = true;
+ var $messages, $target;
+ var $namespace = '', $mDb;
- /**
- * Constructor
- * @param $username Username as a string
- */
- function ContribsFinder( $username ) {
- $this->username = $username;
- $this->namespace = false;
- $this->dbr =& wfGetDB( DB_SLAVE );
- }
-
- function setNamespace( $ns ) {
- $this->namespace = $ns;
- }
+ function __construct( $target, $namespace = false ) {
+ global $wgUser;
- function setLimit( $limit ) {
- $this->limit = $limit;
+ parent::__construct();
+ foreach( explode( ' ', 'uctop diff newarticle rollbacklink diff hist minoreditletter' ) as $msg ) {
+ $this->messages[$msg] = wfMsgExt( $msg, array( 'escape') );
+ }
+ $this->target = $target;
+ $this->namespace = $namespace;
+ $this->mDb = wfGetDB( DB_SLAVE, 'contributions' );
}
- function setOffset( $offset ) {
- $this->offset = $offset;
+ function getDefaultQuery() {
+ $query = parent::getDefaultQuery();
+ $query['target'] = $this->target;
+ return $query;
}
- /**
- * Get timestamp of either first or last contribution made by the user.
- * @todo Maybe it should be private ?
- * @param $dir string 'ASC' or 'DESC'.
- * @return Revision timestamp (rev_timestamp).
- */
- function getEditLimit( $dir ) {
- list( $index, $usercond ) = $this->getUserCond();
- $nscond = $this->getNamespaceCond();
- $use_index = $this->dbr->useIndexClause( $index );
- list( $revision, $page) = $this->dbr->tableNamesN( 'revision', 'page' );
- $sql = "SELECT rev_timestamp " .
- " FROM $page,$revision $use_index " .
- " WHERE rev_page=page_id AND $usercond $nscond" .
- " ORDER BY rev_timestamp $dir LIMIT 1";
-
- $res = $this->dbr->query( $sql, __METHOD__ );
- $row = $this->dbr->fetchObject( $res );
- if ( $row ) {
- return $row->rev_timestamp;
- } else {
- return false;
- }
- }
+ function getQueryInfo() {
+ list( $index, $userCond ) = $this->getUserCond();
+ $conds = array_merge( array( 'page_id=rev_page' ), $userCond, $this->getNamespaceCond() );
- /**
- * Get timestamps of first and last contributions made by the user.
- * @return Array containing first rev_timestamp and last rev_timestamp.
- */
- function getEditLimits() {
return array(
- $this->getEditLimit( "ASC" ),
- $this->getEditLimit( "DESC" )
+ 'tables' => array( 'page', 'revision' ),
+ 'fields' => array(
+ 'page_namespace', 'page_title', 'page_is_new', 'page_latest', 'rev_id', 'rev_page',
+ 'rev_text_id', 'rev_timestamp', 'rev_comment', 'rev_minor_edit', 'rev_user',
+ 'rev_user_text', 'rev_deleted'
+ ),
+ 'conds' => $conds,
+ 'options' => array( 'FORCE INDEX' => $index )
);
}
function getUserCond() {
- $condition = '';
+ $condition = array();
- if ( $this->username == 'newbies' ) {
- $max = $this->dbr->selectField( 'user', 'max(user_id)', false, 'make_sql' );
- $condition = '>' . (int)($max - $max / 100);
- }
-
- if ( $condition == '' ) {
- $condition = ' rev_user_text=' . $this->dbr->addQuotes( $this->username );
- $index = 'usertext_timestamp';
- } else {
- $condition = ' rev_user '.$condition ;
+ if ( $this->target == 'newbies' ) {
+ $max = $this->mDb->selectField( 'user', 'max(user_id)', false, __METHOD__ );
+ $condition[] = 'rev_user >' . (int)($max - $max / 100);
$index = 'user_timestamp';
+ } else {
+ $condition['rev_user_text'] = $this->target;
+ $index = 'usertext_timestamp';
}
return array( $index, $condition );
}
function getNamespaceCond() {
- if ( $this->namespace !== false )
- return ' AND page_namespace = ' . (int)$this->namespace;
- return '';
+ if ( $this->namespace !== '' ) {
+ return array( 'page_namespace' => (int)$this->namespace );
+ } else {
+ return array();
+ }
}
- /**
- * @return Timestamp of first entry in previous page.
- */
- function getPreviousOffsetForPaging() {
- list( $index, $usercond ) = $this->getUserCond();
- $nscond = $this->getNamespaceCond();
-
- $use_index = $this->dbr->useIndexClause( $index );
- list( $page, $revision ) = $this->dbr->tableNamesN( 'page', 'revision' );
-
- $sql = "SELECT rev_timestamp FROM $page, $revision $use_index " .
- "WHERE page_id = rev_page AND rev_timestamp > '" . $this->offset . "' AND " .
- $usercond . $nscond;
- $sql .= " ORDER BY rev_timestamp ASC";
- $sql = $this->dbr->limitResult( $sql, $this->limit, 0 );
- $res = $this->dbr->query( $sql );
-
- $numRows = $this->dbr->numRows( $res );
- if ( $numRows ) {
- $this->dbr->dataSeek( $res, $numRows - 1 );
- $row = $this->dbr->fetchObject( $res );
- $offset = $row->rev_timestamp;
- } else {
- $offset = false;
+ function getIndexField() {
+ return 'rev_timestamp';
+ }
+
+ function getStartBody() {
+ return "<ul>\n";
+ }
+
+ function getEndBody() {
+ return "</ul>\n";
+ }
+
+ function getNavigationBar() {
+ if ( isset( $this->mNavigationBar ) ) {
+ return $this->mNavigationBar;
}
- $this->dbr->freeResult( $res );
- return $offset;
+ $linkTexts = array(
+ 'prev' => wfMsgHtml( "sp-contributions-newer", $this->mLimit ),
+ 'next' => wfMsgHtml( 'sp-contributions-older', $this->mLimit ),
+ 'first' => wfMsgHtml('sp-contributions-newest'),
+ 'last' => wfMsgHtml( 'sp-contributions-oldest' )
+ );
+
+ $pagingLinks = $this->getPagingLinks( $linkTexts );
+ $limitLinks = $this->getLimitLinks();
+ $limits = implode( ' | ', $limitLinks );
+
+ $this->mNavigationBar = "({$pagingLinks['first']} | {$pagingLinks['last']}) " .
+ wfMsgHtml("viewprevnext", $pagingLinks['prev'], $pagingLinks['next'], $limits);
+ return $this->mNavigationBar;
}
/**
- * @return Timestamp of first entry in next page.
- */
- function getFirstOffsetForPaging() {
- list( $index, $usercond ) = $this->getUserCond();
- $use_index = $this->dbr->useIndexClause( $index );
- list( $page, $revision ) = $this->dbr->tableNamesN( 'page', 'revision' );
- $nscond = $this->getNamespaceCond();
- $sql = "SELECT rev_timestamp FROM $page, $revision $use_index " .
- "WHERE page_id = rev_page AND " .
- $usercond . $nscond;
- $sql .= " ORDER BY rev_timestamp ASC";
- $sql = $this->dbr->limitResult( $sql, $this->limit, 0 );
- $res = $this->dbr->query( $sql );
-
- $numRows = $this->dbr->numRows( $res );
- if ( $numRows ) {
- $this->dbr->dataSeek( $res, $numRows - 1 );
- $row = $this->dbr->fetchObject( $res );
- $offset = $row->rev_timestamp;
+ * Generates each row in the contributions list.
+ *
+ * Contributions which are marked "top" are currently on top of the history.
+ * For these contributions, a [rollback] link is shown for users with sysop
+ * privileges. The rollback link restores the most recent version that was not
+ * written by the target user.
+ *
+ * @todo This would probably look a lot nicer in a table.
+ */
+ function formatRow( $row ) {
+ wfProfileIn( __METHOD__ );
+
+ global $wgLang, $wgUser;
+
+ $sk = $this->getSkin();
+ $rev = new Revision( $row );
+
+ $page = Title::makeTitle( $row->page_namespace, $row->page_title );
+ $link = $sk->makeKnownLinkObj( $page );
+ $difftext = $topmarktext = '';
+ if( $row->rev_id == $row->page_latest ) {
+ $topmarktext .= '<strong>' . $this->messages['uctop'] . '</strong>';
+ if( !$row->page_is_new ) {
+ $difftext .= '(' . $sk->makeKnownLinkObj( $page, $this->messages['diff'], 'diff=0' ) . ')';
+ } else {
+ $difftext .= $this->messages['newarticle'];
+ }
+
+ if( $wgUser->isAllowed( 'rollback' ) ) {
+ $topmarktext .= ' '.$sk->generateRollback( $rev );
+ }
+
+ }
+ if( $rev->userCan( Revision::DELETED_TEXT ) ) {
+ $difftext = '(' . $sk->makeKnownLinkObj( $page, $this->messages['diff'], 'diff=prev&oldid='.$row->rev_id ) . ')';
} else {
- $offset = false;
+ $difftext = '(' . $this->messages['diff'] . ')';
}
- $this->dbr->freeResult( $res );
- return $offset;
- }
+ $histlink='('.$sk->makeKnownLinkObj( $page, $this->messages['hist'], 'action=history' ) . ')';
- /* private */ function makeSql() {
- $offsetQuery = '';
+ $comment = $sk->revComment( $rev );
+ $d = $wgLang->timeanddate( wfTimestamp( TS_MW, $row->rev_timestamp ), true );
- list( $page, $revision ) = $this->dbr->tableNamesN( 'page', 'revision' );
- list( $index, $userCond ) = $this->getUserCond();
+ if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
+ $d = '<span class="history-deleted">' . $d . '</span>';
+ }
- if ( $this->offset )
- $offsetQuery = "AND rev_timestamp < '{$this->offset}'";
-
- $nscond = $this->getNamespaceCond();
- $use_index = $this->dbr->useIndexClause( $index );
- $sql = "SELECT
- page_namespace,page_title,page_is_new,page_latest,
- rev_id,rev_page,rev_text_id,rev_timestamp,rev_comment,rev_minor_edit,rev_user,rev_user_text,
- rev_deleted
- FROM $page,$revision $use_index
- WHERE page_id=rev_page AND $userCond $nscond $offsetQuery
- ORDER BY rev_timestamp DESC";
- $sql = $this->dbr->limitResult( $sql, $this->limit, 0 );
- return $sql;
- }
+ if( $row->rev_minor_edit ) {
+ $mflag = '<span class="minor">' . $this->messages['minoreditletter'] . '</span> ';
+ } else {
+ $mflag = '';
+ }
- /**
- * This do the search for the user given when creating the object.
- * It should probably be the only public function in this class.
- * @return Array of contributions.
- */
- function find() {
- $contribs = array();
- $res = $this->dbr->query( $this->makeSql(), __METHOD__ );
- while ( $c = $this->dbr->fetchObject( $res ) )
- $contribs[] = $c;
- $this->dbr->freeResult( $res );
- return $contribs;
+ $ret = "{$d} {$histlink} {$difftext} {$mflag} {$link} {$comment} {$topmarktext}";
+ if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
+ $ret .= ' ' . wfMsgHtml( 'deletedrev' );
+ }
+ $ret = "<li>$ret</li>\n";
+ wfProfileOut( __METHOD__ );
+ return $ret;
}
-};
+}
/**
* Special page "user contributions".
@@ -194,154 +171,105 @@ class ContribsFinder {
function wfSpecialContributions( $par = null ) {
global $wgUser, $wgOut, $wgLang, $wgRequest;
- $target = isset( $par ) ? $par : $wgRequest->getVal( 'target' );
- if ( !strlen( $target ) ) {
- $wgOut->showErrorPage( 'notargettitle', 'notargettext' );
- return;
+ $options = array();
+
+ if ( isset( $par ) && $par == 'newbies' ) {
+ $target = 'newbies';
+ $options['contribs'] = 'newbie';
+ } elseif ( isset( $par ) ) {
+ $target = $par;
+ } else {
+ $target = $wgRequest->getVal( 'target' );
}
- $nt = Title::newFromURL( $target );
- if ( !$nt ) {
- $wgOut->showErrorPage( 'notargettitle', 'notargettext' );
- return;
+ // check for radiobox
+ if ( $wgRequest->getVal( 'contribs' ) == 'newbie' ) {
+ $target = 'newbies';
+ $options['contribs'] = 'newbie';
}
- $options = array();
-
- list( $options['limit'], $options['offset']) = wfCheckLimits();
- $options['offset'] = $wgRequest->getVal( 'offset' );
- /* Offset must be an integral. */
- if ( !strlen( $options['offset'] ) || !preg_match( '/^[0-9]+$/', $options['offset'] ) )
- $options['offset'] = '';
+ if ( !strlen( $target ) ) {
+ $wgOut->addHTML( contributionsForm( '' ) );
+ return;
+ }
- $title = SpecialPage::getTitleFor( 'Contributions' );
+ $options['limit'] = $wgRequest->getInt( 'limit', 50 );
$options['target'] = $target;
- $nt =& Title::makeTitle( NS_USER, $nt->getDBkey() );
- $finder = new ContribsFinder( ( $target == 'newbies' ) ? 'newbies' : $nt->getText() );
- $finder->setLimit( $options['limit'] );
- $finder->setOffset( $options['offset'] );
+ $nt = Title::makeTitleSafe( NS_USER, $target );
+ if ( !$nt ) {
+ $wgOut->addHTML( contributionsForm( '' ) );
+ return;
+ }
+ $id = User::idFromName( $nt->getText() );
+ if ( $target != 'newbies' ) {
+ $target = $nt->getText();
+ $wgOut->setSubtitle( contributionsSub( $nt, $id ) );
+ } else {
+ $wgOut->setSubtitle( wfMsgHtml( 'sp-contributions-newbies-sub') );
+ }
+
if ( ( $ns = $wgRequest->getVal( 'namespace', null ) ) !== null && $ns !== '' ) {
$options['namespace'] = intval( $ns );
- $finder->setNamespace( $options['namespace'] );
} else {
$options['namespace'] = '';
}
-
if ( $wgUser->isAllowed( 'rollback' ) && $wgRequest->getBool( 'bot' ) ) {
$options['bot'] = '1';
}
- if ( $wgRequest->getText( 'go' ) == 'prev' ) {
- $offset = $finder->getPreviousOffsetForPaging();
- if ( $offset !== false ) {
- $options['offset'] = $offset;
- $prevurl = $title->getLocalURL( wfArrayToCGI( $options ) );
- $wgOut->redirect( $prevurl );
- return;
- }
- }
-
- if ( $wgRequest->getText( 'go' ) == 'first' && $target != 'newbies') {
- $offset = $finder->getFirstOffsetForPaging();
- if ( $offset !== false ) {
- $options['offset'] = $offset;
- $prevurl = $title->getLocalURL( wfArrayToCGI( $options ) );
- $wgOut->redirect( $prevurl );
- return;
- }
- }
-
- if ( $target == 'newbies' ) {
- $wgOut->setSubtitle( wfMsgHtml( 'sp-contributions-newbies-sub') );
- } else {
- $wgOut->setSubtitle( wfMsgHtml( 'contribsub', contributionsSub( $nt ) ) );
- }
-
- $id = User::idFromName( $nt->getText() );
wfRunHooks( 'SpecialContributionsBeforeMainOutput', $id );
- $wgOut->addHTML( contributionsForm( $options) );
+ $wgOut->addHTML( contributionsForm( $options ) );
- $contribs = $finder->find();
-
- if ( count( $contribs ) == 0) {
+ $pager = new ContribsPager( $target, $options['namespace'] );
+ if ( !$pager->getNumRows() ) {
$wgOut->addWikiText( wfMsg( 'nocontribs' ) );
return;
}
-
- list( $early, $late ) = $finder->getEditLimits();
- $lastts = count( $contribs ) ? $contribs[count( $contribs ) - 1]->rev_timestamp : 0;
- $atstart = ( !count( $contribs ) || $late == $contribs[0]->rev_timestamp );
- $atend = ( !count( $contribs ) || $early == $lastts );
-
- // These four are defaults
- $newestlink = wfMsgHtml( 'sp-contributions-newest' );
- $oldestlink = wfMsgHtml( 'sp-contributions-oldest' );
- $newerlink = wfMsgHtml( 'sp-contributions-newer', $options['limit'] );
- $olderlink = wfMsgHtml( 'sp-contributions-older', $options['limit'] );
-
- if ( !$atstart ) {
- $stuff = $title->escapeLocalURL( wfArrayToCGI( array( 'offset' => '' ), $options ) );
- $newestlink = "<a href=\"$stuff\">$newestlink</a>";
- $stuff = $title->escapeLocalURL( wfArrayToCGI( array( 'go' => 'prev' ), $options ) );
- $newerlink = "<a href=\"$stuff\">$newerlink</a>";
- }
-
- if ( !$atend ) {
- $stuff = $title->escapeLocalURL( wfArrayToCGI( array( 'go' => 'first' ), $options ) );
- $oldestlink = "<a href=\"$stuff\">$oldestlink</a>";
- $stuff = $title->escapeLocalURL( wfArrayToCGI( array( 'offset' => $lastts ), $options ) );
- $olderlink = "<a href=\"$stuff\">$olderlink</a>";
- }
-
- if ( $target == 'newbies' ) {
- $firstlast ="($newestlink)";
- } else {
- $firstlast = "($newestlink | $oldestlink)";
- }
-
- $urls = array();
- foreach ( array( 20, 50, 100, 250, 500 ) as $num ) {
- $stuff = $title->escapeLocalURL( wfArrayToCGI( array( 'limit' => $num ), $options ) );
- $urls[] = "<a href=\"$stuff\">".$wgLang->formatNum( $num )."</a>";
+ $wgOut->addHTML(
+ '<p>' . $pager->getNavigationBar() . '</p>' .
+ $pager->getBody() .
+ '<p>' . $pager->getNavigationBar() . '</p>' );
+
+ # If there were contributions, and it was a valid user or IP, show
+ # the appropriate "footer" message - WHOIS tools, etc.
+ if( $target != 'newbies' ) {
+ $message = IP::isIPAddress( $target )
+ ? 'sp-contributions-footer-anon'
+ : 'sp-contributions-footer';
+
+
+ $text = wfMsg( $message, $target );
+ if( !wfEmptyMsg( $message, $text ) && $text != '-' ) {
+ $wgOut->addHtml( '<div class="mw-contributions-footer">' );
+ $wgOut->addWikiText( $text );
+ $wgOut->addHtml( '</div>' );
+ }
}
- $bits = implode( $urls, ' | ' );
-
- $prevnextbits = $firstlast .' '. wfMsgHtml( 'viewprevnext', $newerlink, $olderlink, $bits );
-
- $wgOut->addHTML( "<p>{$prevnextbits}</p>\n" );
-
- $wgOut->addHTML( "<ul>\n" );
-
- $sk = $wgUser->getSkin();
- foreach ( $contribs as $contrib )
- $wgOut->addHTML( ucListEdit( $sk, $contrib ) );
-
- $wgOut->addHTML( "</ul>\n" );
- $wgOut->addHTML( "<p>{$prevnextbits}</p>\n" );
}
/**
* Generates the subheading with links
- * @param $nt @see Title object for the target
+ * @param Title $nt Title object for the target
+ * @param integer $id User ID for the target
+ * @return String: appropriately-escaped HTML to be output literally
*/
-function contributionsSub( $nt ) {
+function contributionsSub( $nt, $id ) {
global $wgSysopUserBans, $wgLang, $wgUser;
$sk = $wgUser->getSkin();
- $id = User::idFromName( $nt->getText() );
if ( 0 == $id ) {
- $ul = $nt->getText();
+ $user = $nt->getText();
} else {
- $ul = $sk->makeLinkObj( $nt, htmlspecialchars( $nt->getText() ) );
+ $user = $sk->makeLinkObj( $nt, htmlspecialchars( $nt->getText() ) );
}
$talk = $nt->getTalkPage();
if( $talk ) {
# Talk page link
- $tools[] = $sk->makeLinkObj( $talk, $wgLang->getNsText( NS_TALK ) );
+ $tools[] = $sk->makeLinkObj( $talk, wfMsgHtml( 'talkpagelinktext' ) );
if( ( $id != 0 && $wgSysopUserBans ) || ( $id == 0 && User::isIP( $nt->getText() ) ) ) {
# Block link
if( $wgUser->isAllowed( 'block' ) )
@@ -351,9 +279,18 @@ function contributionsSub( $nt ) {
}
# Other logs link
$tools[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ), wfMsgHtml( 'log' ), 'user=' . $nt->getPartialUrl() );
- $ul .= ' (' . implode( ' | ', $tools ) . ')';
+ $links = implode( ' | ', $tools );
+ }
+
+ // Old message 'contribsub' had one parameter, but that doesn't work for
+ // languages that want to put the "for" bit right after $user but before
+ // $links. If 'contribsub' is around, use it for reverse compatibility,
+ // otherwise use 'contribsub2'.
+ if( wfEmptyMsg( 'contribsub', wfMsg( 'contribsub' ) ) ) {
+ return wfMsgHtml( 'contribsub2', $user, $links );
+ } else {
+ return wfMsgHtml( 'contribsub', "$user ($links)" );
}
- return $ul;
}
/**
@@ -361,97 +298,48 @@ function contributionsSub( $nt ) {
* @param $options Array: the options to be included.
*/
function contributionsForm( $options ) {
- global $wgScript, $wgTitle;
+ global $wgScript, $wgTitle, $wgRequest;
$options['title'] = $wgTitle->getPrefixedText();
-
- $f = "<form method='get' action=\"$wgScript\">\n";
- foreach ( $options as $name => $value ) {
- if( $name === 'namespace') continue;
- $f .= "\t" . wfElement( 'input', array(
- 'name' => $name,
- 'type' => 'hidden',
- 'value' => $value ) ) . "\n";
+ if ( !isset( $options['target'] ) ) {
+ $options['target'] = '';
+ } else {
+ $options['target'] = str_replace( '_' , ' ' , $options['target'] );
}
- $f .= '<p>' . wfMsgHtml( 'namespace' ) . ' ' .
- HTMLnamespaceselector( $options['namespace'], '' ) .
- wfElement( 'input', array(
- 'type' => 'submit',
- 'value' => wfMsg( 'allpagessubmit' ) )
- ) .
- "</p></form>\n";
-
- return $f;
-}
-
-/**
- * Generates each row in the contributions list.
- *
- * Contributions which are marked "top" are currently on top of the history.
- * For these contributions, a [rollback] link is shown for users with sysop
- * privileges. The rollback link restores the most recent version that was not
- * written by the target user.
- *
- * @todo This would probably look a lot nicer in a table.
- */
-function ucListEdit( $sk, $row ) {
- $fname = 'ucListEdit';
- wfProfileIn( $fname );
-
- global $wgLang, $wgUser, $wgRequest;
- static $messages;
- if( !isset( $messages ) ) {
- foreach( explode( ' ', 'uctop diff newarticle rollbacklink diff hist minoreditletter' ) as $msg ) {
- $messages[$msg] = wfMsgExt( $msg, array( 'escape') );
- }
+ if ( !isset( $options['namespace'] ) ) {
+ $options['namespace'] = '';
}
- $rev = new Revision( $row );
-
- $page = Title::makeTitle( $row->page_namespace, $row->page_title );
- $link = $sk->makeKnownLinkObj( $page );
- $difftext = $topmarktext = '';
- if( $row->rev_id == $row->page_latest ) {
- $topmarktext .= '<strong>' . $messages['uctop'] . '</strong>';
- if( !$row->page_is_new ) {
- $difftext .= '(' . $sk->makeKnownLinkObj( $page, $messages['diff'], 'diff=0' ) . ')';
- } else {
- $difftext .= $messages['newarticle'];
- }
-
- if( $wgUser->isAllowed( 'rollback' ) ) {
- $topmarktext .= ' '.$sk->generateRollback( $rev );
- }
-
- }
- if( $rev->userCan( Revision::DELETED_TEXT ) ) {
- $difftext = '(' . $sk->makeKnownLinkObj( $page, $messages['diff'], 'diff=prev&oldid='.$row->rev_id ) . ')';
- } else {
- $difftext = '(' . $messages['diff'] . ')';
+ if ( !isset( $options['contribs'] ) ) {
+ $options['contribs'] = 'user';
}
- $histlink='('.$sk->makeKnownLinkObj( $page, $messages['hist'], 'action=history' ) . ')';
-
- $comment = $sk->revComment( $rev );
- $d = $wgLang->timeanddate( wfTimestamp( TS_MW, $row->rev_timestamp ), true );
- if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
- $d = '<span class="history-deleted">' . $d . '</span>';
+ if ( $options['contribs'] == 'newbie' ) {
+ $options['target'] = '';
}
- if( $row->rev_minor_edit ) {
- $mflag = '<span class="minor">' . $messages['minoreditletter'] . '</span> ';
- } else {
- $mflag = '';
- }
+ $f = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) );
- $ret = "{$d} {$histlink} {$difftext} {$mflag} {$link} {$comment} {$topmarktext}";
- if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
- $ret .= ' ' . wfMsgHtml( 'deletedrev' );
- }
- $ret = "<li>$ret</li>\n";
- wfProfileOut( $fname );
- return $ret;
+ foreach ( $options as $name => $value ) {
+ if ( in_array( $name, array( 'namespace', 'target', 'contribs' ) ) ) {
+ continue;
+ }
+ $f .= "\t" . Xml::hidden( $name, $value ) . "\n";
+ }
+
+ $f .= '<fieldset>' .
+ Xml::element( 'legend', array(), wfMsg( 'sp-contributions-search' ) ) .
+ Xml::radioLabel( wfMsgExt( 'sp-contributions-newbies', array( 'parseinline' ) ), 'contribs' , 'newbie' , 'newbie', $options['contribs'] == 'newbie' ? true : false ) . '<br />' .
+ Xml::radioLabel( wfMsgExt( 'sp-contributions-username', array( 'parseinline' ) ), 'contribs' , 'user', 'user', $options['contribs'] == 'user' ? true : false ) . ' ' .
+ Xml::input( 'target', 20, $options['target']) . ' '.
+ Xml::label( wfMsg( 'namespace' ), 'namespace' ) .
+ Xml::namespaceSelector( $options['namespace'], '' ) .
+ Xml::submitButton( wfMsg( 'sp-contributions-submit' ) ) .
+ '</fieldset>' .
+ Xml::closeElement( 'form' );
+ return $f;
}
+
?>
diff --git a/includes/SpecialDeadendpages.php b/includes/SpecialDeadendpages.php
index 4ffe5e03..48d27add 100644
--- a/includes/SpecialDeadendpages.php
+++ b/includes/SpecialDeadendpages.php
@@ -1,14 +1,12 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
class DeadendPagesPage extends PageQueryPage {
@@ -38,18 +36,18 @@ class DeadendPagesPage extends PageQueryPage {
return false;
}
- /**
+ /**
* @return string an sqlquery
*/
function getSQL() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
list( $page, $pagelinks ) = $dbr->tableNamesN( 'page', 'pagelinks' );
return "SELECT 'Deadendpages' as type, page_namespace AS namespace, page_title as title, page_title AS value " .
"FROM $page LEFT JOIN $pagelinks ON page_id = pl_from " .
"WHERE pl_from IS NULL " .
"AND page_namespace = 0 " .
"AND page_is_redirect = 0";
- }
+ }
}
/**
@@ -57,11 +55,11 @@ class DeadendPagesPage extends PageQueryPage {
*/
function wfSpecialDeadendpages() {
- list( $limit, $offset ) = wfCheckLimits();
+ list( $limit, $offset ) = wfCheckLimits();
- $depp = new DeadendPagesPage();
+ $depp = new DeadendPagesPage();
- return $depp->doQuery( $offset, $limit );
+ return $depp->doQuery( $offset, $limit );
}
?>
diff --git a/includes/SpecialDisambiguations.php b/includes/SpecialDisambiguations.php
index 626b967c..da0562ab 100644
--- a/includes/SpecialDisambiguations.php
+++ b/includes/SpecialDisambiguations.php
@@ -1,15 +1,9 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
-/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
- */
class DisambiguationsPage extends PageQueryPage {
function getName() {
@@ -19,69 +13,67 @@ class DisambiguationsPage extends PageQueryPage {
function isExpensive( ) { return true; }
function isSyndicated() { return false; }
- function getDisambiguationPageObj() {
- return Title::makeTitleSafe( NS_MEDIAWIKI, 'disambiguationspage');
- }
-
- function getPageHeader( ) {
- global $wgUser;
- $sk = $wgUser->getSkin();
- return '<p>'.wfMsg('disambiguationstext', $sk->makeKnownLinkObj($this->getDisambiguationPageObj()))."</p><br />\n";
+ function getPageHeader( ) {
+ global $wgOut;
+ return $wgOut->parse( wfMsg( 'disambiguations-text' ) );
}
function getSQL() {
- $dbr =& wfGetDB( DB_SLAVE );
- list( $page, $pagelinks, $templatelinks) = $dbr->tableNamesN( 'page', 'pagelinks', 'templatelinks' );
+ $dbr = wfGetDB( DB_SLAVE );
+
+ $dMsgText = wfMsgForContent('disambiguationspage');
- $dMsgText = wfMsgForContent('disambiguationspage');
-
$linkBatch = new LinkBatch;
-
- # If the text can be treated as a title, use it verbatim.
- # Otherwise, pull the titles from the links table
- $dp = Title::newFromText($dMsgText);
- if( $dp ) {
- if($dp->getNamespace() != NS_TEMPLATE) {
- # FIXME we assume the disambiguation message is a template but
- # the page can potentially be from another namespace :/
- wfDebug("Mediawiki:disambiguationspage message does not refer to a template!\n");
- }
- $linkBatch->addObj( $dp );
- } else {
- # Get all the templates linked from the Mediawiki:Disambiguationspage
- $disPageObj = $this->getDisambiguationPageObj();
- $res = $dbr->select(
- array('pagelinks', 'page'),
- 'pl_title',
- array('page_id = pl_from', 'pl_namespace' => NS_TEMPLATE,
- 'page_namespace' => $disPageObj->getNamespace(), 'page_title' => $disPageObj->getDBkey()),
- 'DisambiguationsPage::getSQL' );
-
- while ( $row = $dbr->fetchObject( $res ) ) {
- $linkBatch->addObj( Title::makeTitle( NS_TEMPLATE, $row->pl_title ));
- }
- $dbr->freeResult( $res );
- }
-
- $set = $linkBatch->constructSet( 'lb.tl', $dbr );
- if( $set === false ) {
- $set = 'FALSE'; # We must always return a valid sql query, but this way DB will always quicly return an empty result
- wfDebug("Mediawiki:disambiguationspage message does not link to any templates!\n");
- }
-
- $sql = "SELECT 'Disambiguations' AS \"type\", pb.page_namespace AS namespace,"
- ." pb.page_title AS title, la.pl_from AS value"
- ." FROM {$templatelinks} AS lb, {$page} AS pb, {$pagelinks} AS la, {$page} AS pa"
- ." WHERE $set" # disambiguation template(s)
- .' AND pa.page_id = la.pl_from'
- .' AND pa.page_namespace = ' . NS_MAIN # Limit to just articles in the main namespace
- .' AND pb.page_id = lb.tl_from'
- .' AND pb.page_namespace = la.pl_namespace'
- .' AND pb.page_title = la.pl_title'
- .' ORDER BY lb.tl_namespace, lb.tl_title';
-
- return $sql;
+
+ # If the text can be treated as a title, use it verbatim.
+ # Otherwise, pull the titles from the links table
+ $dp = Title::newFromText($dMsgText);
+ if( $dp ) {
+ if($dp->getNamespace() != NS_TEMPLATE) {
+ # FIXME we assume the disambiguation message is a template but
+ # the page can potentially be from another namespace :/
+ wfDebug("Mediawiki:disambiguationspage message does not refer to a template!\n");
+ }
+ $linkBatch->addObj( $dp );
+ } else {
+ # Get all the templates linked from the Mediawiki:Disambiguationspage
+ $disPageObj = Title::makeTitleSafe( NS_MEDIAWIKI, 'disambiguationspage' );
+ $res = $dbr->select(
+ array('pagelinks', 'page'),
+ 'pl_title',
+ array('page_id = pl_from', 'pl_namespace' => NS_TEMPLATE,
+ 'page_namespace' => $disPageObj->getNamespace(), 'page_title' => $disPageObj->getDBkey()),
+ __METHOD__ );
+
+ while ( $row = $dbr->fetchObject( $res ) ) {
+ $linkBatch->addObj( Title::makeTitle( NS_TEMPLATE, $row->pl_title ));
+ }
+
+ $dbr->freeResult( $res );
+ }
+
+ $set = $linkBatch->constructSet( 'lb.tl', $dbr );
+ if( $set === false ) {
+ # We must always return a valid sql query, but this way DB will always quicly return an empty result
+ $set = 'FALSE';
+ wfDebug("Mediawiki:disambiguationspage message does not link to any templates!\n");
+ }
+
+ list( $page, $pagelinks, $templatelinks) = $dbr->tableNamesN( 'page', 'pagelinks', 'templatelinks' );
+
+ $sql = "SELECT 'Disambiguations' AS \"type\", pb.page_namespace AS namespace,"
+ ." pb.page_title AS title, la.pl_from AS value"
+ ." FROM {$templatelinks} AS lb, {$page} AS pb, {$pagelinks} AS la, {$page} AS pa"
+ ." WHERE $set" # disambiguation template(s)
+ .' AND pa.page_id = la.pl_from'
+ .' AND pa.page_namespace = ' . NS_MAIN # Limit to just articles in the main namespace
+ .' AND pb.page_id = lb.tl_from'
+ .' AND pb.page_namespace = la.pl_namespace'
+ .' AND pb.page_title = la.pl_title'
+ .' ORDER BY lb.tl_namespace, lb.tl_title';
+
+ return $sql;
}
function getOrder() {
@@ -93,10 +85,10 @@ class DisambiguationsPage extends PageQueryPage {
$title = Title::newFromId( $result->value );
$dp = Title::makeTitle( $result->namespace, $result->title );
- $from = $skin->makeKnownLinkObj( $title,'');
- $edit = $skin->makeBrokenLinkObj( $title, "(".wfMsg("qbedit").")" , 'redirect=no');
+ $from = $skin->makeKnownLinkObj( $title, '' );
+ $edit = $skin->makeKnownLinkObj( $title, "(".wfMsgHtml("qbedit").")" , 'redirect=no&action=edit' );
$arr = $wgContLang->getArrow();
- $to = $skin->makeKnownLinkObj( $dp,'');
+ $to = $skin->makeKnownLinkObj( $dp, '' );
return "$from $edit $arr $to";
}
@@ -112,4 +104,5 @@ function wfSpecialDisambiguations() {
return $sd->doQuery( $offset, $limit );
}
-?>
+
+?> \ No newline at end of file
diff --git a/includes/SpecialDoubleRedirects.php b/includes/SpecialDoubleRedirects.php
index cf1153ea..e7b355c5 100644
--- a/includes/SpecialDoubleRedirects.php
+++ b/includes/SpecialDoubleRedirects.php
@@ -1,14 +1,13 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
+ * A special page listing redirects to redirecting page.
+ * The software will automatically not follow double redirects, to prevent loops.
+ * @addtogroup SpecialPage
*/
class DoubleRedirectsPage extends PageQueryPage {
@@ -26,7 +25,7 @@ class DoubleRedirectsPage extends PageQueryPage {
function getSQLText( &$dbr, $namespace = null, $title = null ) {
- list( $page, $pagelinks ) = $dbr->tableNamesN( 'page', 'pagelinks' );
+ list( $page, $redirect ) = $dbr->tableNamesN( 'page', 'redirect' );
$limitToTitle = !( $namespace === null && $title === null );
$sql = $limitToTitle ? "SELECT" : "SELECT 'DoubleRedirects' as type," ;
@@ -34,14 +33,13 @@ class DoubleRedirectsPage extends PageQueryPage {
" pa.page_namespace as namespace, pa.page_title as title," .
" pb.page_namespace as nsb, pb.page_title as tb," .
" pc.page_namespace as nsc, pc.page_title as tc" .
- " FROM $pagelinks AS la, $pagelinks AS lb, $page AS pa, $page AS pb, $page AS pc" .
- " WHERE pa.page_is_redirect=1 AND pb.page_is_redirect=1" .
- " AND la.pl_from=pa.page_id" .
- " AND la.pl_namespace=pb.page_namespace" .
- " AND la.pl_title=pb.page_title" .
- " AND lb.pl_from=pb.page_id" .
- " AND lb.pl_namespace=pc.page_namespace" .
- " AND lb.pl_title=pc.page_title";
+ " FROM $redirect AS ra, $redirect AS rb, $page AS pa, $page AS pb, $page AS pc" .
+ " WHERE ra.rd_from=pa.page_id" .
+ " AND ra.rd_namespace=pb.page_namespace" .
+ " AND ra.rd_title=pb.page_title" .
+ " AND rb.rd_from=pb.page_id" .
+ " AND rb.rd_namespace=pc.page_namespace" .
+ " AND rb.rd_title=pc.page_title";
if( $limitToTitle ) {
$encTitle = $dbr->addQuotes( $title );
@@ -53,7 +51,7 @@ class DoubleRedirectsPage extends PageQueryPage {
}
function getSQL() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
return $this->getSQLText( $dbr );
}
@@ -68,7 +66,7 @@ class DoubleRedirectsPage extends PageQueryPage {
$titleA = Title::makeTitle( $result->namespace, $result->title );
if ( $result && !isset( $result->nsb ) ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$sql = $this->getSQLText( $dbr, $result->namespace, $result->title );
$res = $dbr->query( $sql, $fname );
if ( $res ) {
diff --git a/includes/SpecialEmailuser.php b/includes/SpecialEmailuser.php
index 38745a37..900a2c32 100644
--- a/includes/SpecialEmailuser.php
+++ b/includes/SpecialEmailuser.php
@@ -1,15 +1,14 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
-/**
- *
- */
require_once('UserMailer.php');
+/**
+ * @todo document
+ */
function wfSpecialEmailuser( $par ) {
global $wgUser, $wgOut, $wgRequest, $wgEnableEmail, $wgEnableUserEmail;
@@ -51,7 +50,14 @@ function wfSpecialEmailuser( $par ) {
if ( "success" == $action ) {
$f->showSuccess( $nu );
} else if ( "submit" == $action && $wgRequest->wasPosted() &&
- $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
+ $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) )
+ {
+ # Check against the rate limiter
+ if( $wgUser->pingLimiter( 'emailuser' ) ) {
+ $wgOut->rateLimited();
+ return;
+ }
+
$f->doSubmit();
} else {
$f->showForm();
@@ -59,9 +65,8 @@ function wfSpecialEmailuser( $par ) {
}
/**
- * @todo document
- * @package MediaWiki
- * @subpackage SpecialPage
+ * Implements the Special:Emailuser web interface, and invokes userMailer for sending the email message.
+ * @addtogroup SpecialPage
*/
class EmailUserForm {
diff --git a/includes/SpecialExport.php b/includes/SpecialExport.php
index 5e6d6d8d..a597fdd0 100644
--- a/includes/SpecialExport.php
+++ b/includes/SpecialExport.php
@@ -18,10 +18,37 @@
# http://www.gnu.org/copyleft/gpl.html
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
+function wfExportGetPagesFromCategory( $title ) {
+ global $wgContLang;
+
+ $name = $title->getDBKey();
+
+ $dbr = wfGetDB( DB_SLAVE );
+
+ list( $page, $categorylinks ) = $dbr->tableNamesN( 'page', 'categorylinks' );
+ $sql = "SELECT page_namespace, page_title FROM $page " .
+ "JOIN $categorylinks ON cl_from = page_id " .
+ "WHERE cl_to = " . $dbr->addQuotes( $name );
+
+ $pages = array();
+ $res = $dbr->query( $sql, 'wfExportGetPagesFromCategory' );
+ while ( $row = $dbr->fetchObject( $res ) ) {
+ $n = $row->page_title;
+ if ($row->page_namespace) {
+ $ns = $wgContLang->getNsText( $row->page_namespace );
+ $n = $ns . ':' . $n;
+ }
+
+ $pages[] = $n;
+ }
+ $dbr->freeResult($res);
+
+ return $pages;
+}
+
/**
*
*/
@@ -30,7 +57,21 @@ function wfSpecialExport( $page = '' ) {
global $wgExportAllowHistory, $wgExportMaxHistory;
$curonly = true;
- if( $wgRequest->wasPosted() ) {
+ $doexport = false;
+
+ if ( $wgRequest->getCheck( 'addcat' ) ) {
+ $page = $wgRequest->getText( 'pages' );
+ $catname = $wgRequest->getText( 'catname' );
+
+ if ( $catname !== '' && $catname !== NULL && $catname !== false ) {
+ $t = Title::makeTitleSafe( NS_CATEGORY, $catname );
+ if ( $t ) {
+ $catpages = wfExportGetPagesFromCategory( $t );
+ if ( $catpages ) $page .= "\n" . implode( "\n", $catpages );
+ }
+ }
+ }
+ else if( $wgRequest->wasPosted() ) {
$page = $wgRequest->getText( 'pages' );
$curonly = $wgRequest->getCheck( 'curonly' );
$rawOffset = $wgRequest->getVal( 'offset' );
@@ -60,6 +101,8 @@ function wfSpecialExport( $page = '' ) {
$history['dir'] = 'desc';
}
}
+
+ if( $page != '' ) $doexport = true;
} else {
// Default to current-only for GET requests
$page = $wgRequest->getText( 'pages', $page );
@@ -69,7 +112,10 @@ function wfSpecialExport( $page = '' ) {
} else {
$history = WikiExporter::CURRENT;
}
+
+ if( $page != '' ) $doexport = true;
}
+
if( !$wgExportAllowHistory ) {
// Override
$history = WikiExporter::CURRENT;
@@ -78,7 +124,7 @@ function wfSpecialExport( $page = '' ) {
$list_authors = $wgRequest->getCheck( 'listauthors' );
if ( !$curonly || !$wgExportAllowListContributors ) $list_authors = false ;
- if( $page != '' ) {
+ if ( $doexport ) {
$wgOut->disable();
// Cancel output buffering and gzipping if set
@@ -87,7 +133,7 @@ function wfSpecialExport( $page = '' ) {
header( "Content-type: application/xml; charset=utf-8" );
$pages = explode( "\n", $page );
- $db =& wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_SLAVE );
$exporter = new WikiExporter( $db, $history );
$exporter->list_authors = $list_authors ;
$exporter->openStream();
@@ -105,7 +151,13 @@ function wfSpecialExport( $page = '' ) {
}
}
}*/
- $exporter->pageByName( $page );
+
+ #Bug 8824: Only export pages the user can read
+ $title = Title::newFromText( $page );
+ if( is_null( $title ) ) continue; #TODO: perhaps output an <error> tag or something.
+ if( !$title->userCan( 'read' ) ) continue; #TODO: perhaps output an <error> tag or something.
+
+ $exporter->pageByTitle( $title );
}
$exporter->closeStream();
@@ -116,7 +168,12 @@ function wfSpecialExport( $page = '' ) {
$titleObj = SpecialPage::getTitleFor( "Export" );
$form = wfOpenElement( 'form', array( 'method' => 'post', 'action' => $titleObj->getLocalUrl() ) );
- $form .= wfOpenElement( 'textarea', array( 'name' => 'pages', 'cols' => 40, 'rows' => 10 ) ) . '</textarea><br />';
+
+ $form .= wfInputLabel( wfMsg( 'export-addcattext' ), 'catname', 'catname', 40 ) . ' ';
+ $form .= wfSubmitButton( wfMsg( 'export-addcat' ), array( 'name' => 'addcat' ) ) . '<br />';
+
+ $form .= wfOpenElement( 'textarea', array( 'name' => 'pages', 'cols' => 40, 'rows' => 10 ) ) . htmlspecialchars($page). '</textarea><br />';
+
if( $wgExportAllowHistory ) {
$form .= wfCheck( 'curonly', true, array( 'value' => 'true', 'id' => 'curonly' ) );
$form .= wfLabel( wfMsg( 'exportcuronly' ), 'curonly' ) . '<br />';
diff --git a/includes/SpecialFewestrevisions.php b/includes/SpecialFewestrevisions.php
new file mode 100644
index 00000000..4c0cd686
--- /dev/null
+++ b/includes/SpecialFewestrevisions.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * Special page for listing the articles with the fewest revisions.
+ *
+ * @package MediaWiki
+ * @addtogroup SpecialPage
+ * @author Martin Drashkov
+ */
+class FewestrevisionsPage extends QueryPage {
+
+ function getName() {
+ return 'Fewestrevisions';
+ }
+
+ function isExpensive() {
+ return true;
+ }
+
+ function isSyndicated() {
+ return false;
+ }
+
+ function getSql() {
+ $dbr = wfGetDB( DB_SLAVE );
+ list( $revision, $page ) = $dbr->tableNamesN( 'revision', 'page' );
+
+ return "SELECT 'Fewestrevisions' as type,
+ page_namespace as namespace,
+ page_title as title,
+ COUNT(*) as value
+ FROM $revision
+ JOIN $page ON page_id = rev_page
+ WHERE page_namespace = " . NS_MAIN . "
+ GROUP BY 1,2,3
+ HAVING COUNT(*) > 1";
+ }
+
+ function sortDescending() {
+ return false;
+ }
+
+ function formatResult( $skin, $result ) {
+ global $wgLang, $wgContLang;
+
+ $nt = Title::makeTitleSafe( $result->namespace, $result->title );
+ $text = $wgContLang->convert( $nt->getPrefixedText() );
+
+ $plink = $skin->makeKnownLinkObj( $nt, $text );
+
+ $nl = wfMsgExt( 'nrevisions', array( 'parsemag', 'escape'),
+ $wgLang->formatNum( $result->value ) );
+ $nlink = $skin->makeKnownLinkObj( $nt, $nl, 'action=history' );
+
+ return wfSpecialList( $plink, $nlink );
+ }
+}
+
+function wfSpecialFewestrevisions() {
+ list( $limit, $offset ) = wfCheckLimits();
+ $frp = new FewestrevisionsPage();
+ $frp->doQuery( $offset, $limit );
+}
+
+?>
diff --git a/includes/SpecialImagelist.php b/includes/SpecialImagelist.php
index 5ecbe8a6..92b9ae11 100644
--- a/includes/SpecialImagelist.php
+++ b/includes/SpecialImagelist.php
@@ -1,8 +1,7 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
@@ -24,6 +23,10 @@ function wfSpecialImagelist() {
. $nav );
}
+/**
+ * @addtogroup SpecialPage
+ * @addtogroup Pager
+ */
class ImageListPager extends TablePager {
var $mFieldNames = null;
var $mMessages = array();
@@ -40,11 +43,11 @@ class ImageListPager extends TablePager {
if ( $search != '' && !$wgMiserMode ) {
$nt = Title::newFromUrl( $search );
if( $nt ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$m = $dbr->strencode( strtolower( $nt->getDBkey() ) );
$m = str_replace( "%", "\\%", $m );
$m = str_replace( "_", "\\_", $m );
- $this->mQueryConds = array( "LCASE(img_name) LIKE '%{$m}%'" );
+ $this->mQueryConds = array( "LOWER(img_name) LIKE '%{$m}%'" );
}
}
@@ -138,17 +141,14 @@ class ImageListPager extends TablePager {
function getForm() {
global $wgRequest, $wgMiserMode;
$url = $this->getTitle()->escapeLocalURL();
- $msgSubmit = wfMsgHtml( 'table_pager_limit_submit' );
- $msgSearch = wfMsgHtml( 'imagelist_search_for' );
$search = $wgRequest->getText( 'ilsearch' );
- $encSearch = htmlspecialchars( $search );
- $s = "<form method=\"get\" action=\"$url\">\n" .
+ $s = "<form method=\"get\" action=\"$url\">\n" .
wfMsgHtml( 'table_pager_limit', $this->getLimitSelect() );
if ( !$wgMiserMode ) {
- $s .= "<br/>\n" . $msgSearch .
- " <input type=\"text\" size=\"20\" name=\"ilsearch\" value=\"$encSearch\"/><br/>\n";
+ $s .= "<br/>\n" .
+ Xml::inputLabel( wfMsg( 'imagelist_search_for' ), 'ilsearch', 'mw-ilsearch', 20, $search );
}
- $s .= " <input type=\"submit\" value=\"$msgSubmit\"/>\n" .
+ $s .= " " . Xml::submitButton( wfMsg( 'table_pager_limit_submit' ) ) ." \n" .
$this->getHiddenFields( array( 'limit', 'ilsearch' ) ) .
"</form>\n";
return $s;
diff --git a/includes/SpecialImport.php b/includes/SpecialImport.php
index 1c8ee2e0..c7b861d0 100644
--- a/includes/SpecialImport.php
+++ b/includes/SpecialImport.php
@@ -19,8 +19,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
@@ -34,11 +33,11 @@ function wfSpecialImport( $page = '' ) {
$namespace = $wgImportTargetNamespace;
$frompage = '';
$history = true;
-
+
if( $wgRequest->wasPosted() && $wgRequest->getVal( 'action' ) == 'submit') {
$isUpload = false;
$namespace = $wgRequest->getIntOrNull( 'namespace' );
-
+
switch( $wgRequest->getVal( "source" ) ) {
case "upload":
$isUpload = true;
@@ -65,17 +64,17 @@ function wfSpecialImport( $page = '' ) {
$wgOut->addWikiText( wfEscapeWikiText( $source->getMessage() ) );
} else {
$wgOut->addWikiText( wfMsg( "importstart" ) );
-
+
$importer = new WikiImporter( $source );
if( !is_null( $namespace ) ) {
$importer->setTargetNamespace( $namespace );
}
$reporter = new ImportReporter( $importer, $isUpload, $interwiki );
-
+
$reporter->open();
$result = $importer->doImport();
$reporter->close();
-
+
if( WikiError::isError( $result ) ) {
$wgOut->addWikiText( wfMsg( "importfailed",
wfEscapeWikiText( $result->getMessage() ) ) );
@@ -161,6 +160,7 @@ function wfSpecialImport( $page = '' ) {
/**
* Reporting callback
+ * @addtogroup SpecialPage
*/
class ImportReporter {
function __construct( $importer, $upload, $interwiki ) {
@@ -169,27 +169,27 @@ class ImportReporter {
$this->mIsUpload = $upload;
$this->mInterwiki = $interwiki;
}
-
+
function open() {
global $wgOut;
$wgOut->addHtml( "<ul>\n" );
}
-
+
function reportPage( $title, $origTitle, $revisionCount, $successCount ) {
global $wgOut, $wgUser, $wgLang, $wgContLang;
-
+
$skin = $wgUser->getSkin();
-
+
$this->mPageCount++;
-
+
$localCount = $wgLang->formatNum( $successCount );
$contentCount = $wgContLang->formatNum( $successCount );
-
+
$wgOut->addHtml( "<li>" . $skin->makeKnownLinkObj( $title ) .
" " .
wfMsgExt( 'import-revision-count', array( 'parsemag', 'escape' ), $localCount ) .
"</li>\n" );
-
+
if( $successCount > 0 ) {
$log = new LogPage( 'import' );
if( $this->mIsUpload ) {
@@ -203,7 +203,7 @@ class ImportReporter {
$contentCount, $interwiki );
$log->addEntry( 'interwiki', $title, $detail );
}
-
+
$comment = $detail; // quick
$dbw = wfGetDB( DB_MASTER );
$nullRevision = Revision::newNullRevision(
@@ -211,7 +211,7 @@ class ImportReporter {
$nullRevision->insertOn( $dbw );
}
}
-
+
function close() {
global $wgOut;
if( $this->mPageCount == 0 ) {
@@ -223,8 +223,7 @@ class ImportReporter {
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
class WikiRevision {
var $title = null;
@@ -279,7 +278,7 @@ class WikiRevision {
return $this->title;
}
- function getID() {
+ function getID() {
return $this->id;
}
@@ -304,7 +303,7 @@ class WikiRevision {
}
function importOldRevision() {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
# Sneak a single revision into place
$user = User::newFromName( $this->getUser() );
@@ -338,7 +337,7 @@ class WikiRevision {
return false;
}
}
-
+
# FIXME: Use original rev_id optionally
# FIXME: blah blah blah
@@ -362,14 +361,14 @@ class WikiRevision {
if( $created ) {
wfDebug( __METHOD__ . ": running onArticleCreate\n" );
Article::onArticleCreate( $this->title );
-
+
wfDebug( __METHOD__ . ": running create updates\n" );
$article->createUpdates( $revision );
-
+
} elseif( $changed ) {
wfDebug( __METHOD__ . ": running onArticleEdit\n" );
Article::onArticleEdit( $this->title );
-
+
wfDebug( __METHOD__ . ": running edit updates\n" );
$article->editUpdates(
$this->getText(),
@@ -378,16 +377,15 @@ class WikiRevision {
$this->timestamp,
$revId );
}
-
+
return true;
}
}
/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
+ * implements Special:Import
+ * @addtogroup SpecialPage
*/
class WikiImporter {
var $mSource = null;
@@ -446,7 +444,7 @@ class WikiImporter {
print "$data\n";
} else {
global $wgOut;
- $wgOut->addHTML( "<li>$data</li>\n" );
+ $wgOut->addHTML( "<li>" . htmlspecialchars( $data ) . "</li>\n" );
}
}
@@ -486,7 +484,7 @@ class WikiImporter {
$this->mRevisionCallback = $callback;
return $previous;
}
-
+
/**
* Set a target namespace to override the defaults
*/
@@ -508,7 +506,7 @@ class WikiImporter {
* @private
*/
function importRevision( &$revision ) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
return $dbw->deadlockLoop( array( &$revision, 'importOldRevision' ) );
}
@@ -627,9 +625,14 @@ class WikiImporter {
xml_set_character_data_handler( $parser, "char_append" );
break;
case "revision":
- $this->workRevision = new WikiRevision;
- $this->workRevision->setTitle( $this->pageTitle );
- $this->workRevisionCount++;
+ if( is_object( $this->pageTitle ) ) {
+ $this->workRevision = new WikiRevision;
+ $this->workRevision->setTitle( $this->pageTitle );
+ $this->workRevisionCount++;
+ } else {
+ // Skipping items due to invalid page title
+ $this->workRevision = null;
+ }
xml_set_element_handler( $parser, "in_revision", "out_revision" );
break;
default:
@@ -646,7 +649,7 @@ class WikiImporter {
$this->pageOutCallback( $this->pageTitle, $this->origTitle,
$this->workRevisionCount, $this->workSuccessCount );
-
+
$this->workTitle = null;
$this->workRevision = null;
$this->workRevisionCount = 0;
@@ -681,30 +684,42 @@ class WikiImporter {
} else {
$this->pageTitle = Title::newFromText( $this->workTitle );
}
- $this->pageCallback( $this->workTitle );
+ if( is_null( $this->pageTitle ) ) {
+ // Invalid page title? Ignore the page
+ $this->notice( "Skipping invalid page title '$this->workTitle'" );
+ } else {
+ $this->pageCallback( $this->workTitle );
+ }
break;
case "id":
if ( $this->parenttag == 'revision' ) {
- $this->workRevision->setID( $this->appenddata );
+ if( $this->workRevision )
+ $this->workRevision->setID( $this->appenddata );
}
break;
case "text":
- $this->workRevision->setText( $this->appenddata );
+ if( $this->workRevision )
+ $this->workRevision->setText( $this->appenddata );
break;
case "username":
- $this->workRevision->setUsername( $this->appenddata );
+ if( $this->workRevision )
+ $this->workRevision->setUsername( $this->appenddata );
break;
case "ip":
- $this->workRevision->setUserIP( $this->appenddata );
+ if( $this->workRevision )
+ $this->workRevision->setUserIP( $this->appenddata );
break;
case "timestamp":
- $this->workRevision->setTimestamp( $this->appenddata );
+ if( $this->workRevision )
+ $this->workRevision->setTimestamp( $this->appenddata );
break;
case "comment":
- $this->workRevision->setComment( $this->appenddata );
+ if( $this->workRevision )
+ $this->workRevision->setComment( $this->appenddata );
break;
case "minor":
- $this->workRevision->setMinor( true );
+ if( $this->workRevision )
+ $this->workRevision->setMinor( true );
break;
default:
$this->debug( "Bad append: {$this->appendfield}" );
@@ -741,10 +756,12 @@ class WikiImporter {
}
xml_set_element_handler( $parser, "in_page", "out_page" );
- $ok = call_user_func_array( $this->mRevisionCallback,
- array( &$this->workRevision, &$this ) );
- if( $ok ) {
- $this->workSuccessCount++;
+ if( $this->workRevision ) {
+ $ok = call_user_func_array( $this->mRevisionCallback,
+ array( &$this->workRevision, &$this ) );
+ if( $ok ) {
+ $this->workSuccessCount++;
+ }
}
}
@@ -774,7 +791,10 @@ class WikiImporter {
}
-/** @package MediaWiki */
+/**
+ * @todo document (e.g. one-sentence class description).
+ * @addtogroup SpecialPage
+ */
class ImportStringSource {
function ImportStringSource( $string ) {
$this->mString = $string;
@@ -795,7 +815,10 @@ class ImportStringSource {
}
}
-/** @package MediaWiki */
+/**
+ * @todo document (e.g. one-sentence class description).
+ * @addtogroup SpecialPage
+ */
class ImportStreamSource {
function ImportStreamSource( $handle ) {
$this->mHandle = $handle;
@@ -809,7 +832,7 @@ class ImportStreamSource {
return fread( $this->mHandle, 32768 );
}
- function newFromFile( $filename ) {
+ static function newFromFile( $filename ) {
$file = @fopen( $filename, 'rt' );
if( !$file ) {
return new WikiErrorMsg( "importcantopen" );
diff --git a/includes/SpecialIpblocklist.php b/includes/SpecialIpblocklist.php
index 293059f2..8cb5729e 100644
--- a/includes/SpecialIpblocklist.php
+++ b/includes/SpecialIpblocklist.php
@@ -1,8 +1,7 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
@@ -10,7 +9,7 @@
*/
function wfSpecialIpblocklist() {
global $wgUser, $wgOut, $wgRequest;
-
+
$ip = $wgRequest->getVal( 'wpUnblockAddress', $wgRequest->getVal( 'ip' ) );
$id = $wgRequest->getVal( 'id' );
$reason = $wgRequest->getText( 'wpUnblockReason' );
@@ -27,8 +26,18 @@ function wfSpecialIpblocklist() {
$wgOut->permissionRequired( 'block' );
return;
}
+ # Can't unblock when the database is locked
+ if( wfReadOnly() ) {
+ $wgOut->readOnlyPage();
+ return;
+ }
$ipu->doSubmit();
} else if ( "unblock" == $action ) {
+ # Can't unblock when the database is locked
+ if( wfReadOnly() ) {
+ $wgOut->readOnlyPage();
+ return;
+ }
$ipu->showForm( "" );
} else {
$ipu->showList( "" );
@@ -36,15 +45,14 @@ function wfSpecialIpblocklist() {
}
/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
+ * implements Special:ipblocklist GUI
+ * @addtogroup SpecialPage
*/
class IPUnblockForm {
var $ip, $reason, $id;
function IPUnblockForm( $ip, $id, $reason ) {
- $this->ip = $ip;
+ $this->ip = strtr( $ip, '_', ' ' );
$this->id = $id;
$this->reason = $reason;
}
@@ -154,7 +162,7 @@ class IPUnblockForm {
}
function showList( $msg ) {
- global $wgOut;
+ global $wgOut, $wgUser;
$wgOut->setPagetitle( wfMsg( "ipblocklist" ) );
if ( "" != $msg ) {
@@ -168,6 +176,9 @@ class IPUnblockForm {
$conds = array();
$matches = array();
+ // Is user allowed to see all the blocks?
+ if ( !$wgUser->isAllowed( 'oversight' ) )
+ $conds['ipb_deleted'] = 0;
if ( $this->ip == '' ) {
// No extra conditions
} elseif ( substr( $this->ip, 0, 1 ) == '#' ) {
@@ -189,17 +200,21 @@ class IPUnblockForm {
}
}
+ # TODO: difference message between
+ # a) an real empty list and
+ # b) requested ip/username not on list
$pager = new IPBlocklistPager( $this, $conds );
- $s = $pager->getNavigationBar() .
- $this->searchForm();
if ( $pager->getNumRows() ) {
+ $s = $this->searchForm() .
+ $pager->getNavigationBar();
$s .= "<ul>" .
$pager->getBody() .
"</ul>";
+ $s .= $pager->getNavigationBar();
} else {
- $s .= '<p>' . wfMsgHTML( 'ipblocklistempty' ) . '</p>';
+ $s = $this->searchForm() .
+ '<p>' . wfMsgHTML( 'ipblocklistempty' ) . '</p>';
}
- $s .= $pager->getNavigationBar();
$wgOut->addHTML( $s );
}
@@ -223,7 +238,7 @@ class IPUnblockForm {
'value' => $this->ip ) ) .
wfElement( 'input', array(
'type' => 'submit',
- 'value' => wfMsg( 'searchbutton' ) ) ) .
+ 'value' => wfMsg( 'ipblocklist-submit' ) ) ) .
'</form>';
}
@@ -287,19 +302,27 @@ class IPUnblockForm {
$line = wfMsgReplaceArgs( $msg['blocklistline'], array( $formattedTime, $blocker, $target, $properties ) );
- $s = "<li>{$line}";
-
+ $unblocklink = '';
if ( $wgUser->isAllowed('block') ) {
$titleObj = SpecialPage::getTitleFor( "Ipblocklist" );
- $s .= ' (' . $sk->makeKnownLinkObj($titleObj, $msg['unblocklink'], 'action=unblock&id=' . urlencode( $block->mId ) ) . ')';
+ $unblocklink = ' (' . $sk->makeKnownLinkObj($titleObj, $msg['unblocklink'], 'action=unblock&id=' . urlencode( $block->mId ) ) . ')';
}
- $s .= $sk->commentBlock( $block->mReason );
- $s .= "</li>\n";
+
+ $comment = $sk->commentBlock( $block->mReason );
+
+ $s = "{$line} $comment";
+ if ( $block->mHideName )
+ $s = '<span class="history-deleted">' . $s . '</span>';
+
wfProfileOut( __METHOD__ );
- return $s;
+ return "<li>$s $unblocklink</li>\n";
}
}
+/**
+ * @todo document
+ * @addtogroup Pager
+ */
class IPBlocklistPager extends ReverseChronologicalPager {
public $mForm, $mConds;
diff --git a/includes/SpecialListredirects.php b/includes/SpecialListredirects.php
index f717ef72..09dc2b39 100644
--- a/includes/SpecialListredirects.php
+++ b/includes/SpecialListredirects.php
@@ -1,7 +1,6 @@
<?php
/**
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*
* @author Rob Church <robchur@gmail.com>
* @copyright © 2006 Rob Church
@@ -9,10 +8,9 @@
*/
/**
- * @package MediaWiki
- * @subpackage SpecialPage
+ * Special:Listredirects - Lists all the redirects on the wiki.
+ * @addtogroup SpecialPage
*/
-
class ListredirectsPage extends QueryPage {
function getName() { return( 'Listredirects' ); }
@@ -21,7 +19,7 @@ class ListredirectsPage extends QueryPage {
function sortDescending() { return( false ); }
function getSQL() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$page = $dbr->tableName( 'page' );
$sql = "SELECT 'Listredirects' AS type, page_title AS title, page_namespace AS namespace, 0 AS value FROM $page WHERE page_is_redirect = 1";
return( $sql );
diff --git a/includes/SpecialListusers.php b/includes/SpecialListusers.php
index b0794344..42498430 100644
--- a/includes/SpecialListusers.php
+++ b/includes/SpecialListusers.php
@@ -23,8 +23,7 @@
# http://www.gnu.org/copyleft/gpl.html
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
@@ -32,176 +31,169 @@
* rights (sysop, bureaucrat, developer) will have them displayed
* next to their names.
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
-class ListUsersPage extends QueryPage {
- var $requestedGroup = '';
- var $requestedUser = '';
- function getName() {
- return 'Listusers';
+class UsersPager extends AlphabeticPager {
+
+ function __construct($group=null) {
+ global $wgRequest;
+ $this->requestedGroup = $group != "" ? $group : $wgRequest->getVal( 'group' );
+ $un = $wgRequest->getText( 'username' );
+ $this->requestedUser = '';
+ if ( $un != '' ) {
+ $username = Title::makeTitleSafe( NS_USER, $un );
+ if( ! is_null( $username ) ) {
+ $this->requestedUser = $username->getText();
+ }
+ }
+ parent::__construct();
}
- function isSyndicated() { return false; }
- /**
- * Not expensive, this class won't work properly with the caching system anyway
- */
- function isExpensive() {
- return false;
+
+ function getIndexField() {
+ return 'user_name';
}
- /**
- * Fetch user page links and cache their existence
- */
- function preprocessResults( &$db, &$res ) {
- $batch = new LinkBatch;
- while ( $row = $db->fetchObject( $res ) ) {
- $batch->addObj( Title::makeTitleSafe( $row->namespace, $row->title ) );
+ function getQueryInfo() {
+ $conds=array();
+ // don't show hidden names
+ $conds[]='ipb_deleted IS NULL OR ipb_deleted = 0';
+ if ($this->requestedGroup != "") {
+ $conds['ug_group'] = $this->requestedGroup;
}
- $batch->execute();
+ if ($this->requestedUser != "") {
+ $conds[] = 'user_name >= ' . wfGetDB()->addQuotes( $this->requestedUser );
+ }
+
+ list ($user,$user_groups,$ipblocks) = wfGetDB()->tableNamesN('user','user_groups','ipblocks');
- // Back to start for display
- if( $db->numRows( $res ) > 0 ) {
- // If there are no rows we get an error seeking.
- $db->dataSeek( $res, 0 );
+ return array(
+ 'tables' => " $user LEFT JOIN $user_groups ON user_id=ug_user LEFT JOIN $ipblocks ON user_id=ipb_user AND ipb_auto=0 ",
+ 'fields' => array('user_name',
+ 'MAX(user_id) AS user_id',
+ 'COUNT(ug_group) AS numgroups',
+ 'MAX(ug_group) AS singlegroup'),
+ 'options' => array('GROUP BY' => 'user_name'),
+ 'conds' => $conds
+ );
+
+ }
+
+ function formatRow( $row ) {
+ $userPage = Title::makeTitle( NS_USER, $row->user_name );
+ $name = $this->getSkin()->makeLinkObj( $userPage, htmlspecialchars( $userPage->getText() ) );
+
+ if( $row->numgroups > 1 || ( $this->requestedGroup && $row->numgroups == 1 ) ) {
+ $list = array();
+ foreach( self::getGroups( $row->user_id ) as $group )
+ $list[] = self::buildGroupLink( $group );
+ $groups = implode( ', ', $list );
+ } elseif( $row->numgroups == 1 ) {
+ $groups = self::buildGroupLink( $row->singlegroup );
+ } else {
+ $groups = '';
}
+
+ return '<li>' . wfSpecialList( $name, $groups ) . '</li>';
+ }
+
+ function getBody() {
+ if (!$this->mQueryDone) {
+ $this->doQuery();
+ }
+ $batch = new LinkBatch;
+ $db = $this->mDb;
+
+ $this->mResult->rewind();
+
+ while ( $row = $this->mResult->fetchObject() ) {
+ $batch->addObj( Title::makeTitleSafe( NS_USER, $row->user_name ) );
+ }
+ $batch->execute();
+ $this->mResult->rewind();
+ return parent::getBody();
}
- /**
- * Show a drop down list to select a group as well as a user name
- * search box.
- * @todo localize
- */
function getPageHeader( ) {
+ global $wgRequest;
$self = $this->getTitle();
# Form tag
- $out = wfOpenElement( 'form', array( 'method' => 'post', 'action' => $self->getLocalUrl() ) );
-
+ $out = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $self->getLocalUrl() ) ) .
+ '<fieldset>' .
+ Xml::element( 'legend', array(), wfMsg( 'listusers' ) );
+
+ # Username field
+ $out .= Xml::label( wfMsg( 'listusersfrom' ), 'offset' ) . ' ' .
+ Xml::input( 'username', 20, $this->requestedUser, array( 'id' => 'offset' ) ) . ' ';
+
+ if( $this->mLimit )
+ $out .= Xml::hidden( 'limit', $this->mLimit );
+
# Group drop-down list
- $out .= wfElement( 'label', array( 'for' => 'group' ), wfMsg( 'group' ) ) . ' ';
- $out .= wfOpenElement( 'select', array( 'name' => 'group' ) );
- $out .= wfElement( 'option', array( 'value' => '' ), wfMsg( 'group-all' ) ); # Item for "all groups"
+ $out .= Xml::label( wfMsg( 'group' ), 'group' ) . ' ' .
+ Xml::openElement('select', array( 'name' => 'group', 'id' => 'group' ) ) .
+ Xml::option( wfMsg( 'group-all' ), '' ); # Item for "all groups"
+
$groups = User::getAllGroups();
foreach( $groups as $group ) {
$attribs = array( 'value' => $group );
- if( $group == $this->requestedGroup )
- $attribs['selected'] = 'selected';
- $out .= wfElement( 'option', $attribs, User::getGroupName( $group ) );
+ $attribs['selected'] = ( $group == $this->requestedGroup ) ? 'selected' : '';
+ $out .= Xml::option( User::getGroupName( $group ), $attribs['value'], $attribs['selected'] );
}
- $out .= wfCloseElement( 'select' ) . ' ';;# . wfElement( 'br' );
-
- # Username field
- $out .= wfElement( 'label', array( 'for' => 'username' ), wfMsg( 'listusersfrom' ) ) . ' ';
- $out .= wfElement( 'input', array( 'type' => 'text', 'id' => 'username', 'name' => 'username',
- 'value' => $this->requestedUser ) ) . ' ';
-
- # Preserve offset and limit
- if( $this->offset )
- $out .= wfElement( 'input', array( 'type' => 'hidden', 'name' => 'offset', 'value' => $this->offset ) );
- if( $this->limit )
- $out .= wfElement( 'input', array( 'type' => 'hidden', 'name' => 'limit', 'value' => $this->limit ) );
+ $out .= Xml::closeElement( 'select' ) . ' ';
# Submit button and form bottom
- $out .= wfElement( 'input', array( 'type' => 'submit', 'value' => wfMsg( 'allpagessubmit' ) ) );
- $out .= wfCloseElement( 'form' );
+ $out .= Xml::submitButton( wfMsg( 'allpagessubmit' ) ) .
+ '</fieldset>' .
+ Xml::closeElement( 'form' );
return $out;
}
- function getSQL() {
- global $wgDBtype;
- $dbr =& wfGetDB( DB_SLAVE );
- $user = $dbr->tableName( 'user' );
- $user_groups = $dbr->tableName( 'user_groups' );
-
- // We need to get an 'atomic' list of users, so that we
- // don't break the list half-way through a user's group set
- // and so that lists by group will show all group memberships.
- //
- // On MySQL 4.1 we could use GROUP_CONCAT to grab group
- // assignments together with users pretty easily. On other
- // versions, it's not so easy to do it consistently.
- // For now we'll just grab the number of memberships, so
- // we can then do targetted checks on those who are in
- // non-default groups as we go down the list.
-
- $userspace = NS_USER;
- $sql = "SELECT 'Listusers' as type, $userspace AS namespace, user_name AS title, " .
- "user_name as value, user_id, COUNT(ug_group) as numgroups " .
- "FROM $user ".
- "LEFT JOIN $user_groups ON user_id=ug_user " .
- $this->userQueryWhere( $dbr ) .
- " GROUP BY user_name";
- if ( $wgDBtype != 'mysql' ) {
- $sql .= ",user_id";
- }
- return $sql;
- }
-
- function userQueryWhere( &$dbr ) {
- $conds = $this->userQueryConditions( $dbr );
- return empty( $conds )
- ? ""
- : "WHERE " . $dbr->makeList( $conds, LIST_AND );
- }
-
- function userQueryConditions( $dbr ) {
- $conds = array();
- if( $this->requestedGroup != '' ) {
- $conds['ug_group'] = $this->requestedGroup;
- }
- if( $this->requestedUser != '' ) {
- $conds[] = 'user_name >= ' . $dbr->addQuotes( $this->requestedUser );
- }
- return $conds;
+ /**
+ * Preserve group and username offset parameters when paging
+ * @return array
+ */
+ function getDefaultQuery() {
+ $query = parent::getDefaultQuery();
+ if( $this->requestedGroup != '' )
+ $query['group'] = $this->requestedGroup;
+ if( $this->requestedUser != '' )
+ $query['username'] = $this->requestedUser;
+ return $query;
}
- function linkParameters() {
- $conds = array();
- if( $this->requestedGroup != '' ) {
- $conds['group'] = $this->requestedGroup;
- }
- if( $this->requestedUser != '' ) {
- $conds['username'] = $this->requestedUser;
+ /**
+ * Get a list of groups the specified user belongs to
+ *
+ * @param int $uid
+ * @return array
+ */
+ private static function getGroups( $uid ) {
+ $dbr = wfGetDB( DB_SLAVE );
+ $groups = array();
+ $res = $dbr->select( 'user_groups', 'ug_group', array( 'ug_user' => $uid ), __METHOD__ );
+ if( $res && $dbr->numRows( $res ) > 0 ) {
+ while( $row = $dbr->fetchObject( $res ) )
+ $groups[] = $row->ug_group;
+ $dbr->freeResult( $res );
}
- return $conds;
- }
-
- function sortDescending() {
- return false;
+ return $groups;
}
- function formatResult( $skin, $result ) {
- $userPage = Title::makeTitle( $result->namespace, $result->title );
- $name = $skin->makeLinkObj( $userPage, htmlspecialchars( $userPage->getText() ) );
- $groups = null;
-
- if( !isset( $result->numgroups ) || $result->numgroups > 0 ) {
- $dbr =& wfGetDB( DB_SLAVE );
- $result = $dbr->select( 'user_groups',
- array( 'ug_group' ),
- array( 'ug_user' => $result->user_id ),
- 'ListUsersPage::formatResult' );
- $groups = array();
- while( $row = $dbr->fetchObject( $result ) ) {
- $groups[$row->ug_group] = User::getGroupMember( $row->ug_group );
- }
- $dbr->freeResult( $result );
-
- if( count( $groups ) > 0 ) {
- foreach( $groups as $group => $desc ) {
- $list[] = User::makeGroupLinkHTML( $group, $desc );
- }
- $groups = implode( ', ', $list );
- } else {
- $groups = '';
- }
-
- }
-
- return wfSpecialList( $name, $groups );
+ /**
+ * Format a link to a group description page
+ *
+ * @param string $group
+ * @return string
+ */
+ private static function buildGroupLink( $group ) {
+ static $cache = array();
+ if( !isset( $cache[$group] ) )
+ $cache[$group] = User::makeGroupLinkHtml( $group, User::getGroupMember( $group ) );
+ return $cache[$group];
}
}
@@ -210,25 +202,26 @@ class ListUsersPage extends QueryPage {
* $par string (optional) A group to list users from
*/
function wfSpecialListusers( $par = null ) {
- global $wgRequest;
+ global $wgRequest, $wgOut;
list( $limit, $offset ) = wfCheckLimits();
-
- $slu = new ListUsersPage();
-
- /**
- * Get some parameters
- */
$groupTarget = isset($par) ? $par : $wgRequest->getVal( 'group' );
- $slu->requestedGroup = $groupTarget;
- # 'Validate' the username first
- $username = $wgRequest->getText( 'username', '' );
- $user = User::newFromName( $username );
- $slu->requestedUser = is_object( $user ) ? $user->getName() : '';
+ $up = new UsersPager($par);
+
+ # getBody() first to check, if empty
+ $usersbody = $up->getBody();
+ $s = $up->getPageHeader();
+ if( $usersbody ) {
+ $s .= $up->getNavigationBar();
+ $s .= '<ul>' . $usersbody . '</ul>';
+ $s .= $up->getNavigationBar() ;
+ } else {
+ $s .= '<p>' . wfMsgHTML('listusers-noresult') . '</p>';
+ };
- return $slu->doQuery( $offset, $limit );
+ $wgOut->addHTML( $s );
}
?>
diff --git a/includes/SpecialLockdb.php b/includes/SpecialLockdb.php
index f0142e5c..db4006f5 100644
--- a/includes/SpecialLockdb.php
+++ b/includes/SpecialLockdb.php
@@ -1,8 +1,7 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
@@ -37,9 +36,8 @@ function wfSpecialLockdb() {
}
/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
+ * A form to make the database readonly (eg for maintenance purposes).
+ * @addtogroup SpecialPage
*/
class DBLockForm {
var $reason = '';
@@ -126,7 +124,7 @@ END
$wgOut->addWikiText( wfMsg( 'lockdbsuccesstext' ) );
}
- function notWritable() {
+ public static function notWritable() {
global $wgOut;
$wgOut->errorPage( 'lockdb', 'lockfilenotwritable' );
}
diff --git a/includes/SpecialLog.php b/includes/SpecialLog.php
index 7076d819..3c9d0960 100644
--- a/includes/SpecialLog.php
+++ b/includes/SpecialLog.php
@@ -19,8 +19,7 @@
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
@@ -38,18 +37,17 @@ function wfSpecialLog( $par = '' ) {
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
class LogReader {
var $db, $joinClauses, $whereClauses;
- var $type = '', $user = '', $title = null;
+ var $type = '', $user = '', $title = null, $pattern = false;
/**
* @param WebRequest $request For internal use use a FauxRequest object to pass arbitrary parameters.
*/
function LogReader( $request ) {
- $this->db =& wfGetDB( DB_SLAVE );
+ $this->db = wfGetDB( DB_SLAVE );
$this->setupQuery( $request );
}
@@ -68,11 +66,15 @@ class LogReader {
$this->limitType( $request->getVal( 'type' ) );
$this->limitUser( $request->getText( 'user' ) );
- $this->limitTitle( $request->getText( 'page' ) );
+ $this->limitTitle( $request->getText( 'page' ) , $request->getBool( 'pattern' ) );
$this->limitTime( $request->getVal( 'from' ), '>=' );
$this->limitTime( $request->getVal( 'until' ), '<=' );
list( $this->limit, $this->offset ) = $request->getLimitOffset();
+
+ // XXX This all needs to use Pager, ugly hack for now.
+ global $wgMiserMode;
+ if ($wgMiserMode && ($this->offset >10000)) $this->offset=10000;
}
/**
@@ -118,15 +120,22 @@ class LogReader {
* @param string $page Title name as text
* @private
*/
- function limitTitle( $page ) {
+ function limitTitle( $page , $pattern ) {
+ global $wgMiserMode;
$title = Title::newFromText( $page );
if( empty( $page ) || is_null( $title ) ) {
return false;
}
$this->title =& $title;
- $safetitle = $this->db->strencode( $title->getDBkey() );
+ $this->pattern = $pattern;
$ns = $title->getNamespace();
- $this->whereClauses[] = "log_namespace=$ns AND log_title='$safetitle'";
+ if ( $pattern && !$wgMiserMode ) {
+ $safetitle = $this->db->escapeLike( $title->getDBkey() ); // use escapeLike to avoid expensive search patterns like 't%st%'
+ $this->whereClauses[] = "log_namespace=$ns AND log_title LIKE '$safetitle%'";
+ } else {
+ $safetitle = $this->db->strencode( $title->getDBkey() );
+ $this->whereClauses[] = "log_namespace=$ns AND log_title = '$safetitle'";
+ }
}
/**
@@ -190,6 +199,13 @@ class LogReader {
}
/**
+ * @return boolean The checkbox, if titles should be searched by a pattern too
+ */
+ function queryPattern() {
+ return $this->pattern;
+ }
+
+ /**
* @return string The text of the title that this LogReader has been limited to.
*/
function queryTitle() {
@@ -203,8 +219,7 @@ class LogReader {
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
class LogViewer {
/**
@@ -218,7 +233,7 @@ class LogViewer {
*/
function LogViewer( &$reader ) {
global $wgUser;
- $this->skin =& $wgUser->getSkin();
+ $this->skin = $wgUser->getSkin();
$this->reader =& $reader;
}
@@ -230,9 +245,13 @@ class LogViewer {
$this->showHeader( $wgOut );
$this->showOptions( $wgOut );
$result = $this->getLogRows();
- $this->showPrevNext( $wgOut );
- $this->doShowList( $wgOut, $result );
- $this->showPrevNext( $wgOut );
+ if ( $this->numResults > 0 ) {
+ $this->showPrevNext( $wgOut );
+ $this->doShowList( $wgOut, $result );
+ $this->showPrevNext( $wgOut );
+ } else {
+ $this->showError( $wgOut );
+ }
}
/**
@@ -252,8 +271,8 @@ class LogViewer {
$batch = new LinkBatch;
while ( $s = $result->fetchObject() ) {
// User link
- $title = Title::makeTitleSafe( NS_USER, $s->user_name );
- $batch->addObj( $title );
+ $batch->addObj( Title::makeTitleSafe( NS_USER, $s->user_name ) );
+ $batch->addObj( Title::makeTitleSafe( NS_USER_TALK, $s->user_name ) );
// Move destination link
if ( $s->log_type == 'move' ) {
@@ -276,32 +295,38 @@ class LogViewer {
* @param OutputPage $out where to send output
*/
function showList( &$out ) {
- $this->doShowList( $out, $this->getLogRows() );
+ $result = $this->getLogRows();
+ if ( $this->numResults > 0 ) {
+ $this->doShowList( $out, $result );
+ } else {
+ $this->showError( $out );
+ }
}
function doShowList( &$out, $result ) {
// Rewind result pointer and go through it again, making the HTML
- if ($this->numResults > 0) {
- $html = "\n<ul>\n";
- $result->seek( 0 );
- while( $s = $result->fetchObject() ) {
- $html .= $this->logLine( $s );
- }
- $html .= "\n</ul>\n";
- $out->addHTML( $html );
- } else {
- $out->addWikiText( wfMsg( 'logempty' ) );
+ $html = "\n<ul>\n";
+ $result->seek( 0 );
+ while( $s = $result->fetchObject() ) {
+ $html .= $this->logLine( $s );
}
+ $html .= "\n</ul>\n";
+ $out->addHTML( $html );
$result->free();
}
+ function showError( &$out ) {
+ $out->addWikiText( wfMsg( 'logempty' ) );
+ }
+
/**
* @param Object $s a single row from the result set
* @return string Formatted HTML list item
* @private
*/
function logLine( $s ) {
- global $wgLang;
+ global $wgLang, $wgUser;;
+ $skin = $wgUser->getSkin();
$title = Title::makeTitle( $s->log_namespace, $s->log_title );
$time = $wgLang->timeanddate( wfTimestamp(TS_MW, $s->log_timestamp), true );
@@ -314,20 +339,43 @@ class LogViewer {
$linkCache->addBadLinkObj( $title );
}
- $userLink = $this->skin->userLink( $s->log_user, $s->user_name ) . $this->skin->userToolLinks( $s->log_user, $s->user_name );
+ $userLink = $this->skin->userLink( $s->log_user, $s->user_name ) . $this->skin->userToolLinksRedContribs( $s->log_user, $s->user_name );
$comment = $this->skin->commentBlock( $s->log_comment );
$paramArray = LogPage::extractParams( $s->log_params );
$revert = '';
+ // show revertmove link
if ( $s->log_type == 'move' && isset( $paramArray[0] ) ) {
- $specialTitle = SpecialPage::getTitleFor( 'Movepage' );
$destTitle = Title::newFromText( $paramArray[0] );
if ( $destTitle ) {
- $revert = '(' . $this->skin->makeKnownLinkObj( $specialTitle, wfMsg( 'revertmove' ),
+ $revert = '(' . $this->skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Movepage' ),
+ wfMsg( 'revertmove' ),
'wpOldTitle=' . urlencode( $destTitle->getPrefixedDBkey() ) .
'&wpNewTitle=' . urlencode( $title->getPrefixedDBkey() ) .
'&wpReason=' . urlencode( wfMsgForContent( 'revertmove' ) ) .
'&wpMovetalk=0' ) . ')';
}
+ // show undelete link
+ } elseif ( $s->log_action == 'delete' && $wgUser->isAllowed( 'delete' ) ) {
+ $revert = '(' . $this->skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Undelete' ),
+ wfMsg( 'undeletebtn' ) ,
+ 'target='. urlencode( $title->getPrefixedDBkey() ) ) . ')';
+
+ // show unblock link
+ } elseif ( $s->log_action == 'block' && $wgUser->isAllowed( 'block' ) ) {
+ $revert = '(' . $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Ipblocklist' ),
+ wfMsg( 'unblocklink' ),
+ 'action=unblock&ip=' . urlencode( $s->log_title ) ) . ')';
+ // show change protection link
+ } elseif ( $s->log_action == 'protect' && $wgUser->isAllowed( 'protect' ) ) {
+ $revert = '(' . $skin->makeKnownLink( $title->getPrefixedDBkey() ,
+ wfMsg( 'protect_change' ),
+ 'action=unprotect' ) . ')';
+ // show user tool links for self created users
+ } elseif ( $s->log_action == 'create2' ) {
+ $revert = $this->skin->userToolLinksRedContribs( $s->log_user, $s->log_title );
+ // do not show $comment for self created accounts. It includes wrong user tool links:
+ // 'blockip' for users w/o block allowance and broken links for very long usernames (bug 4756)
+ $comment = '';
}
$action = LogPage::actionText( $s->log_type, $s->log_action, $title, $this->skin, $paramArray, true, true );
@@ -352,17 +400,20 @@ class LogViewer {
* @private
*/
function showOptions( &$out ) {
- global $wgScript;
+ global $wgScript, $wgMiserMode;
$action = htmlspecialchars( $wgScript );
$title = SpecialPage::getTitleFor( 'Log' );
$special = htmlspecialchars( $title->getPrefixedDBkey() );
$out->addHTML( "<form action=\"$action\" method=\"get\">\n" .
- "<input type='hidden' name='title' value=\"$special\" />\n" .
- $this->getTypeMenu() .
- $this->getUserInput() .
- $this->getTitleInput() .
- "<input type='submit' value=\"" . wfMsg( 'allpagessubmit' ) . "\" />" .
- "</form>" );
+ '<fieldset>' .
+ Xml::element( 'legend', array(), wfMsg( 'log' ) ) .
+ Xml::hidden( 'title', $special ) . "\n" .
+ $this->getTypeMenu() . "\n" .
+ $this->getUserInput() . "\n" .
+ $this->getTitleInput() . "\n" .
+ (!$wgMiserMode?($this->getTitlePattern()."\n"):"") .
+ Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . "\n" .
+ "</fieldset></form>" );
}
/**
@@ -371,12 +422,26 @@ class LogViewer {
*/
function getTypeMenu() {
$out = "<select name='type'>\n";
- foreach( LogPage::validTypes() as $type ) {
- $text = htmlspecialchars( LogPage::logName( $type ) );
- $selected = ($type == $this->reader->queryType()) ? ' selected="selected"' : '';
- $out .= "<option value=\"$type\"$selected>$text</option>\n";
+
+ $validTypes = LogPage::validTypes();
+ $m = array(); // Temporary array
+
+ // First pass to load the log names
+ foreach( $validTypes as $type ) {
+ $text = LogPage::logName( $type );
+ $m[$text] = $type;
+ }
+
+ // Second pass to sort by name
+ ksort($m);
+
+ // Third pass generates sorted XHTML content
+ foreach( $m as $text => $type ) {
+ $selected = ($type == $this->reader->queryType());
+ $out .= Xml::option( $text, $type, $selected ) . "\n";
}
- $out .= "</select>\n";
+
+ $out .= '</select>';
return $out;
}
@@ -385,8 +450,8 @@ class LogViewer {
* @private
*/
function getUserInput() {
- $user = htmlspecialchars( $this->reader->queryUser() );
- return wfMsg('specialloguserlabel') . "<input type='text' name='user' size='12' value=\"$user\" />\n";
+ $user = $this->reader->queryUser();
+ return Xml::inputLabel( wfMsg( 'specialloguserlabel' ), 'user', 'user', 12, $user );
}
/**
@@ -394,8 +459,17 @@ class LogViewer {
* @private
*/
function getTitleInput() {
- $title = htmlspecialchars( $this->reader->queryTitle() );
- return wfMsg('speciallogtitlelabel') . "<input type='text' name='page' size='20' value=\"$title\" />\n";
+ $title = $this->reader->queryTitle();
+ return Xml::inputLabel( wfMsg( 'speciallogtitlelabel' ), 'page', 'page', 20, $title );
+ }
+
+ /**
+ * @return boolean Checkbox
+ * @private
+ */
+ function getTitlePattern() {
+ $pattern = $this->reader->queryPattern();
+ return Xml::checkLabel( wfMsg( 'log-title-wildcard' ), 'pattern', 'pattern', $pattern );
}
/**
@@ -408,6 +482,7 @@ class LogViewer {
$pieces[] = 'type=' . urlencode( $this->reader->queryType() );
$pieces[] = 'user=' . urlencode( $this->reader->queryUser() );
$pieces[] = 'page=' . urlencode( $this->reader->queryTitle() );
+ $pieces[] = 'pattern=' . urlencode( $this->reader->queryPattern() );
$bits = implode( '&', $pieces );
list( $limit, $offset ) = $wgRequest->getLimitOffset();
diff --git a/includes/SpecialLonelypages.php b/includes/SpecialLonelypages.php
index 8770a9e7..430af7a7 100644
--- a/includes/SpecialLonelypages.php
+++ b/includes/SpecialLonelypages.php
@@ -1,14 +1,13 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
+ * A special page looking for articles with no article linking to them,
+ * thus being lonely.
+ * @addtogroup SpecialPage
*/
class LonelyPagesPage extends PageQueryPage {
@@ -29,7 +28,7 @@ class LonelyPagesPage extends PageQueryPage {
function isSyndicated() { return false; }
function getSQL() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
list( $page, $pagelinks ) = $dbr->tableNamesN( 'page', 'pagelinks' );
return
diff --git a/includes/SpecialLongpages.php b/includes/SpecialLongpages.php
index 3736d6fc..40659889 100644
--- a/includes/SpecialLongpages.php
+++ b/includes/SpecialLongpages.php
@@ -1,14 +1,12 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
class LongPagesPage extends ShortPagesPage {
@@ -24,13 +22,12 @@ class LongPagesPage extends ShortPagesPage {
/**
* constructor
*/
-function wfSpecialLongpages()
-{
- list( $limit, $offset ) = wfCheckLimits();
+function wfSpecialLongpages() {
+ list( $limit, $offset ) = wfCheckLimits();
- $lpp = new LongPagesPage();
+ $lpp = new LongPagesPage();
- $lpp->doQuery( $offset, $limit );
+ $lpp->doQuery( $offset, $limit );
}
?>
diff --git a/includes/SpecialMIMEsearch.php b/includes/SpecialMIMEsearch.php
index 8678118f..d50efc02 100644
--- a/includes/SpecialMIMEsearch.php
+++ b/includes/SpecialMIMEsearch.php
@@ -3,16 +3,16 @@
* A special page to search for files by MIME type as defined in the
* img_major_mime and img_minor_mime fields in the image table
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*
* @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
*/
/**
- * @package MediaWiki
- * @subpackage SpecialPage
+ * Searches the database for files of the requested MIME type, comparing this with the
+ * 'img_major_mime' and 'img_minor_mime' fields in the image table.
+ * @addtogroup SpecialPage
*/
class MIMEsearchPage extends QueryPage {
var $major, $minor;
@@ -38,7 +38,7 @@ class MIMEsearchPage extends QueryPage {
}
function getSQL() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$image = $dbr->tableName( 'image' );
$major = $dbr->addQuotes( $this->major );
$minor = $dbr->addQuotes( $this->minor );
@@ -69,7 +69,7 @@ class MIMEsearchPage extends QueryPage {
$download = $skin->makeMediaLink( $nt->getText(), 'fuck me!', wfMsgHtml( 'download' ) );
$bytes = wfMsgExt( 'nbytes', array( 'parsemag', 'escape'),
$wgLang->formatNum( $result->img_size ) );
- $dimensions = wfMsg( 'widthheight', $wgLang->formatNum( $result->img_width ),
+ $dimensions = wfMsgHtml( 'widthheight', $wgLang->formatNum( $result->img_width ),
$wgLang->formatNum( $result->img_height ) );
$user = $skin->makeLinkObj( Title::makeTitle( NS_USER, $result->img_user_text ), $result->img_user_text );
$time = $wgLang->timeanddate( $result->img_timestamp );
@@ -79,7 +79,7 @@ class MIMEsearchPage extends QueryPage {
}
/**
- * constructor
+ * Output the HTML search form, and constructs the MIMEsearchPage object.
*/
function wfSpecialMIMEsearch( $par = null ) {
global $wgRequest, $wgTitle, $wgOut;
diff --git a/includes/SpecialMostcategories.php b/includes/SpecialMostcategories.php
index 41bfb0cd..df2b9adf 100644
--- a/includes/SpecialMostcategories.php
+++ b/includes/SpecialMostcategories.php
@@ -1,7 +1,6 @@
<?php
/**
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*
* @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
* @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason
@@ -9,8 +8,8 @@
*/
/**
- * @package MediaWiki
- * @subpackage SpecialPage
+ * implements Special:Mostcategories
+ * @addtogroup SpecialPage
*/
class MostcategoriesPage extends QueryPage {
@@ -19,7 +18,7 @@ class MostcategoriesPage extends QueryPage {
function isSyndicated() { return false; }
function getSQL() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
list( $categorylinks, $page) = $dbr->tableNamesN( 'categorylinks', 'page' );
return
"
diff --git a/includes/SpecialMostimages.php b/includes/SpecialMostimages.php
index 17c07c70..9d16f389 100644
--- a/includes/SpecialMostimages.php
+++ b/includes/SpecialMostimages.php
@@ -1,7 +1,6 @@
<?php
/**
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*
* @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
* @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason
@@ -9,17 +8,17 @@
*/
/**
- * @package MediaWiki
- * @subpackage SpecialPage
+ * implements Special:Mostimages
+ * @addtogroup SpecialPage
*/
-class MostimagesPage extends QueryPage {
+class MostimagesPage extends ImageQueryPage {
function getName() { return 'Mostimages'; }
function isExpensive() { return true; }
function isSyndicated() { return false; }
function getSQL() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$imagelinks = $dbr->tableName( 'imagelinks' );
return
"
@@ -34,20 +33,12 @@ class MostimagesPage extends QueryPage {
";
}
- function formatResult( $skin, $result ) {
- global $wgLang, $wgContLang;
-
- $nt = Title::makeTitle( $result->namespace, $result->title );
- $text = $wgContLang->convert( $nt->getPrefixedText() );
-
- $plink = $skin->makeKnownLink( $nt->getPrefixedText(), $text );
-
- $nl = wfMsgExt( 'nlinks', array( 'parsemag', 'escape'),
- $wgLang->formatNum ( $result->value ) );
- $nlink = $skin->makeKnownLink( $nt->getPrefixedText() . '#filelinks', $nl );
-
- return wfSpecialList($plink, $nlink);
+ function getCellHtml( $row ) {
+ global $wgLang;
+ return wfMsgExt( 'nlinks', array( 'parsemag', 'escape' ),
+ $wgLang->formatNum( $row->value ) ) . '<br />';
}
+
}
/**
diff --git a/includes/SpecialMostlinked.php b/includes/SpecialMostlinked.php
index 2794ecbb..ab089cf8 100644
--- a/includes/SpecialMostlinked.php
+++ b/includes/SpecialMostlinked.php
@@ -1,10 +1,10 @@
<?php
/**
- * A special page to show pages ordered by the number of pages linking to them
+ * A special page to show pages ordered by the number of pages linking to them.
+ * Implements Special:Mostlinked
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*
* @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
* @author Rob Church <robchur@gmail.com>
@@ -12,11 +12,6 @@
* @copyright © 2006 Rob Church
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
*/
-
-/**
- * @package MediaWiki
- * @subpackage SpecialPage
- */
class MostlinkedPage extends QueryPage {
function getName() { return 'Mostlinked'; }
@@ -27,7 +22,7 @@ class MostlinkedPage extends QueryPage {
* Note: Getting page_namespace only works if $this->isCached() is false
*/
function getSQL() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
list( $pagelinks, $page ) = $dbr->tableNamesN( 'pagelinks', 'page' );
return
"SELECT 'Mostlinked' AS type,
diff --git a/includes/SpecialMostlinkedcategories.php b/includes/SpecialMostlinkedcategories.php
index e1f84847..725e5b39 100644
--- a/includes/SpecialMostlinkedcategories.php
+++ b/includes/SpecialMostlinkedcategories.php
@@ -2,18 +2,12 @@
/**
* A querypage to show categories ordered in descending order by the pages in them
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*
* @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
*/
-
-/**
- * @package MediaWiki
- * @subpackage SpecialPage
- */
class MostlinkedCategoriesPage extends QueryPage {
function getName() { return 'Mostlinkedcategories'; }
@@ -21,7 +15,7 @@ class MostlinkedCategoriesPage extends QueryPage {
function isSyndicated() { return false; }
function getSQL() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$categorylinks = $dbr->tableName( 'categorylinks' );
$name = $dbr->addQuotes( $this->getName() );
return
diff --git a/includes/SpecialMostrevisions.php b/includes/SpecialMostrevisions.php
index 1e3334e9..59157056 100644
--- a/includes/SpecialMostrevisions.php
+++ b/includes/SpecialMostrevisions.php
@@ -2,8 +2,7 @@
/**
* A special page to show pages in the
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*
* @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
* @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason
@@ -11,8 +10,7 @@
*/
/**
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
class MostrevisionsPage extends QueryPage {
@@ -21,7 +19,7 @@ class MostrevisionsPage extends QueryPage {
function isSyndicated() { return false; }
function getSQL() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
list( $revision, $page ) = $dbr->tableNamesN( 'revision', 'page' );
return
"
diff --git a/includes/SpecialMovepage.php b/includes/SpecialMovepage.php
index e3112c4c..d8f01874 100644
--- a/includes/SpecialMovepage.php
+++ b/includes/SpecialMovepage.php
@@ -1,8 +1,7 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
@@ -42,9 +41,8 @@ function wfSpecialMovepage( $par = null ) {
}
/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
+ * HTML form for Special:Movepage
+ * @addtogroup SpecialPage
*/
class MovePageForm {
var $oldTitle, $newTitle, $reason; # Text input
@@ -68,7 +66,10 @@ class MovePageForm {
}
function showForm( $err ) {
- global $wgOut, $wgUser;
+ global $wgOut, $wgUser, $wgContLang;
+
+ $start = $wgContLang->isRTL() ? 'right' : 'left';
+ $end = $wgContLang->isRTL() ? 'left' : 'right';
$wgOut->setPagetitle( wfMsg( 'movepage' ) );
@@ -108,10 +109,10 @@ class MovePageForm {
$submitVar = 'wpDeleteAndMove';
$confirm = "
<tr>
- <td align='right'>
+ <td align='$end'>
<input type='checkbox' name='wpConfirm' id='wpConfirm' value=\"true\" />
</td>
- <td align='left'><label for='wpConfirm'>{$confirmText}</label></td>
+ <td align='$start'><label for='wpConfirm'>{$confirmText}</label></td>
</tr>";
$err = '';
} else {
@@ -148,19 +149,19 @@ class MovePageForm {
<form id=\"movepage\" method=\"post\" action=\"{$action}\">
<table border='0'>
<tr>
- <td align='right'>{$movearticle}:</td>
- <td align='left'><strong>{$oldTitle}</strong></td>
+ <td align='$end'>{$movearticle}:</td>
+ <td align='$start'><strong>{$oldTitle}</strong></td>
</tr>
<tr>
- <td align='right'><label for='wpNewTitle'>{$newtitle}:</label></td>
- <td align='left'>
+ <td align='$end'><label for='wpNewTitle'>{$newtitle}:</label></td>
+ <td align='$start'>
<input type='text' size='40' name='wpNewTitle' id='wpNewTitle' value=\"{$encNewTitle}\" />
<input type='hidden' name=\"wpOldTitle\" value=\"{$encOldTitle}\" />
</td>
</tr>
<tr>
- <td align='right' valign='top'><br /><label for='wpReason'>{$movereason}:</label></td>
- <td align='left' valign='top'><br />
+ <td align='$end' valign='top'><br /><label for='wpReason'>{$movereason}:</label></td>
+ <td align='$start' valign='top'><br />
<textarea cols='60' rows='2' name='wpReason' id='wpReason'>{$encReason}</textarea>
</td>
</tr>" );
@@ -168,7 +169,7 @@ class MovePageForm {
if ( $considerTalk ) {
$wgOut->addHTML( "
<tr>
- <td align='right'>
+ <td align='$end'>
<input type='checkbox' id=\"wpMovetalk\" name=\"wpMovetalk\"{$moveTalkChecked} value=\"1\" />
</td>
<td><label for=\"wpMovetalk\">{$movetalk}</label></td>
@@ -177,7 +178,7 @@ class MovePageForm {
$watchChecked = $this->watch || $wgUser->getBoolOption( 'watchmoves' ) || $ot->userIsWatching();
$watch = '<tr>';
- $watch .= '<td align="right">' . Xml::check( 'wpWatch', $watchChecked, array( 'id' => 'watch' ) ) . '</td>';
+ $watch .= "<td align=\"$end\">" . Xml::check( 'wpWatch', $watchChecked, array( 'id' => 'watch' ) ) . '</td>';
$watch .= '<td>' . Xml::label( wfMsg( 'move-watch' ), 'watch' ) . '</td>';
$watch .= '</tr>';
$wgOut->addHtml( $watch );
@@ -186,7 +187,7 @@ class MovePageForm {
{$confirm}
<tr>
<td>&nbsp;</td>
- <td align='left'>
+ <td align='$start'>
<input type='submit' name=\"{$submitVar}\" value=\"{$movepagebtn}\" />
</td>
</tr>
diff --git a/includes/SpecialNewimages.php b/includes/SpecialNewimages.php
index 062e7e12..72b169b1 100644
--- a/includes/SpecialNewimages.php
+++ b/includes/SpecialNewimages.php
@@ -1,18 +1,17 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
*
*/
function wfSpecialNewimages( $par, $specialPage ) {
- global $wgUser, $wgOut, $wgLang, $wgRequest, $wgGroupPermissions;
+ global $wgUser, $wgOut, $wgLang, $wgRequest, $wgGroupPermissions, $wgMiserMode;
$wpIlMatch = $wgRequest->getText( 'wpIlMatch' );
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$sk = $wgUser->getSkin();
$shownav = !$specialPage->including();
$hidebots = $wgRequest->getBool('hidebots',1);
@@ -75,23 +74,23 @@ function wfSpecialNewimages( $par, $specialPage ) {
$where = array();
$searchpar = '';
- if ( $wpIlMatch != '' ) {
+ if ( $wpIlMatch != '' && !$wgMiserMode) {
$nt = Title::newFromUrl( $wpIlMatch );
if($nt ) {
$m = $dbr->strencode( strtolower( $nt->getDBkey() ) );
$m = str_replace( '%', "\\%", $m );
$m = str_replace( '_', "\\_", $m );
- $where[] = "LCASE(img_name) LIKE '%{$m}%'";
+ $where[] = "LOWER(img_name) LIKE '%{$m}%'";
$searchpar = '&wpIlMatch=' . urlencode( $wpIlMatch );
}
}
$invertSort = false;
if( $until = $wgRequest->getVal( 'until' ) ) {
- $where[] = 'img_timestamp < ' . $dbr->timestamp( $until );
+ $where[] = "img_timestamp < '" . $dbr->timestamp( $until ) . "'";
}
if( $from = $wgRequest->getVal( 'from' ) ) {
- $where[] = 'img_timestamp >= ' . $dbr->timestamp( $from );
+ $where[] = "img_timestamp >= '" . $dbr->timestamp( $from ) . "'";
$invertSort = true;
}
$sql='SELECT img_size, img_name, img_user, img_user_text,'.
@@ -158,12 +157,12 @@ function wfSpecialNewimages( $par, $specialPage ) {
$sub = wfMsg( 'ilsubmit' );
$titleObj = SpecialPage::getTitleFor( 'Newimages' );
$action = $titleObj->escapeLocalURL( $hidebots ? '' : 'hidebots=0' );
- if ($shownav) {
+ if ($shownav && !$wgMiserMode) {
$wgOut->addHTML( "<form id=\"imagesearch\" method=\"post\" action=\"" .
"{$action}\">" .
- "<input type='text' size='20' name=\"wpIlMatch\" value=\"" .
- htmlspecialchars( $wpIlMatch ) . "\" /> " .
- "<input type='submit' name=\"wpIlSubmit\" value=\"{$sub}\" /></form>" );
+ Xml::input( 'wpIlMatch', 20, $wpIlMatch ) . ' ' .
+ Xml::submitButton( $sub, array( 'name' => 'wpIlSubmit' ) ) .
+ "</form>" );
}
/**
@@ -178,21 +177,21 @@ function wfSpecialNewimages( $par, $specialPage ) {
}
$now = wfTimestampNow();
$date = $wgLang->timeanddate( $now, true );
- $dateLink = $sk->makeKnownLinkObj( $titleObj, wfMsg( 'sp-newimages-showfrom', $date ), 'from='.$now.$botpar.$searchpar );
+ $dateLink = $sk->makeKnownLinkObj( $titleObj, wfMsgHtml( 'sp-newimages-showfrom', $date ), 'from='.$now.$botpar.$searchpar );
- $botLink = $sk->makeKnownLinkObj($titleObj, wfMsg( 'showhidebots', ($hidebots ? wfMsg('show') : wfMsg('hide'))),'hidebots='.($hidebots ? '0' : '1').$searchpar);
+ $botLink = $sk->makeKnownLinkObj($titleObj, wfMsgHtml( 'showhidebots', ($hidebots ? wfMsgHtml('show') : wfMsgHtml('hide'))),'hidebots='.($hidebots ? '0' : '1').$searchpar);
- $prevLink = wfMsg( 'prevn', $wgLang->formatNum( $limit ) );
+ $prevLink = wfMsgHtml( 'prevn', $wgLang->formatNum( $limit ) );
if( $firstTimestamp && $firstTimestamp != $latestTimestamp ) {
$prevLink = $sk->makeKnownLinkObj( $titleObj, $prevLink, 'from=' . $firstTimestamp . $botpar . $searchpar );
}
- $nextLink = wfMsg( 'nextn', $wgLang->formatNum( $limit ) );
+ $nextLink = wfMsgHtml( 'nextn', $wgLang->formatNum( $limit ) );
if( $shownImages > $limit && $lastTimestamp ) {
$nextLink = $sk->makeKnownLinkObj( $titleObj, $nextLink, 'until=' . $lastTimestamp.$botpar.$searchpar );
}
- $prevnext = '<p>' . $botLink . ' '. wfMsg( 'viewprevnext', $prevLink, $nextLink, $dateLink ) .'</p>';
+ $prevnext = '<p>' . $botLink . ' '. wfMsgHtml( 'viewprevnext', $prevLink, $nextLink, $dateLink ) .'</p>';
if ($shownav)
$wgOut->addHTML( $prevnext );
diff --git a/includes/SpecialNewpages.php b/includes/SpecialNewpages.php
index 62007383..48037a73 100644
--- a/includes/SpecialNewpages.php
+++ b/includes/SpecialNewpages.php
@@ -1,14 +1,12 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
+ * implements Special:Newpages
+ * @addtogroup SpecialPage
*/
class NewPagesPage extends QueryPage {
@@ -41,7 +39,7 @@ class NewPagesPage extends QueryPage {
function getSQL() {
global $wgUser, $wgUseRCPatrol;
$usepatrol = ( $wgUseRCPatrol && $wgUser->isAllowed( 'patrol' ) ) ? 1 : 0;
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
list( $recentchanges, $page ) = $dbr->tableNamesN( 'recentchanges', 'page' );
$uwhere = $this->makeUserWhere( $dbr );
@@ -52,9 +50,9 @@ class NewPagesPage extends QueryPage {
rc_namespace AS namespace,
rc_title AS title,
rc_cur_id AS cur_id,
- rc_user AS user,
+ rc_user AS \"user\",
rc_user_text AS user_text,
- rc_comment as comment,
+ rc_comment as \"comment\",
rc_timestamp AS timestamp,
rc_timestamp AS value,
'{$usepatrol}' as usepatrol,
@@ -133,13 +131,16 @@ class NewPagesPage extends QueryPage {
*/
function getPageHeader() {
$self = SpecialPage::getTitleFor( $this->getName() );
- $form = wfOpenElement( 'form', array( 'method' => 'post', 'action' => $self->getLocalUrl() ) );
- $form .= '<table><tr><td align="right">' . wfMsgHtml( 'namespace' ) . '</td>';
- $form .= '<td>' . HtmlNamespaceSelector( $this->namespace ) . '</td><tr>';
- $form .= '<tr><td align="right">' . wfMsgHtml( 'newpages-username' ) . '</td>';
- $form .= '<td>' . wfInput( 'username', 30, $this->username ) . '</td></tr>';
- $form .= '<tr><td></td><td>' . wfSubmitButton( wfMsg( 'allpagessubmit' ) ) . '</td></tr></table>';
- $form .= wfHidden( 'offset', $this->offset ) . wfHidden( 'limit', $this->limit ) . '</form>';
+ $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $self->getLocalUrl() ) );
+ # Namespace selector
+ $form .= '<table><tr><td align="right">' . Xml::label( wfMsg( 'namespace' ), 'namespace' ) . '</td>';
+ $form .= '<td>' . Xml::namespaceSelector( $this->namespace ) . '</td></tr>';
+ # Username filter
+ $form .= '<tr><td align="right">' . Xml::label( wfMsg( 'newpages-username' ), 'mw-np-username' ) . '</td>';
+ $form .= '<td>' . Xml::input( 'username', 30, $this->username, array( 'id' => 'mw-np-username' ) ) . '</td></tr>';
+
+ $form .= '<tr><td></td><td>' . Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . '</td></tr></table>';
+ $form .= Xml::hidden( 'offset', $this->offset ) . Xml::hidden( 'limit', $this->limit ) . '</form>';
return $form;
}
diff --git a/includes/SpecialPage.php b/includes/SpecialPage.php
index 86438756..cf882509 100644
--- a/includes/SpecialPage.php
+++ b/includes/SpecialPage.php
@@ -17,8 +17,7 @@
* SpecialPage::$mList. To remove a core static special page at runtime, use
* a SpecialPage_initList hook.
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
@@ -27,8 +26,8 @@
/**
* Parent special page class, also static functions for handling the special
- * page list
- * @package MediaWiki
+ * page list.
+ * @addtogroup SpecialPage
*/
class SpecialPage
{
@@ -104,16 +103,18 @@ class SpecialPage
'Mostcategories' => array( 'SpecialPage', 'Mostcategories' ),
'Mostimages' => array( 'SpecialPage', 'Mostimages' ),
'Mostrevisions' => array( 'SpecialPage', 'Mostrevisions' ),
+ 'Fewestrevisions' => array( 'SpecialPage', 'Fewestrevisions' ),
'Shortpages' => array( 'SpecialPage', 'Shortpages' ),
'Longpages' => array( 'SpecialPage', 'Longpages' ),
'Newpages' => array( 'IncludableSpecialPage', 'Newpages' ),
'Ancientpages' => array( 'SpecialPage', 'Ancientpages' ),
'Deadendpages' => array( 'SpecialPage', 'Deadendpages' ),
+ 'Protectedpages' => array( 'SpecialPage', 'Protectedpages' ),
'Allpages' => array( 'IncludableSpecialPage', 'Allpages' ),
'Prefixindex' => array( 'IncludableSpecialPage', 'Prefixindex' ) ,
'Ipblocklist' => array( 'SpecialPage', 'Ipblocklist' ),
'Specialpages' => array( 'UnlistedSpecialPage', 'Specialpages' ),
- 'Contributions' => array( 'UnlistedSpecialPage', 'Contributions' ),
+ 'Contributions' => array( 'SpecialPage', 'Contributions' ),
'Emailuser' => array( 'UnlistedSpecialPage', 'Emailuser' ),
'Whatlinkshere' => array( 'UnlistedSpecialPage', 'Whatlinkshere' ),
'Recentchangeslinked' => array( 'UnlistedSpecialPage', 'Recentchangeslinked' ),
@@ -138,6 +139,7 @@ class SpecialPage
'Revisiondelete' => array( 'SpecialPage', 'Revisiondelete', 'deleterevision' ),
'Unusedtemplates' => array( 'SpecialPage', 'Unusedtemplates' ),
'Randomredirect' => array( 'SpecialPage', 'Randomredirect' ),
+ 'Withoutinterwiki' => array( 'SpecialPage', 'Withoutinterwiki' ),
'Mypage' => array( 'SpecialMypage' ),
'Mytalk' => array( 'SpecialMytalk' ),
@@ -535,7 +537,7 @@ class SpecialPage
$this->mFunction = $function;
}
if ( $file === 'default' ) {
- $this->mFile = "Special{$name}.php";
+ $this->mFile = dirname(__FILE__) . "/Special{$name}.php";
} else {
$this->mFile = $file;
}
@@ -691,7 +693,7 @@ class SpecialPage
/**
* Shortcut to construct a special page which is unlisted by default
- * @package MediaWiki
+ * @addtogroup SpecialPage
*/
class UnlistedSpecialPage extends SpecialPage
{
@@ -702,7 +704,7 @@ class UnlistedSpecialPage extends SpecialPage
/**
* Shortcut to construct an includable special page
- * @package MediaWiki
+ * @addtogroup SpecialPage
*/
class IncludableSpecialPage extends SpecialPage
{
@@ -711,6 +713,10 @@ class IncludableSpecialPage extends SpecialPage
}
}
+/**
+ * Shortcut to construct a special page alias.
+ * @addtogroup SpecialPage
+ */
class SpecialRedirectToSpecial extends UnlistedSpecialPage {
var $redirName, $redirSubpage;
@@ -730,6 +736,17 @@ class SpecialRedirectToSpecial extends UnlistedSpecialPage {
}
}
+/** SpecialMypage, SpecialMytalk and SpecialMycontributions special pages
+ * are used to get user independant links pointing to the user page, talk
+ * page and list of contributions.
+ * This can let us cache a single copy of any generated content for all
+ * users.
+ */
+
+/**
+ * Shortcut to construct a special page pointing to current user user's page.
+ * @addtogroup SpecialPage
+ */
class SpecialMypage extends UnlistedSpecialPage {
function __construct() {
parent::__construct( 'Mypage' );
@@ -746,6 +763,10 @@ class SpecialMypage extends UnlistedSpecialPage {
}
}
+/**
+ * Shortcut to construct a special page pointing to current user talk page.
+ * @addtogroup SpecialPage
+ */
class SpecialMytalk extends UnlistedSpecialPage {
function __construct() {
parent::__construct( 'Mytalk' );
@@ -762,6 +783,10 @@ class SpecialMytalk extends UnlistedSpecialPage {
}
}
+/**
+ * Shortcut to construct a special page pointing to current user contributions.
+ * @addtogroup SpecialPage
+ */
class SpecialMycontributions extends UnlistedSpecialPage {
function __construct() {
parent::__construct( 'Mycontributions' );
diff --git a/includes/SpecialPopularpages.php b/includes/SpecialPopularpages.php
index 77d41437..cd2f60e7 100644
--- a/includes/SpecialPopularpages.php
+++ b/includes/SpecialPopularpages.php
@@ -1,14 +1,12 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
+ * implements Special:Popularpages
+ * @addtogroup SpecialPage
*/
class PopularPagesPage extends QueryPage {
@@ -23,16 +21,28 @@ class PopularPagesPage extends QueryPage {
function isSyndicated() { return false; }
function getSQL() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$page = $dbr->tableName( 'page' );
- return
+ $query =
"SELECT 'Popularpages' as type,
page_namespace as namespace,
page_title as title,
page_counter as value
- FROM $page
- WHERE page_namespace=".NS_MAIN." AND page_is_redirect=0";
+ FROM $page ";
+ $where =
+ "WHERE page_is_redirect=0 AND page_namespace";
+
+ global $wgContentNamespaces;
+ if( empty( $wgContentNamespaces ) ) {
+ $where .= '='.NS_MAIN;
+ } else if( count( $wgContentNamespaces ) > 1 ) {
+ $where .= ' in (' . implode( ', ', $wgContentNamespaces ) . ')';
+ } else {
+ $where .= '='.$wgContentNamespaces[0];
+ }
+
+ return $query . $where;
}
function formatResult( $skin, $result ) {
@@ -49,11 +59,11 @@ class PopularPagesPage extends QueryPage {
* Constructor
*/
function wfSpecialPopularpages() {
- list( $limit, $offset ) = wfCheckLimits();
+ list( $limit, $offset ) = wfCheckLimits();
- $ppp = new PopularPagesPage();
+ $ppp = new PopularPagesPage();
- return $ppp->doQuery( $offset, $limit );
+ return $ppp->doQuery( $offset, $limit );
}
?>
diff --git a/includes/SpecialPreferences.php b/includes/SpecialPreferences.php
index 643932c4..5ca818cd 100644
--- a/includes/SpecialPreferences.php
+++ b/includes/SpecialPreferences.php
@@ -1,8 +1,7 @@
<?php
/**
* Hold things related to displaying and saving user preferences.
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
@@ -18,14 +17,13 @@ function wfSpecialPreferences() {
/**
* Preferences form handling
* This object will show the preferences form and can save it as well.
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
class PreferencesForm {
var $mQuickbar, $mOldpass, $mNewpass, $mRetypePass, $mStubs;
var $mRows, $mCols, $mSkin, $mMath, $mDate, $mUserEmail, $mEmailFlag, $mNick;
var $mUserLanguage, $mUserVariant;
- var $mSearch, $mRecent, $mHourDiff, $mSearchLines, $mSearchChars, $mAction;
+ var $mSearch, $mRecent, $mRecentDays, $mHourDiff, $mSearchLines, $mSearchChars, $mAction;
var $mReset, $mPosted, $mToggles, $mSearchNs, $mRealName, $mImageSize;
var $mUnderline, $mWatchlistEdits;
@@ -54,6 +52,7 @@ class PreferencesForm {
$this->mUserVariant = $request->getVal( 'wpUserVariant' );
$this->mSearch = $request->getVal( 'wpSearch' );
$this->mRecent = $request->getVal( 'wpRecent' );
+ $this->mRecentDays = $request->getVal( 'wpRecentDays' );
$this->mHourDiff = $request->getVal( 'wpHourDiff' );
$this->mSearchLines = $request->getVal( 'wpSearchLines' );
$this->mSearchChars = $request->getVal( 'wpSearchChars' );
@@ -170,7 +169,7 @@ class PreferencesForm {
/**
* Used to validate the user inputed timezone before saving it as
- * 'timeciorrection', will return '00:00' if fed bogus data.
+ * 'timecorrection', will return '00:00' if fed bogus data.
* Note: It's not a 100% correct implementation timezone-wise, it will
* accept stuff like '14:30',
* @access private
@@ -263,6 +262,7 @@ class PreferencesForm {
$wgUser->setOption( 'contextlines', $this->validateIntOrNull( $this->mSearchLines ) );
$wgUser->setOption( 'contextchars', $this->validateIntOrNull( $this->mSearchChars ) );
$wgUser->setOption( 'rclimit', $this->validateIntOrNull( $this->mRecent ) );
+ $wgUser->setOption( 'rcdays', $this->validateInt( $this->mRecentDays, 1, 7 ) );
$wgUser->setOption( 'wllimit', $this->validateIntOrNull( $this->mWatchlistEdits, 0, 1000 ) );
$wgUser->setOption( 'rows', $this->validateInt( $this->mRows, 4, 1000 ) );
$wgUser->setOption( 'cols', $this->validateInt( $this->mCols, 4, 1000 ) );
@@ -365,6 +365,7 @@ class PreferencesForm {
$this->mImageSize = $wgUser->getOption( 'imagesize' );
$this->mThumbSize = $wgUser->getOption( 'thumbsize' );
$this->mRecent = $wgUser->getOption( 'rclimit' );
+ $this->mRecentDays = $wgUser->getOption( 'rcdays' );
$this->mWatchlistEdits = $wgUser->getOption( 'wllimit' );
$this->mUnderline = $wgUser->getOption( 'underline' );
$this->mWatchlistDays = $wgUser->getOption( 'watchlistdays' );
@@ -838,7 +839,7 @@ class PreferencesForm {
# Editing
#
- global $wgLivePreview, $wgUseRCPatrol;
+ global $wgLivePreview;
$wgOut->addHTML( '<fieldset><legend>' . wfMsg( 'textboxsize' ) . '</legend>
<div>' .
wfInputLabel( wfMsg( 'rows' ), 'wpRows', 'wpRows', 3, $this->mRows ) .
@@ -861,15 +862,27 @@ class PreferencesForm {
) ) . '</fieldset>'
);
- $wgOut->addHTML( '<fieldset><legend>' . htmlspecialchars(wfMsg('prefs-rc')) . '</legend>' .
- wfInputLabel( wfMsg( 'recentchangescount' ),
- 'wpRecent', 'wpRecent', 3, $this->mRecent ) .
- $this->getToggles( array(
- 'hideminor',
- $wgRCShowWatchingUsers ? 'shownumberswatching' : false,
- 'usenewrc' )
- ) . '</fieldset>'
- );
+ # Recent changes
+ $wgOut->addHtml( '<fieldset><legend>' . wfMsgHtml( 'prefs-rc' ) . '</legend>' );
+
+ $rc = '<table><tr>';
+ $rc .= '<td>' . Xml::label( wfMsg( 'recentchangesdays' ), 'wpRecentDays' ) . '</td>';
+ $rc .= '<td>' . Xml::input( 'wpRecentDays', 3, $this->mRecentDays, array( 'id' => 'wpRecentDays' ) ) . '</td>';
+ $rc .= '</tr><tr>';
+ $rc .= '<td>' . Xml::label( wfMsg( 'recentchangescount' ), 'wpRecent' ) . '</td>';
+ $rc .= '<td>' . Xml::input( 'wpRecent', 3, $this->mRecent, array( 'id' => 'wpRecent' ) ) . '</td>';
+ $rc .= '</tr></table>';
+ $wgOut->addHtml( $rc );
+
+ $wgOut->addHtml( '<br />' );
+
+ $toggles[] = 'hideminor';
+ if( $wgRCShowWatchingUsers )
+ $toggles[] = 'shownumberswatching';
+ $toggles[] = 'usenewrc';
+ $wgOut->addHtml( $this->getToggles( $toggles ) );
+
+ $wgOut->addHtml( '</fieldset>' );
# Watchlist
$wgOut->addHtml( '<fieldset><legend>' . wfMsgHtml( 'prefs-watchlist' ) . '</legend>' );
@@ -941,11 +954,11 @@ class PreferencesForm {
$wgOut->addHTML( '</fieldset>' );
$token = $wgUser->editToken();
+ $skin = $wgUser->getSkin();
$wgOut->addHTML( "
<div id='prefsubmit'>
<div>
- <input type='submit' name='wpSaveprefs' class='btnSavePrefs' value=\"" . wfMsgHtml( 'saveprefs' ) . "\" accesskey=\"".
- wfMsgHtml('accesskey-save')."\" title=\"".wfMsgHtml('tooltip-save')."\" />
+ <input type='submit' name='wpSaveprefs' class='btnSavePrefs' value=\"" . wfMsgHtml( 'saveprefs' ) . '"'.$skin->tooltipAndAccesskey('save')." />
<input type='submit' name='wpReset' value=\"" . wfMsgHtml( 'resetprefs' ) . "\" />
</div>
diff --git a/includes/SpecialPrefixindex.php b/includes/SpecialPrefixindex.php
index ce296b4b..b7c51d49 100644
--- a/includes/SpecialPrefixindex.php
+++ b/includes/SpecialPrefixindex.php
@@ -1,11 +1,8 @@
<?php
/**
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
-require_once 'SpecialAllpages.php';
-
/**
* Entry point : initialise variables and call subfunctions.
* @param $par String: becomes "FOO" when called like Special:Prefixindex/FOO (default NULL)
@@ -44,6 +41,10 @@ function wfSpecialPrefixIndex( $par=NULL, $specialPage ) {
}
}
+/**
+ * implements Special:Prefixindex
+ * @addtogroup SpecialPage
+ */
class SpecialPrefixindex extends SpecialAllpages {
var $maxPerPage=960;
var $topLevelMax=50;
@@ -71,11 +72,11 @@ function showChunk( $namespace = NS_MAIN, $prefix, $including = false, $from = n
$out = wfMsgWikiHtml( 'allpagesbadtitle' );
} else {
list( $namespace, $prefixKey, $prefix ) = $prefixList;
- list( $fromNs, $fromKey, $from ) = $fromList;
+ list( /* $fromNs */, $fromKey, $from ) = $fromList;
### FIXME: should complain if $fromNs != $namespace
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'page',
array( 'page_namespace', 'page_title', 'page_is_redirect' ),
diff --git a/includes/SpecialProtectedpages.php b/includes/SpecialProtectedpages.php
new file mode 100644
index 00000000..91b138ff
--- /dev/null
+++ b/includes/SpecialProtectedpages.php
@@ -0,0 +1,260 @@
+<?php
+/**
+ *
+ * @addtogroup SpecialPage
+ */
+
+/**
+ * @todo document
+ * @addtogroup SpecialPage
+ */
+class ProtectedPagesForm {
+ function showList( $msg = '' ) {
+ global $wgOut, $wgRequest;
+
+ $wgOut->setPagetitle( wfMsg( "protectedpages" ) );
+ if ( "" != $msg ) {
+ $wgOut->setSubtitle( $msg );
+ }
+
+ // Purge expired entries on one in every 10 queries
+ if ( !mt_rand( 0, 10 ) ) {
+ Title::purgeExpiredRestrictions();
+ }
+
+ $type = $wgRequest->getVal( 'type' );
+ $level = $wgRequest->getVal( 'level' );
+ $minsize = $wgRequest->getIntOrNull( 'minsize' );
+ $NS = $wgRequest->getIntOrNull( 'namespace' );
+
+ $pager = new ProtectedPagesPager( $this, array(), $type, $level, $NS, $minsize );
+
+ $wgOut->addHTML( $this->showOptions( $NS, $type, $level, $minsize ) );
+
+ if ( $pager->getNumRows() ) {
+ $s = $pager->getNavigationBar();
+ $s .= "<ul>" .
+ $pager->getBody() .
+ "</ul>";
+ $s .= $pager->getNavigationBar();
+ } else {
+ $s = '<p>' . wfMsgHTML( 'protectedpagesempty' ) . '</p>';
+ }
+ $wgOut->addHTML( $s );
+ }
+
+ /**
+ * Callback function to output a restriction
+ */
+ function formatRow( $row ) {
+ global $wgUser, $wgLang;
+
+ wfProfileIn( __METHOD__ );
+
+ static $skin=null;
+
+ if( is_null( $skin ) )
+ $skin = $wgUser->getSkin();
+
+ $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
+ $link = $skin->makeLinkObj( $title );
+
+ $description_items = array ();
+
+ $protType = wfMsgHtml( 'restriction-level-' . $row->pr_level );
+
+ $description_items[] = $protType;
+
+ $expiry_description = ''; $stxt = '';
+
+ if ( $row->pr_expiry != 'infinity' && strlen($row->pr_expiry) ) {
+ $expiry = Block::decodeExpiry( $row->pr_expiry );
+
+ $expiry_description = wfMsgForContent( 'protect-expiring', $wgLang->timeanddate( $expiry ) );
+
+ $description_items[] = $expiry_description;
+ }
+
+ if (!is_null($size = $row->page_len)) {
+ if ($size == 0)
+ $stxt = ' <small>' . wfMsgHtml('historyempty') . '</small>';
+ else
+ $stxt = ' <small>' . wfMsgHtml('historysize', $wgLang->formatNum( $size ) ) . '</small>';
+ }
+ wfProfileOut( __METHOD__ );
+
+ return '<li>' . wfSpecialList( $link . $stxt, implode( $description_items, ', ' ) ) . "</li>\n";
+ }
+
+ /**
+ * @param $namespace int
+ * @param $type string
+ * @param $level string
+ * @param $minsize int
+ * @private
+ */
+ function showOptions( $namespace, $type='edit', $level, $minsize ) {
+ global $wgScript;
+ $action = htmlspecialchars( $wgScript );
+ $title = SpecialPage::getTitleFor( 'ProtectedPages' );
+ $special = htmlspecialchars( $title->getPrefixedDBkey() );
+ return "<form action=\"$action\" method=\"get\">\n" .
+ '<fieldset>' .
+ Xml::element( 'legend', array(), wfMsg( 'protectedpages' ) ) .
+ Xml::hidden( 'title', $special ) . "\n" .
+ $this->getNamespaceMenu( $namespace ) . "\n" .
+ $this->getTypeMenu( $type ) . "\n" .
+ $this->getLevelMenu( $level ) . "<br/>\n" .
+ $this->getSizeLimit( $minsize ) . "\n" .
+ Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . "\n" .
+ "</fieldset></form>";
+ }
+
+ function getNamespaceMenu( $namespace=NULL ) {
+ return "<label for='namespace'>" . wfMsgHtml('namespace') . "</label>" . HTMLnamespaceselector($namespace, '');
+ }
+
+ /**
+ * @return string Formatted HTML
+ * @private
+ */
+ function getSizeLimit( $minsize=0 ) {
+ $out = Xml::input('minsize', 9, $minsize, array( 'id' => 'minsize' ) );
+ return "<label for='minsize'>" . wfMsgHtml('minimum-size') . "</label>: " . $out;
+ }
+
+ /**
+ * @return string Formatted HTML
+ * @private
+ */
+ function getTypeMenu( $pr_type ) {
+ global $wgRestrictionTypes, $wgUser;
+
+ $out = "<select name='type'>\n";
+ $m = array(); // Temporary array
+
+ // First pass to load the log names
+ foreach( $wgRestrictionTypes as $type ) {
+ $text = wfMsgHtml("restriction-$type");
+ $m[$text] = $type;
+ }
+
+ // Second pass to sort by name
+ ksort($m);
+
+ // Third pass generates sorted XHTML content
+ foreach( $m as $text => $type ) {
+ $selected = ($type == $pr_type );
+ $out .= Xml::option( $text, $type, $selected ) . "\n";
+ }
+
+ $out .= '</select>';
+ return "<label for='type'>" . wfMsgHtml('restriction-type') . "</label>: " . $out;
+ }
+
+ /**
+ * @return string Formatted HTML
+ * @private
+ */
+ function getLevelMenu( $pr_level ) {
+ global $wgRestrictionLevels, $wgUser;
+
+ $out = "<select name='level'>\n";
+ $m = array( wfMsgHtml('restriction-level-all') => 0 ); // Temporary array
+
+ // First pass to load the log names
+ foreach( $wgRestrictionLevels as $type ) {
+ if ( $type !='' && $type !='*') {
+ $text = wfMsgHtml("restriction-level-$type");
+ $m[$text] = $type;
+ }
+ }
+
+ // Second pass to sort by name
+ ksort($m);
+
+ // Third pass generates sorted XHTML content
+ foreach( $m as $text => $type ) {
+ $selected = ($type == $pr_level );
+ $out .= Xml::option( $text, $type, $selected ) . "\n";
+ }
+
+ $out .= '</select>';
+ return "<label for='level'>" . wfMsgHtml('restriction-level') . "</label>: " . $out;
+ }
+}
+
+/**
+ * @todo document
+ * @addtogroup Pager
+ */
+class ProtectedPagesPager extends ReverseChronologicalPager {
+ public $mForm, $mConds;
+
+ function __construct( $form, $conds = array(), $type, $level, $namespace, $minsize ) {
+ $this->mForm = $form;
+ $this->mConds = $conds;
+ $this->type = ( $type ) ? $type : 'edit';
+ $this->level = $level;
+ $this->namespace = $namespace;
+ $this->minsize = intval($minsize);
+ parent::__construct();
+ }
+
+ function getStartBody() {
+ wfProfileIn( __METHOD__ );
+ # Do a link batch query
+ $this->mResult->seek( 0 );
+ $lb = new LinkBatch;
+
+ while ( $row = $this->mResult->fetchObject() ) {
+ $name = str_replace( ' ', '_', $row->page_title );
+ $lb->add( $row->page_namespace, $name );
+ }
+
+ $lb->execute();
+ wfProfileOut( __METHOD__ );
+ return '';
+ }
+
+ function formatRow( $row ) {
+ $block = new Block;
+ return $this->mForm->formatRow( $row );
+ }
+
+ function getQueryInfo() {
+ $conds = $this->mConds;
+ $conds[] = 'pr_expiry>' . $this->mDb->addQuotes( $this->mDb->timestamp() );
+ $conds[] = 'page_id=pr_page';
+ $conds[] = 'page_len>=' . $this->minsize;
+ $conds[] = 'pr_type=' . $this->mDb->addQuotes( $this->type );
+ if ( $this->level )
+ $conds[] = 'pr_level=' . $this->mDb->addQuotes( $this->level );
+ if ( !is_null($this->namespace) )
+ $conds[] = 'page_namespace=' . $this->mDb->addQuotes( $this->namespace );
+ return array(
+ 'tables' => array( 'page_restrictions', 'page' ),
+ 'fields' => 'max(pr_id) AS pr_id,page_namespace,page_title,page_len,pr_type,pr_level,pr_expiry',
+ 'conds' => $conds,
+ 'options' => array( 'GROUP BY' => 'page_namespace,page_title,pr_level,pr_expiry,page_len,pr_type' ),
+ );
+ }
+
+ function getIndexField() {
+ return 'pr_id';
+ }
+}
+
+/**
+ * Constructor
+ */
+function wfSpecialProtectedpages() {
+
+ list( $limit, $offset ) = wfCheckLimits();
+
+ $ppForm = new ProtectedPagesForm();
+
+ $ppForm->showList();
+}
+
+?>
diff --git a/includes/SpecialRandompage.php b/includes/SpecialRandompage.php
index 2cd31eb5..e6c4abe8 100644
--- a/includes/SpecialRandompage.php
+++ b/includes/SpecialRandompage.php
@@ -1,58 +1,108 @@
<?php
+
/**
- * @package MediaWiki
- * @subpackage SpecialPage
+ * Special page to direct the user to a random page
+ *
+ * @addtogroup SpecialPage
+ * @author Rob Church <robchur@gmail.com>, Ilmari Karonen
+ * @license GNU General Public Licence 2.0 or later
*/
/**
- * Constructor
- *
- * @param $par The namespace to get a random page from (default NS_MAIN),
- * used as e.g. Special:Randompage/Category
+ * Main execution point
+ * @param $par Namespace to select the page from
*/
-function wfSpecialRandompage( $par = NS_MAIN ) {
- global $wgOut, $wgExtraRandompageSQL;
- $fname = 'wfSpecialRandompage';
-
- # Determine namespace
- $t = Title::newFromText ( $par . ":Dummy" ) ;
- $namespace = $t->getNamespace () ;
-
- # NOTE! We use a literal constant in the SQL instead of the RAND()
- # function because RAND() will return a different value for every row
- # in the table. That's both very slow and returns results heavily
- # biased towards low values, as rows later in the table will likely
- # never be reached for comparison.
- #
- # Using a literal constant means the whole thing gets optimized on
- # the index, and the comparison is both fast and fair.
-
- # interpolation and sprintf() can muck up with locale-specific decimal separator
- $randstr = wfRandom();
-
- $db =& wfGetDB( DB_SLAVE );
- $use_index = $db->useIndexClause( 'page_random' );
- $page = $db->tableName( 'page' );
-
- $extra = $wgExtraRandompageSQL ? "AND ($wgExtraRandompageSQL)" : '';
- $sql = "SELECT page_id,page_title
- FROM $page $use_index
- WHERE page_namespace=$namespace AND page_is_redirect=0 $extra
- AND page_random>$randstr
- ORDER BY page_random";
- $sql = $db->limitResult($sql, 1, 0);
- $res = $db->query( $sql, $fname );
-
- $title = null;
- if( $s = $db->fetchObject( $res ) ) {
- $title =& Title::makeTitle( $namespace, $s->page_title );
- }
+function wfSpecialRandompage( $par = null ) {
+ global $wgOut, $wgContLang;
+
+ $rnd = new RandomPage();
+ $rnd->setNamespace( $wgContLang->getNsIndex( $par ) );
+ $rnd->setRedirect( false );
+
+ $title = $rnd->getRandomTitle();
+
if( is_null( $title ) ) {
- # That's not supposed to happen :)
- $title = Title::newMainPage();
+ $wgOut->addWikiText( wfMsg( 'randompage-nopages' ) );
+ return;
}
- $wgOut->reportTime(); # for logfile
+
+ $wgOut->reportTime();
$wgOut->redirect( $title->getFullUrl() );
}
+
+/**
+ * Special page to direct the user to a random page
+ *
+ * @addtogroup SpecialPage
+ */
+class RandomPage {
+ private $namespace = NS_MAIN; // namespace to select pages from
+ private $redirect = false; // select redirects instead of normal pages?
+
+ public function getNamespace ( ) {
+ return $this->namespace;
+ }
+ public function setNamespace ( $ns ) {
+ if( $ns < NS_MAIN ) $ns = NS_MAIN;
+ $this->namespace = $ns;
+ }
+ public function getRedirect ( ) {
+ return $this->redirect;
+ }
+ public function setRedirect ( $redirect ) {
+ $this->redirect = $redirect;
+ }
+
+ /**
+ * Choose a random title.
+ * @return Title object (or null if nothing to choose from)
+ */
+ public function getRandomTitle ( ) {
+ $randstr = wfRandom();
+ $row = $this->selectRandomPageFromDB( $randstr );
+
+ /* If we picked a value that was higher than any in
+ * the DB, wrap around and select the page with the
+ * lowest value instead! One might think this would
+ * skew the distribution, but in fact it won't cause
+ * any more bias than what the page_random scheme
+ * causes anyway. Trust me, I'm a mathematician. :)
+ */
+ if( !$row )
+ $row = $this->selectRandomPageFromDB( "0" );
+
+ if( $row )
+ return Title::makeTitleSafe( $this->namespace, $row->page_title );
+ else
+ return null;
+ }
+
+ private function selectRandomPageFromDB ( $randstr ) {
+ global $wgExtraRandompageSQL;
+ $fname = 'RandomPage::selectRandomPageFromDB';
+
+ $dbr = wfGetDB( DB_SLAVE );
+
+ $use_index = $dbr->useIndexClause( 'page_random' );
+ $page = $dbr->tableName( 'page' );
+
+ $ns = (int) $this->namespace;
+ $redirect = $this->redirect ? 1 : 0;
+
+ $extra = $wgExtraRandompageSQL ? "AND ($wgExtraRandompageSQL)" : "";
+ $sql = "SELECT page_title
+ FROM $page $use_index
+ WHERE page_namespace = $ns
+ AND page_is_redirect = $redirect
+ AND page_random >= $randstr
+ $extra
+ ORDER BY page_random";
+
+ $sql = $dbr->limitResult( $sql, 1, 0 );
+ $res = $dbr->query( $sql, $fname );
+ return $dbr->fetchObject( $res );
+ }
+}
+
?>
diff --git a/includes/SpecialRandomredirect.php b/includes/SpecialRandomredirect.php
index 2cb2498b..75a6b81d 100644
--- a/includes/SpecialRandomredirect.php
+++ b/includes/SpecialRandomredirect.php
@@ -3,50 +3,29 @@
/**
* Special page to direct the user to a random redirect page (minus the second redirect)
*
- * @package MediaWiki
- * @subpackage Special pages
- * @author Rob Church <robchur@gmail.com>
- * @licence GNU General Public Licence 2.0 or later
+ * @addtogroup SpecialPage
+ * @author Rob Church <robchur@gmail.com>, Ilmari Karonen
+ * @license GNU General Public Licence 2.0 or later
*/
/**
* Main execution point
* @param $par Namespace to select the redirect from
*/
-function wfSpecialRandomredirect( $par = NULL ) {
- global $wgOut, $wgExtraRandompageSQL, $wgContLang;
- $fname = 'wfSpecialRandomredirect';
+function wfSpecialRandomredirect( $par = null ) {
+ global $wgOut, $wgContLang;
- # Validate the namespace
- $namespace = $wgContLang->getNsIndex( $par );
- if( $namespace === false || $namespace < NS_MAIN )
- $namespace = NS_MAIN;
+ $rnd = new RandomPage();
+ $rnd->setNamespace( $wgContLang->getNsIndex( $par ) );
+ $rnd->setRedirect( true );
- # Same logic as RandomPage
- $randstr = wfRandom();
+ $title = $rnd->getRandomTitle();
- $dbr =& wfGetDB( DB_SLAVE );
- $use_index = $dbr->useIndexClause( 'page_random' );
- $page = $dbr->tableName( 'page' );
+ if( is_null( $title ) ) {
+ $wgOut->addWikiText( wfMsg( 'randomredirect-nopages' ) );
+ return;
+ }
- $extra = $wgExtraRandompageSQL ? "AND ($wgExtraRandompageSQL)" : '';
- $sql = "SELECT page_id,page_title
- FROM $page $use_index
- WHERE page_namespace = $namespace AND page_is_redirect = 1 $extra
- AND page_random > $randstr
- ORDER BY page_random";
-
- $sql = $dbr->limitResult( $sql, 1, 0 );
- $res = $dbr->query( $sql, $fname );
-
- $title = NULL;
- if( $row = $dbr->fetchObject( $res ) )
- $title = Title::makeTitleSafe( $namespace, $row->page_title );
-
- # Catch dud titles and return to the main page
- if( is_null( $title ) )
- $title = Title::newMainPage();
-
$wgOut->reportTime();
$wgOut->redirect( $title->getFullUrl( 'redirect=no' ) );
}
diff --git a/includes/SpecialRecentchanges.php b/includes/SpecialRecentchanges.php
index 3b8d69f2..84444e62 100644
--- a/includes/SpecialRecentchanges.php
+++ b/includes/SpecialRecentchanges.php
@@ -1,14 +1,13 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
*
*/
-require_once( 'ChangesList.php' );
+require_once( dirname(__FILE__) . '/ChangesList.php' );
/**
* Constructor
@@ -22,7 +21,7 @@ function wfSpecialRecentchanges( $par, $specialPage ) {
# Get query parameters
$feedFormat = $wgRequest->getVal( 'feed' );
- /* Checkbox values can't be true be default, because
+ /* Checkbox values can't be true by default, because
* we cannot differentiate between unset and not set at all
*/
$defaults = array(
@@ -58,7 +57,7 @@ function wfSpecialRecentchanges( $par, $specialPage ) {
if( $feedFormat ) {
global $wgFeedLimit;
if( $limit > $wgFeedLimit ) {
- $options['limit'] = $wgFeedLimit;
+ $limit = $wgFeedLimit;
}
} else {
@@ -105,7 +104,7 @@ function wfSpecialRecentchanges( $par, $specialPage ) {
# Database connection and caching
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
list( $recentchanges, $watchlist ) = $dbr->tableNamesN( 'recentchanges', 'watchlist' );
@@ -470,7 +469,7 @@ function rcDayLimitLinks( $days, $limit, $page='Recentchanges', $more='', $doall
/**
* Makes change an option link which carries all the other options
- * @param $title @see Title
+ * @param $title see Title
* @param $override
* @param $options
*/
@@ -625,7 +624,7 @@ function rcFormatDiffRow( $title, $oldid, $newid, $timestamp, $comment ) {
$skin = $wgUser->getSkin();
$completeText = '<p>' . $skin->formatComment( $comment ) . "</p>\n";
- if( $title->getNamespace() >= 0 ) {
+ if( $title->getNamespace() >= 0 && $title->userCan( 'read' ) ) {
if( $oldid ) {
wfProfileIn( "$fname-dodiff" );
@@ -692,7 +691,7 @@ function rcApplyDiffStyle( $text ) {
'diff-addedline' => 'background: #cfc; font-size: smaller;',
'diff-deletedline' => 'background: #ffa; font-size: smaller;',
'diff-context' => 'background: #eee; font-size: smaller;',
- 'diffchange' => 'color: red; font-weight: bold;',
+ 'diffchange' => 'color: red; font-weight: bold; text-decoration: none;',
);
foreach( $styles as $class => $style ) {
diff --git a/includes/SpecialRecentchangeslinked.php b/includes/SpecialRecentchangeslinked.php
index 15292898..14508d3a 100644
--- a/includes/SpecialRecentchangeslinked.php
+++ b/includes/SpecialRecentchangeslinked.php
@@ -1,8 +1,7 @@
<?php
/**
* This is to display changes made to all articles linked in an article.
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
@@ -25,20 +24,15 @@ function wfSpecialRecentchangeslinked( $par = NULL ) {
$wgOut->setPagetitle( wfMsg( 'recentchangeslinked' ) );
$sk = $wgUser->getSkin();
- # Validate the title
- $nt = Title::newFromURL( $target );
- if( !is_object( $nt ) ) {
- $wgOut->errorPage( 'notargettitle', 'notargettext' );
+ if (is_null($target)) {
+ $wgOut->errorpage( 'notargettitle', 'notargettext' );
return;
}
-
- # Check for existence
- # Do a quiet redirect back to the page itself if it doesn't
- if( !$nt->exists() ) {
- $wgOut->redirect( $nt->getLocalUrl() );
+ $nt = Title::newFromURL( $target );
+ if( !$nt ) {
+ $wgOut->errorpage( 'notargettitle', 'notargettext' );
return;
}
-
$id = $nt->getArticleId();
$wgOut->setSubtitle( htmlspecialchars( wfMsg( 'rclsub', $nt->getPrefixedText() ) ) );
@@ -48,7 +42,7 @@ function wfSpecialRecentchangeslinked( $par = NULL ) {
}
list( $limit, /* offset */ ) = wfCheckLimits( 100, 'rclimit' );
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE,'recentchangeslinked' );
$cutoff = $dbr->timestamp( time() - ( $days * 86400 ) );
$hideminor = ($hideminor ? 1 : 0);
@@ -72,7 +66,7 @@ function wfSpecialRecentchangeslinked( $par = NULL ) {
$GROUPBY = "
GROUP BY rc_cur_id,rc_namespace,rc_title,
- rc_user,rc_comment,rc_user_text,rc_timestamp,rc_minor,
+ rc_user,rc_comment,rc_user_text,rc_timestamp,rc_minor,rc_deleted,
rc_new, rc_id, rc_this_oldid, rc_last_oldid, rc_bot, rc_patrolled, rc_type, rc_old_len, rc_new_len
" . ($uid ? ",wl_user" : "") . "
ORDER BY rc_timestamp DESC
@@ -98,7 +92,8 @@ function wfSpecialRecentchangeslinked( $par = NULL ) {
rc_patrolled,
rc_type,
rc_old_len,
- rc_new_len
+ rc_new_len,
+ rc_deleted
" . ($uid ? ",wl_user" : "") . "
FROM $categorylinks, $recentchanges
" . ($uid ? "LEFT OUTER JOIN $watchlist ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace " : "") . "
@@ -127,7 +122,8 @@ $GROUPBY
rc_patrolled,
rc_type,
rc_old_len,
- rc_new_len
+ rc_new_len,
+ rc_deleted
" . ($uid ? ",wl_user" : "") . "
FROM $pagelinks, $recentchanges
" . ($uid ? " LEFT OUTER JOIN $watchlist ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace " : "") . "
@@ -141,8 +137,8 @@ $GROUPBY
}
$res = $dbr->query( $sql, $fname );
- $wgOut->addHTML("&lt; ".$sk->makeKnownLinkObj($nt, "", "redirect=no" )."<br />\n");
- $note = wfMsg( "rcnote", $limit, $days, $wgLang->timeAndDate( wfTimestampNow(), true ) );
+ $wgOut->addHTML("&lt; ".$sk->makeLinkObj($nt, "", "redirect=no" )."<br />\n");
+ $note = wfMsgExt( "rcnote", array ( 'parseinline' ), $limit, $days, $wgLang->timeAndDate( wfTimestampNow(), true ) );
$wgOut->addHTML( "<hr />\n{$note}\n<br />" );
$note = rcDayLimitlinks( $days, $limit, "Recentchangeslinked",
@@ -155,15 +151,19 @@ $GROUPBY
$s = $list->beginRecentChangesList();
$count = $dbr->numRows( $res );
- $counter = 1;
- while ( $limit ) {
- if ( 0 == $count ) { break; }
- $obj = $dbr->fetchObject( $res );
- --$count;
- $rc = RecentChange::newFromRow( $obj );
- $rc->counter = $counter++;
- $s .= $list->recentChangesLine( $rc , !empty( $obj->wl_user) );
- --$limit;
+ if ( $count ) {
+ $counter = 1;
+ while ( $limit ) {
+ if ( 0 == $count ) { break; }
+ $obj = $dbr->fetchObject( $res );
+ --$count;
+ $rc = RecentChange::newFromRow( $obj );
+ $rc->counter = $counter++;
+ $s .= $list->recentChangesLine( $rc , !empty( $obj->wl_user) );
+ --$limit;
+ }
+ } else {
+ $wgOut->addWikiText( wfMsg('recentchangeslinked-noresult') );
}
$s .= $list->endRecentChangesList();
diff --git a/includes/SpecialResetpass.php b/includes/SpecialResetpass.php
index cde582b1..dc1e53c4 100644
--- a/includes/SpecialResetpass.php
+++ b/includes/SpecialResetpass.php
@@ -1,10 +1,15 @@
<?php
+/** Constructor */
function wfSpecialResetpass( $par ) {
$form = new PasswordResetForm();
$form->execute( $par );
}
+/**
+ * Let users recover their password.
+ * @addtogroup SpecialPage
+ */
class PasswordResetForm extends SpecialPage {
function __construct( $name=null, $reset=null ) {
if( $name !== null ) {
@@ -68,7 +73,7 @@ class PasswordResetForm extends SpecialPage {
}
function showForm() {
- global $wgOut, $wgUser, $wgLang, $wgRequest;
+ global $wgOut, $wgUser, $wgRequest;
$self = SpecialPage::getTitleFor( 'Resetpass' );
$form =
@@ -134,7 +139,7 @@ class PasswordResetForm extends SpecialPage {
}
/**
- * @throws PasswordError
+ * @throws PasswordError when cannot set the new password because requirements not met.
*/
function attemptReset( $newpass, $retype ) {
$user = User::newFromName( $this->mName );
diff --git a/includes/SpecialRevisiondelete.php b/includes/SpecialRevisiondelete.php
index fb5e9ec8..5c70d5ae 100644
--- a/includes/SpecialRevisiondelete.php
+++ b/includes/SpecialRevisiondelete.php
@@ -35,6 +35,10 @@ function wfSpecialRevisiondelete( $par = null ) {
}
}
+/**
+ * Implements the GUI for Revision Deletion.
+ * @addtogroup SpecialPage
+ */
class RevisionDeleteForm {
/**
* @param Title $page
@@ -170,7 +174,10 @@ class RevisionDeleteForm {
}
}
-
+/**
+ * Implements the actions for Revision Deletion.
+ * @addtogroup SpecialPage
+ */
class RevisionDeleter {
function __construct( $db ) {
$this->db = $db;
diff --git a/includes/SpecialSearch.php b/includes/SpecialSearch.php
index 9ecd39ef..fdaa8541 100644
--- a/includes/SpecialSearch.php
+++ b/includes/SpecialSearch.php
@@ -19,8 +19,7 @@
/**
* Run text & title search and display the output
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
@@ -43,9 +42,8 @@ function wfSpecialSearch( $par = '' ) {
}
/**
- * @todo document
- * @package MediaWiki
- * @subpackage SpecialPage
+ * implements Special:Search - Run text & title search and display the output
+ * @addtogroup SpecialPage
*/
class SpecialSearch {
@@ -161,12 +159,14 @@ class SpecialSearch {
$num = ( $titleMatches ? $titleMatches->numRows() : 0 )
+ ( $textMatches ? $textMatches->numRows() : 0);
- if ( $num >= $this->limit ) {
- $top = wfShowingResults( $this->offset, $this->limit );
- } else {
- $top = wfShowingResultsNum( $this->offset, $this->limit, $num );
+ if ( $num > 0 ) {
+ if ( $num >= $this->limit ) {
+ $top = wfShowingResults( $this->offset, $this->limit );
+ } else {
+ $top = wfShowingResultsNum( $this->offset, $this->limit, $num );
+ }
+ $wgOut->addHTML( "<p>{$top}</p>\n" );
}
- $wgOut->addHTML( "<p>{$top}</p>\n" );
if( $num || $this->offset ) {
$prevnext = wfViewPrevNext( $this->offset, $this->limit,
@@ -314,7 +314,7 @@ class SpecialSearch {
wfProfileOut( $fname );
return "<!-- Broken link in search result -->\n";
}
- $sk =& $wgUser->getSkin();
+ $sk = $wgUser->getSkin();
$contextlines = $wgUser->getOption( 'contextlines', 5 );
$contextchars = $wgUser->getOption( 'contextchars', 50 );
diff --git a/includes/SpecialShortpages.php b/includes/SpecialShortpages.php
index 03164deb..72b093e0 100644
--- a/includes/SpecialShortpages.php
+++ b/includes/SpecialShortpages.php
@@ -1,15 +1,13 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
* SpecialShortpages extends QueryPage. It is used to return the shortest
* pages in the database.
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
class ShortPagesPage extends QueryPage {
@@ -29,7 +27,7 @@ class ShortPagesPage extends QueryPage {
}
function getSQL() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$page = $dbr->tableName( 'page' );
$name = $dbr->addQuotes( $this->getName() );
diff --git a/includes/SpecialSpecialpages.php b/includes/SpecialSpecialpages.php
index 78f9dee5..bb202358 100644
--- a/includes/SpecialSpecialpages.php
+++ b/includes/SpecialSpecialpages.php
@@ -1,8 +1,7 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
@@ -51,7 +50,7 @@ function wfSpecialSpecialpages_gen($pages,$heading,$sk) {
/** Now output the HTML */
$wgOut->addHTML( '<h2>' . wfMsgHtml( $heading ) . "</h2>\n<ul>" );
foreach ( $sortedPages as $desc => $title ) {
- $link = $sk->makeKnownLinkObj( $title, $desc );
+ $link = $sk->makeKnownLinkObj( $title , htmlspecialchars( $desc ) );
$wgOut->addHTML( "<li>{$link}</li>\n" );
}
$wgOut->addHTML( "</ul>\n" );
diff --git a/includes/SpecialStatistics.php b/includes/SpecialStatistics.php
index a5a0fc3a..1c9e0ab6 100644
--- a/includes/SpecialStatistics.php
+++ b/includes/SpecialStatistics.php
@@ -1,8 +1,7 @@
<?php
/**
*
-* @package MediaWiki
-* @subpackage SpecialPage
+* @addtogroup SpecialPage
*/
/**
@@ -14,7 +13,7 @@ function wfSpecialStatistics() {
$action = $wgRequest->getVal( 'action' );
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$views = SiteStats::views();
$edits = SiteStats::edits();
@@ -24,7 +23,7 @@ function wfSpecialStatistics() {
$users = SiteStats::users();
$admins = $dbr->selectField( 'user_groups', 'COUNT(*)', array( 'ug_group' => 'sysop' ), $fname );
- $numJobs = $dbr->selectField( 'job', 'COUNT(*)', '', $fname );
+ $numJobs = $dbr->estimateRowCount('job');
if ($action == 'raw') {
$wgOut->disable();
@@ -33,7 +32,7 @@ function wfSpecialStatistics() {
return;
} else {
$text = '==' . wfMsg( 'sitestats' ) . "==\n" ;
- $text .= wfMsg( 'sitestatstext',
+ $text .= wfMsgExt( 'sitestatstext', array ( 'parsemag' ),
$wgLang->formatNum( $total ),
$wgLang->formatNum( $good ),
$wgLang->formatNum( $views ),
@@ -46,7 +45,7 @@ function wfSpecialStatistics() {
$text .= "\n==" . wfMsg( 'userstats' ) . "==\n";
- $text .= wfMsg( 'userstatstext',
+ $text .= wfMsgExt( 'userstatstext', array ( 'parsemag' ),
$wgLang->formatNum( $users ),
$wgLang->formatNum( $admins ),
'[[' . wfMsgForContent( 'grouppage-sysop' ) . ']]', # TODO somehow remove, kept for backwards compatibility
@@ -64,7 +63,7 @@ function wfSpecialStatistics() {
$res = $dbr->query( $sql, $fname );
if( $res ) {
$wgOut->addHtml( '<h2>' . wfMsgHtml( 'statistics-mostpopular' ) . '</h2>' );
- $skin =& $wgUser->getSkin();
+ $skin = $wgUser->getSkin();
$wgOut->addHtml( '<ol>' );
while( $row = $dbr->fetchObject( $res ) ) {
$link = $skin->makeKnownLinkObj( Title::makeTitleSafe( $row->page_namespace, $row->page_title ) );
@@ -76,6 +75,10 @@ function wfSpecialStatistics() {
}
}
+ $footer = wfMsg( 'statistics-footer' );
+ if( !wfEmptyMsg( 'statistics-footer', $footer ) && $footer != '' )
+ $wgOut->addWikiText( $footer );
+
}
}
?>
diff --git a/includes/SpecialUncategorizedcategories.php b/includes/SpecialUncategorizedcategories.php
index ba399f0c..e02c9bbd 100644
--- a/includes/SpecialUncategorizedcategories.php
+++ b/includes/SpecialUncategorizedcategories.php
@@ -1,8 +1,7 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
@@ -11,9 +10,8 @@
require_once( "SpecialUncategorizedpages.php" );
/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
+ * implements Special:Uncategorizedcategories
+ * @addtogroup SpecialPage
*/
class UncategorizedCategoriesPage extends UncategorizedPagesPage {
function UncategorizedCategoriesPage() {
diff --git a/includes/SpecialUncategorizedimages.php b/includes/SpecialUncategorizedimages.php
index 1daba8ed..22e34669 100644
--- a/includes/SpecialUncategorizedimages.php
+++ b/includes/SpecialUncategorizedimages.php
@@ -3,31 +3,30 @@
/**
* Special page lists images which haven't been categorised
*
- * @package MediaWiki
- * @subpackage Special pages
+ * @addtogroup SpecialPage
* @author Rob Church <robchur@gmail.com>
*/
-
-class UncategorizedImagesPage extends QueryPage {
+
+class UncategorizedImagesPage extends ImageQueryPage {
function getName() {
return 'Uncategorizedimages';
}
-
+
function sortDescending() {
return false;
}
-
+
function isExpensive() {
return true;
}
-
+
function isSyndicated() {
return false;
}
-
+
function getSQL() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
list( $page, $categorylinks ) = $dbr->tableNamesN( 'page', 'categorylinks' );
$ns = NS_IMAGE;
@@ -36,14 +35,7 @@ class UncategorizedImagesPage extends QueryPage {
FROM {$page} LEFT JOIN {$categorylinks} ON page_id = cl_from
WHERE cl_from IS NULL AND page_namespace = {$ns} AND page_is_redirect = 0";
}
-
- function formatResult( &$skin, $row ) {
- global $wgContLang;
- $title = Title::makeTitleSafe( NS_IMAGE, $row->title );
- $label = htmlspecialchars( $wgContLang->convert( $title->getText() ) );
- return $skin->makeKnownLinkObj( $title, $label );
- }
-
+
}
function wfSpecialUncategorizedimages() {
diff --git a/includes/SpecialUncategorizedpages.php b/includes/SpecialUncategorizedpages.php
index dbf23a60..408ac726 100644
--- a/includes/SpecialUncategorizedpages.php
+++ b/includes/SpecialUncategorizedpages.php
@@ -1,14 +1,12 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
+ * A special page looking for page without any category.
+ * @addtogroup SpecialPage
*/
class UncategorizedPagesPage extends PageQueryPage {
var $requestedNamespace = NS_MAIN;
@@ -27,7 +25,7 @@ class UncategorizedPagesPage extends PageQueryPage {
function isSyndicated() { return false; }
function getSQL() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
list( $page, $categorylinks ) = $dbr->tableNamesN( 'page', 'categorylinks' );
$name = $dbr->addQuotes( $this->getName() );
diff --git a/includes/SpecialUndelete.php b/includes/SpecialUndelete.php
index 7c9b1191..8e740f6d 100644
--- a/includes/SpecialUndelete.php
+++ b/includes/SpecialUndelete.php
@@ -4,52 +4,92 @@
* Special page allowing users with the appropriate permissions to view
* and restore deleted content
*
- * @package MediaWiki
- * @subpackage Special pages
+ * @addtogroup SpecialPage
*/
/**
- *
+ * Constructor
*/
function wfSpecialUndelete( $par ) {
- global $wgRequest;
+ global $wgRequest;
$form = new UndeleteForm( $wgRequest, $par );
$form->execute();
}
/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
+ * Used to show archived pages and eventually restore them.
+ * @addtogroup SpecialPage
*/
class PageArchive {
- var $title;
+ protected $title;
- function PageArchive( &$title ) {
+ function __construct( $title ) {
if( is_null( $title ) ) {
throw new MWException( 'Archiver() given a null title.');
}
- $this->title =& $title;
+ $this->title = $title;
}
/**
* List all deleted pages recorded in the archive table. Returns result
* wrapper with (ar_namespace, ar_title, count) fields, ordered by page
- * namespace/title. Can be called staticaly.
+ * namespace/title.
*
* @return ResultWrapper
*/
- /* static */ function listAllPages() {
- $dbr =& wfGetDB( DB_SLAVE );
- $archive = $dbr->tableName( 'archive' );
-
- $sql = "SELECT ar_namespace,ar_title, COUNT(*) AS count FROM $archive " .
- "GROUP BY ar_namespace,ar_title ORDER BY ar_namespace,ar_title";
-
- return $dbr->resultObject( $dbr->query( $sql, 'PageArchive::listAllPages' ) );
+ public static function listAllPages() {
+ $dbr = wfGetDB( DB_SLAVE );
+ return self::listPages( $dbr, '' );
+ }
+
+ /**
+ * List deleted pages recorded in the archive table matching the
+ * given title prefix.
+ * Returns result wrapper with (ar_namespace, ar_title, count) fields.
+ *
+ * @return ResultWrapper
+ */
+ public static function listPagesByPrefix( $prefix ) {
+ $dbr = wfGetDB( DB_SLAVE );
+
+ $title = Title::newFromText( $prefix );
+ if( $title ) {
+ $ns = $title->getNamespace();
+ $encPrefix = $dbr->escapeLike( $title->getDbKey() );
+ } else {
+ // Prolly won't work too good
+ // @todo handle bare namespace names cleanly?
+ $ns = 0;
+ $encPrefix = $dbr->escapeLike( $prefix );
+ }
+ $conds = array(
+ 'ar_namespace' => $ns,
+ "ar_title LIKE '$encPrefix%'",
+ );
+ return self::listPages( $dbr, $conds );
}
+ protected static function listPages( $dbr, $condition ) {
+ return $dbr->resultObject(
+ $dbr->select(
+ array( 'archive' ),
+ array(
+ 'ar_namespace',
+ 'ar_title',
+ 'COUNT(*) AS count',
+ ),
+ $condition,
+ __METHOD__,
+ array(
+ 'GROUP BY' => 'ar_namespace,ar_title',
+ 'ORDER BY' => 'ar_namespace,ar_title',
+ 'LIMIT' => 100,
+ )
+ )
+ );
+ }
+
/**
* List the revisions of the given page. Returns result wrapper with
* (ar_minor_edit, ar_timestamp, ar_user, ar_user_text, ar_comment) fields.
@@ -57,9 +97,9 @@ class PageArchive {
* @return ResultWrapper
*/
function listRevisions() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'archive',
- array( 'ar_minor_edit', 'ar_timestamp', 'ar_user', 'ar_user_text', 'ar_comment' ),
+ array( 'ar_minor_edit', 'ar_timestamp', 'ar_user', 'ar_user_text', 'ar_comment', 'ar_len' ),
array( 'ar_namespace' => $this->title->getNamespace(),
'ar_title' => $this->title->getDBkey() ),
'PageArchive::listRevisions',
@@ -74,11 +114,11 @@ class PageArchive {
* if not a file page.
*
* @return ResultWrapper
- * @fixme Does this belong in Image for fuller encapsulation?
+ * @todo Does this belong in Image for fuller encapsulation?
*/
function listFiles() {
if( $this->title->getNamespace() == NS_IMAGE ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'filearchive',
array(
'fa_id',
@@ -119,7 +159,7 @@ class PageArchive {
* @return Revision
*/
function getRevision( $timestamp ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow( 'archive',
array(
'ar_rev_id',
@@ -130,7 +170,8 @@ class PageArchive {
'ar_timestamp',
'ar_minor_edit',
'ar_flags',
- 'ar_text_id' ),
+ 'ar_text_id',
+ 'ar_len' ),
array( 'ar_namespace' => $this->title->getNamespace(),
'ar_title' => $this->title->getDbkey(),
'ar_timestamp' => $dbr->timestamp( $timestamp ) ),
@@ -163,7 +204,7 @@ class PageArchive {
return Revision::getRevisionText( $row, "ar_" );
} else {
// New-style: keyed to the text storage backend.
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$text = $dbr->selectRow( 'text',
array( 'old_text', 'old_flags' ),
array( 'old_id' => $row->ar_text_id ),
@@ -182,7 +223,7 @@ class PageArchive {
* @return string
*/
function getLastRevisionText() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow( 'archive',
array( 'ar_text', 'ar_flags', 'ar_text_id' ),
array( 'ar_namespace' => $this->title->getNamespace(),
@@ -201,7 +242,7 @@ class PageArchive {
* @return bool
*/
function isDeleted() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$n = $dbr->selectField( 'archive', 'COUNT(ar_title)',
array( 'ar_namespace' => $this->title->getNamespace(),
'ar_title' => $this->title->getDBkey() ) );
@@ -282,7 +323,7 @@ class PageArchive {
$restoreAll = empty( $timestamps );
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$page = $dbw->tableName( 'archive' );
# Does this page already exist? We'll have to update it...
@@ -333,7 +374,8 @@ class PageArchive {
'ar_timestamp',
'ar_minor_edit',
'ar_flags',
- 'ar_text_id' ),
+ 'ar_text_id',
+ 'ar_len' ),
/* WHERE */ array(
'ar_namespace' => $this->title->getNamespace(),
'ar_title' => $this->title->getDBkey(),
@@ -373,6 +415,7 @@ class PageArchive {
'timestamp' => $row->ar_timestamp,
'minor_edit' => $row->ar_minor_edit,
'text_id' => $row->ar_text_id,
+ 'len' => $row->ar_len
) );
$revision->insertOn( $dbw );
$restored++;
@@ -389,8 +432,10 @@ class PageArchive {
}
if( $newid ) {
+ wfRunHooks( 'ArticleUndelete', array( &$this->title, true ) );
Article::onArticleCreate( $this->title );
} else {
+ wfRunHooks( 'ArticleUndelete', array( &$this->title, false ) );
Article::onArticleEdit( $this->title );
}
} else {
@@ -411,18 +456,19 @@ class PageArchive {
}
/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
+ * The HTML form for Special:Undelete, which allows users with the appropriate
+ * permissions to view and restore deleted content.
+ * @addtogroup SpecialPage
*/
class UndeleteForm {
var $mAction, $mTarget, $mTimestamp, $mRestore, $mTargetObj;
var $mTargetTimestamp, $mAllowed, $mComment;
- function UndeleteForm( &$request, $par = "" ) {
+ function UndeleteForm( $request, $par = "" ) {
global $wgUser;
$this->mAction = $request->getVal( 'action' );
$this->mTarget = $request->getVal( 'target' );
+ $this->mSearchPrefix = $request->getText( 'prefix' );
$time = $request->getVal( 'timestamp' );
$this->mTimestamp = $time ? wfTimestamp( TS_MW, $time ) : '';
$this->mFile = $request->getVal( 'file' );
@@ -467,9 +513,23 @@ class UndeleteForm {
}
function execute() {
-
+ global $wgOut;
+ if ( $this->mAllowed ) {
+ $wgOut->setPagetitle( wfMsg( "undeletepage" ) );
+ } else {
+ $wgOut->setPagetitle( wfMsg( "viewdeletedpage" ) );
+ }
+
if( is_null( $this->mTargetObj ) ) {
- return $this->showList();
+ $this->showSearchForm();
+
+ # List undeletable articles
+ if( $this->mSearchPrefix ) {
+ $result = PageArchive::listPagesByPrefix(
+ $this->mSearchPrefix );
+ $this->showList( $result );
+ }
+ return;
}
if( $this->mTimestamp !== '' ) {
return $this->showRevision( $this->mTimestamp );
@@ -483,17 +543,35 @@ class UndeleteForm {
return $this->showHistory();
}
- /* private */ function showList() {
- global $wgLang, $wgContLang, $wgUser, $wgOut;
-
- # List undeletable articles
- $result = PageArchive::listAllPages();
+ function showSearchForm() {
+ global $wgOut, $wgScript;
+ $wgOut->addWikiText( wfMsg( 'undelete-header' ) );
+
+ $wgOut->addHtml(
+ Xml::openElement( 'form', array(
+ 'method' => 'get',
+ 'action' => $wgScript ) ) .
+ '<fieldset>' .
+ Xml::element( 'legend', array(),
+ wfMsg( 'undelete-search-box' ) ) .
+ Xml::hidden( 'title',
+ SpecialPage::getTitleFor( 'Undelete' )->getPrefixedDbKey() ) .
+ Xml::inputLabel( wfMsg( 'undelete-search-prefix' ),
+ 'prefix', 'prefix', 20,
+ $this->mSearchPrefix ) .
+ Xml::submitButton( wfMsg( 'undelete-search-submit' ) ) .
+ '</fieldset>' .
+ '</form>' );
+ }
- if ( $this->mAllowed ) {
- $wgOut->setPagetitle( wfMsg( "undeletepage" ) );
- } else {
- $wgOut->setPagetitle( wfMsg( "viewdeletedpage" ) );
+ /* private */ function showList( $result ) {
+ global $wgLang, $wgContLang, $wgUser, $wgOut;
+
+ if( $result->numRows() == 0 ) {
+ $wgOut->addWikiText( wfMsg( 'undelete-no-results' ) );
+ return;
}
+
$wgOut->addWikiText( wfMsg( "undeletepagetext" ) );
$sk = $wgUser->getSkin();
@@ -502,7 +580,10 @@ class UndeleteForm {
while( $row = $result->fetchObject() ) {
$title = Title::makeTitleSafe( $row->ar_namespace, $row->ar_title );
$link = $sk->makeKnownLinkObj( $undelete, htmlspecialchars( $title->getPrefixedText() ), 'target=' . $title->getPrefixedUrl() );
- $revs = wfMsgHtml( 'undeleterevisions', $wgLang->formatNum( $row->count ) );
+ #$revs = wfMsgHtml( 'undeleterevisions', $wgLang->formatNum( $row->count ) );
+ $revs = wfMsgExt( 'undeleterevisions',
+ array( 'parseinline' ),
+ $wgLang->formatNum( $row->count ) );
$wgOut->addHtml( "<li>{$link} ({$revs})</li>\n" );
}
$result->free();
@@ -513,15 +594,19 @@ class UndeleteForm {
/* private */ function showRevision( $timestamp ) {
global $wgLang, $wgUser, $wgOut;
+ $self = SpecialPage::getTitleFor( 'Undelete' );
+ $skin = $wgUser->getSkin();
if(!preg_match("/[0-9]{14}/",$timestamp)) return 0;
$archive = new PageArchive( $this->mTargetObj );
$rev = $archive->getRevision( $timestamp );
- $wgOut->setPagetitle( wfMsg( "undeletepage" ) );
- $wgOut->addWikiText( "(" . wfMsg( "undeleterevision",
- $wgLang->timeAndDate( $timestamp ) ) . ")\n" );
+ $wgOut->setPageTitle( wfMsg( 'undeletepage' ) );
+ $link = $skin->makeKnownLinkObj( $self, htmlspecialchars( $this->mTargetObj->getPrefixedText() ),
+ 'target=' . $this->mTargetObj->getPrefixedUrl() );
+ $wgOut->addHtml( '<p>' . wfMsgHtml( 'undelete-revision', $link,
+ htmlspecialchars( $wgLang->timeAndDate( $timestamp ) ) ) . '</p>' );
if( !$rev ) {
$wgOut->addWikiText( wfMsg( 'undeleterevision-missing' ) );
@@ -532,12 +617,9 @@ class UndeleteForm {
if( $this->mPreview ) {
$wgOut->addHtml( "<hr />\n" );
- $article = new Article ( $archive->title ); # OutputPage wants an Article obj
- $wgOut->addPrimaryWikiText( $rev->getText(), $article, false );
+ $wgOut->addWikiTextTitleTidy( $rev->getText(), $this->mTargetObj, false );
}
-
- $self = SpecialPage::getTitleFor( "Undelete" );
-
+
$wgOut->addHtml(
wfElement( 'textarea', array(
'readonly' => true,
@@ -673,7 +755,7 @@ class UndeleteForm {
}
$wgOut->addHTML( "<h2>" . htmlspecialchars( wfMsg( "history" ) ) . "</h2>\n" );
-
+
if( $haveRevisions ) {
# The page's stored (deleted) history:
$wgOut->addHTML("<ul>");
@@ -690,8 +772,16 @@ class UndeleteForm {
$pageLink = $wgLang->timeanddate( $ts, true );
}
$userLink = $sk->userLink( $row->ar_user, $row->ar_user_text ) . $sk->userToolLinks( $row->ar_user, $row->ar_user_text );
+ $stxt = '';
+ if (!is_null($size = $row->ar_len)) {
+ if ($size == 0) {
+ $stxt = wfMsgHtml('historyempty');
+ } else {
+ $stxt = wfMsgHtml('historysize', $wgLang->formatNum( $size ) );
+ }
+ }
$comment = $sk->commentBlock( $row->ar_comment );
- $wgOut->addHTML( "<li>$checkBox $pageLink . . $userLink $comment</li>\n" );
+ $wgOut->addHTML( "<li>$checkBox $pageLink . . $userLink $stxt $comment</li>\n" );
}
$revisions->free();
@@ -753,7 +843,7 @@ class UndeleteForm {
$this->mFileVersions );
if( $ok ) {
- $skin =& $wgUser->getSkin();
+ $skin = $wgUser->getSkin();
$link = $skin->makeKnownLinkObj( $this->mTargetObj );
$wgOut->addHtml( wfMsgWikiHtml( 'undeletedpage', $link ) );
return true;
diff --git a/includes/SpecialUnlockdb.php b/includes/SpecialUnlockdb.php
index 1f24d131..e864a182 100644
--- a/includes/SpecialUnlockdb.php
+++ b/includes/SpecialUnlockdb.php
@@ -1,8 +1,7 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
@@ -31,8 +30,7 @@ function wfSpecialUnlockdb() {
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
class DBUnlockForm {
function showForm( $err )
diff --git a/includes/SpecialUnusedcategories.php b/includes/SpecialUnusedcategories.php
index 80f46a87..5cd3406b 100644
--- a/includes/SpecialUnusedcategories.php
+++ b/includes/SpecialUnusedcategories.php
@@ -1,14 +1,12 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
class UnusedCategoriesPage extends QueryPage {
@@ -22,7 +20,7 @@ class UnusedCategoriesPage extends QueryPage {
function getSQL() {
$NScat = NS_CATEGORY;
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
list( $categorylinks, $page ) = $dbr->tableNamesN( 'categorylinks', 'page' );
return "SELECT 'Unusedcategories' as type,
{$NScat} as namespace, page_title as title, page_title as value
diff --git a/includes/SpecialUnusedimages.php b/includes/SpecialUnusedimages.php
index 75d702c8..6b99192a 100644
--- a/includes/SpecialUnusedimages.php
+++ b/includes/SpecialUnusedimages.php
@@ -1,15 +1,14 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
- * @package MediaWiki
- * @subpackage SpecialPage
+ * implements Special:Unusedimages
+ * @addtogroup SpecialPage
*/
-class UnusedimagesPage extends QueryPage {
+class UnusedimagesPage extends ImageQueryPage {
function getName() {
return 'Unusedimages';
@@ -22,7 +21,7 @@ class UnusedimagesPage extends QueryPage {
function getSQL() {
global $wgCountCategorizedImagesAsUsed;
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
if ( $wgCountCategorizedImagesAsUsed ) {
list( $page, $image, $imagelinks, $categorylinks ) = $dbr->tableNamesN( 'page', 'image', 'imagelinks', 'categorylinks' );
@@ -40,34 +39,6 @@ class UnusedimagesPage extends QueryPage {
}
}
- function formatResult( $skin, $result ) {
- global $wgLang, $wgContLang;
- $title = Title::makeTitle( NS_IMAGE, $result->title );
-
- $imageUrl = htmlspecialchars( Image::imageUrl( $result->title ) );
- $dirmark = $wgContLang->getDirMark(); // To keep text in correct order
-
- $return =
- # The 'desc' linking to the image page
- '('.$skin->makeKnownLinkObj( $title, wfMsg('imgdesc') ).') ' . $dirmark .
-
- # Link to the image itself
- '<a href="' . $imageUrl . '">' . htmlspecialchars( $title->getText() ) .
- '</a> . . ' . $dirmark .
-
- # Last modified date
- $wgLang->timeanddate($result->value) . ' . . ' . $dirmark .
-
- # Link to username
- $skin->makeLinkObj( Title::makeTitle( NS_USER, $result->img_user_text ),
- $result->img_user_text) . $dirmark .
-
- # If there is a description, show it
- $skin->commentBlock( $wgContLang->convert( $result->img_description ) );
-
- return $return;
- }
-
function getPageHeader() {
return wfMsg( "unusedimagestext" );
}
diff --git a/includes/SpecialUnusedtemplates.php b/includes/SpecialUnusedtemplates.php
index 2af9abc6..8b72e8a7 100644
--- a/includes/SpecialUnusedtemplates.php
+++ b/includes/SpecialUnusedtemplates.php
@@ -1,19 +1,12 @@
<?php
/**
- * @package MediaWiki
- * @subpackage Special pages
- *
+ * implements Special:Unusedtemplates
* @author Rob Church <robchur@gmail.com>
* @copyright © 2006 Rob Church
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ * @addtogroup SpecialPage
*/
-
-/**
- * @package MediaWiki
- * @subpackage SpecialPage
- */
-
class UnusedtemplatesPage extends QueryPage {
function getName() { return( 'Unusedtemplates' ); }
@@ -22,7 +15,7 @@ class UnusedtemplatesPage extends QueryPage {
function sortDescending() { return false; }
function getSQL() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
list( $page, $templatelinks) = $dbr->tableNamesN( 'page', 'templatelinks' );
$sql = "SELECT 'Unusedtemplates' AS type, page_title AS title,
page_namespace AS namespace, 0 AS value
diff --git a/includes/SpecialUnwatchedpages.php b/includes/SpecialUnwatchedpages.php
index f9dff724..fed0b590 100644
--- a/includes/SpecialUnwatchedpages.php
+++ b/includes/SpecialUnwatchedpages.php
@@ -1,19 +1,13 @@
<?php
/**
- * A special page that displays a list of pages that are not on anyones watchlist
- *
- * @package MediaWiki
- * @subpackage SpecialPage
+ * A special page that displays a list of pages that are not on anyones watchlist.
+ * Implements Special:Unwatchedpages
*
+ * @addtogroup SpecialPage
* @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
*/
-
-/**
- * @package MediaWiki
- * @subpackage SpecialPage
- */
class UnwatchedpagesPage extends QueryPage {
function getName() { return 'Unwatchedpages'; }
@@ -21,7 +15,7 @@ class UnwatchedpagesPage extends QueryPage {
function isSyndicated() { return false; }
function getSQL() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
list( $page, $watchlist ) = $dbr->tableNamesN( 'page', 'watchlist' );
$mwns = NS_MEDIAWIKI;
return
diff --git a/includes/SpecialUpload.php b/includes/SpecialUpload.php
index d2fd839c..e07c414c 100644
--- a/includes/SpecialUpload.php
+++ b/includes/SpecialUpload.php
@@ -1,8 +1,7 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
@@ -16,9 +15,8 @@ function wfSpecialUpload() {
}
/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
+ * implements Special:Upload
+ * @addtogroup SpecialPage
*/
class UploadForm {
/**#@+
@@ -126,11 +124,11 @@ class UploadForm {
$this->mOname = array_pop( explode( '/', $url ) );
$this->mSessionKey = false;
$this->mStashed = false;
-
+
// PHP won't auto-cleanup the file
$this->mRemoveTempFile = file_exists( $local_file );
}
-
+
/**
* Safe copy from URL
* Returns true if there was an error, false otherwise
@@ -158,19 +156,19 @@ class UploadForm {
$wgOut->errorPage( 'upload-file-error', 'upload-file-error-text');
return true;
}
-
+
$ch = curl_init();
curl_setopt( $ch, CURLOPT_HTTP_VERSION, 1.0); # Probably not needed, but apparently can work around some bug
curl_setopt( $ch, CURLOPT_TIMEOUT, 10); # 10 seconds timeout
curl_setopt( $ch, CURLOPT_LOW_SPEED_LIMIT, 512); # 0.5KB per second minimum transfer speed
- curl_setopt( $ch, CURLOPT_URL, $url);
+ curl_setopt( $ch, CURLOPT_URL, $url);
curl_setopt( $ch, CURLOPT_WRITEFUNCTION, array( $this, 'uploadCurlCallback' ) );
curl_exec( $ch );
$error = curl_errno( $ch ) ? true : false;
$errornum = curl_errno( $ch );
// if ( $error ) print curl_error ( $ch ) ; # Debugging output
curl_close( $ch );
-
+
fclose( $this->mUploadTempFile );
unset( $this->mUploadTempFile );
if( $error ) {
@@ -180,10 +178,10 @@ class UploadForm {
else
$wgOut->errorPage( "upload-curl-error$errornum", "upload-curl-error$errornum-text" );
}
-
+
return $error;
}
-
+
/**
* Callback function for CURL-based web transfer
* Write data to file unless we've passed the length limit;
@@ -200,7 +198,7 @@ class UploadForm {
fwrite( $this->mUploadTempFile, $data );
return $length;
}
-
+
/**
* Start doing stuff
* @access public
@@ -298,13 +296,12 @@ class UploadForm {
* only the final one for the whitelist.
*/
list( $partname, $ext ) = $this->splitExtensions( $basename );
-
+
if( count( $ext ) ) {
$finalExt = $ext[count( $ext ) - 1];
} else {
$finalExt = '';
}
- $fullExt = implode( '.', $ext );
# If there was more than one "extension", reassemble the base
# filename to prevent bogus complaints about length
@@ -335,7 +332,7 @@ class UploadForm {
* If the image is protected, non-sysop users won't be able
* to modify it by uploading a new revision.
*/
- if( !$nt->userCanEdit() ) {
+ if( !$nt->userCan( 'edit' ) ) {
return $this->uploadError( wfMsgWikiHtml( 'protectedpage' ) );
}
@@ -350,10 +347,12 @@ class UploadForm {
/* Don't allow users to override the blacklist (check file extension) */
global $wgStrictFileExtensions;
global $wgFileExtensions, $wgFileBlacklist;
- if( $this->checkFileExtensionList( $ext, $wgFileBlacklist ) ||
- ($wgStrictFileExtensions &&
- !$this->checkFileExtension( $finalExt, $wgFileExtensions ) ) ) {
- return $this->uploadError( wfMsgHtml( 'badfiletype', htmlspecialchars( $finalExt ) ) );
+ if ($finalExt == '') {
+ return $this->uploadError( wfMsgExt( 'filetype-missing', array ( 'parseinline' ) ) );
+ } elseif ( $this->checkFileExtensionList( $ext, $wgFileBlacklist ) ||
+ ($wgStrictFileExtensions &&
+ !$this->checkFileExtension( $finalExt, $wgFileExtensions ) ) ) {
+ return $this->uploadError( wfMsgExt( 'filetype-badtype', array ( 'parseinline' ), htmlspecialchars( $finalExt ), implode ( ', ', $wgFileExtensions ) ) );
}
/**
@@ -396,13 +395,13 @@ class UploadForm {
global $wgCheckFileExtensions;
if ( $wgCheckFileExtensions ) {
if ( ! $this->checkFileExtension( $finalExt, $wgFileExtensions ) ) {
- $warning .= '<li>'.wfMsgHtml( 'badfiletype', htmlspecialchars( $finalExt ) ).'</li>';
+ $warning .= '<li>'.wfMsgExt( 'filetype-badtype', array ( 'parseinline' ), htmlspecialchars( $finalExt ), implode ( ', ', $wgFileExtensions ) ).'</li>';
}
}
global $wgUploadSizeWarning;
if ( $wgUploadSizeWarning && ( $this->mUploadSize > $wgUploadSizeWarning ) ) {
- $skin =& $wgUser->getSkin();
+ $skin = $wgUser->getSkin();
$wsize = $skin->formatSize( $wgUploadSizeWarning );
$asize = $skin->formatSize( $this->mUploadSize );
$warning .= '<li>' . wfMsgHtml( 'large-file', $wsize, $asize ) . '</li>';
@@ -411,21 +410,73 @@ class UploadForm {
$warning .= '<li>'.wfMsgHtml( 'emptyfile' ).'</li>';
}
- if( $nt->getArticleID() ) {
- global $wgUser;
- $sk = $wgUser->getSkin();
+ global $wgUser;
+ $sk = $wgUser->getSkin();
+ $image = new Image( $nt );
+
+ // Check for uppercase extension. We allow these filenames but check if an image
+ // with lowercase extension exists already
+ if ( $finalExt != strtolower( $finalExt ) ) {
+ $nt_lc = Title::newFromText( $partname . '.' . strtolower( $finalExt ) );
+ $image_lc = new Image( $nt_lc );
+ }
+
+ if( $image->exists() ) {
$dlink = $sk->makeKnownLinkObj( $nt );
- $warning .= '<li>'.wfMsgHtml( 'fileexists', $dlink ).'</li>';
- } else {
+ if ( $image->allowInlineDisplay() ) {
+ $dlink2 = $sk->makeImageLinkObj( $nt, wfMsgExt( 'fileexists-thumb', 'parseinline', $dlink ), $nt->getText(), 'right', array(), false, true );
+ } elseif ( !$image->allowInlineDisplay() && $image->isSafeFile() ) {
+ $icon = $image->iconThumb();
+ $dlink2 = '<div style="float:right" id="mw-media-icon"><a href="' . $image->getURL() . '">' . $icon->toHtml() . '</a><br />' . $dlink . '</div>';
+ } else {
+ $dlink2 = '';
+ }
+
+ $warning .= '<li>' . wfMsgExt( 'fileexists', 'parseline', $dlink ) . '</li>' . $dlink2;
+
+ } elseif ( isset( $image_lc) && $image_lc->exists() ) {
+ # Check if image with lowercase extension exists.
+ # It's not forbidden but in 99% it makes no sense to upload the same filename with uppercase extension
+ $dlink = $sk->makeKnownLinkObj( $nt_lc );
+ if ( $image_lc->allowInlineDisplay() ) {
+ $dlink2 = $sk->makeImageLinkObj( $nt_lc, wfMsgExt( 'fileexists-thumb', 'parseinline', $dlink ), $nt_lc->getText(), 'right', array(), false, true );
+ } elseif ( !$image_lc->allowInlineDisplay() && $image_lc->isSafeFile() ) {
+ $icon = $image_lc->iconThumb();
+ $dlink2 = '<div style="float:right" id="mw-media-icon"><a href="' . $image_lc->getURL() . '">' . $icon->toHtml() . '</a><br />' . $dlink . '</div>';
+ } else {
+ $dlink2 = '';
+ }
+
+ $warning .= '<li>' . wfMsgExt( 'fileexists-extension', 'parsemag' , $partname . '.' . $finalExt , $dlink ) . '</li>' . $dlink2;
+
+ } elseif ( ( substr( $partname , 3, 3 ) == 'px-' || substr( $partname , 2, 3 ) == 'px-' ) && ereg( "[0-9]{2}" , substr( $partname , 0, 2) ) ) {
+ # Check for filenames like 50px- or 180px-, these are mostly thumbnails
+ $nt_thb = Title::newFromText( substr( $partname , strpos( $partname , '-' ) +1 ) . '.' . $finalExt );
+ $image_thb = new Image( $nt_thb );
+ if ($image_thb->exists() ) {
+ # Check if an image without leading '180px-' (or similiar) exists
+ $dlink = $sk->makeKnownLinkObj( $nt_thb);
+ if ( $image_thb->allowInlineDisplay() ) {
+ $dlink2 = $sk->makeImageLinkObj( $nt_thb, wfMsgExt( 'fileexists-thumb', 'parseinline', $dlink ), $nt_thb->getText(), 'right', array(), false, true );
+ } elseif ( !$image_thb->allowInlineDisplay() && $image_thb->isSafeFile() ) {
+ $icon = $image_thb->iconThumb();
+ $dlink2 = '<div style="float:right" id="mw-media-icon"><a href="' . $image_thb->getURL() . '">' . $icon->toHtml() . '</a><br />' . $dlink . '</div>';
+ } else {
+ $dlink2 = '';
+ }
+
+ $warning .= '<li>' . wfMsgExt( 'fileexists-thumbnail-yes', 'parsemag', $dlink ) . '</li>' . $dlink2;
+ } else {
+ # Image w/o '180px-' does not exists, but we do not like these filenames
+ $warning .= '<li>' . wfMsgExt( 'file-thumbnail-no', 'parseinline' , substr( $partname , 0, strpos( $partname , '-' ) +1 ) ) . '</li>';
+ }
+ }
+ if ( $image->wasDeleted() ) {
# If the file existed before and was deleted, warn the user of this
# Don't bother doing so if the image exists now, however
- $image = new Image( $nt );
- if( $image->wasDeleted() ) {
- $skin = $wgUser->getSkin();
- $ltitle = SpecialPage::getTitleFor( 'Log' );
- $llink = $skin->makeKnownLinkObj( $ltitle, wfMsgHtml( 'deletionlog' ), 'type=delete&page=' . $nt->getPrefixedUrl() );
- $warning .= wfOpenElement( 'li' ) . wfMsgWikiHtml( 'filewasdeleted', $llink ) . wfCloseElement( 'li' );
- }
+ $ltitle = SpecialPage::getTitleFor( 'Log' );
+ $llink = $sk->makeKnownLinkObj( $ltitle, wfMsgHtml( 'deletionlog' ), 'type=delete&page=' . $nt->getPrefixedUrl() );
+ $warning .= wfOpenElement( 'li' ) . wfMsgWikiHtml( 'filewasdeleted', $llink ) . wfCloseElement( 'li' );
}
if( $warning != '' ) {
@@ -482,7 +533,7 @@ class UploadForm {
*/
function saveUploadedFile( $saveName, $tempName, $useRename = false ) {
global $wgOut, $wgAllowCopyUploads;
-
+
if ( !$useRename AND $wgAllowCopyUploads AND $this->mSourceType == 'web' ) $useRename = true;
$fname= "SpecialUpload::saveUploadedFile";
@@ -491,7 +542,7 @@ class UploadForm {
$archive = wfImageArchiveDir( $saveName );
if ( !is_dir( $dest ) ) wfMkdirParents( $dest );
if ( !is_dir( $archive ) ) wfMkdirParents( $archive );
-
+
$this->mSavedFile = "{$dest}/{$saveName}";
if( is_file( $this->mSavedFile ) ) {
@@ -725,7 +776,7 @@ class UploadForm {
"<span class='error'>{$msg}</span>\n" );
}
$wgOut->addHTML( '<div id="uploadtext">' );
- $wgOut->addWikiText( wfMsg( 'uploadtext' ) );
+ $wgOut->addWikiText( wfMsgNoTrans( 'uploadtext', $this->mDestFile ) );
$wgOut->addHTML( '</div>' );
$sourcefilename = wfMsgHtml( 'sourcefilename' );
@@ -753,19 +804,19 @@ class UploadForm {
// Prepare form for upload or upload/copy
if( $wgAllowCopyUploads && $wgUser->isAllowed( 'upload_by_url' ) ) {
- $filename_form =
- "<input type='radio' id='wpSourceTypeFile' name='wpSourceType' value='file' onchange='toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\")' checked />" .
- "<input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' onfocus='toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\");toggle_element_check(\"wpSourceTypeFile\",\"wpSourceTypeURL\")'" .
+ $filename_form =
+ "<input type='radio' id='wpSourceTypeFile' name='wpSourceType' value='file' onchange='toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\")' checked />" .
+ "<input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' onfocus='toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\");toggle_element_check(\"wpSourceTypeFile\",\"wpSourceTypeURL\")'" .
($this->mDestFile?"":"onchange='fillDestFilename(\"wpUploadFile\")' ") . "size='40' />" .
wfMsgHTML( 'upload_source_file' ) . "<br/>" .
"<input type='radio' id='wpSourceTypeURL' name='wpSourceType' value='web' onchange='toggle_element_activation(\"wpUploadFile\",\"wpUploadFileURL\")' />" .
- "<input tabindex='1' type='text' name='wpUploadFileURL' id='wpUploadFileURL' onfocus='toggle_element_activation(\"wpUploadFile\",\"wpUploadFileURL\");toggle_element_check(\"wpSourceTypeURL\",\"wpSourceTypeFile\")'" .
+ "<input tabindex='1' type='text' name='wpUploadFileURL' id='wpUploadFileURL' onfocus='toggle_element_activation(\"wpUploadFile\",\"wpUploadFileURL\");toggle_element_check(\"wpSourceTypeURL\",\"wpSourceTypeFile\")'" .
($this->mDestFile?"":"onchange='fillDestFilename(\"wpUploadFileURL\")' ") . "size='40' DISABLED />" .
wfMsgHtml( 'upload_source_url' ) ;
} else {
- $filename_form =
- "<input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' " .
- ($this->mDestFile?"":"onchange='fillDestFilename(\"wpUploadFile\")' ") .
+ $filename_form =
+ "<input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' " .
+ ($this->mDestFile?"":"onchange='fillDestFilename(\"wpUploadFile\")' ") .
"size='40' />" .
"<input type='hidden' name='wpSourceType' value='file' />" ;
}
@@ -817,7 +868,7 @@ class UploadForm {
$copystatus = htmlspecialchars( $this->mUploadCopyStatus );
$filesource = wfMsgHtml ( 'filesource' );
$uploadsource = htmlspecialchars( $this->mUploadSource );
-
+
$wgOut->addHTML( "
<td align='right' nowrap='nowrap'><label for='wpUploadCopyStatus'>$filestatus:</label></td>
<td><input tabindex='5' type='text' name='wpUploadCopyStatus' id='wpUploadCopyStatus' value=\"$copystatus\" size='40' /></td>
@@ -930,7 +981,7 @@ class UploadForm {
global $wgMimeTypeBlacklist;
if( isset($wgMimeTypeBlacklist) && !is_null($wgMimeTypeBlacklist)
&& $this->checkFileExtension( $mime, $wgMimeTypeBlacklist ) ) {
- return new WikiErrorMsg( 'badfiletype', htmlspecialchars( $mime ) );
+ return new WikiErrorMsg( 'filetype-badmime', htmlspecialchars( $mime ) );
}
}
@@ -1074,13 +1125,13 @@ class UploadForm {
$chunk = Sanitizer::decodeCharReferences( $chunk );
#look for script-types
- if (preg_match('!type\s*=\s*[\'"]?\s*(\w*/)?(ecma|java)!sim',$chunk)) return true;
+ if (preg_match('!type\s*=\s*[\'"]?\s*(?:\w*/)?(?:ecma|java)!sim',$chunk)) return true;
#look for html-style script-urls
- if (preg_match('!(href|src|data)\s*=\s*[\'"]?\s*(ecma|java)script:!sim',$chunk)) return true;
+ if (preg_match('!(?:href|src|data)\s*=\s*[\'"]?\s*(?:ecma|java)script:!sim',$chunk)) return true;
#look for css-style script-urls
- if (preg_match('!url\s*\(\s*[\'"]?\s*(ecma|java)script:!sim',$chunk)) return true;
+ if (preg_match('!url\s*\(\s*[\'"]?\s*(?:ecma|java)script:!sim',$chunk)) return true;
wfDebug("SpecialUpload::detectScript: no scripts found\n");
return false;
@@ -1255,6 +1306,4 @@ class UploadForm {
}
}
-
-
?>
diff --git a/includes/SpecialUploadMogile.php b/includes/SpecialUploadMogile.php
index 05bfca08..27af62e7 100644
--- a/includes/SpecialUploadMogile.php
+++ b/includes/SpecialUploadMogile.php
@@ -1,12 +1,11 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
- *
+ * You will need the extension MogileClient to use this special page.
*/
require_once( 'MogileFS.php' );
@@ -19,7 +18,10 @@ function wfSpecialUploadMogile() {
$form->execute();
}
-/** @package MediaWiki */
+/**
+ * Extends Special:Upload with MogileFS.
+ * @addtogroup SpecialPage
+ */
class UploadFormMogile extends UploadForm {
/**
* Move the uploaded file from its temporary location to the final
diff --git a/includes/SpecialUserlogin.php b/includes/SpecialUserlogin.php
index e60e3d54..e8f33b8d 100644
--- a/includes/SpecialUserlogin.php
+++ b/includes/SpecialUserlogin.php
@@ -1,8 +1,7 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
@@ -11,7 +10,7 @@
function wfSpecialUserlogin() {
global $wgCommandLineMode;
global $wgRequest;
- if( !$wgCommandLineMode && !isset( $_COOKIE[session_name()] ) ) {
+ if( session_id() == '' ) {
wfSetupSession();
}
@@ -20,11 +19,9 @@ function wfSpecialUserlogin() {
}
/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
+ * implements Special:Login
+ * @addtogroup SpecialPage
*/
-
class LoginForm {
const SUCCESS = 0;
@@ -42,7 +39,7 @@ class LoginForm {
/**
* Constructor
- * @param webrequest $request A webrequest object passed by reference
+ * @param WebRequest $request A WebRequest object passed by reference
*/
function LoginForm( &$request ) {
global $wgLang, $wgAllowRealName, $wgEnableEmail;
@@ -149,12 +146,12 @@ class LoginForm {
*/
function addNewAccount() {
global $wgUser, $wgEmailAuthentication;
-
+
# Create the account and abort if there's a problem doing so
$u = $this->addNewAccountInternal();
if( $u == NULL )
return;
-
+
# If we showed up language selection links, and one was in use, be
# smart (and sensible) and save that language as the user's preference
global $wgLoginLanguageSelector;
@@ -231,6 +228,7 @@ class LoginForm {
return false;
}
+ # Check anonymous user ($wgUser) limitations :
if (!$wgUser->isAllowedToCreateAccount()) {
$this->userNotPrivilegedMessage();
return false;
@@ -244,6 +242,7 @@ class LoginForm {
return;
}
+ # Now create a dummy user ($u) and check if it is valid
$name = trim( $this->mName );
$u = User::newFromName( $name, 'creatable' );
if ( is_null( $u ) ) {
@@ -261,7 +260,7 @@ class LoginForm {
return false;
}
- if ( !$wgUser->isValidPassword( $this->mPassword ) ) {
+ if ( !$u->isValidPassword( $this->mPassword ) ) {
$this->mainLoginForm( wfMsg( 'passwordtooshort', $wgMinimalPasswordLength ) );
return false;
}
@@ -274,7 +273,7 @@ class LoginForm {
return false;
}
- if ( $wgAccountCreationThrottle ) {
+ if ( $wgAccountCreationThrottle && $wgUser->isPingLimitable() ) {
$key = wfMemcKey( 'acctcreate', 'ip', $ip );
$value = $wgMemc->incr( $key );
if ( !$value ) {
@@ -286,15 +285,11 @@ class LoginForm {
}
}
- if( !$wgAuth->addUser( $u, $this->mPassword ) ) {
+ if( !$wgAuth->addUser( $u, $this->mPassword, $this->mEmail, $this->mRealName ) ) {
$this->mainLoginForm( wfMsg( 'externaldberror' ) );
return false;
}
- # Update user count
- $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
- $ssUpdate->doUpdate();
-
return $this->initUser( $u );
}
@@ -307,18 +302,27 @@ class LoginForm {
* @private
*/
function initUser( $u ) {
+ global $wgAuth;
+
$u->addToDatabase();
- $u->setPassword( $this->mPassword );
+
+ if ( $wgAuth->allowPasswordChange() ) {
+ $u->setPassword( $this->mPassword );
+ }
+
$u->setEmail( $this->mEmail );
$u->setRealName( $this->mRealName );
$u->setToken();
- global $wgAuth;
$wgAuth->initUser( $u );
$u->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 );
$u->saveSettings();
+ # Update user count
+ $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
+ $ssUpdate->doUpdate();
+
return $u;
}
@@ -361,6 +365,7 @@ class LoginForm {
}
if (!$u->checkPassword( $this->mPassword )) {
+
if( $u->checkTemporaryPassword( $this->mPassword ) ) {
// The e-mailed temporary password should not be used
// for actual logins; that's a very sloppy habit,
@@ -383,7 +388,7 @@ class LoginForm {
if( !$u->isEmailConfirmed() ) {
$u->confirmEmail();
}
-
+
// At this point we just return an appropriate code
// indicating that the UI should show a password
// reset form; bot interfaces etc will probably just
@@ -393,14 +398,14 @@ class LoginForm {
} else {
return '' == $this->mPassword ? self::EMPTY_PASS : self::WRONG_PASS;
}
- } else {
+ } else {
$wgAuth->updateUser( $u );
$wgUser = $u;
return self::SUCCESS;
}
}
-
+
function processLogin() {
global $wgUser, $wgAuth;
@@ -446,7 +451,7 @@ class LoginForm {
wfDebugDieBacktrace( "Unhandled case value" );
}
}
-
+
function resetLoginForm( $error ) {
global $wgOut;
$wgOut->addWikiText( "<div class=\"errorbox\">$error</div>" );
@@ -459,19 +464,19 @@ class LoginForm {
*/
function mailPassword() {
global $wgUser, $wgOut, $wgAuth;
-
+
if( !$wgAuth->allowPasswordChange() ) {
$this->mainLoginForm( wfMsg( 'resetpass_forbidden' ) );
return;
}
-
+
# Check against blocked IPs
# fixme -- should we not?
if( $wgUser->isBlocked() ) {
$this->mainLoginForm( wfMsg( 'blocked-mailpassword' ) );
return;
}
-
+
# Check against the rate limiter
if( $wgUser->pingLimiter( 'mailpassword' ) ) {
$wgOut->rateLimited();
@@ -496,7 +501,7 @@ class LoginForm {
if ( $u->isPasswordReminderThrottled() ) {
global $wgPasswordReminderResendTime;
# Round the time in hours to 3 d.p., in case someone is specifying minutes or seconds.
- $this->mainLoginForm( wfMsg( 'throttled-mailpassword',
+ $this->mainLoginForm( wfMsg( 'throttled-mailpassword',
round( $wgPasswordReminderResendTime, 3 ) ) );
return;
}
@@ -587,7 +592,7 @@ class LoginForm {
# haven't bothered to log out before trying to create an account to
# evade it, but we'll leave that to their guilty conscience to figure
# out.
-
+
$wgOut->setPageTitle( wfMsg( 'cantcreateaccounttitle' ) );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
$wgOut->setArticleRelated( false );
@@ -642,7 +647,7 @@ class LoginForm {
$q .= $returnto;
$linkq .= $returnto;
}
-
+
# Pass any language selection on to the mode switch link
if( $wgLoginLanguageSelector && $this->mLanguage )
$linkq .= '&uselang=' . $this->mLanguage;
@@ -656,7 +661,7 @@ class LoginForm {
$template->set( 'link', wfMsgHtml( $linkmsg, $link ) );
else
$template->set( 'link', '' );
-
+
$template->set( 'header', '' );
$template->set( 'name', $this->mName );
$template->set( 'password', $this->mPassword );
@@ -673,14 +678,14 @@ class LoginForm {
$template->set( 'useemail', $wgEnableEmail );
$template->set( 'canreset', $wgAuth->allowPasswordChange() );
$template->set( 'remember', $wgUser->getOption( 'rememberpassword' ) or $this->mRemember );
-
+
# Prepare language selection links as needed
if( $wgLoginLanguageSelector ) {
$template->set( 'languages', $this->makeLanguageSelector() );
if( $this->mLanguage )
$template->set( 'uselang', $this->mLanguage );
}
-
+
// Give authentication and captcha plugins a chance to modify the form
$wgAuth->modifyUITemplate( $template );
if ( $this->mType == 'signup' ) {
@@ -694,7 +699,7 @@ class LoginForm {
$wgOut->setArticleRelated( false );
$wgOut->addTemplate( $template );
}
-
+
/**
* @private
*/
@@ -709,11 +714,17 @@ class LoginForm {
}
/**
+ * Check if a session cookie is present.
+ *
+ * This will not pick up a cookie set during _this_ request, but is
+ * meant to ensure that the client is returning the cookie which was
+ * set on a previous pass through the system.
+ *
* @private
*/
function hasSessionCookie() {
- global $wgDisableCookieCheck;
- return ( $wgDisableCookieCheck ) ? true : ( isset( $_COOKIE[session_name()] ) );
+ global $wgDisableCookieCheck, $wgRequest;
+ return $wgDisableCookieCheck ? true : $wgRequest->checkSessionCookie();
}
/**
@@ -756,7 +767,7 @@ class LoginForm {
$wgOut->addWikiText( wfMsg( 'acct_creation_throttle_hit', $limit ) );
}
-
+
/**
* Produce a bar of links which allow the user to select another language
* during login/registration but retain "returnto"
@@ -778,7 +789,7 @@ class LoginForm {
return '';
}
}
-
+
/**
* Create a language selector link for a particular language
* Links back to this page preserving type and returnto
@@ -794,9 +805,8 @@ class LoginForm {
$attr[] = 'type=signup';
if( $this->mReturnTo )
$attr[] = 'returnto=' . $this->mReturnTo;
- $skin =& $wgUser->getSkin();
+ $skin = $wgUser->getSkin();
return $skin->makeKnownLinkObj( $self, htmlspecialchars( $text ), implode( '&', $attr ) );
}
-
}
?>
diff --git a/includes/SpecialUserlogout.php b/includes/SpecialUserlogout.php
index f3fcbc4f..9f1bdb3a 100644
--- a/includes/SpecialUserlogout.php
+++ b/includes/SpecialUserlogout.php
@@ -1,8 +1,7 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
diff --git a/includes/SpecialUserrights.php b/includes/SpecialUserrights.php
index 99abd7a7..d12a9cc4 100644
--- a/includes/SpecialUserrights.php
+++ b/includes/SpecialUserrights.php
@@ -3,13 +3,12 @@
/**
* Special page to allow managing user group membership
*
- * @package MediaWiki
- * @subpackage Special pages
+ * @addtogroup SpecialPage
* @todo This code is disgusting and needs a total rewrite
*/
/** */
-require_once('HTMLForm.php');
+require_once( dirname(__FILE__) . '/HTMLForm.php');
/** Entry point */
function wfSpecialUserrights() {
@@ -20,8 +19,7 @@ function wfSpecialUserrights() {
/**
* A class to manage user levels rights.
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
class UserrightsForm extends HTMLForm {
var $mPosted, $mRequest, $mSaveprefs;
@@ -55,10 +53,12 @@ class UserrightsForm extends HTMLForm {
if( $this->mRequest->getCheck( 'saveusergroups' ) ) {
global $wgUser;
$username = $this->mRequest->getVal( 'user-editname' );
+ $reason = $this->mRequest->getVal( 'user-reason' );
if( $wgUser->matchEditToken( $this->mRequest->getVal( 'wpEditToken' ), $username ) ) {
$this->saveUserGroups( $username,
$this->mRequest->getArray( 'member' ),
- $this->mRequest->getArray( 'available' ) );
+ $this->mRequest->getArray( 'available' ),
+ $reason );
}
}
}
@@ -71,9 +71,10 @@ class UserrightsForm extends HTMLForm {
* @param string $username Username to apply changes to.
* @param array $removegroup id of groups to be removed.
* @param array $addgroup id of groups to be added.
+ * @param string $reason Reason for group change
*
*/
- function saveUserGroups( $username, $removegroup, $addgroup) {
+ function saveUserGroups( $username, $removegroup, $addgroup, $reason ) {
global $wgOut;
$u = User::newFromName($username);
@@ -109,7 +110,7 @@ class UserrightsForm extends HTMLForm {
wfRunHooks( 'UserRights', array( &$u, $addgroup, $removegroup ) );
$log = new LogPage( 'rights' );
- $log->addEntry( 'rights', Title::makeTitle( NS_USER, $u->getName() ), '', array( $this->makeGroupNameList( $oldGroups ),
+ $log->addEntry( 'rights', Title::makeTitle( NS_USER, $u->getName() ), $reason, array( $this->makeGroupNameList( $oldGroups ),
$this->makeGroupNameList( $newGroups ) ) );
}
@@ -137,7 +138,7 @@ class UserrightsForm extends HTMLForm {
* @param string $username Name of the user.
*/
function editUserGroupsForm($username) {
- global $wgOut, $wgUser;
+ global $wgOut;
$user = User::newFromName($username);
if( is_null( $user ) ) {
@@ -149,30 +150,52 @@ class UserrightsForm extends HTMLForm {
}
$groups = $user->getGroups();
-
- $wgOut->addHTML( "<form name=\"editGroup\" action=\"$this->action\" method=\"post\">\n".
- wfElement( 'input', array(
- 'type' => 'hidden',
- 'name' => 'user-editname',
- 'value' => $username ) ) .
- wfElement( 'input', array(
- 'type' => 'hidden',
- 'name' => 'wpEditToken',
- 'value' => $wgUser->editToken( $username ) ) ) .
- $this->fieldset( 'editusergroup',
- $wgOut->parse( wfMsg('editinguser', $username ) ) .
- '<table border="0" align="center"><tr><td>'.
- HTMLSelectGroups('member', $this->mName.'-groupsmember', $groups,true,6).
- '</td><td>'.
- HTMLSelectGroups('available', $this->mName.'-groupsavailable', $groups,true,6,true).
- '</td></tr></table>'."\n".
- $wgOut->parse( wfMsg('userrights-groupshelp') ) .
- wfElement( 'input', array(
- 'type' => 'submit',
- 'name' => 'saveusergroups',
- 'value' => wfMsg( 'saveusergroups' ) ) )
- ));
- $wgOut->addHTML( "</form>\n" );
+ $this->showEditUserGroupsForm( $username, $groups );
+ }
+
+ function showEditUserGroupsForm( $username, $groups ) {
+ global $wgOut, $wgUser;
+ $wgOut->addHTML(
+ Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->action, 'name' => 'editGroup' ) ) .
+ Xml::hidden( 'user-editname', $username ) .
+ Xml::hidden( 'wpEditToken', $wgUser->editToken( $username ) ) .
+ Xml::openElement( 'fieldset' ) .
+ Xml::element( 'legend', array(), wfMsg( 'userrights-editusergroup' ) ) .
+ $wgOut->parse( wfMsg( 'editinguser', $username ) ) .
+ "<table border='0'>
+ <tr>
+ <td></td>
+ <td>
+ <table width='400'>
+ <tr>
+ <td width='50%'>" . HTMLSelectGroups( 'member', $this->mName.'-groupsmember', $groups, true, 6 ) . "</td>
+ <td width='50%'>" . HTMLSelectGroups( 'available', $this->mName.'-groupsavailable', $groups, true, 6, true) . "</td>
+ </tr>
+ </table>
+ </tr>
+ <tr>
+ <td colspan='2'>" .
+ $wgOut->parse( wfMsg('userrights-groupshelp') ) .
+ "</td>
+ </tr>
+ <tr>
+ <td>" .
+ Xml::label( wfMsg( 'userrights-reason' ), 'wpReason' ) .
+ "</td>
+ <td>" .
+ Xml::input( 'user-reason', 60, false, array( 'id' => 'wpReason' ) ) .
+ "</td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>" .
+ Xml::submitButton( wfMsg( 'saveusergroups' ), array( 'name' => 'saveusergroups' ) ) .
+ "</td>
+ </tr>
+ </table>\n" .
+ Xml::closeElement( 'fieldset' ) .
+ Xml::closeElement( 'form' ) . "\n"
+ );
}
} // end class UserrightsForm
?>
diff --git a/includes/SpecialVersion.php b/includes/SpecialVersion.php
index dba694c0..6de2da11 100644
--- a/includes/SpecialVersion.php
+++ b/includes/SpecialVersion.php
@@ -2,10 +2,7 @@
/**#@+
* Give information about the version of MediaWiki, PHP, the DB and extensions
*
- * @package MediaWiki
- * @subpackage SpecialPage
- *
- * @bug 2019, 4531
+ * @addtogroup SpecialPage
*
* @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
* @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason
@@ -50,10 +47,7 @@ class SpecialVersion {
*/
function MediaWikiCredits() {
$version = self::getVersion();
- $dbr =& wfGetDB( DB_SLAVE );
-
- global $wgLanguageNames, $wgLanguageCode;
- $mwlang = $wgLanguageNames[$wgLanguageCode];
+ $dbr = wfGetDB( DB_SLAVE );
$ret =
"__NOTOC__
@@ -110,21 +104,19 @@ class SpecialVersion {
$out .= wfOpenElement('table', array('id' => 'sv-ext') );
foreach ( $extensionTypes as $type => $text ) {
- if ( count( @$wgExtensionCredits[$type] ) ) {
+ if ( isset ( $wgExtensionCredits[$type] ) && count ( $wgExtensionCredits[$type] ) ) {
$out .= $this->openExtType( $text );
usort( $wgExtensionCredits[$type], array( $this, 'compare' ) );
foreach ( $wgExtensionCredits[$type] as $extension ) {
- wfSuppressWarnings();
$out .= $this->formatCredits(
- $extension['name'],
- $extension['version'],
- $extension['author'],
- $extension['url'],
- $extension['description']
+ isset ( $extension['name'] ) ? $extension['name'] : '',
+ isset ( $extension['version'] ) ? $extension['version'] : null,
+ isset ( $extension['author'] ) ? $extension['author'] : '',
+ isset ( $extension['url'] ) ? $extension['url'] : null,
+ isset ( $extension['description'] ) ? $extension['description'] : ''
);
- wfRestoreWarnings();
}
}
}
@@ -195,7 +187,7 @@ class SpecialVersion {
foreach ($myWgHooks as $hook => $hooks)
$ret .= "<tr><td>$hook</td><td>" . $this->listToText( $hooks ) . "</td></tr>\n";
-
+
$ret .= '</table>';
return $ret;
} else
@@ -269,8 +261,6 @@ class SpecialVersion {
/**
* Retrieve the revision number of a Subversion working directory.
*
- * @bug 7335
- *
* @param string $dir
* @return mixed revision number as int, or false if not a SVN checkout
*/
diff --git a/includes/SpecialWantedcategories.php b/includes/SpecialWantedcategories.php
index 05ee7ec0..27a9f176 100644
--- a/includes/SpecialWantedcategories.php
+++ b/includes/SpecialWantedcategories.php
@@ -1,19 +1,13 @@
<?php
/**
- * A querypage to list the most wanted categories
+ * A querypage to list the most wanted categories - implements Special:Wantedcategories
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*
* @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
*/
-
-/**
- * @package MediaWiki
- * @subpackage SpecialPage
- */
class WantedCategoriesPage extends QueryPage {
function getName() { return 'Wantedcategories'; }
@@ -21,7 +15,7 @@ class WantedCategoriesPage extends QueryPage {
function isSyndicated() { return false; }
function getSQL() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
list( $categorylinks, $page ) = $dbr->tableNamesN( 'categorylinks', 'page' );
$name = $dbr->addQuotes( $this->getName() );
return
diff --git a/includes/SpecialWantedpages.php b/includes/SpecialWantedpages.php
index 8e5cee3e..8b700209 100644
--- a/includes/SpecialWantedpages.php
+++ b/includes/SpecialWantedpages.php
@@ -1,14 +1,12 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
+ * implements Special:Wantedpages
+ * @addtogroup SpecialPage
*/
class WantedPagesPage extends QueryPage {
var $nlinks;
@@ -30,7 +28,7 @@ class WantedPagesPage extends QueryPage {
function getSQL() {
global $wgWantedPagesThreshold;
$count = $wgWantedPagesThreshold - 1;
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$pagelinks = $dbr->tableName( 'pagelinks' );
$page = $dbr->tableName( 'page' );
return
diff --git a/includes/SpecialWatchlist.php b/includes/SpecialWatchlist.php
index 33e19a2b..2e660bd5 100644
--- a/includes/SpecialWatchlist.php
+++ b/includes/SpecialWatchlist.php
@@ -1,14 +1,13 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
*
*/
-require_once( 'SpecialRecentchanges.php' );
+require_once( dirname(__FILE__) . '/SpecialRecentchanges.php' );
/**
* Constructor
@@ -16,12 +15,12 @@ require_once( 'SpecialRecentchanges.php' );
* @param $par Parameter passed to the page
*/
function wfSpecialWatchlist( $par ) {
- global $wgUser, $wgOut, $wgLang, $wgMemc, $wgRequest, $wgContLang;
+ global $wgUser, $wgOut, $wgLang, $wgRequest, $wgContLang;
global $wgRCShowWatchingUsers, $wgEnotifWatchlist, $wgShowUpdatedMarker;
global $wgEnotifWatchlist;
$fname = 'wfSpecialWatchlist';
- $skin =& $wgUser->getSkin();
+ $skin = $wgUser->getSkin();
$specialTitle = SpecialPage::getTitleFor( 'Watchlist' );
$wgOut->setRobotPolicy( 'noindex,nofollow' );
@@ -86,25 +85,25 @@ function wfSpecialWatchlist( $par ) {
# Deleting items from watchlist
if(($action == 'submit') && isset($remove) && is_array($id)) {
$wgOut->addWikiText( wfMsg( 'removingchecked' ) );
- $wgOut->addHTML( '<p>' );
+ $wgOut->addHTML( "<ul id=\"mw-unwatch-list\">\n" );
foreach($id as $one) {
$t = Title::newFromURL( $one );
if( !is_null( $t ) ) {
$wl = WatchedItem::fromUserTitle( $wgUser, $t );
if( $wl->removeWatch() === false ) {
- $wgOut->addHTML( wfMsg( 'couldntremove', htmlspecialchars($one) ) . "<br />\n" );
+ $wgOut->addHTML( '<li class="mw-unwatch-failure">' . wfMsg( 'couldntremove', htmlspecialchars($one) ) . "</li>\n" );
} else {
wfRunHooks('UnwatchArticle', array(&$wgUser, new Article($t)));
- $wgOut->addHTML( '(' . htmlspecialchars($one) . ')<br />' );
+ $wgOut->addHTML( '<li class="mw-unwatch-success">[[' . htmlspecialchars($one) . "]]</li>\n" );
}
} else {
- $wgOut->addHTML( wfMsg( 'iteminvalidname', htmlspecialchars($one) ) . "<br />\n" );
+ $wgOut->addHTML( '<li class="mw-unwatch-invalid">' . wfMsg( 'iteminvalidname', htmlspecialchars($one) ) . "</li>\n" );
}
}
- $wgOut->addHTML( "</p>\n<p>" . wfMsg( 'wldone' ) . "</p>\n" );
+ $wgOut->addHTML( "</ul>\n<p>" . wfMsg( 'wldone' ) . "</p>\n" );
}
- $dbr =& wfGetDB( DB_SLAVE, 'watchlist' );
+ $dbr = wfGetDB( DB_SLAVE, 'watchlist' );
list( $page, $watchlist, $recentchanges ) = $dbr->tableNamesN( 'page', 'watchlist', 'recentchanges' );
$sql = "SELECT COUNT(*) AS n FROM $watchlist WHERE wl_user=$uid";
@@ -158,7 +157,7 @@ function wfSpecialWatchlist( $par ) {
/* Edit watchlist form */
if($wgRequest->getBool('edit') || $par == 'edit' ) {
- $wgOut->addWikiText( wfMsg( 'watchlistcontains', $wgLang->formatNum( $nitems ) ) .
+ $wgOut->addWikiText( wfMsgExt( 'watchlistcontains', array( 'parseinline' ), $wgLang->formatNum( $nitems ) ) .
"\n\n" . wfMsg( 'watcheditlist' ) );
$wgOut->addHTML( '<form action=\'' .
@@ -258,7 +257,8 @@ function wfSpecialWatchlist( $par ) {
$andLatest='';
$limitWatchlist = 'LIMIT ' . intval( $wgUser->getOption( 'wllimit' ) );
} else {
- $andLatest= 'AND rc_this_oldid=page_latest';
+ # Top log Ids for a page are not stored
+ $andLatest= 'AND (rc_this_oldid=page_latest OR rc_type=' . RC_LOG . ') ';
$limitWatchlist = '';
}
@@ -299,10 +299,10 @@ function wfSpecialWatchlist( $par ) {
$wgOut->addHTML( "<hr />\n" );
if($days >= 1) {
- $wgOut->addWikiText( wfMsg( 'rcnote', $wgLang->formatNum( $numRows ),
+ $wgOut->addWikiText( wfMsgExt( 'rcnote', array( 'parseinline' ), $wgLang->formatNum( $numRows ),
$wgLang->formatNum( $days ), $wgLang->timeAndDate( wfTimestampNow(), true ) ) . '<br />' , false );
} elseif($days > 0) {
- $wgOut->addWikiText( wfMsg( 'wlnote', $wgLang->formatNum( $numRows ),
+ $wgOut->addWikiText( wfMsgExt( 'wlnote', array( 'parseinline' ), $wgLang->formatNum( $numRows ),
$wgLang->formatNum( round($days*24) ) ) . '<br />' , false );
}
@@ -353,6 +353,18 @@ function wfSpecialWatchlist( $par ) {
/* End bottom header */
+ /* Do link batch query */
+ $linkBatch = new LinkBatch;
+ while ( $row = $dbr->fetchObject( $res ) ) {
+ $userNameUnderscored = str_replace( ' ', '_', $row->rc_user_text );
+ if ( $row->rc_user != 0 ) {
+ $linkBatch->add( NS_USER, $userNameUnderscored );
+ }
+ $linkBatch->add( NS_USER_TALK, $userNameUnderscored );
+ }
+ $linkBatch->execute();
+ $dbr->dataSeek( $res, 0 );
+
$list = ChangesList::newFromUser( $wgUser );
$s = $list->beginRecentChangesList();
@@ -435,7 +447,7 @@ function wlCutoffLinks( $days, $page = 'Watchlist', $options = array() ) {
* @return integer
*/
function wlCountItems( &$user, $talk = true ) {
- $dbr =& wfGetDB( DB_SLAVE, 'watchlist' );
+ $dbr = wfGetDB( DB_SLAVE, 'watchlist' );
# Fetch the raw count
$res = $dbr->select( 'watchlist', 'COUNT(*) AS count', array( 'wl_user' => $user->mId ), 'wlCountItems' );
@@ -471,7 +483,7 @@ function wlHandleClear( &$out, &$request, $par ) {
# See if we're clearing or confirming
if( $request->wasPosted() && $wgUser->matchEditToken( $request->getText( 'token' ), 'clearwatchlist' ) ) {
# Clearing, so do it and report the result
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'watchlist', array( 'wl_user' => $wgUser->mId ), 'wlHandleClear' );
$out->addWikiText( wfMsgExt( 'watchlistcleardone', array( 'parsemag', 'escape'), $wgLang->formatNum( $count ) ) );
$out->returnToMain();
diff --git a/includes/SpecialWhatlinkshere.php b/includes/SpecialWhatlinkshere.php
index bed783f8..277e279f 100644
--- a/includes/SpecialWhatlinkshere.php
+++ b/includes/SpecialWhatlinkshere.php
@@ -1,8 +1,7 @@
<?php
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
/**
@@ -15,15 +14,21 @@ function wfSpecialWhatlinkshere($par = NULL) {
$page->execute();
}
+/**
+ * implements Special:Whatlinkshere
+ * @addtogroup SpecialPage
+ */
class WhatLinksHerePage {
var $request, $par;
- var $limit, $from, $dir, $target;
+ var $limit, $from, $back, $target;
var $selfTitle, $skin;
+ private $namespace;
+
function WhatLinksHerePage( &$request, $par = null ) {
global $wgUser;
$this->request =& $request;
- $this->skin =& $wgUser->getSkin();
+ $this->skin = $wgUser->getSkin();
$this->par = $par;
}
@@ -35,10 +40,7 @@ class WhatLinksHerePage {
$this->limit = 50;
}
$this->from = $this->request->getInt( 'from' );
- $this->dir = $this->request->getText( 'dir', 'next' );
- if ( $this->dir != 'prev' ) {
- $this->dir = 'next';
- }
+ $this->back = $this->request->getInt( 'back' );
$targetString = isset($this->par) ? $this->par : $this->request->getVal( 'target' );
@@ -59,7 +61,7 @@ class WhatLinksHerePage {
$wgOut->addHTML( wfMsg( 'whatlinkshere-barrow' ) . ' ' .$this->skin->makeLinkObj($this->target, '', 'redirect=no' )."<br />\n");
- $this->showIndirectLinks( 0, $this->target, $this->limit, $this->from, $this->dir );
+ $this->showIndirectLinks( 0, $this->target, $this->limit, $this->from, $this->back );
}
/**
@@ -67,20 +69,21 @@ class WhatLinksHerePage {
* @param Title $target Target title
* @param int $limit Number of entries to display
* @param Title $from Display from this article ID
- * @param string $dir 'next' or 'prev', whether $fromTitle is the start or end of the list
+ * @param Title $back Display from this article ID at backwards scrolling
* @private
*/
- function showIndirectLinks( $level, $target, $limit, $from = 0, $dir = 'next' ) {
+ function showIndirectLinks( $level, $target, $limit, $from = 0, $back = 0 ) {
global $wgOut;
$fname = 'WhatLinksHerePage::showIndirectLinks';
+ $dbr = wfGetDB( DB_READ );
+ $options = array();
- $dbr =& wfGetDB( DB_READ );
-
- // Some extra validation
- $from = intval( $from );
- if ( !$from && $dir == 'prev' ) {
- // Before start? No make sense
- $dir = 'next';
+ $ns = $this->request->getIntOrNull( 'namespace' );
+ if ( isset( $ns ) ) {
+ $options['namespace'] = $ns;
+ $this->setNamespace( $options['namespace'] );
+ } else {
+ $options['namespace'] = '';
}
// Make the query
@@ -96,18 +99,18 @@ class WhatLinksHerePage {
'tl_title' => $target->getDBkey(),
);
+ if ( $this->namespace !== null ){
+ $plConds['page_namespace'] = (int)$this->namespace;
+ $tlConds['page_namespace'] = (int)$this->namespace;
+ }
+
if ( $from ) {
- if ( 'prev' == $dir ) {
- $offsetCond = "page_id < $from";
- $options = array( 'ORDER BY page_id DESC' );
- } else {
- $offsetCond = "page_id >= $from";
- $options = array( 'ORDER BY page_id' );
- }
+ $offsetCond = "page_id >= $from";
} else {
$offsetCond = false;
- $options = array( 'ORDER BY page_id,is_template DESC' );
}
+ $options['ORDER BY'] = 'page_id';
+
// Read an extra row as an at-end check
$queryLimit = $limit + 1;
$options['LIMIT'] = $queryLimit;
@@ -121,14 +124,37 @@ class WhatLinksHerePage {
$plConds, $fname, $options );
$tlRes = $dbr->select( array( 'templatelinks', 'page' ), $fields,
$tlConds, $fname, $options );
-
if ( !$dbr->numRows( $plRes ) && !$dbr->numRows( $tlRes ) ) {
- if ( 0 == $level ) {
+ if ( 0 == $level && !isset( $this->namespace ) ) {
+ // really no links to here
$wgOut->addWikiText( wfMsg( 'nolinkshere', $this->target->getPrefixedText() ) );
+ } elseif ( 0 == $level && isset( $this->namespace ) ) {
+ // no links from requested namespace to here
+ $options = array(); // reinitialize for a further namespace search
+ $options['namespace'] = $this->namespace;
+ $options['target'] = $this->target->getPrefixedText();
+ list( $options['limit'], $options['offset']) = wfCheckLimits();
+ $wgOut->addHTML( $this->whatlinkshereForm( $options ) );
+ $wgOut->addWikiText( wfMsg( 'nolinkshere-ns', $this->target->getPrefixedText() ) );
}
return;
}
+ $options = array();
+ list( $options['limit'], $options['offset']) = wfCheckLimits();
+ if ( ( $ns = $this->request->getVal( 'namespace', null ) ) !== null && $ns !== '' && ctype_digit($ns) ) {
+ $options['namespace'] = intval( $ns );
+ $this->setNamespace( $options['namespace'] );
+ } else {
+ $options['namespace'] = '';
+ $this->setNamespace( null );
+ }
+ $options['offset'] = $this->request->getVal( 'offset' );
+ /* Offset must be an integral. */
+ if ( !strlen( $options['offset'] ) || !preg_match( '/^[0-9]+$/', $options['offset'] ) )
+ $options['offset'] = '';
+ $options['target'] = $this->target->getPrefixedDBkey();
+
// Read the rows into an array and remove duplicates
// templatelinks comes second so that the templatelinks row overwrites the
// pagelinks row, so we get (inclusion) rather than nothing
@@ -150,46 +176,27 @@ class WhatLinksHerePage {
$numRows = count( $rows );
// Work out the start and end IDs, for prev/next links
- if ( $dir == 'prev' ) {
- // Descending order
- if ( $numRows > $limit ) {
- // More rows available before these ones
- // Get the ID from the next row past the end of the displayed set
- $prevId = $rows[$limit]->page_id;
- // Remove undisplayed rows
- $rows = array_slice( $rows, 0, $limit );
- } else {
- // No more rows available before
- $prevId = 0;
- }
- // Assume that the ID specified in $from exists, so there must be another page
- $nextId = $from;
-
- // Reverse order ready for display
- $rows = array_reverse( $rows );
+ if ( $numRows > $limit ) {
+ // More rows available after these ones
+ // Get the ID from the last row in the result set
+ $nextId = $rows[$limit]->page_id;
+ // Remove undisplayed rows
+ $rows = array_slice( $rows, 0, $limit );
} else {
- // Ascending
- if ( $numRows > $limit ) {
- // More rows available after these ones
- // Get the ID from the last row in the result set
- $nextId = $rows[$limit]->page_id;
- // Remove undisplayed rows
- $rows = array_slice( $rows, 0, $limit );
- } else {
- // No more rows after
- $nextId = false;
- }
- $prevId = $from;
+ // No more rows after
+ $nextId = false;
}
+ $prevId = $from;
- if ( 0 == $level ) {
+ if ( $level == 0 ) {
+ $wgOut->addHTML( $this->whatlinkshereForm( $options ) );
$wgOut->addWikiText( wfMsg( 'linkshere', $this->target->getPrefixedText() ) );
}
$isredir = wfMsg( 'isredirect' );
$istemplate = wfMsg( 'istemplate' );
if( $level == 0 ) {
- $prevnext = $this->getPrevNext( $limit, $prevId, $nextId );
+ $prevnext = $this->getPrevNext( $limit, $prevId, $nextId, $options['namespace'] );
$wgOut->addHTML( $prevnext );
}
@@ -240,16 +247,21 @@ class WhatLinksHerePage {
function getPrevNext( $limit, $prevId, $nextId ) {
global $wgLang;
$fmtLimit = $wgLang->formatNum( $limit );
- $prev = wfMsg( 'prevn', $fmtLimit );
- $next = wfMsg( 'nextn', $fmtLimit );
+ $prev = wfMsgExt( 'whatlinkshere-prev', array( 'parsemag', 'escape' ), $fmtLimit );
+ $next = wfMsgExt( 'whatlinkshere-next', array( 'parsemag', 'escape' ), $fmtLimit );
+
+ $nsText = '';
+ if( is_int($this->namespace) ) {
+ $nsText = "&namespace={$this->namespace}";
+ }
if ( 0 != $prevId ) {
- $prevLink = $this->makeSelfLink( $prev, "limit={$limit}&from={$prevId}&dir=prev" );
+ $prevLink = $this->makeSelfLink( $prev, "limit={$limit}&from={$this->back}{$nsText}" );
} else {
$prevLink = $prev;
}
if ( 0 != $nextId ) {
- $nextLink = $this->makeSelfLink( $next, "limit={$limit}&from={$nextId}" );
+ $nextLink = $this->makeSelfLink( $next, "limit={$limit}&from={$nextId}&back={$prevId}{$nsText}" );
} else {
$nextLink = $next;
}
@@ -262,12 +274,42 @@ class WhatLinksHerePage {
return wfMsg( 'viewprevnext', $prevLink, $nextLink, $nums );
}
- function numLink( $limit, $from ) {
+ function numLink( $limit, $from, $ns = null ) {
global $wgLang;
$query = "limit={$limit}&from={$from}";
+ if( is_int($this->namespace) ) { $query .= "&namespace={$this->namespace}";}
$fmtLimit = $wgLang->formatNum( $limit );
return $this->makeSelfLink( $fmtLimit, $query );
}
+
+ function whatlinkshereForm( $options ) {
+ global $wgScript, $wgTitle;
+
+ $options['title'] = $wgTitle->getPrefixedText();
+
+ $f = Xml::openElement( 'form', array( 'method' => 'get', 'action' => "$wgScript" ) ) .
+ '<fieldset>' .
+ Xml::element( 'legend', array(), wfMsg( 'whatlinkshere' ) );
+
+ foreach ( $options as $name => $value ) {
+ if( $name === 'namespace') continue;
+ $f .= "\t" . Xml::hidden( $name, $value ). "\n";
+ }
+
+ $f .= Xml::label( wfMsg( 'namespace' ), 'namespace' ) . ' ' .
+ Xml::namespaceSelector( $options['namespace'], '' ) .
+ Xml::submitButton( wfMsg( 'allpagessubmit' ) ) .
+ '</fieldset>' .
+ Xml::closeElement( 'form' ) . "\n";
+
+ return $f;
+ }
+
+ /** Set the namespace we are filtering on */
+ private function setNamespace( $ns ) {
+ $this->namespace = $ns;
+ }
+
}
?>
diff --git a/includes/SpecialWithoutinterwiki.php b/includes/SpecialWithoutinterwiki.php
new file mode 100644
index 00000000..e5341d5d
--- /dev/null
+++ b/includes/SpecialWithoutinterwiki.php
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * Special page lists pages without language links
+ *
+ * @package MediaWiki
+ * @addtogroup SpecialPage
+ * @author Rob Church <robchur@gmail.com>
+ */
+class WithoutInterwikiPage extends PageQueryPage {
+
+ function getName() {
+ return 'Withoutinterwiki';
+ }
+
+ function getPageHeader() {
+ return '<p>' . wfMsgHtml( 'withoutinterwiki-header' ) . '</p>';
+ }
+
+ function sortDescending() {
+ return false;
+ }
+
+ function isExpensive() {
+ return true;
+ }
+
+ function isSyndicated() {
+ return false;
+ }
+
+ function getSQL() {
+ $dbr = wfGetDB( DB_SLAVE );
+ list( $page, $langlinks ) = $dbr->tableNamesN( 'page', 'langlinks' );
+ return
+ "SELECT 'Withoutinterwiki' AS type,
+ page_namespace AS namespace,
+ page_title AS title,
+ page_title AS value
+ FROM $page
+ LEFT JOIN $langlinks
+ ON ll_from = page_id
+ WHERE ll_title IS NULL
+ AND page_namespace=" . NS_MAIN . "
+ AND page_is_redirect = 0";
+ }
+
+}
+
+function wfSpecialWithoutinterwiki() {
+ list( $limit, $offset ) = wfCheckLimits();
+ $wip = new WithoutInterwikiPage();
+ $wip->doQuery( $offset, $limit );
+}
+
+?>
diff --git a/includes/SquidUpdate.php b/includes/SquidUpdate.php
index 2e2a4a5d..700fc8ef 100644
--- a/includes/SquidUpdate.php
+++ b/includes/SquidUpdate.php
@@ -1,17 +1,15 @@
<?php
/**
* See deferred.txt
- * @package MediaWiki
*/
/**
*
- * @package MediaWiki
*/
class SquidUpdate {
var $urlArr, $mMaxTitles;
- function SquidUpdate( $urlArr = Array(), $maxTitles = false ) {
+ function __construct( $urlArr = Array(), $maxTitles = false ) {
global $wgMaxSquidPurgeTitles;
if ( $maxTitles === false ) {
$this->mMaxTitles = $wgMaxSquidPurgeTitles;
@@ -29,7 +27,7 @@ class SquidUpdate {
wfProfileIn( $fname );
# Get a list of URLs linking to this page
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( array( 'links', 'page' ),
array( 'page_namespace', 'page_title' ),
array(
diff --git a/includes/StringUtils.php b/includes/StringUtils.php
index 0090604d..9a451aa8 100644
--- a/includes/StringUtils.php
+++ b/includes/StringUtils.php
@@ -1,5 +1,7 @@
<?php
-
+/**
+ * A collection of static methods to play with strings.
+ */
class StringUtils {
/**
* Perform an operation equivalent to
diff --git a/includes/StubObject.php b/includes/StubObject.php
index 1501d963..894550cd 100644
--- a/includes/StubObject.php
+++ b/includes/StubObject.php
@@ -24,7 +24,7 @@ class StubObject {
}
static function isRealObject( $obj ) {
- return is_object( $obj ) && !is_a( $obj, 'StubObject' );
+ return is_object( $obj ) && !($obj instanceof StubObject);
}
function _call( $name, $args ) {
@@ -35,7 +35,7 @@ class StubObject {
function _newObject() {
return wfCreateObject( $this->mClass, $this->mParams );
}
-
+
function __call( $name, $args ) {
return $this->_call( $name, $args );
}
@@ -100,7 +100,8 @@ class StubUserLang extends StubObject {
}
# Validate $code
- if( empty( $code ) || !preg_match( '/^[a-z]+(-[a-z]+)?$/', $code ) ) {
+ if( empty( $code ) || !preg_match( '/^[a-z-]+$/', $code ) ) {
+ wfDebug( "Invalid user language code\n" );
$code = $wgContLanguageCode;
}
diff --git a/includes/Title.php b/includes/Title.php
index 56414c8a..0ff2e807 100644
--- a/includes/Title.php
+++ b/includes/Title.php
@@ -2,11 +2,12 @@
/**
* See title.txt
*
- * @package MediaWiki
*/
/** */
-require_once( 'normal/UtfNormal.php' );
+if ( !class_exists( 'UtfNormal' ) ) {
+ require_once( dirname(__FILE__) . '/normal/UtfNormal.php' );
+}
define ( 'GAID_FOR_UPDATE', 1 );
@@ -17,12 +18,14 @@ define ( 'GAID_FOR_UPDATE', 1 );
# reset the cache.
define( 'MW_TITLECACHE_MAX', 1000 );
+# Constants for pr_cascade bitfield
+define( 'CASCADE', 1 );
+
/**
* Title class
* - Represents a title, which may contain an interwiki designation or namespace
* - Can fetch various kinds of data from the database, albeit inefficiently.
*
- * @package MediaWiki
*/
class Title {
/**
@@ -41,21 +44,24 @@ class Title {
* @private
*/
- var $mTextform; # Text form (spaces not underscores) of the main part
- var $mUrlform; # URL-encoded form of the main part
- var $mDbkeyform; # Main part with underscores
- var $mNamespace; # Namespace index, i.e. one of the NS_xxxx constants
- var $mInterwiki; # Interwiki prefix (or null string)
- var $mFragment; # Title fragment (i.e. the bit after the #)
- var $mArticleID; # Article ID, fetched from the link cache on demand
- var $mLatestID; # ID of most recent revision
- var $mRestrictions; # Array of groups allowed to edit this article
- # Only null or "sysop" are supported
- var $mRestrictionsLoaded; # Boolean for initialisation on demand
- var $mPrefixedText; # Text form including namespace/interwiki, initialised on demand
- var $mDefaultNamespace; # Namespace index when there is no namespace
- # Zero except in {{transclusion}} tags
- var $mWatched; # Is $wgUser watching this page? NULL if unfilled, accessed through userIsWatching()
+ var $mTextform; # Text form (spaces not underscores) of the main part
+ var $mUrlform; # URL-encoded form of the main part
+ var $mDbkeyform; # Main part with underscores
+ var $mNamespace; # Namespace index, i.e. one of the NS_xxxx constants
+ var $mInterwiki; # Interwiki prefix (or null string)
+ var $mFragment; # Title fragment (i.e. the bit after the #)
+ var $mArticleID; # Article ID, fetched from the link cache on demand
+ var $mLatestID; # ID of most recent revision
+ var $mRestrictions; # Array of groups allowed to edit this article
+ var $mCascadeRestriction; # Cascade restrictions on this page to included templates and images?
+ var $mRestrictionsExpiry; # When do the restrictions on this page expire?
+ var $mHasCascadingRestrictions; # Are cascading restrictions in effect on this page?
+ var $mCascadeRestrictionSources;# Where are the cascading restrictions coming from on this page?
+ var $mRestrictionsLoaded; # Boolean for initialisation on demand
+ var $mPrefixedText; # Text form including namespace/interwiki, initialised on demand
+ var $mDefaultNamespace; # Namespace index when there is no namespace
+ # Zero except in {{transclusion}} tags
+ var $mWatched; # Is $wgUser watching this page? NULL if unfilled, accessed through userIsWatching()
/**#@-*/
@@ -63,7 +69,7 @@ class Title {
* Constructor
* @private
*/
- /* private */ function Title() {
+ /* private */ function __construct() {
$this->mInterwiki = $this->mUrlform =
$this->mTextform = $this->mDbkeyform = '';
$this->mArticleID = -1;
@@ -75,6 +81,7 @@ class Title {
$this->mDefaultNamespace = NS_MAIN;
$this->mWatched = NULL;
$this->mLatestID = false;
+ $this->mOldRestrictions = false;
}
/**
@@ -83,10 +90,8 @@ class Title {
* instead of spaces, possibly including namespace and
* interwiki prefixes
* @return Title the new object, or NULL on an error
- * @static
- * @access public
*/
- /* static */ function newFromDBkey( $key ) {
+ public static function newFromDBkey( $key ) {
$t = new Title();
$t->mDbkeyform = $key;
if( $t->secureAndSplit() )
@@ -105,8 +110,6 @@ class Title {
* @param int $defaultNamespace the namespace to use if
* none is specified by a prefix
* @return Title the new object, or NULL on an error
- * @static
- * @access public
*/
public static function newFromText( $text, $defaultNamespace = NS_MAIN ) {
if( is_object( $text ) ) {
@@ -157,8 +160,6 @@ class Title {
* the given title's length does not exceed the maximum.
* @param string $url the title, as might be taken from a URL
* @return Title the new object, or NULL on an error
- * @static
- * @access public
*/
public static function newFromURL( $url ) {
global $wgLegalTitleChars;
@@ -187,12 +188,10 @@ class Title {
*
* @param int $id the page_id corresponding to the Title to create
* @return Title the new object, or NULL on an error
- * @access public
- * @static
*/
public static function newFromID( $id ) {
$fname = 'Title::newFromID';
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow( 'page', array( 'page_namespace', 'page_title' ),
array( 'page_id' => $id ), $fname );
if ( $row !== false ) {
@@ -206,8 +205,8 @@ class Title {
/**
* Make an array of titles from an array of IDs
*/
- function newFromIDs( $ids ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ public static function newFromIDs( $ids ) {
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'page', array( 'page_namespace', 'page_title' ),
'page_id IN (' . $dbr->makeList( $ids ) . ')', __METHOD__ );
@@ -228,8 +227,6 @@ class Title {
* @param int $ns the namespace of the article
* @param string $title the unprefixed database key form
* @return Title the new object
- * @static
- * @access public
*/
public static function &makeTitle( $ns, $title ) {
$t = new Title();
@@ -251,8 +248,6 @@ class Title {
* @param int $ns the namespace of the article
* @param string $title the database key form
* @return Title the new object, or NULL on an error
- * @static
- * @access public
*/
public static function makeTitleSafe( $ns, $title ) {
$t = new Title();
@@ -266,10 +261,7 @@ class Title {
/**
* Create a new Title for the Main Page
- *
- * @static
* @return Title the new object
- * @access public
*/
public static function newMainPage() {
return Title::newFromText( wfMsgForContent( 'mainpage' ) );
@@ -280,8 +272,6 @@ class Title {
* @param string $text the redirect title text
* @return Title the new object, or NULL if the text is not a
* valid redirect
- * @static
- * @access public
*/
public static function newFromRedirect( $text ) {
$mwRedir = MagicWord::get( 'redirect' );
@@ -320,7 +310,7 @@ class Title {
*/
function nameOf( $id ) {
$fname = 'Title::nameOf';
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$s = $dbr->selectRow( 'page', array( 'page_namespace','page_title' ), array( 'page_id' => $id ), $fname );
if ( $s === false ) { return NULL; }
@@ -332,8 +322,6 @@ class Title {
/**
* Get a regex character class describing the legal characters in a link
* @return string the list of characters, not delimited
- * @static
- * @access public
*/
public static function legalChars() {
global $wgLegalTitleChars;
@@ -349,7 +337,7 @@ class Title {
* @return string a stripped-down title string ready for the
* search index
*/
- /* static */ function indexTitle( $ns, $title ) {
+ public static function indexTitle( $ns, $title ) {
global $wgContLang;
$lc = SearchEngine::legalSearchChars() . '&#;';
@@ -388,9 +376,8 @@ class Title {
* @return the associated URL, containing "$1", which should be
* replaced by an article title
* @static (arguably)
- * @access public
*/
- function getInterwikiLink( $key ) {
+ public function getInterwikiLink( $key ) {
global $wgMemc, $wgInterwikiExpiry;
global $wgInterwikiCache, $wgContLang;
$fname = 'Title::getInterwikiLink';
@@ -413,7 +400,7 @@ class Title {
return $s->iw_url;
}
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'interwiki',
array( 'iw_url', 'iw_local', 'iw_trans' ),
array( 'iw_prefix' => $key ), $fname );
@@ -441,9 +428,8 @@ class Title {
* More logic is explained in DefaultSettings
*
* @return string URL of interwiki site
- * @access public
*/
- function getInterwikiCached( $key ) {
+ public static function getInterwikiCached( $key ) {
global $wgInterwikiCache, $wgInterwikiScopes, $wgInterwikiFallbackSite;
static $db, $site;
@@ -484,9 +470,8 @@ class Title {
*
* @return bool TRUE if this is an in-project interwiki link
* or a wikilink, FALSE otherwise
- * @access public
*/
- function isLocal() {
+ public function isLocal() {
if ( $this->mInterwiki != '' ) {
# Make sure key is loaded into cache
$this->getInterwikiLink( $this->mInterwiki );
@@ -502,9 +487,8 @@ class Title {
* this project and is transcludable.
*
* @return bool TRUE if this is transcludable
- * @access public
*/
- function isTrans() {
+ public function isTrans() {
if ($this->mInterwiki == '')
return false;
# Make sure key is loaded into cache
@@ -514,60 +498,6 @@ class Title {
}
/**
- * Update the page_touched field for an array of title objects
- * @todo Inefficient unless the IDs are already loaded into the
- * link cache
- * @param array $titles an array of Title objects to be touched
- * @param string $timestamp the timestamp to use instead of the
- * default current time
- * @static
- * @access public
- */
- function touchArray( $titles, $timestamp = '' ) {
-
- if ( count( $titles ) == 0 ) {
- return;
- }
- $dbw =& wfGetDB( DB_MASTER );
- if ( $timestamp == '' ) {
- $timestamp = $dbw->timestamp();
- }
- /*
- $page = $dbw->tableName( 'page' );
- $sql = "UPDATE $page SET page_touched='{$timestamp}' WHERE page_id IN (";
- $first = true;
-
- foreach ( $titles as $title ) {
- if ( $wgUseFileCache ) {
- $cm = new HTMLFileCache($title);
- @unlink($cm->fileCacheName());
- }
-
- if ( ! $first ) {
- $sql .= ',';
- }
- $first = false;
- $sql .= $title->getArticleID();
- }
- $sql .= ')';
- if ( ! $first ) {
- $dbw->query( $sql, 'Title::touchArray' );
- }
- */
- // hack hack hack -- brion 2005-07-11. this was unfriendly to db.
- // do them in small chunks:
- $fname = 'Title::touchArray';
- foreach( $titles as $title ) {
- $dbw->update( 'page',
- array( 'page_touched' => $timestamp ),
- array(
- 'page_namespace' => $title->getNamespace(),
- 'page_title' => $title->getDBkey() ),
- $fname );
- }
- }
-
- /**
* Escape a text fragment, say from a link, for a URL
*/
static function escapeFragmentForURL( $fragment ) {
@@ -588,33 +518,28 @@ class Title {
/**
* Get the text form (spaces not underscores) of the main part
* @return string
- * @access public
*/
- function getText() { return $this->mTextform; }
+ public function getText() { return $this->mTextform; }
/**
* Get the URL-encoded form of the main part
* @return string
- * @access public
*/
- function getPartialURL() { return $this->mUrlform; }
+ public function getPartialURL() { return $this->mUrlform; }
/**
* Get the main part with underscores
* @return string
- * @access public
*/
- function getDBkey() { return $this->mDbkeyform; }
+ public function getDBkey() { return $this->mDbkeyform; }
/**
* Get the namespace index, i.e. one of the NS_xxxx constants
* @return int
- * @access public
*/
- function getNamespace() { return $this->mNamespace; }
+ public function getNamespace() { return $this->mNamespace; }
/**
* Get the namespace text
* @return string
- * @access public
*/
- function getNsText() {
+ public function getNsText() {
global $wgContLang, $wgCanonicalNamespaceNames;
if ( '' != $this->mInterwiki ) {
@@ -633,9 +558,8 @@ class Title {
/**
* Get the namespace text of the subject (rather than talk) page
* @return string
- * @access public
*/
- function getSubjectNsText() {
+ public function getSubjectNsText() {
global $wgContLang;
return $wgContLang->getNsText( Namespace::getSubject( $this->mNamespace ) );
}
@@ -644,38 +568,34 @@ class Title {
* Get the namespace text of the talk page
* @return string
*/
- function getTalkNsText() {
+ public function getTalkNsText() {
global $wgContLang;
return( $wgContLang->getNsText( Namespace::getTalk( $this->mNamespace ) ) );
}
-
+
/**
* Could this title have a corresponding talk page?
* @return bool
*/
- function canTalk() {
+ public function canTalk() {
return( Namespace::canTalk( $this->mNamespace ) );
}
-
+
/**
* Get the interwiki prefix (or null string)
* @return string
- * @access public
*/
- function getInterwiki() { return $this->mInterwiki; }
+ public function getInterwiki() { return $this->mInterwiki; }
/**
* Get the Title fragment (i.e. the bit after the #) in text form
* @return string
- * @access public
*/
- function getFragment() { return $this->mFragment; }
+ public function getFragment() { return $this->mFragment; }
/**
* Get the fragment in URL form, including the "#" character if there is one
- *
* @return string
- * @access public
*/
- function getFragmentForURL() {
+ public function getFragmentForURL() {
if ( $this->mFragment == '' ) {
return '';
} else {
@@ -685,16 +605,15 @@ class Title {
/**
* Get the default namespace index, for when there is no namespace
* @return int
- * @access public
*/
- function getDefaultNamespace() { return $this->mDefaultNamespace; }
+ public function getDefaultNamespace() { return $this->mDefaultNamespace; }
/**
* Get title for search index
* @return string a stripped-down title string ready for the
* search index
*/
- function getIndexTitle() {
+ public function getIndexTitle() {
return Title::indexTitle( $this->mNamespace, $this->mTextform );
}
@@ -702,9 +621,8 @@ class Title {
* Get the prefixed database key form
* @return string the prefixed title, with underscores and
* any interwiki and namespace prefixes
- * @access public
*/
- function getPrefixedDBkey() {
+ public function getPrefixedDBkey() {
$s = $this->prefix( $this->mDbkeyform );
$s = str_replace( ' ', '_', $s );
return $s;
@@ -714,9 +632,8 @@ class Title {
* Get the prefixed title with spaces.
* This is the form usually used for display
* @return string the prefixed title, with spaces
- * @access public
*/
- function getPrefixedText() {
+ public function getPrefixedText() {
if ( empty( $this->mPrefixedText ) ) { // FIXME: bad usage of empty() ?
$s = $this->prefix( $this->mTextform );
$s = str_replace( '_', ' ', $s );
@@ -730,9 +647,8 @@ class Title {
* (part beginning with '#')
* @return string the prefixed title, with spaces and
* the fragment, including '#'
- * @access public
*/
- function getFullText() {
+ public function getFullText() {
$text = $this->getPrefixedText();
if( '' != $this->mFragment ) {
$text .= '#' . $this->mFragment;
@@ -744,7 +660,7 @@ class Title {
* Get the base name, i.e. the leftmost parts before the /
* @return string Base name
*/
- function getBaseText() {
+ public function getBaseText() {
global $wgNamespacesWithSubpages;
if( isset( $wgNamespacesWithSubpages[ $this->mNamespace ] ) && $wgNamespacesWithSubpages[ $this->mNamespace ] ) {
$parts = explode( '/', $this->getText() );
@@ -761,7 +677,7 @@ class Title {
* Get the lowest-level subpage name, i.e. the rightmost part after /
* @return string Subpage name
*/
- function getSubpageText() {
+ public function getSubpageText() {
global $wgNamespacesWithSubpages;
if( isset( $wgNamespacesWithSubpages[ $this->mNamespace ] ) && $wgNamespacesWithSubpages[ $this->mNamespace ] ) {
$parts = explode( '/', $this->mTextform );
@@ -770,12 +686,12 @@ class Title {
return( $this->mTextform );
}
}
-
+
/**
* Get a URL-encoded form of the subpage text
* @return string URL-encoded subpage name
*/
- function getSubpageUrlForm() {
+ public function getSubpageUrlForm() {
$text = $this->getSubpageText();
$text = wfUrlencode( str_replace( ' ', '_', $text ) );
$text = str_replace( '%28', '(', str_replace( '%29', ')', $text ) ); # Clean up the URL; per below, this might not be safe
@@ -785,9 +701,8 @@ class Title {
/**
* Get a URL-encoded title (not an actual URL) including interwiki
* @return string the URL-encoded form
- * @access public
*/
- function getPrefixedURL() {
+ public function getPrefixedURL() {
$s = $this->prefix( $this->mDbkeyform );
$s = str_replace( ' ', '_', $s );
@@ -808,9 +723,8 @@ class Title {
* for interwiki links
* @param string $variant language variant of url (for sr, zh..)
* @return string the URL
- * @access public
*/
- function getFullURL( $query = '', $variant = false ) {
+ public function getFullURL( $query = '', $variant = false ) {
global $wgContLang, $wgServer, $wgRequest;
if ( '' == $this->mInterwiki ) {
@@ -831,14 +745,7 @@ class Title {
$namespace .= ':';
}
$url = str_replace( '$1', $namespace . $this->mUrlform, $baseUrl );
- if( $query != '' ) {
- if( false === strpos( $url, '?' ) ) {
- $url .= '?';
- } else {
- $url .= '&';
- }
- $url .= $query;
- }
+ $url = wfAppendQuery( $url, $query );
}
# Finally, add the fragment.
@@ -855,9 +762,8 @@ class Title {
* $wgArticlePath will be used.
* @param string $variant language variant of url (for sr, zh..)
* @return string the URL
- * @access public
*/
- function getLocalURL( $query = '', $variant = false ) {
+ public function getLocalURL( $query = '', $variant = false ) {
global $wgArticlePath, $wgScript, $wgServer, $wgRequest;
global $wgVariantArticlePath, $wgContLang, $wgUser;
@@ -881,17 +787,17 @@ class Title {
$dbkey = wfUrlencode( $this->getPrefixedDBkey() );
if ( $query == '' ) {
if($variant!=false && $wgContLang->hasVariants()){
- if($wgVariantArticlePath==false)
+ if($wgVariantArticlePath==false) {
$variantArticlePath = "$wgScript?title=$1&variant=$2"; // default
- else
+ } else {
$variantArticlePath = $wgVariantArticlePath;
-
+ }
$url = str_replace( '$2', urlencode( $variant ), $variantArticlePath );
$url = str_replace( '$1', $dbkey, $url );
-
}
- else
+ else {
$url = str_replace( '$1', $dbkey, $wgArticlePath );
+ }
} else {
global $wgActionPaths;
$url = false;
@@ -930,9 +836,8 @@ class Title {
* using in a link, without a server name or fragment
* @param string $query an optional query string
* @return string the URL
- * @access public
*/
- function escapeLocalURL( $query = '' ) {
+ public function escapeLocalURL( $query = '' ) {
return htmlspecialchars( $this->getLocalURL( $query ) );
}
@@ -942,9 +847,8 @@ class Title {
*
* @return string the URL
* @param string $query an optional query string
- * @access public
*/
- function escapeFullURL( $query = '' ) {
+ public function escapeFullURL( $query = '' ) {
return htmlspecialchars( $this->getFullURL( $query ) );
}
@@ -956,9 +860,8 @@ class Title {
* @param string $query an optional query string
* @param string $variant language variant of url (for sr, zh..)
* @return string the URL
- * @access public
*/
- function getInternalURL( $query = '', $variant = false ) {
+ public function getInternalURL( $query = '', $variant = false ) {
global $wgInternalServer;
$url = $wgInternalServer . $this->getLocalURL( $query, $variant );
wfRunHooks( 'GetInternalURL', array( &$this, &$url, $query ) );
@@ -969,9 +872,8 @@ class Title {
* Get the edit URL for this Title
* @return string the URL, or a null string if this is an
* interwiki link
- * @access public
*/
- function getEditURL() {
+ public function getEditURL() {
if ( '' != $this->mInterwiki ) { return ''; }
$s = $this->getLocalURL( 'action=edit' );
@@ -982,18 +884,16 @@ class Title {
* Get the HTML-escaped displayable text form.
* Used for the title field in <a> tags.
* @return string the text, including any prefixes
- * @access public
*/
- function getEscapedText() {
+ public function getEscapedText() {
return htmlspecialchars( $this->getPrefixedText() );
}
/**
* Is this Title interwiki?
* @return boolean
- * @access public
*/
- function isExternal() { return ( '' != $this->mInterwiki ); }
+ public function isExternal() { return ( '' != $this->mInterwiki ); }
/**
* Is this page "semi-protected" - the *only* protection is autoconfirm?
@@ -1001,7 +901,7 @@ class Title {
* @param string Action to check (default: edit)
* @return bool
*/
- function isSemiProtected( $action = 'edit' ) {
+ public function isSemiProtected( $action = 'edit' ) {
if( $this->exists() ) {
$restrictions = $this->getRestrictions( $action );
if( count( $restrictions ) > 0 ) {
@@ -1025,12 +925,15 @@ class Title {
* @param string $what the action the page is protected from,
* by default checks move and edit
* @return boolean
- * @access public
*/
- function isProtected( $action = '' ) {
+ public function isProtected( $action = '' ) {
global $wgRestrictionLevels;
- if ( NS_SPECIAL == $this->mNamespace ) { return true; }
-
+
+ # Special pages have inherent protection
+ if( $this->getNamespace() == NS_SPECIAL )
+ return true;
+
+ # Check regular protection levels
if( $action == 'edit' || $action == '' ) {
$r = $this->getRestrictions( 'edit' );
foreach( $wgRestrictionLevels as $level ) {
@@ -1055,9 +958,8 @@ class Title {
/**
* Is $wgUser is watching this page?
* @return boolean
- * @access public
*/
- function userIsWatching() {
+ public function userIsWatching() {
global $wgUser;
if ( is_null( $this->mWatched ) ) {
@@ -1071,16 +973,32 @@ class Title {
}
/**
- * Can $wgUser perform $action this page?
+ * Can $wgUser perform $action on this page?
+ * This skips potentially expensive cascading permission checks.
+ *
+ * Suitable for use for nonessential UI controls in common cases, but
+ * _not_ for functional access control.
+ *
+ * May provide false positives, but should never provide a false negative.
+ *
* @param string $action action that permission needs to be checked for
* @return boolean
- * @private
*/
- function userCan($action) {
+ public function quickUserCan( $action ) {
+ return $this->userCan( $action, false );
+ }
+
+ /**
+ * Can $wgUser perform $action on this page?
+ * @param string $action action that permission needs to be checked for
+ * @param bool $doExpensiveQueries Set this to false to avoid doing unnecessary queries.
+ * @return boolean
+ */
+ public function userCan( $action, $doExpensiveQueries = true ) {
$fname = 'Title::userCan';
wfProfileIn( $fname );
- global $wgUser;
+ global $wgUser, $wgNamespaceProtection;
$result = null;
wfRunHooks( 'userCan', array( &$this, &$wgUser, $action, &$result ) );
@@ -1093,12 +1011,16 @@ class Title {
wfProfileOut( $fname );
return false;
}
- // XXX: This is the code that prevents unprotecting a page in NS_MEDIAWIKI
- // from taking effect -ævar
- if( NS_MEDIAWIKI == $this->mNamespace &&
- !$wgUser->isAllowed('editinterface') ) {
- wfProfileOut( $fname );
- return false;
+
+ if ( array_key_exists( $this->mNamespace, $wgNamespaceProtection ) ) {
+ $nsProt = $wgNamespaceProtection[ $this->mNamespace ];
+ if ( !is_array($nsProt) ) $nsProt = array($nsProt);
+ foreach( $nsProt as $right ) {
+ if( '' != $right && !$wgUser->isAllowed( $right ) ) {
+ wfProfileOut( $fname );
+ return false;
+ }
+ }
}
if( $this->mDbkeyform == '_' ) {
@@ -1116,7 +1038,28 @@ class Title {
wfProfileOut( $fname );
return false;
}
-
+
+ if ( $doExpensiveQueries && !$this->isCssJsSubpage() ) {
+ # We /could/ use the protection level on the source page, but it's fairly ugly
+ # as we have to establish a precedence hierarchy for pages included by multiple
+ # cascade-protected pages. So just restrict it to people with 'protect' permission,
+ # as they could remove the protection anyway.
+ list( $cascadingSources, $restrictions ) = $this->getCascadeProtectionSources();
+ # Cascading protection depends on more than this page...
+ # Several cascading protected pages may include this page...
+ # Check each cascading level
+ # This is only for protection restrictions, not for all actions
+ if( $cascadingSources > 0 && isset($restrictions[$action]) ) {
+ foreach( $restrictions[$action] as $right ) {
+ $right = ( $right == 'sysop' ) ? 'protect' : $right;
+ if( '' != $right && !$wgUser->isAllowed( $right ) ) {
+ wfProfileOut( $fname );
+ return false;
+ }
+ }
+ }
+ }
+
foreach( $this->getRestrictions($action) as $right ) {
// Backwards compatibility, rewrite sysop -> protect
if ( $right == 'sysop' ) {
@@ -1149,28 +1092,28 @@ class Title {
/**
* Can $wgUser edit this page?
* @return boolean
- * @access public
+ * @deprecated use userCan('edit')
*/
- function userCanEdit() {
- return $this->userCan('edit');
+ public function userCanEdit( $doExpensiveQueries = true ) {
+ return $this->userCan( 'edit', $doExpensiveQueries );
}
/**
* Can $wgUser create this page?
* @return boolean
- * @access public
+ * @deprecated use userCan('create')
*/
- function userCanCreate() {
- return $this->userCan('create');
+ public function userCanCreate( $doExpensiveQueries = true ) {
+ return $this->userCan( 'create', $doExpensiveQueries );
}
/**
* Can $wgUser move this page?
* @return boolean
- * @access public
+ * @deprecated use userCan('move')
*/
- function userCanMove() {
- return $this->userCan('move');
+ public function userCanMove( $doExpensiveQueries = true ) {
+ return $this->userCan( 'move', $doExpensiveQueries );
}
/**
@@ -1178,9 +1121,8 @@ class Title {
* Some pages just aren't movable.
*
* @return boolean
- * @access public
*/
- function isMovable() {
+ public function isMovable() {
return Namespace::isMovable( $this->getNamespace() )
&& $this->getInterwiki() == '';
}
@@ -1188,9 +1130,9 @@ class Title {
/**
* Can $wgUser read this page?
* @return boolean
- * @access public
+ * @todo fold these checks into userCan()
*/
- function userCanRead() {
+ public function userCanRead() {
global $wgUser;
$result = null;
@@ -1231,18 +1173,16 @@ class Title {
/**
* Is this a talk page of some sort?
* @return bool
- * @access public
*/
- function isTalkPage() {
+ public function isTalkPage() {
return Namespace::isTalk( $this->getNamespace() );
}
/**
* Is this a subpage?
* @return bool
- * @access public
*/
- function isSubpage() {
+ public function isSubpage() {
global $wgNamespacesWithSubpages;
if( isset( $wgNamespacesWithSubpages[ $this->mNamespace ] ) ) {
@@ -1255,16 +1195,15 @@ class Title {
/**
* Is this a .css or .js subpage of a user page?
* @return bool
- * @access public
*/
- function isCssJsSubpage() {
- return ( NS_USER == $this->mNamespace and preg_match("/\\/.*\\.(css|js)$/", $this->mTextform ) );
+ public function isCssJsSubpage() {
+ return ( NS_USER == $this->mNamespace and preg_match("/\\/.*\\.(?:css|js)$/", $this->mTextform ) );
}
/**
* Is this a *valid* .css or .js subpage of a user page?
* Check that the corresponding skin exists
*/
- function isValidCssJsSubpage() {
+ public function isValidCssJsSubpage() {
if ( $this->isCssJsSubpage() ) {
$skinNames = Skin::getSkinNames();
return array_key_exists( $this->getSkinFromCssJsSubpage(), $skinNames );
@@ -1275,7 +1214,7 @@ class Title {
/**
* Trim down a .css or .js subpage title to get the corresponding skin name
*/
- function getSkinFromCssJsSubpage() {
+ public function getSkinFromCssJsSubpage() {
$subpage = explode( '/', $this->mTextform );
$subpage = $subpage[ count( $subpage ) - 1 ];
return( str_replace( array( '.css', '.js' ), array( '', '' ), $subpage ) );
@@ -1283,17 +1222,15 @@ class Title {
/**
* Is this a .css subpage of a user page?
* @return bool
- * @access public
*/
- function isCssSubpage() {
+ public function isCssSubpage() {
return ( NS_USER == $this->mNamespace and preg_match("/\\/.*\\.css$/", $this->mTextform ) );
}
/**
* Is this a .js subpage of a user page?
* @return bool
- * @access public
*/
- function isJsSubpage() {
+ public function isJsSubpage() {
return ( NS_USER == $this->mNamespace and preg_match("/\\/.*\\.js$/", $this->mTextform ) );
}
/**
@@ -1302,54 +1239,223 @@ class Title {
*
* @return boolean
* @todo XXX: this might be better using restrictions
- * @access public
*/
- function userCanEditCssJsSubpage() {
+ public function userCanEditCssJsSubpage() {
global $wgUser;
return ( $wgUser->isAllowed('editinterface') or preg_match('/^'.preg_quote($wgUser->getName(), '/').'\//', $this->mTextform) );
}
/**
+ * Cascading protection: Return true if cascading restrictions apply to this page, false if not.
+ *
+ * @return bool If the page is subject to cascading restrictions.
+ */
+ public function isCascadeProtected() {
+ list( $sources, $restrictions ) = $this->getCascadeProtectionSources( false );
+ return ( $sources > 0 );
+ }
+
+ /**
+ * Cascading protection: Get the source of any cascading restrictions on this page.
+ *
+ * @param $get_pages bool Whether or not to retrieve the actual pages that the restrictions have come from.
+ * @return array( mixed title array, restriction array)
+ * Array of the Title objects of the pages from which cascading restrictions have come, false for none, or true if such restrictions exist, but $get_pages was not set.
+ * The restriction array is an array of each type, each of which contains an array of unique groups
+ */
+ public function getCascadeProtectionSources( $get_pages = true ) {
+ global $wgEnableCascadingProtection, $wgRestrictionTypes;
+
+ # Define our dimension of restrictions types
+ $pagerestrictions = array();
+ foreach( $wgRestrictionTypes as $action )
+ $pagerestrictions[$action] = array();
+
+ if (!$wgEnableCascadingProtection)
+ return array( false, $pagerestrictions );
+
+ if ( isset( $this->mCascadeSources ) && $get_pages ) {
+ return array( $this->mCascadeSources, $this->mCascadingRestrictions );
+ } else if ( isset( $this->mHasCascadingRestrictions ) && !$get_pages ) {
+ return array( $this->mHasCascadingRestrictions, $pagerestrictions );
+ }
+
+ wfProfileIn( __METHOD__ );
+
+ $dbr = wfGetDb( DB_SLAVE );
+
+ if ( $this->getNamespace() == NS_IMAGE ) {
+ $tables = array ('imagelinks', 'page_restrictions');
+ $where_clauses = array(
+ 'il_to' => $this->getDBkey(),
+ 'il_from=pr_page',
+ 'pr_cascade' => 1 );
+ } else {
+ $tables = array ('templatelinks', 'page_restrictions');
+ $where_clauses = array(
+ 'tl_namespace' => $this->getNamespace(),
+ 'tl_title' => $this->getDBkey(),
+ 'tl_from=pr_page',
+ 'pr_cascade' => 1 );
+ }
+
+ if ( $get_pages ) {
+ $cols = array('pr_page', 'page_namespace', 'page_title', 'pr_expiry', 'pr_type', 'pr_level' );
+ $where_clauses[] = 'page_id=pr_page';
+ $tables[] = 'page';
+ } else {
+ $cols = array( 'pr_expiry' );
+ }
+
+ $res = $dbr->select( $tables, $cols, $where_clauses, __METHOD__ );
+
+ $sources = $get_pages ? array() : false;
+ $now = wfTimestampNow();
+ $purgeExpired = false;
+
+ while( $row = $dbr->fetchObject( $res ) ) {
+ $expiry = Block::decodeExpiry( $row->pr_expiry );
+ if( $expiry > $now ) {
+ if ($get_pages) {
+ $page_id = $row->pr_page;
+ $page_ns = $row->page_namespace;
+ $page_title = $row->page_title;
+ $sources[$page_id] = Title::makeTitle($page_ns, $page_title);
+ # Add groups needed for each restriction type if its not already there
+ # Make sure this restriction type still exists
+ if ( isset($pagerestrictions[$row->pr_type]) && !in_array($row->pr_level, $pagerestrictions[$row->pr_type]) ) {
+ $pagerestrictions[$row->pr_type][]=$row->pr_level;
+ }
+ } else {
+ $sources = true;
+ }
+ } else {
+ // Trigger lazy purge of expired restrictions from the db
+ $purgeExpired = true;
+ }
+ }
+ if( $purgeExpired ) {
+ Title::purgeExpiredRestrictions();
+ }
+
+ wfProfileOut( __METHOD__ );
+
+ if ( $get_pages ) {
+ $this->mCascadeSources = $sources;
+ $this->mCascadingRestrictions = $pagerestrictions;
+ } else {
+ $this->mHasCascadingRestrictions = $sources;
+ }
+
+ return array( $sources, $pagerestrictions );
+ }
+
+ function areRestrictionsCascading() {
+ if (!$this->mRestrictionsLoaded) {
+ $this->loadRestrictions();
+ }
+
+ return $this->mCascadeRestriction;
+ }
+
+ /**
* Loads a string into mRestrictions array
- * @param string $res restrictions in string format
- * @access public
+ * @param resource $res restrictions as an SQL result.
*/
- function loadRestrictions( $res ) {
+ private function loadRestrictionsFromRow( $res, $oldFashionedRestrictions = NULL ) {
+ $dbr = wfGetDb( DB_SLAVE );
+
$this->mRestrictions['edit'] = array();
$this->mRestrictions['move'] = array();
-
- if( !$res ) {
- # No restrictions (page_restrictions blank)
- $this->mRestrictionsLoaded = true;
- return;
+
+ # Backwards-compatibility: also load the restrictions from the page record (old format).
+
+ if ( $oldFashionedRestrictions == NULL ) {
+ $oldFashionedRestrictions = $dbr->selectField( 'page', 'page_restrictions', array( 'page_id' => $this->getArticleId() ), __METHOD__ );
}
-
- foreach( explode( ':', trim( $res ) ) as $restrict ) {
- $temp = explode( '=', trim( $restrict ) );
- if(count($temp) == 1) {
- // old format should be treated as edit/move restriction
- $this->mRestrictions["edit"] = explode( ',', trim( $temp[0] ) );
- $this->mRestrictions["move"] = explode( ',', trim( $temp[0] ) );
- } else {
- $this->mRestrictions[$temp[0]] = explode( ',', trim( $temp[1] ) );
+
+ if ($oldFashionedRestrictions != '') {
+
+ foreach( explode( ':', trim( $oldFashionedRestrictions ) ) as $restrict ) {
+ $temp = explode( '=', trim( $restrict ) );
+ if(count($temp) == 1) {
+ // old old format should be treated as edit/move restriction
+ $this->mRestrictions["edit"] = explode( ',', trim( $temp[0] ) );
+ $this->mRestrictions["move"] = explode( ',', trim( $temp[0] ) );
+ } else {
+ $this->mRestrictions[$temp[0]] = explode( ',', trim( $temp[1] ) );
+ }
+ }
+
+ $this->mOldRestrictions = true;
+ $this->mCascadeRestriction = false;
+ $this->mRestrictionsExpiry = Block::decodeExpiry('');
+
+ }
+
+ if( $dbr->numRows( $res ) ) {
+ # Current system - load second to make them override.
+ $now = wfTimestampNow();
+ $purgeExpired = false;
+
+ while ($row = $dbr->fetchObject( $res ) ) {
+ # Cycle through all the restrictions.
+
+ // This code should be refactored, now that it's being used more generally,
+ // But I don't really see any harm in leaving it in Block for now -werdna
+ $expiry = Block::decodeExpiry( $row->pr_expiry );
+
+ // Only apply the restrictions if they haven't expired!
+ if ( !$expiry || $expiry > $now ) {
+ $this->mRestrictionsExpiry = $expiry;
+ $this->mRestrictions[$row->pr_type] = explode( ',', trim( $row->pr_level ) );
+
+ $this->mCascadeRestriction |= $row->pr_cascade;
+ } else {
+ // Trigger a lazy purge of expired restrictions
+ $purgeExpired = true;
+ }
+ }
+
+ if( $purgeExpired ) {
+ Title::purgeExpiredRestrictions();
}
}
+
$this->mRestrictionsLoaded = true;
}
+ public function loadRestrictions( $oldFashionedRestrictions = NULL ) {
+ if( !$this->mRestrictionsLoaded ) {
+ $dbr = wfGetDB( DB_SLAVE );
+
+ $res = $dbr->select( 'page_restrictions', '*',
+ array ( 'pr_page' => $this->getArticleId() ), __METHOD__ );
+
+ $this->loadRestrictionsFromRow( $res, $oldFashionedRestrictions );
+ }
+ }
+
+ /**
+ * Purge expired restrictions from the page_restrictions table
+ */
+ static function purgeExpiredRestrictions() {
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->delete( 'page_restrictions',
+ array( 'pr_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ),
+ __METHOD__ );
+ }
+
/**
* Accessor/initialisation for mRestrictions
*
- * @access public
* @param string $action action that permission needs to be checked for
* @return array the array of groups allowed to edit this article
*/
- function getRestrictions( $action ) {
+ public function getRestrictions( $action ) {
if( $this->exists() ) {
if( !$this->mRestrictionsLoaded ) {
- $dbr =& wfGetDB( DB_SLAVE );
- $res = $dbr->selectField( 'page', 'page_restrictions', array( 'page_id' => $this->getArticleId() ) );
- $this->loadRestrictions( $res );
+ $this->loadRestrictions();
}
return isset( $this->mRestrictions[$action] )
? $this->mRestrictions[$action]
@@ -1362,14 +1468,13 @@ class Title {
/**
* Is there a version of this page in the deletion archive?
* @return int the number of archived revisions
- * @access public
*/
- function isDeleted() {
+ public function isDeleted() {
$fname = 'Title::isDeleted';
if ( $this->getNamespace() < 0 ) {
$n = 0;
} else {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$n = $dbr->selectField( 'archive', 'COUNT(*)', array( 'ar_namespace' => $this->getNamespace(),
'ar_title' => $this->getDBkey() ), $fname );
if( $this->getNamespace() == NS_IMAGE ) {
@@ -1386,9 +1491,8 @@ class Title {
* @param int $flags a bit field; may be GAID_FOR_UPDATE to select
* for update
* @return int the ID
- * @access public
*/
- function getArticleID( $flags = 0 ) {
+ public function getArticleID( $flags = 0 ) {
$linkCache =& LinkCache::singleton();
if ( $flags & GAID_FOR_UPDATE ) {
$oldUpdate = $linkCache->forUpdate( true );
@@ -1402,11 +1506,11 @@ class Title {
return $this->mArticleID;
}
- function getLatestRevID() {
+ public function getLatestRevID() {
if ($this->mLatestID !== false)
return $this->mLatestID;
- $db =& wfGetDB(DB_SLAVE);
+ $db = wfGetDB(DB_SLAVE);
return $this->mLatestID = $db->selectField( 'revision',
"max(rev_id)",
array('rev_page' => $this->getArticleID()),
@@ -1422,9 +1526,8 @@ class Title {
* Article::doDeleteArticle()
*
* @param int $newid the new Article ID
- * @access public
*/
- function resetArticleID( $newid ) {
+ public function resetArticleID( $newid ) {
$linkCache =& LinkCache::singleton();
$linkCache->clearBadLink( $this->getPrefixedDBkey() );
@@ -1437,16 +1540,15 @@ class Title {
/**
* Updates page_touched for this page; called from LinksUpdate.php
* @return bool true if the update succeded
- * @access public
*/
- function invalidateCache() {
+ public function invalidateCache() {
global $wgUseFileCache;
if ( wfReadOnly() ) {
return;
}
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$success = $dbw->update( 'page',
array( /* SET */
'page_touched' => $dbw->timestamp()
@@ -1492,9 +1594,8 @@ class Title {
* namespace prefixes, sets the other forms, and canonicalizes
* everything.
* @return bool true on success
- * @private
*/
- /* private */ function secureAndSplit() {
+ private function secureAndSplit() {
global $wgContLang, $wgLocalInterwiki, $wgCapitalLinks;
# Initialisation
@@ -1536,6 +1637,7 @@ class Title {
if ( ':' == $dbkey{0} ) {
$this->mNamespace = NS_MAIN;
$dbkey = substr( $dbkey, 1 ); # remove the colon but continue processing
+ $dbkey = trim( $dbkey, '_' ); # remove any subsequent whitespace
}
# Namespace or interwiki prefix
@@ -1544,12 +1646,7 @@ class Title {
$m = array();
if ( preg_match( "/^(.+?)_*:_*(.*)$/S", $dbkey, $m ) ) {
$p = $m[1];
- $lowerNs = $wgContLang->lc( $p );
- if ( $ns = Namespace::getCanonicalIndex( $lowerNs ) ) {
- # Canonical namespace
- $dbkey = $m[2];
- $this->mNamespace = $ns;
- } elseif ( $ns = $wgContLang->getNsIndex( $lowerNs )) {
+ if ( $ns = $wgContLang->getNsIndex( $p )) {
# Ordinary namespace
$dbkey = $m[2];
$this->mNamespace = $ns;
@@ -1623,6 +1720,13 @@ class Title {
{
return false;
}
+
+ /**
+ * Magic tilde sequences? Nu-uh!
+ */
+ if( strpos( $dbkey, '~~~' ) !== false ) {
+ return false;
+ }
/**
* Limit the size of titles to 255 bytes.
@@ -1681,18 +1785,17 @@ class Title {
* members directly, which is what Linker::formatComment was doing previously.
*
* @param string $fragment text
- * @access kind of public
+ * @todo clarify whether access is supposed to be public (was marked as "kind of public")
*/
- function setFragment( $fragment ) {
+ public function setFragment( $fragment ) {
$this->mFragment = str_replace( '_', ' ', substr( $fragment, 1 ) );
}
/**
* Get a Title object associated with the talk page of this article
* @return Title the object for the talk page
- * @access public
*/
- function getTalkPage() {
+ public function getTalkPage() {
return Title::makeTitle( Namespace::getTalk( $this->getNamespace() ), $this->getDBkey() );
}
@@ -1701,9 +1804,8 @@ class Title {
* talk page
*
* @return Title the object for the subject page
- * @access public
*/
- function getSubjectPage() {
+ public function getSubjectPage() {
return Title::makeTitle( Namespace::getSubject( $this->getNamespace() ), $this->getDBkey() );
}
@@ -1716,15 +1818,14 @@ class Title {
*
* @param string $options may be FOR UPDATE
* @return array the Title objects linking here
- * @access public
*/
- function getLinksTo( $options = '', $table = 'pagelinks', $prefix = 'pl' ) {
+ public function getLinksTo( $options = '', $table = 'pagelinks', $prefix = 'pl' ) {
$linkCache =& LinkCache::singleton();
if ( $options ) {
- $db =& wfGetDB( DB_MASTER );
+ $db = wfGetDB( DB_MASTER );
} else {
- $db =& wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_SLAVE );
}
$res = $db->select( array( 'page', $table ),
@@ -1758,9 +1859,8 @@ class Title {
*
* @param string $options may be FOR UPDATE
* @return array the Title objects linking here
- * @access public
*/
- function getTemplateLinksTo( $options = '' ) {
+ public function getTemplateLinksTo( $options = '' ) {
return $this->getLinksTo( $options, 'templatelinks', 'tl' );
}
@@ -1769,13 +1869,12 @@ class Title {
*
* @param string $options may be FOR UPDATE
* @return array the Title objects
- * @access public
*/
- function getBrokenLinksFrom( $options = '' ) {
+ public function getBrokenLinksFrom( $options = '' ) {
if ( $options ) {
- $db =& wfGetDB( DB_MASTER );
+ $db = wfGetDB( DB_MASTER );
} else {
- $db =& wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_SLAVE );
}
$res = $db->safeQuery(
@@ -1808,9 +1907,8 @@ class Title {
* page changes
*
* @return array the URLs
- * @access public
*/
- function getSquidURLs() {
+ public function getSquidURLs() {
global $wgContLang;
$urls = array(
@@ -1830,7 +1928,7 @@ class Title {
return $urls;
}
- function purgeSquid() {
+ public function purgeSquid() {
global $wgUseSquid;
if ( $wgUseSquid ) {
$urls = $this->getSquidURLs();
@@ -1842,9 +1940,8 @@ class Title {
/**
* Move this page without authentication
* @param Title &$nt the new page Title
- * @access public
*/
- function moveNoAuth( &$nt ) {
+ public function moveNoAuth( &$nt ) {
return $this->moveTo( $nt, false );
}
@@ -1856,9 +1953,8 @@ class Title {
* @param bool $auth indicates whether $wgUser's permissions
* should be checked
* @return mixed true on success, message name on failure
- * @access public
*/
- function isValidMoveOperation( &$nt, $auth = true ) {
+ public function isValidMoveOperation( &$nt, $auth = true ) {
if( !$this or !$nt ) {
return 'badtitletext';
}
@@ -1882,8 +1978,8 @@ class Title {
}
if ( $auth && (
- !$this->userCanEdit() || !$nt->userCanEdit() ||
- !$this->userCanMove() || !$nt->userCanMove() ) ) {
+ !$this->userCan( 'edit' ) || !$nt->userCan( 'edit' ) ||
+ !$this->userCan( 'move' ) || !$nt->userCan( 'move' ) ) ) {
return 'protectedpage';
}
@@ -1905,9 +2001,8 @@ class Title {
* @param bool $auth indicates whether $wgUser's permissions
* should be checked
* @return mixed true on success, message name on failure
- * @access public
*/
- function moveTo( &$nt, $auth = true, $reason = '' ) {
+ public function moveTo( &$nt, $auth = true, $reason = '' ) {
$err = $this->isValidMoveOperation( $nt, $auth );
if( is_string( $err ) ) {
return $err;
@@ -1924,7 +2019,7 @@ class Title {
$redirid = $this->getArticleID();
# Fixing category links (those without piped 'alternate' names) to be sorted under the new title
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$categorylinks = $dbw->tableName( 'categorylinks' );
$sql = "UPDATE $categorylinks SET cl_sortkey=" . $dbw->addQuotes( $nt->getPrefixedText() ) .
" WHERE cl_from=" . $dbw->addQuotes( $pageid ) .
@@ -1949,24 +2044,24 @@ class Title {
$u->doUpdate();
# Update site_stats
- if ( $this->getNamespace() == NS_MAIN and $nt->getNamespace() != NS_MAIN ) {
- # Moved out of main namespace
- # not viewed, edited, removing
- $u = new SiteStatsUpdate( 0, 1, -1, $pageCountChange);
- } elseif ( $this->getNamespace() != NS_MAIN and $nt->getNamespace() == NS_MAIN ) {
- # Moved into main namespace
- # not viewed, edited, adding
+ if( $this->isContentPage() && !$nt->isContentPage() ) {
+ # No longer a content page
+ # Not viewed, edited, removing
+ $u = new SiteStatsUpdate( 0, 1, -1, $pageCountChange );
+ } elseif( !$this->isContentPage() && $nt->isContentPage() ) {
+ # Now a content page
+ # Not viewed, edited, adding
$u = new SiteStatsUpdate( 0, 1, +1, $pageCountChange );
- } elseif ( $pageCountChange ) {
- # Added redirect
+ } elseif( $pageCountChange ) {
+ # Redirect added
$u = new SiteStatsUpdate( 0, 0, 0, 1 );
- } else{
+ } else {
+ # Nothing special
$u = false;
}
- if ( $u ) {
+ if( $u )
$u->doUpdate();
- }
-
+
global $wgUser;
wfRunHooks( 'TitleMoveComplete', array( &$this, &$nt, &$wgUser, $pageid, $redirid ) );
return true;
@@ -1978,12 +2073,11 @@ class Title {
*
* @param Title &$nt the page to move to, which should currently
* be a redirect
- * @private
*/
- function moveOverExistingRedirect( &$nt, $reason = '' ) {
+ private function moveOverExistingRedirect( &$nt, $reason = '' ) {
global $wgUseSquid;
$fname = 'Title::moveOverExistingRedirect';
- $comment = wfMsgForContent( '1movedto2', $this->getPrefixedText(), $nt->getPrefixedText() );
+ $comment = wfMsgForContent( '1movedto2_redir', $this->getPrefixedText(), $nt->getPrefixedText() );
if ( $reason ) {
$comment .= ": $reason";
@@ -1992,7 +2086,7 @@ class Title {
$now = wfTimestampNow();
$newid = $nt->getArticleID();
$oldid = $this->getArticleID();
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$linkCache =& LinkCache::singleton();
# Delete the old redirect. We don't save it to history since
@@ -2056,9 +2150,8 @@ class Title {
/**
* Move page to non-existing title.
* @param Title &$nt the new Title
- * @private
*/
- function moveToNewTitle( &$nt, $reason = '' ) {
+ private function moveToNewTitle( &$nt, $reason = '' ) {
global $wgUseSquid;
$fname = 'MovePageForm::moveToNewTitle';
$comment = wfMsgForContent( '1movedto2', $this->getPrefixedText(), $nt->getPrefixedText() );
@@ -2068,7 +2161,7 @@ class Title {
$newid = $nt->getArticleID();
$oldid = $this->getArticleID();
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$now = $dbw->timestamp();
$linkCache =& LinkCache::singleton();
@@ -2128,12 +2221,11 @@ class Title {
* - Selects for update, so don't call it unless you mean business
*
* @param Title &$nt the new title to check
- * @access public
*/
- function isValidMoveTarget( $nt ) {
+ public function isValidMoveTarget( $nt ) {
$fname = 'Title::isValidMoveTarget';
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
# Is it a redirect?
$id = $nt->getArticleID();
@@ -2180,58 +2272,17 @@ class Title {
}
/**
- * Create a redirect; fails if the title already exists; does
- * not notify RC
- *
- * @param Title $dest the destination of the redirect
- * @param string $comment the comment string describing the move
- * @return bool true on success
- * @access public
- */
- function createRedirect( $dest, $comment ) {
- if ( $this->getArticleID() ) {
- return false;
- }
-
- $fname = 'Title::createRedirect';
- $dbw =& wfGetDB( DB_MASTER );
-
- $article = new Article( $this );
- $newid = $article->insertOn( $dbw );
- $revision = new Revision( array(
- 'page' => $newid,
- 'comment' => $comment,
- 'text' => "#REDIRECT [[" . $dest->getPrefixedText() . "]]\n",
- ) );
- $revision->insertOn( $dbw );
- $article->updateRevisionOn( $dbw, $revision, 0 );
-
- # Link table
- $dbw->insert( 'pagelinks',
- array(
- 'pl_from' => $newid,
- 'pl_namespace' => $dest->getNamespace(),
- 'pl_title' => $dest->getDbKey()
- ), $fname
- );
-
- Article::onArticleCreate( $this );
- return true;
- }
-
- /**
* Get categories to which this Title belongs and return an array of
* categories' names.
*
* @return array an array of parents in the form:
* $parent => $currentarticle
- * @access public
*/
- function getParentCategories() {
+ public function getParentCategories() {
global $wgContLang;
$titlekey = $this->getArticleId();
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$categorylinks = $dbr->tableName( 'categorylinks' );
# NEW SQL
@@ -2257,9 +2308,8 @@ class Title {
* Get a tree of parent categories
* @param array $children an array with the children in the keys, to check for circular refs
* @return array
- * @access public
*/
- function getParentCategoryTree( $children = array() ) {
+ public function getParentCategoryTree( $children = array() ) {
$parents = $this->getParentCategories();
if($parents != '') {
@@ -2286,9 +2336,8 @@ class Title {
* the "page" table
*
* @return array
- * @access public
*/
- function pageCond() {
+ public function pageCond() {
return array( 'page_namespace' => $this->mNamespace, 'page_title' => $this->mDbkeyform );
}
@@ -2298,8 +2347,8 @@ class Title {
* @param integer $revision Revision ID. Get the revision that was before this one.
* @return integer $oldrevision|false
*/
- function getPreviousRevisionID( $revision ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ public function getPreviousRevisionID( $revision ) {
+ $dbr = wfGetDB( DB_SLAVE );
return $dbr->selectField( 'revision', 'rev_id',
'rev_page=' . intval( $this->getArticleId() ) .
' AND rev_id<' . intval( $revision ) . ' ORDER BY rev_id DESC' );
@@ -2311,8 +2360,8 @@ class Title {
* @param integer $revision Revision ID. Get the revision that was after this one.
* @return integer $oldrevision|false
*/
- function getNextRevisionID( $revision ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ public function getNextRevisionID( $revision ) {
+ $dbr = wfGetDB( DB_SLAVE );
return $dbr->selectField( 'revision', 'rev_id',
'rev_page=' . intval( $this->getArticleId() ) .
' AND rev_id>' . intval( $revision ) . ' ORDER BY rev_id' );
@@ -2325,8 +2374,8 @@ class Title {
* @param integer $new Revision ID.
* @return integer Number of revisions between these IDs.
*/
- function countRevisionsBetween( $old, $new ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ public function countRevisionsBetween( $old, $new ) {
+ $dbr = wfGetDB( DB_SLAVE );
return $dbr->selectField( 'revision', 'count(*)',
'rev_page = ' . intval( $this->getArticleId() ) .
' AND rev_id > ' . intval( $old ) .
@@ -2339,7 +2388,7 @@ class Title {
* @param Title $title
* @return bool
*/
- function equals( $title ) {
+ public function equals( $title ) {
// Note: === is necessary for proper matching of number-like titles.
return $this->getInterwiki() === $title->getInterwiki()
&& $this->getNamespace() == $title->getNamespace()
@@ -2350,7 +2399,7 @@ class Title {
* Check if page exists
* @return bool
*/
- function exists() {
+ public function exists() {
return $this->getArticleId() != 0;
}
@@ -2360,7 +2409,7 @@ class Title {
* Currently, a self-link with a fragment and special pages are in
* this category. Special pages never exist in the database.
*/
- function isAlwaysKnown() {
+ public function isAlwaysKnown() {
return $this->isExternal() || ( 0 == $this->mNamespace && "" == $this->mDbkeyform )
|| NS_SPECIAL == $this->mNamespace;
}
@@ -2370,7 +2419,7 @@ class Title {
* pages linking to this title. May be sent to the job queue depending
* on the number of links. Typically called on create and delete.
*/
- function touchLinks() {
+ public function touchLinks() {
$u = new HTMLCacheUpdate( $this, 'pagelinks' );
$u->doUpdate();
@@ -2383,8 +2432,8 @@ class Title {
/**
* Get the last touched timestamp
*/
- function getTouched() {
- $dbr =& wfGetDB( DB_SLAVE );
+ public function getTouched() {
+ $dbr = wfGetDB( DB_SLAVE );
$touched = $dbr->selectField( 'page', 'page_touched',
array(
'page_namespace' => $this->getNamespace(),
@@ -2394,26 +2443,14 @@ class Title {
return $touched;
}
- /**
- * Get a cached value from a global cache that is invalidated when this page changes
- * @param string $key the key
- * @param callback $callback A callback function which generates the value on cache miss
- *
- * @deprecated use DependencyWrapper
- */
- function getRelatedCache( $memc, $key, $expiry, $callback, $params = array() ) {
- return DependencyWrapper::getValueFromCache( $memc, $key, $expiry, $callback,
- $params, new TitleDependency( $this ) );
- }
-
- function trackbackURL() {
+ public function trackbackURL() {
global $wgTitle, $wgScriptPath, $wgServer;
return "$wgServer$wgScriptPath/trackback.php?article="
. htmlspecialchars(urlencode($wgTitle->getPrefixedDBkey()));
}
- function trackbackRDF() {
+ public function trackbackRDF() {
$url = htmlspecialchars($this->getFullURL());
$title = htmlspecialchars($this->getText());
$tburl = $this->trackbackURL();
@@ -2434,7 +2471,7 @@ class Title {
* Generate strings used for xml 'id' names in monobook tabs
* @return string
*/
- function getNamespaceKey() {
+ public function getNamespaceKey() {
global $wgContLang;
switch ($this->getNamespace()) {
case NS_MAIN:
@@ -2473,9 +2510,8 @@ class Title {
/**
* Returns true if this title resolves to the named special page
* @param string $name The special page name
- * @access public
*/
- function isSpecial( $name ) {
+ public function isSpecial( $name ) {
if ( $this->getNamespace() == NS_SPECIAL ) {
list( $thisName, /* $subpage */ ) = SpecialPage::resolveAliasWithSubpage( $this->getDBkey() );
if ( $name == $thisName ) {
@@ -2489,7 +2525,7 @@ class Title {
* If the Title refers to a special page alias which is not the local default,
* returns a new Title which points to the local default. Otherwise, returns $this.
*/
- function fixSpecialName() {
+ public function fixSpecialName() {
if ( $this->getNamespace() == NS_SPECIAL ) {
$canonicalName = SpecialPage::resolveAlias( $this->mDbkeyform );
if ( $canonicalName ) {
@@ -2501,5 +2537,18 @@ class Title {
}
return $this;
}
+
+ /**
+ * Is this Title in a namespace which contains content?
+ * In other words, is this a content page, for the purposes of calculating
+ * statistics, etc?
+ *
+ * @return bool
+ */
+ public function isContentPage() {
+ return Namespace::isContent( $this->getNamespace() );
+ }
+
}
+
?>
diff --git a/includes/User.php b/includes/User.php
index 35ff8299..4ecd49de 100644
--- a/includes/User.php
+++ b/includes/User.php
@@ -2,14 +2,13 @@
/**
* See user.txt
*
- * @package MediaWiki
*/
# Number of characters in user_token field
define( 'USER_TOKEN_LENGTH', 32 );
# Serialized record version
-define( 'MW_USER_VERSION', 4 );
+define( 'MW_USER_VERSION', 5 );
# Some punctuation to prevent editing from broken text-mangling proxies.
# FIXME: this is embedded unescaped into HTML attributes in various
@@ -18,14 +17,21 @@ define( 'EDIT_TOKEN_SUFFIX', '\\' );
/**
* Thrown by User::setPassword() on error
+ * @addtogroup Exception
*/
class PasswordError extends MWException {
// NOP
}
/**
- *
- * @package MediaWiki
+ * The User object encapsulates all of the user-specific settings (user_id,
+ * name, rights, password, email address, options, last login time). Client
+ * classes use the getXXX() functions to access these fields. These functions
+ * do all the work of determining whether the user is logged in,
+ * whether the requested option can be satisfied from cookies or
+ * whether a database query is needed. Most of the settings needed
+ * for rendering normal pages are set in the cookie to minimize use
+ * of the database.
*/
class User {
@@ -71,6 +77,7 @@ class User {
'watchlisthidebots',
'watchlisthideminor',
'ccmeonemails',
+ 'diffonly',
);
/**
@@ -94,7 +101,7 @@ class User {
'mEmailToken',
'mEmailTokenExpires',
'mRegistration',
-
+ 'mEditCount',
# user_group table
'mGroups',
);
@@ -188,7 +195,6 @@ class User {
# Try cache
$key = wfMemcKey( 'user', 'id', $this->mId );
$data = $wgMemc->get( $key );
-
if ( !is_array( $data ) || $data['mVersion'] < MW_USER_VERSION ) {
# Object is expired, load from DB
$data = false;
@@ -270,7 +276,7 @@ class User {
* @static
*/
static function newFromConfirmationCode( $code ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$id = $dbr->selectField( 'user', 'user_id', array(
'user_email_token' => md5( $code ),
'user_email_token_expires > ' . $dbr->addQuotes( $dbr->timestamp() ),
@@ -302,7 +308,7 @@ class User {
* @static
*/
static function whoIs( $id ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
return $dbr->selectField( 'user', 'user_name', array( 'user_id' => $id ), 'User::whoIs' );
}
@@ -313,7 +319,7 @@ class User {
* @static
*/
static function whoIsReal( $id ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
return $dbr->selectField( 'user', 'user_real_name', array( 'user_id' => $id ), 'User::whoIsReal' );
}
@@ -329,7 +335,7 @@ class User {
# Illegal name
return null;
}
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$s = $dbr->selectRow( 'user', array( 'user_id' ), array( 'user_name' => $nt->getText() ), __METHOD__ );
if ( $s === false ) {
@@ -352,14 +358,12 @@ class User {
* addresses like this, if we allowed accounts like this to be created
* new users could get the old edits of these anonymous users.
*
- * @bug 3631
- *
* @static
* @param string $name Nickname of a user
* @return bool
*/
static function isIP( $name ) {
- return preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/',$name);
+ return preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/',$name) || User::isIPv6($name);
/*return preg_match("/^
(?:[01]?\d{1,2}|2(:?[0-4]\d|5[0-5]))\.
(?:[01]?\d{1,2}|2(:?[0-4]\d|5[0-5]))\.
@@ -369,6 +373,27 @@ class User {
}
/**
+ * Check if $name is an IPv6 IP.
+ */
+ static function isIPv6($name) {
+ /*
+ * if it has any non-valid characters, it can't be a valid IPv6
+ * address.
+ */
+ if (preg_match("/[^:a-fA-F0-9]/", $name))
+ return false;
+
+ $parts = explode(":", $name);
+ if (count($parts) < 3)
+ return false;
+ foreach ($parts as $part) {
+ if (!preg_match("/^[0-9a-fA-F]{0,4}$/", $part))
+ return false;
+ }
+ return true;
+ }
+
+ /**
* Is the input a valid username?
*
* Checks if the input is a valid username, we don't want an empty string,
@@ -462,11 +487,15 @@ class User {
*
* @param string $password
* @return bool
- * @static
*/
- static function isValidPassword( $password ) {
- global $wgMinimalPasswordLength;
- return strlen( $password ) >= $wgMinimalPasswordLength;
+ function isValidPassword( $password ) {
+ global $wgMinimalPasswordLength, $wgContLang;
+
+ $result = null;
+ if( !wfRunHooks( 'isValidPassword', array( $password, &$result ) ) ) return $result;
+ if ($result === false) return false;
+ return (strlen( $password ) >= $wgMinimalPasswordLength) &&
+ ($wgContLang->lc( $password ) !== $wgContLang->lc( $this->mName ));
}
/**
@@ -476,8 +505,7 @@ class User {
* rejected valid addresses. Actually just check if there is '@' somewhere
* in the given address.
*
- * @todo Check for RFC 2822 compilance
- * @bug 959
+ * @todo Check for RFC 2822 compilance (bug 959)
*
* @param string $addr email address
* @static
@@ -541,22 +569,45 @@ class User {
/**
* Count the number of edits of a user
*
+ * It should not be static and some day should be merged as proper member function / deprecated -- domas
+ *
* @param int $uid The user ID to check
* @return int
* @static
*/
static function edits( $uid ) {
- $dbr =& wfGetDB( DB_SLAVE );
- return $dbr->selectField(
- 'revision', 'count(*)',
- array( 'rev_user' => $uid ),
+ wfProfileIn( __METHOD__ );
+ $dbr = wfGetDB( DB_SLAVE );
+ // check if the user_editcount field has been initialized
+ $field = $dbr->selectField(
+ 'user', 'user_editcount',
+ array( 'user_id' => $uid ),
__METHOD__
);
+
+ if( $field === null ) { // it has not been initialized. do so.
+ $dbw = wfGetDb( DB_MASTER );
+ $count = $dbr->selectField(
+ 'revision', 'count(*)',
+ array( 'rev_user' => $uid ),
+ __METHOD__
+ );
+ $dbw->update(
+ 'user',
+ array( 'user_editcount' => $count ),
+ array( 'user_id' => $uid ),
+ __METHOD__
+ );
+ } else {
+ $count = $field;
+ }
+ wfProfileOut( __METHOD__ );
+ return $count;
}
/**
* Return a random password. Sourced from mt_rand, so it's not particularly secure.
- * @todo: hash random numbers to improve security, like generateToken()
+ * @todo hash random numbers to improve security, like generateToken()
*
* @return string
* @static
@@ -622,10 +673,8 @@ class User {
* Load user data from the session or login cookie. If there are no valid
* credentials, initialises the user as an anon.
* @return true if the user is logged in, false otherwise
- *
- * @private
*/
- function loadFromSession() {
+ private function loadFromSession() {
global $wgMemc, $wgCookiePrefix;
if ( isset( $_SESSION['wsUserID'] ) ) {
@@ -699,7 +748,7 @@ class User {
return false;
}
- $dbr =& wfGetDB( DB_MASTER );
+ $dbr = wfGetDB( DB_MASTER );
$s = $dbr->selectRow( 'user', '*', array( 'user_id' => $this->mId ), __METHOD__ );
if ( $s !== false ) {
@@ -717,6 +766,8 @@ class User {
$this->mEmailToken = $s->user_email_token;
$this->mEmailTokenExpires = wfTimestampOrNull( TS_MW, $s->user_email_token_expires );
$this->mRegistration = wfTimestampOrNull( TS_MW, $s->user_registration );
+ $this->mEditCount = $s->user_editcount;
+ $this->getEditCount(); // revalidation for nulls
# Load group data
$res = $dbr->select( 'user_groups',
@@ -833,7 +884,8 @@ class User {
wfProfileIn( __METHOD__ );
wfDebug( __METHOD__.": checking...\n" );
- $this->mBlockedby = 0;
+ $this->mBlockedby = 0;
+ $this->mHideName = 0;
$ip = wfGetIP();
if ($this->isAllowed( 'ipblock-exempt' ) ) {
@@ -848,6 +900,7 @@ class User {
wfDebug( __METHOD__.": Found block.\n" );
$this->mBlockedby = $this->mBlock->mBy;
$this->mBlockreason = $this->mBlock->mReason;
+ $this->mHideName = $this->mBlock->mHideName;
if ( $this->isLoggedIn() ) {
$this->spreadBlock();
}
@@ -917,6 +970,16 @@ class User {
}
/**
+ * Is this user subject to rate limiting?
+ *
+ * @return bool
+ */
+ public function isPingLimitable() {
+ global $wgRateLimitsExcludedGroups;
+ return array_intersect($this->getEffectiveGroups(), $wgRateLimitsExcludedGroups) == array();
+ }
+
+ /**
* Primitive rate limits: enforce maximum actions per time period
* to put a brake on flooding.
*
@@ -927,24 +990,22 @@ class User {
* @public
*/
function pingLimiter( $action='edit' ) {
-
+
# Call the 'PingLimiter' hook
$result = false;
if( !wfRunHooks( 'PingLimiter', array( &$this, $action, $result ) ) ) {
return $result;
}
-
+
global $wgRateLimits, $wgRateLimitsExcludedGroups;
if( !isset( $wgRateLimits[$action] ) ) {
return false;
}
-
+
# Some groups shouldn't trigger the ping limiter, ever
- foreach( $this->getGroups() as $group ) {
- if( array_search( $group, $wgRateLimitsExcludedGroups ) !== false )
- return false;
- }
-
+ if( !$this->isPingLimitable() )
+ return false;
+
global $wgMemc, $wgRateLimitLog;
wfProfileIn( __METHOD__ );
@@ -1018,14 +1079,13 @@ class User {
wfProfileIn( __METHOD__ );
wfDebug( __METHOD__.": enter\n" );
- if ( $wgBlockAllowsUTEdit && $title->getText() === $this->getName() &&
- $title->getNamespace() == NS_USER_TALK )
- {
+ wfDebug( __METHOD__.": asking isBlocked()\n" );
+ $blocked = $this->isBlocked( $bFromSlave );
+ # If a user's name is suppressed, they cannot make edits anywhere
+ if ( !$this->mHideName && $wgBlockAllowsUTEdit && $title->getText() === $this->getName() &&
+ $title->getNamespace() == NS_USER_TALK ) {
$blocked = false;
wfDebug( __METHOD__.": self-talk page, ignoring any blocks\n" );
- } else {
- wfDebug( __METHOD__.": asking isBlocked()\n" );
- $blocked = $this->isBlocked( $bFromSlave );
}
wfProfileOut( __METHOD__ );
return $blocked;
@@ -1076,7 +1136,8 @@ class User {
} else {
$this->load();
if ( $this->mName === false ) {
- $this->mName = wfGetIP();
+ # Clean up IPs
+ $this->mName = IP::sanitizeIP( wfGetIP() );
}
return $this->mName;
}
@@ -1121,11 +1182,11 @@ class User {
global $wgMemc;
$key = wfMemcKey( 'newtalk', 'ip', $this->getName() );
$newtalk = $wgMemc->get( $key );
- if( is_integer( $newtalk ) ) {
+ if( $newtalk != "" ) {
$this->mNewtalk = (bool)$newtalk;
} else {
$this->mNewtalk = $this->checkNewtalk( 'user_ip', $this->getName() );
- $wgMemc->set( $key, $this->mNewtalk, time() ); // + 1800 );
+ $wgMemc->set( $key, (int)$this->mNewtalk, time() + 1800 );
}
} else {
$this->mNewtalk = $this->checkNewtalk( 'user_id', $this->mId );
@@ -1162,7 +1223,7 @@ class User {
* @private
*/
function checkNewtalk( $field, $id ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$ok = $dbr->selectField( 'user_newtalk', $field,
array( $field => $id ), __METHOD__ );
return $ok !== false;
@@ -1179,7 +1240,7 @@ class User {
wfDebug( __METHOD__." already set ($field, $id), ignoring\n" );
return false;
}
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->insert( 'user_newtalk',
array( $field => $id ),
__METHOD__,
@@ -1199,7 +1260,7 @@ class User {
wfDebug( __METHOD__.": already gone ($field, $id), ignoring\n" );
return false;
}
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'user_newtalk',
array( $field => $id ),
__METHOD__ );
@@ -1284,7 +1345,7 @@ class User {
if( $this->mId ) {
$this->mTouched = self::newTouchedTimestamp();
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->update( 'user',
array( 'user_touched' => $dbw->timestamp( $this->mTouched ) ),
array( 'user_id' => $this->mId ),
@@ -1338,11 +1399,23 @@ class User {
$wgMinimalPasswordLength ) );
}
}
-
+
if( !$wgAuth->setPassword( $this, $str ) ) {
throw new PasswordError( wfMsg( 'externaldberror' ) );
}
+ $this->setInternalPassword( $str );
+
+ return true;
+ }
+
+ /**
+ * Set the password and reset the random token no matter
+ * what.
+ *
+ * @param string $str
+ */
+ function setInternalPassword( $str ) {
$this->load();
$this->setToken();
@@ -1354,10 +1427,7 @@ class User {
}
$this->mNewpassword = '';
$this->mNewpassTime = null;
-
- return true;
}
-
/**
* Set the random token (used for persistent authentication)
* Called from loadDefaults() among other places.
@@ -1546,12 +1616,12 @@ class User {
if( $this->mId ) {
$this->mEffectiveGroups[] = 'user';
- global $wgAutoConfirmAge;
+ global $wgAutoConfirmAge, $wgAutoConfirmCount;
+
$accountAge = time() - wfTimestampOrNull( TS_UNIX, $this->mRegistration );
- if( $accountAge >= $wgAutoConfirmAge ) {
+ if( $accountAge >= $wgAutoConfirmAge && $this->getEditCount() >= $wgAutoConfirmCount ) {
$this->mEffectiveGroups[] = 'autoconfirmed';
}
-
# Implicit group for users whose email addresses are confirmed
global $wgEmailAuthentication;
if( self::isValidEmailAddr( $this->mEmail ) ) {
@@ -1566,15 +1636,29 @@ class User {
}
return $this->mEffectiveGroups;
}
-
+
+ /* Return the edit count for the user. This is where User::edits should have been */
+ function getEditCount() {
+ if ($this->mId) {
+ if ( !isset( $this->mEditCount ) ) {
+ /* Populate the count, if it has not been populated yet */
+ $this->mEditCount = User::edits($this->mId);
+ }
+ return $this->mEditCount;
+ } else {
+ /* nil */
+ return null;
+ }
+ }
+
/**
* Add the user to the given group.
* This takes immediate effect.
- * @string $group
+ * @param string $group
*/
function addGroup( $group ) {
$this->load();
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
if( $this->getId() ) {
$dbw->insert( 'user_groups',
array(
@@ -1594,11 +1678,11 @@ class User {
/**
* Remove the user from the given group.
* This takes immediate effect.
- * @string $group
+ * @param string $group
*/
function removeGroup( $group ) {
$this->load();
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'user_groups',
array(
'ug_user' => $this->getID(),
@@ -1750,7 +1834,7 @@ class User {
// If the page is watched by the user (or may be watched), update the timestamp on any
// any matching rows
if ( $watched ) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->update( 'watchlist',
array( /* SET */
'wl_notificationtimestamp' => NULL
@@ -1781,7 +1865,7 @@ class User {
}
if( $currentUser != 0 ) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->update( 'watchlist',
array( /* SET */
'wl_notificationtimestamp' => NULL
@@ -1865,7 +1949,7 @@ class User {
/**
* Save object settings into database
- * @fixme Only rarely do all these fields need to be set!
+ * @todo Only rarely do all these fields need to be set!
*/
function saveSettings() {
$this->load();
@@ -1874,7 +1958,7 @@ class User {
$this->mTouched = self::newTouchedTimestamp();
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->update( 'user',
array( /* SET */
'user_name' => $this->mName,
@@ -1902,7 +1986,7 @@ class User {
$s = trim( $this->getName() );
if ( 0 == strcmp( '', $s ) ) return 0;
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$id = $dbr->selectField( 'user', 'user_id', array( 'user_name' => $s ), __METHOD__ );
if ( $id === false ) {
$id = 0;
@@ -1933,7 +2017,7 @@ class User {
$user->mOptions = $params['options'] + $user->mOptions;
unset( $params['options'] );
}
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
$fields = array(
'user_id' => $seqVal,
@@ -1966,7 +2050,7 @@ class User {
*/
function addToDatabase() {
$this->load();
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
$dbw->insert( 'user',
array(
@@ -2096,7 +2180,7 @@ class User {
if ( isset( $res ) )
return $res;
else {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
return $res = $dbr->selectField( 'user', 'max(user_id)', false, 'User::getMaxID' );
}
}
@@ -2272,7 +2356,7 @@ class User {
$token = $this->generateToken( $this->mId . $this->mEmail . $expires );
$hash = md5( $token );
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->update( 'user',
array( 'user_email_token' => $hash,
'user_email_token_expires' => $dbw->timestamp( $expires ) ),
@@ -2385,13 +2469,12 @@ class User {
* @static
*/
static function getGroupName( $group ) {
+ MessageCache::loadAllMessages();
$key = "group-$group";
$name = wfMsg( $key );
- if( $name == '' || wfEmptyMsg( $key, $name ) ) {
- return $group;
- } else {
- return $name;
- }
+ return $name == '' || wfEmptyMsg( $key, $name )
+ ? $group
+ : $name;
}
/**
@@ -2400,13 +2483,12 @@ class User {
* @static
*/
static function getGroupMember( $group ) {
+ MessageCache::loadAllMessages();
$key = "group-$group-member";
$name = wfMsg( $key );
- if( $name == '' || wfEmptyMsg( $key, $name ) ) {
- return $group;
- } else {
- return $name;
- }
+ return $name == '' || wfEmptyMsg( $key, $name )
+ ? $group
+ : $name;
}
/**
@@ -2431,6 +2513,7 @@ class User {
* @return mixed
*/
static function getGroupPage( $group ) {
+ MessageCache::loadAllMessages();
$page = wfMsgForContent( 'grouppage-' . $group );
if( !wfEmptyMsg( 'grouppage-' . $group, $page ) ) {
$title = Title::newFromText( $page );
@@ -2455,7 +2538,7 @@ class User {
if( $title ) {
global $wgUser;
$sk = $wgUser->getSkin();
- return $sk->makeLinkObj( $title, $text );
+ return $sk->makeLinkObj( $title, htmlspecialchars( $text ) );
} else {
return $text;
}
@@ -2521,6 +2604,8 @@ class User {
__METHOD__ );
}
}
+ // edit count in user cache too
+ $this->invalidateCache();
}
}
diff --git a/includes/UserMailer.php b/includes/UserMailer.php
index 0101f744..9f5f178c 100644
--- a/includes/UserMailer.php
+++ b/includes/UserMailer.php
@@ -22,7 +22,6 @@
* @author <brion@pobox.com>
* @author <mail@tgries.de>
*
- * @package MediaWiki
*/
/**
@@ -33,12 +32,17 @@ function wfRFC822Phrase( $phrase ) {
return '"' . $phrase . '"';
}
+/**
+ * Stores a single person's name and email address.
+ * These are passed in via the constructor, and will be returned in SMTP
+ * header format when requested.
+ */
class MailAddress {
/**
* @param mixed $address String with an email address, or a User object
* @param string $name Human-readable name if a string address is given
*/
- function MailAddress( $address, $name=null ) {
+ function __construct( $address, $name=null ) {
if( is_object( $address ) && $address instanceof User ) {
$this->address = $address->getEmail();
$this->name = $address->getName();
@@ -78,9 +82,9 @@ class MailAddress {
* @param $from MailAddress: sender's email
* @param $subject String: email's subject.
* @param $body String: email's text.
- * @param $replyto String: optional reply-to email (default: false).
+ * @param $replyto String: optional reply-to email (default: null).
*/
-function userMailer( $to, $from, $subject, $body, $replyto=false ) {
+function userMailer( $to, $from, $subject, $body, $replyto=null ) {
global $wgUser, $wgSMTP, $wgOutputEncoding, $wgErrorString;
if (is_array( $wgSMTP )) {
@@ -92,7 +96,7 @@ function userMailer( $to, $from, $subject, $body, $replyto=false ) {
$headers['From'] = $from->toString();
$headers['To'] = $to->toString();
if ( $replyto ) {
- $headers['Reply-To'] = $replyto;
+ $headers['Reply-To'] = $replyto->toString();
}
$headers['Subject'] = wfQuotedPrintable( $subject );
$headers['Date'] = date( 'r' );
@@ -141,7 +145,7 @@ function userMailer( $to, $from, $subject, $body, $replyto=false ) {
"X-Mailer: MediaWiki mailer$endl".
'From: ' . $from->toString();
if ($replyto) {
- $headers .= "{$endl}Reply-To: $replyto";
+ $headers .= "{$endl}Reply-To: " . $replyto->toString();
}
$dest = $to->toString();
@@ -189,7 +193,6 @@ function mailErrorHandler( $code, $string ) {
*
* Visit the documentation pages under http://meta.wikipedia.com/Enotif
*
- * @package MediaWiki
*
*/
class EmailNotification {
@@ -226,6 +229,13 @@ class EmailNotification {
$enotifusertalkpage = ($isUserTalkPage && $wgEnotifUserTalk);
$enotifwatchlistpage = $wgEnotifWatchlist;
+ $this->title =& $title;
+ $this->timestamp = $timestamp;
+ $this->summary = $summary;
+ $this->minorEdit = $minorEdit;
+ $this->oldid = $oldid;
+ $this->composeCommonMailtext();
+
if ( (!$minorEdit || $wgEnotifMinorEdits) ) {
if( $wgEnotifWatchlist ) {
// Send updates to watchers other than the current editor
@@ -247,7 +257,7 @@ class EmailNotification {
$userCondition = false;
}
if( $userCondition ) {
- $dbr =& wfGetDB( DB_MASTER );
+ $dbr = wfGetDB( DB_MASTER );
$res = $dbr->select( 'watchlist', array( 'wl_user' ),
array(
@@ -260,13 +270,7 @@ class EmailNotification {
# if anyone is watching ... set up the email message text which is
# common for all receipients ...
if ( $dbr->numRows( $res ) > 0 ) {
- $this->title =& $title;
- $this->timestamp = $timestamp;
- $this->summary = $summary;
- $this->minorEdit = $minorEdit;
- $this->oldid = $oldid;
- $this->composeCommonMailtext();
$watchingUser = new User();
# ... now do for all watching users ... if the options fit
@@ -291,10 +295,16 @@ class EmailNotification {
} # if anyone is watching
} # if $wgEnotifWatchlist = true
+ global $wgUsersNotifedOnAllChanges;
+ foreach ( $wgUsersNotifedOnAllChanges as $name ) {
+ $user = User::newFromName( $name );
+ $this->composeAndSendPersonalisedMail( $user );
+ }
+
if ( $wgShowUpdatedMarker || $wgEnotifWatchlist ) {
# mark the changed watch-listed page with a timestamp, so that the page is
# listed with an "updated since your last visit" icon in the watch list, ...
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$success = $dbw->update( 'watchlist',
array( /* SET */
'wl_notificationtimestamp' => $dbw->timestamp($timestamp)
@@ -370,7 +380,7 @@ class EmailNotification {
}
} else {
$from = $adminAddress;
- $replyto = $wgNoReplyAddress;
+ $replyto = new MailAddress( $wgNoReplyAddress );
}
if( $wgUser->isIP( $name ) ) {
diff --git a/includes/Utf8Case.php b/includes/Utf8Case.php
index 8c7fdd0b..279c0e32 100644
--- a/includes/Utf8Case.php
+++ b/includes/Utf8Case.php
@@ -7,8 +7,7 @@
*
* These are pulled from memcached if possible, as this is faster than filling
* up a big array manually.
- * @package MediaWiki
- * @subpackage Language
+ * @addtogroup Language
*/
/*
diff --git a/includes/WatchedItem.php b/includes/WatchedItem.php
index 788774fb..b0376e3d 100644
--- a/includes/WatchedItem.php
+++ b/includes/WatchedItem.php
@@ -1,12 +1,10 @@
<?php
/**
*
- * @package MediaWiki
*/
/**
*
- * @package MediaWiki
*/
class WatchedItem {
var $mTitle, $mUser;
@@ -32,30 +30,17 @@ class WatchedItem {
}
/**
- * Returns the memcached key for this item
- */
- function watchKey() {
- return wfMemcKey( 'watchlist', 'user', $this->id, 'page', $this->ns, $this->ti );
- }
-
- /**
* Is mTitle being watched by mUser?
*/
function isWatched() {
# Pages and their talk pages are considered equivalent for watching;
# remember that talk namespaces are numbered as page namespace+1.
- global $wgMemc;
$fname = 'WatchedItem::isWatched';
- $key = $this->watchKey();
- $iswatched = $wgMemc->get( $key );
- if( is_integer( $iswatched ) ) return $iswatched;
-
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'watchlist', 1, array( 'wl_user' => $this->id, 'wl_namespace' => $this->ns,
'wl_title' => $this->ti ), $fname );
$iswatched = ($dbr->numRows( $res ) > 0) ? 1 : 0;
- $wgMemc->set( $key, $iswatched );
return $iswatched;
}
@@ -68,7 +53,7 @@ class WatchedItem {
// Use INSERT IGNORE to avoid overwriting the notification timestamp
// if there's already an entry for this page
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->insert( 'watchlist',
array(
'wl_user' => $this->id,
@@ -87,18 +72,15 @@ class WatchedItem {
'wl_notificationtimestamp' => NULL
), $fname, 'IGNORE' );
- global $wgMemc;
- $wgMemc->set( $this->watchkey(), 1 );
wfProfileOut( $fname );
return true;
}
function removeWatch() {
- global $wgMemc;
$fname = 'WatchedItem::removeWatch';
$success = false;
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'watchlist',
array(
'wl_user' => $this->id,
@@ -125,9 +107,6 @@ class WatchedItem {
if ( $dbw->affectedRows() ) {
$success = true;
}
- if ( $success ) {
- $wgMemc->set( $this->watchkey(), 0 );
- }
return $success;
}
@@ -155,7 +134,7 @@ class WatchedItem {
$oldtitle = $ot->getDBkey();
$newtitle = $nt->getDBkey();
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$res = $dbw->select( 'watchlist', 'wl_user',
array( 'wl_namespace' => $oldnamespace, 'wl_title' => $oldtitle ),
$fname, 'FOR UPDATE'
diff --git a/includes/WebRequest.php b/includes/WebRequest.php
index 7648b75f..53273a22 100644
--- a/includes/WebRequest.php
+++ b/includes/WebRequest.php
@@ -1,7 +1,6 @@
<?php
/**
* Deal with importing all those nasssty globals and things
- * @package MediaWiki
*/
# Copyright (C) 2003 Brion Vibber <brion@pobox.com>
@@ -22,6 +21,15 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# http://www.gnu.org/copyleft/gpl.html
+
+/**
+ * Some entry points may use this file without first enabling the
+ * autoloader.
+ */
+if ( !function_exists( '__autoload' ) ) {
+ require_once( dirname(__FILE__) . '/normal/UtfNormal.php' );
+}
+
/**
* The WebRequest class encapsulates getting at data passed in the
* URL or via a POSTed form, handling remove of "magic quotes" slashes,
@@ -32,19 +40,9 @@
* you want to pass arbitrary data to some function in place of the web
* input.
*
- * @package MediaWiki
- */
-
-/**
- * Some entry points may use this file without first enabling the
- * autoloader.
*/
-if ( !function_exists( '__autoload' ) ) {
- require_once( dirname(__FILE__) . '/normal/UtfNormal.php' );
-}
-
class WebRequest {
- function WebRequest() {
+ function __construct() {
$this->checkMagicQuotes();
global $wgUsePathInfo;
if ( $wgUsePathInfo ) {
@@ -142,7 +140,9 @@ class WebRequest {
/**
* Fetch a scalar from the input or return $default if it's not set.
- * Returns a string. Arrays are discarded.
+ * Returns a string. Arrays are discarded. Useful for
+ * non-freeform text inputs (e.g. predefined internal text keys
+ * selected by a drop-down menu). For freeform input, see getText().
*
* @param string $name
* @param string $default optional default (or NULL)
@@ -252,7 +252,9 @@ class WebRequest {
* Fetch a text string from the given array or return $default if it's not
* set. \r is stripped from the text, and with some language modules there
* is an input transliteration applied. This should generally be used for
- * form <textarea> and <input> fields.
+ * form <textarea> and <input> fields. Used for user-supplied freeform text
+ * input (for which input transformations may be required - e.g. Esperanto
+ * x-coding).
*
* @param string $name
* @param string $default optional
@@ -303,10 +305,15 @@ class WebRequest {
* Returns true if there is a session cookie set.
* This does not necessarily mean that the user is logged in!
*
+ * If you want to check for an open session, use session_id()
+ * instead; that will also tell you if the session was opened
+ * during the current request (in which case the cookie will
+ * be sent back to the client at the end of the script run).
+ *
* @return bool
*/
function checkSessionCookie() {
- return isset( $_COOKIE[ini_get('session.name')] );
+ return isset( $_COOKIE[session_name()] );
}
/**
@@ -328,6 +335,14 @@ class WebRequest {
"REQUEST_URI or SCRIPT_NAME. Report details of your " .
"web server configuration to http://bugzilla.wikimedia.org/" );
}
+ // User-agents should not send a fragment with the URI, but
+ // if they do, and the web server passes it on to us, we
+ // need to strip it or we get false-positive redirect loops
+ // or weird output URLs
+ $hash = strpos( $base, '#' );
+ if( $hash !== false ) {
+ $base = substr( $base, 0, $hash );
+ }
if( $base{0} == '/' ) {
return $base;
} else {
@@ -483,7 +498,6 @@ class WebRequest {
/**
* WebRequest clone which takes values from a provided array.
*
- * @package MediaWiki
*/
class FauxRequest extends WebRequest {
var $data = null;
diff --git a/includes/WebResponse.php b/includes/WebResponse.php
index e159152e..92343195 100644
--- a/includes/WebResponse.php
+++ b/includes/WebResponse.php
@@ -1,18 +1,20 @@
<?php
-
-/*
- * Allow programs to request this object from WebRequest::response() and handle all outputting (or lack of outputting) via it.
+/**
+ * Allow programs to request this object from WebRequest::response()
+ * and handle all outputting (or lack of outputting) via it.
*/
-
class WebResponse {
+
+ /** Output a HTTP header */
function header($string, $replace=true) {
header($string,$replace);
}
-
+
+ /** Set the browser cookie */
function setcookie($name, $value, $expire) {
global $wgCookiePath, $wgCookieDomain, $wgCookieSecure;
setcookie($name,$value,$expire, $wgCookiePath, $wgCookieDomain, $wgCookieSecure);
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/WebStart.php b/includes/WebStart.php
index 37582290..55c96488 100644
--- a/includes/WebStart.php
+++ b/includes/WebStart.php
@@ -85,6 +85,18 @@ if( !file_exists( './LocalSettings.php' ) ) {
# Include this site setttings
require_once( './LocalSettings.php' );
wfProfileOut( 'WebStart.php-conf' );
+wfProfileIn( 'WebStart.php-ob_start' );
+
+# Initialise output buffering
+if ( ob_get_level() ) {
+ # Someone's been mixing configuration data with code!
+ # How annoying.
+} elseif ( !defined( 'MW_NO_OUTPUT_BUFFER' ) ) {
+ require_once( './includes/OutputHandler.php' );
+ ob_start( 'wfOutputHandler' );
+}
+
+wfProfileOut( 'WebStart.php-ob_start' );
if ( !defined( 'MW_NO_SETUP' ) ) {
require_once( './includes/Setup.php' );
diff --git a/includes/Wiki.php b/includes/Wiki.php
index 06248b35..612e58ee 100644
--- a/includes/Wiki.php
+++ b/includes/Wiki.php
@@ -7,14 +7,12 @@ class MediaWiki {
var $GET; /* Stores the $_GET variables at time of creation, can be changed */
var $params = array();
-
- /**
- * Constructor
- */
- function MediaWiki () {
+
+ /** Constructor. It just save the $_GET variable */
+ function __construct() {
$this->GET = $_GET;
}
-
+
/**
* Stores key/value pairs to circumvent global variables
* Note that keys are case-insensitive!
@@ -23,7 +21,7 @@ class MediaWiki {
$key = strtolower( $key );
$this->params[$key] =& $value;
}
-
+
/**
* Retrieves key/value pairs to circumvent global variables
* Note that keys are case-insensitive!
@@ -35,7 +33,7 @@ class MediaWiki {
}
return $default;
}
-
+
/**
* Initialization of ... everything
@return Article either the object to become $wgArticle, or NULL
@@ -57,7 +55,23 @@ class MediaWiki {
wfProfileOut( 'MediaWiki::initialize' );
return $article;
}
-
+
+ function checkMaxLag( $maxLag ) {
+ global $wgLoadBalancer;
+ list( $host, $lag ) = $wgLoadBalancer->getMaxLag();
+ if ( $lag > $maxLag ) {
+ header( 'HTTP/1.1 503 Service Unavailable' );
+ header( 'Retry-After: ' . max( intval( $maxLag ), 5 ) );
+ header( 'X-Database-Lag: ' . intval( $lag ) );
+ header( 'Content-Type: text/plain' );
+ echo "Waiting for $host: $lag seconds lagged\n";
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+
/**
* Checks some initial queries
* Note that $title here is *not* a Title object, but a string!
@@ -66,10 +80,10 @@ class MediaWiki {
if ($request->getVal( 'printable' ) == 'yes') {
$output->setPrintable();
}
-
+
$ret = NULL ;
-
-
+
+
if ( '' == $title && 'delete' != $action ) {
$ret = Title::newMainPage();
} elseif ( $curid = $request->getInt( 'curid' ) ) {
@@ -82,19 +96,19 @@ class MediaWiki {
*/
if( count($lang->getVariants()) > 1 && !is_null($ret) && $ret->getArticleID() == 0 )
$lang->findVariantLink( $title, $ret );
-
+
}
return $ret ;
}
-
+
/**
* Checks for search query and anon-cannot-read case
*/
function preliminaryChecks ( &$title, &$output, $request ) {
-
+
# Debug statement for user levels
// print_r($wgUser);
-
+
$search = $request->getText( 'search' );
if( !is_null( $search ) && $search !== '' ) {
// Compatibility with old search URLs which didn't use Special:Search
@@ -111,16 +125,16 @@ class MediaWiki {
$output->output();
exit;
}
-
+
}
-
+
/**
* Initialize the object to be known as $wgArticle for special cases
*/
function initializeSpecialCases ( &$title, &$output, $request ) {
global $wgRequest;
wfProfileIn( 'MediaWiki::initializeSpecialCases' );
-
+
$search = $this->getVal('Search');
$action = $this->getVal('Action');
if( !$this->getVal('DisableInternalSearch') && !is_null( $search ) && $search !== '' ) {
@@ -150,13 +164,13 @@ class MediaWiki {
{
$targetUrl = $title->getFullURL();
// Redirect to canonical url, make it a 301 to allow caching
- global $wgServer, $wgUsePathInfo;
+ global $wgUsePathInfo;
if( $targetUrl == $wgRequest->getFullRequestURL() ) {
$message = "Redirect loop detected!\n\n" .
"This means the wiki got confused about what page was " .
"requested; this sometimes happens when moving a wiki " .
"to a new server or changing the server configuration.\n\n";
-
+
if( $wgUsePathInfo ) {
$message .= "The wiki is trying to interpret the page " .
"title from the URL path portion (PATH_INFO), which " .
@@ -206,7 +220,7 @@ class MediaWiki {
// FIXME: where should this go?
$title = Title::makeTitle( NS_IMAGE, $title->getDBkey() );
}
-
+
switch( $title->getNamespace() ) {
case NS_IMAGE:
return new ImagePage( $title );
@@ -216,7 +230,7 @@ class MediaWiki {
return new Article( $title );
}
}
-
+
/**
* Initialize the object to be known as $wgArticle for "standard" actions
* Create an Article object for the page, following redirects if needed.
@@ -228,17 +242,17 @@ class MediaWiki {
function initializeArticle( $title, $request ) {
global $wgTitle;
wfProfileIn( 'MediaWiki::initializeArticle' );
-
+
$action = $this->getVal('Action');
$article = $this->articleFromTitle( $title );
-
+
// Namespace might change when using redirects
if( $action == 'view' && !$request->getVal( 'oldid' ) &&
$request->getVal( 'redirect' ) != 'no' ) {
-
- $dbr =& wfGetDB(DB_SLAVE);
+
+ $dbr = wfGetDB(DB_SLAVE);
$article->loadPageData($article->pageDataFromTitle($dbr, $title));
-
+
/* Follow redirects only for... redirects */
if ($article->mIsRedirect) {
$target = $article->followRedirect();
@@ -290,7 +304,7 @@ class MediaWiki {
*/
function doUpdates ( &$updates ) {
wfProfileIn( 'MediaWiki::doUpdates' );
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
foreach( $updates as $up ) {
$up->doUpdate();
@@ -307,7 +321,7 @@ class MediaWiki {
*/
function doJobs() {
global $wgJobRunRate;
-
+
if ( $wgJobRunRate <= 0 || wfReadOnly() ) {
return;
}
@@ -335,7 +349,7 @@ class MediaWiki {
wfDebugLog( 'jobqueue', $output );
}
}
-
+
/**
* Ends this task peacefully
*/
@@ -401,24 +415,26 @@ class MediaWiki {
showCreditsPage( $article );
break;
case 'submit':
- if( !$this->getVal( 'CommandLineMode' ) && !$request->checkSessionCookie() ) {
+ if( session_id() == '' ) {
/* Send a cookie so anons get talk message notifications */
- User::SetupSession();
+ wfSetupSession();
}
/* Continue... */
case 'edit':
- $internal = $request->getVal( 'internaledit' );
- $external = $request->getVal( 'externaledit' );
- $section = $request->getVal( 'section' );
- $oldid = $request->getVal( 'oldid' );
- if( !$this->getVal( 'UseExternalEditor' ) || $action=='submit' || $internal ||
- $section || $oldid || ( !$user->getOption( 'externaleditor' ) && !$external ) ) {
- $editor = new EditPage( $article );
- $editor->submit();
- } elseif( $this->getVal( 'UseExternalEditor' ) && ( $external || $user->getOption( 'externaleditor' ) ) ) {
- $mode = $request->getVal( 'mode' );
- $extedit = new ExternalEdit( $article, $mode );
- $extedit->edit();
+ if( wfRunHooks( 'CustomEditor', array( $article, $user ) ) ) {
+ $internal = $request->getVal( 'internaledit' );
+ $external = $request->getVal( 'externaledit' );
+ $section = $request->getVal( 'section' );
+ $oldid = $request->getVal( 'oldid' );
+ if( !$this->getVal( 'UseExternalEditor' ) || $action=='submit' || $internal ||
+ $section || $oldid || ( !$user->getOption( 'externaleditor' ) && !$external ) ) {
+ $editor = new EditPage( $article );
+ $editor->submit();
+ } elseif( $this->getVal( 'UseExternalEditor' ) && ( $external || $user->getOption( 'externaleditor' ) ) ) {
+ $mode = $request->getVal( 'mode' );
+ $extedit = new ExternalEdit( $article, $mode );
+ $extedit->edit();
+ }
}
break;
case 'history':
@@ -440,7 +456,6 @@ class MediaWiki {
}
wfProfileOut( 'MediaWiki::performAction' );
-
}
}; /* End of class MediaWiki */
diff --git a/includes/WikiError.php b/includes/WikiError.php
index 029184d4..064db61a 100644
--- a/includes/WikiError.php
+++ b/includes/WikiError.php
@@ -19,19 +19,18 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
- * @package MediaWiki
*/
/**
* Since PHP4 doesn't have exceptions, here's some error objects
* loosely modeled on the standard PEAR_Error model...
- * @package MediaWiki
+ * @addtogroup Exception
*/
class WikiError {
/**
* @param string $message
*/
- function WikiError( $message ) {
+ function __construct( $message ) {
$this->mMessage = $message;
}
@@ -66,7 +65,7 @@ class WikiError {
/**
* Localized error message object
- * @package MediaWiki
+ * @addtogroup Exception
*/
class WikiErrorMsg extends WikiError {
/**
@@ -81,8 +80,8 @@ class WikiErrorMsg extends WikiError {
}
/**
- * @package MediaWiki
* @todo document
+ * @addtogroup Exception
*/
class WikiXmlError extends WikiError {
/**
diff --git a/includes/Xml.php b/includes/Xml.php
index 67dda7fe..0fedcfa0 100644
--- a/includes/Xml.php
+++ b/includes/Xml.php
@@ -50,7 +50,9 @@ class Xml {
$attribs = array_map( array( 'UtfNormal', 'cleanUp' ), $attribs );
}
if( $contents ) {
+ wfProfileIn( __METHOD__ . '-norm' );
$contents = UtfNormal::cleanUp( $contents );
+ wfProfileOut( __METHOD__ . '-norm' );
}
return self::element( $element, $attribs, $contents );
}
@@ -60,6 +62,14 @@ class Xml {
public static function closeElement( $element ) { return "</$element>"; }
/**
+ * Same as <link>element</link>, but does not escape contents. Handy when the
+ * content you have is already valid xml.
+ */
+ public static function tags( $element, $attribs = null, $contents ) {
+ return self::element( $element, $attribs, null ) . $contents . "</$element>";
+ }
+
+ /**
* Create a namespace selector
*
* @param $selected Mixed: the namespace which should be selected, default ''
@@ -67,7 +77,7 @@ class Xml {
* @param $includehidden Bool: include hidden namespaces?
* @return String: Html string containing the namespace selector
*/
- public static function &namespaceSelector($selected = '', $allnamespaces = null, $includehidden=false) {
+ public static function namespaceSelector($selected = '', $allnamespaces = null, $includehidden=false) {
global $wgContLang;
if( $selected !== '' ) {
if( is_null( $selected ) ) {
diff --git a/includes/XmlFunctions.php b/includes/XmlFunctions.php
index cbdcf5c4..326c4953 100644
--- a/includes/XmlFunctions.php
+++ b/includes/XmlFunctions.php
@@ -15,7 +15,7 @@ function wfOpenElement( $element, $attribs = null ) {
function wfCloseElement( $element ) {
return "</$element>";
}
-function &HTMLnamespaceselector($selected = '', $allnamespaces = null, $includehidden=false) {
+function HTMLnamespaceselector($selected = '', $allnamespaces = null, $includehidden=false) {
return Xml::namespaceSelector( $selected, $allnamespaces, $includehidden );
}
function wfSpan( $text, $class, $attribs=array() ) {
diff --git a/includes/ZhClient.php b/includes/ZhClient.php
index 9c9461d5..fe965f65 100644
--- a/includes/ZhClient.php
+++ b/includes/ZhClient.php
@@ -1,12 +1,10 @@
<?php
/**
- * @package MediaWiki
*/
/**
* Client for querying zhdaemon
*
- * @package MediaWiki
*/
class ZhClient {
var $mHost, $mPort, $mFP, $mConnected;
diff --git a/includes/ZhConversion.php b/includes/ZhConversion.php
index e63281eb..1ccd6a7e 100644
--- a/includes/ZhConversion.php
+++ b/includes/ZhConversion.php
@@ -5,7 +5,6 @@
* Automatically generated using code and data in includes/zhtable/
* Do not modify directly!
*
- * @package MediaWiki
*/
$zh2TW=array(
diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php
index 1a9c1e3d..c4218825 100644
--- a/includes/api/ApiBase.php
+++ b/includes/api/ApiBase.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Sep 5, 2006
*
@@ -24,6 +23,10 @@
* http://www.gnu.org/copyleft/gpl.html
*/
+/**
+ * @todo Document - e.g. Provide top-level description of this class.
+ * @addtogroup API
+ */
abstract class ApiBase {
// These constants allow modules to specify exactly how to treat incomming parameters.
@@ -527,7 +530,7 @@ abstract class ApiBase {
public abstract function getVersion();
public static function getBaseVersion() {
- return __CLASS__ . ': $Id: ApiBase.php 17880 2006-11-23 08:25:56Z nickj $';
+ return __CLASS__ . ': $Id: ApiBase.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/api/ApiFeedWatchlist.php b/includes/api/ApiFeedWatchlist.php
index 7d1c1519..7918ee0e 100644
--- a/includes/api/ApiFeedWatchlist.php
+++ b/includes/api/ApiFeedWatchlist.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Oct 13, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ("ApiBase.php");
}
+/**
+ * @addtogroup API
+ */
class ApiFeedWatchlist extends ApiBase {
public function __construct($main, $action) {
@@ -119,7 +121,7 @@ class ApiFeedWatchlist extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFeedWatchlist.php 17987 2006-11-29 05:45:03Z nickj $';
+ return __CLASS__ . ': $Id: ApiFeedWatchlist.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
?>
diff --git a/includes/api/ApiFormatBase.php b/includes/api/ApiFormatBase.php
index 338a6c07..192c51a7 100644
--- a/includes/api/ApiFormatBase.php
+++ b/includes/api/ApiFormatBase.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Sep 19, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiBase.php');
}
+/**
+ * @addtogroup API
+ */
abstract class ApiFormatBase extends ApiBase {
private $mIsHtml, $mFormat;
@@ -170,12 +172,13 @@ for more information.
}
public static function getBaseVersion() {
- return __CLASS__ . ': $Id: ApiFormatBase.php 19434 2007-01-18 02:04:11Z brion $';
+ return __CLASS__ . ': $Id: ApiFormatBase.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
/**
* This printer is used to wrap an instance of the Feed class
+ * @addtogroup API
*/
class ApiFormatFeedWrapper extends ApiFormatBase {
@@ -226,7 +229,7 @@ class ApiFormatFeedWrapper extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatBase.php 19434 2007-01-18 02:04:11Z brion $';
+ return __CLASS__ . ': $Id: ApiFormatBase.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
?>
diff --git a/includes/api/ApiFormatJson.php b/includes/api/ApiFormatJson.php
index 45c735c8..dd1847c4 100644
--- a/includes/api/ApiFormatJson.php
+++ b/includes/api/ApiFormatJson.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Sep 19, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiFormatBase.php');
}
+/**
+ * @addtogroup API
+ */
class ApiFormatJson extends ApiFormatBase {
private $mIsRaw;
@@ -63,7 +65,7 @@ class ApiFormatJson extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatJson.php 17374 2006-11-03 06:53:47Z yurik $';
+ return __CLASS__ . ': $Id: ApiFormatJson.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/api/ApiFormatJson_json.php b/includes/api/ApiFormatJson_json.php
index 375de7eb..2cd87930 100644
--- a/includes/api/ApiFormatJson_json.php
+++ b/includes/api/ApiFormatJson_json.php
@@ -45,15 +45,14 @@
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
-* @category
-* @package Services_JSON
+* @addtogroup API
* @author Michal Migurski <mike-json@teczno.com>
* @author Matt Knapp <mdknapp[at]gmail[dot]com>
* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
* @copyright 2005 Michal Migurski
* @version CVS: $Id: JSON.php,v 1.30 2006/03/08 16:10:20 migurski Exp $
* @license http://www.opensource.org/licenses/bsd-license.php
-* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
+* @see http://pear.php.net/pepr/pepr-proposal-show.php?id=198
*/
/**
@@ -92,26 +91,28 @@ define('SERVICES_JSON_LOOSE_TYPE', 16);
define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
/**
-* Converts to and from JSON format.
-*
-* Brief example of use:
-*
-* <code>
-* // create a new instance of Services_JSON
-* $json = new Services_JSON();
-*
-* // convert a complexe value to JSON notation, and send it to the browser
-* $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
-* $output = $json->encode($value);
-*
-* print($output);
-* // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
-*
-* // accept incoming POST data, assumed to be in JSON notation
-* $input = file_get_contents('php://input', 1000000);
-* $value = $json->decode($input);
-* </code>
-*/
+ * Converts to and from JSON format.
+ *
+ * Brief example of use:
+ *
+ * <code>
+ * // create a new instance of Services_JSON
+ * $json = new Services_JSON();
+ *
+ * // convert a complexe value to JSON notation, and send it to the browser
+ * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
+ * $output = $json->encode($value);
+ *
+ * print($output);
+ * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
+ *
+ * // accept incoming POST data, assumed to be in JSON notation
+ * $input = file_get_contents('php://input', 1000000);
+ * $value = $json->decode($input);
+ * </code>
+ *
+ * @addtogroup API
+ */
class Services_JSON
{
/**
@@ -813,6 +814,9 @@ class Services_JSON
if (class_exists('PEAR_Error')) {
+ /**
+ * @addtogroup API
+ */
class Services_JSON_Error extends PEAR_Error
{
function Services_JSON_Error($message = 'unknown error', $code = null,
@@ -826,6 +830,7 @@ if (class_exists('PEAR_Error')) {
/**
* @todo Ultimately, this class shall be descended from PEAR_Error
+ * @addtogroup API
*/
class Services_JSON_Error
{
diff --git a/includes/api/ApiFormatPhp.php b/includes/api/ApiFormatPhp.php
index 938ba032..add63362 100644
--- a/includes/api/ApiFormatPhp.php
+++ b/includes/api/ApiFormatPhp.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Oct 22, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiFormatBase.php');
}
+/**
+ * @addtogroup API
+ */
class ApiFormatPhp extends ApiFormatBase {
public function __construct($main, $format) {
@@ -48,7 +50,7 @@ class ApiFormatPhp extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatPhp.php 17374 2006-11-03 06:53:47Z yurik $';
+ return __CLASS__ . ': $Id: ApiFormatPhp.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/api/ApiFormatWddx.php b/includes/api/ApiFormatWddx.php
index e97b996c..bc720490 100644
--- a/includes/api/ApiFormatWddx.php
+++ b/includes/api/ApiFormatWddx.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Oct 22, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiFormatBase.php');
}
+/**
+ * @addtogroup API
+ */
class ApiFormatWddx extends ApiFormatBase {
public function __construct($main, $format) {
@@ -83,7 +85,7 @@ class ApiFormatWddx extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatWddx.php 17374 2006-11-03 06:53:47Z yurik $';
+ return __CLASS__ . ': $Id: ApiFormatWddx.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/api/ApiFormatXml.php b/includes/api/ApiFormatXml.php
index 2326ba42..7d54b441 100644
--- a/includes/api/ApiFormatXml.php
+++ b/includes/api/ApiFormatXml.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Sep 19, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiFormatBase.php');
}
+/**
+ * @addtogroup API
+ */
class ApiFormatXml extends ApiFormatBase {
private $mRootElemName = 'api';
@@ -139,7 +141,7 @@ class ApiFormatXml extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatXml.php 17374 2006-11-03 06:53:47Z yurik $';
+ return __CLASS__ . ': $Id: ApiFormatXml.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/api/ApiFormatYaml.php b/includes/api/ApiFormatYaml.php
index 2371903f..0107eb2b 100644
--- a/includes/api/ApiFormatYaml.php
+++ b/includes/api/ApiFormatYaml.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Sep 19, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiFormatBase.php');
}
+/**
+ * @addtogroup API
+ */
class ApiFormatYaml extends ApiFormatBase {
public function __construct($main, $format) {
@@ -48,7 +50,7 @@ class ApiFormatYaml extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatYaml.php 17374 2006-11-03 06:53:47Z yurik $';
+ return __CLASS__ . ': $Id: ApiFormatYaml.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/api/ApiFormatYaml_spyc.php b/includes/api/ApiFormatYaml_spyc.php
index 1ec8af48..a67bbb22 100644
--- a/includes/api/ApiFormatYaml_spyc.php
+++ b/includes/api/ApiFormatYaml_spyc.php
@@ -3,15 +3,14 @@
* Spyc -- A Simple PHP YAML Class
* @version 0.2.3 -- 2006-02-04
* @author Chris Wanstrath <chris@ozmm.org>
- * @link http://spyc.sourceforge.net/
+ * @see http://spyc.sourceforge.net/
* @copyright Copyright 2005-2006 Chris Wanstrath
* @license http://www.opensource.org/licenses/mit-license.php MIT License
- * @package Spyc
*/
/**
* A node, used by Spyc for parsing YAML.
- * @package Spyc
+ * @addtogroup API
*/
class YAMLNode {
/**#@+
@@ -20,7 +19,7 @@
*/
var $parent;
var $id;
- /**#@+*/
+ /**#@-*/
/**
* @access public
* @var mixed
@@ -59,7 +58,7 @@
* $parser = new Spyc;
* $array = $parser->load($file);
* </code>
- * @package Spyc
+ * @addtogroup API
*/
class Spyc {
@@ -340,7 +339,7 @@
var $_isInline;
var $_dumpIndent;
var $_dumpWordWrap;
- /**#@+*/
+ /**#@-*/
/**** Private Methods ****/
@@ -858,4 +857,4 @@
return $ret;
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/api/ApiHelp.php b/includes/api/ApiHelp.php
index 33fb67fd..7c5144fd 100644
--- a/includes/api/ApiHelp.php
+++ b/includes/api/ApiHelp.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Sep 6, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiBase.php');
}
+/**
+ * @addtogroup API
+ */
class ApiHelp extends ApiBase {
public function __construct($main, $action) {
@@ -49,7 +51,7 @@ class ApiHelp extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiHelp.php 16757 2006-10-03 05:41:55Z yurik $';
+ return __CLASS__ . ': $Id: ApiHelp.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/api/ApiLogin.php b/includes/api/ApiLogin.php
index d9697dc3..147d37a1 100644
--- a/includes/api/ApiLogin.php
+++ b/includes/api/ApiLogin.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Sep 19, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiBase.php');
}
+/**
+ * @addtogroup API
+ */
class ApiLogin extends ApiBase {
public function __construct($main, $action) {
@@ -116,7 +118,7 @@ class ApiLogin extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiLogin.php 17065 2006-10-17 02:11:29Z yurik $';
+ return __CLASS__ . ': $Id: ApiLogin.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
?>
diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php
index 606f022b..9a6b0f83 100644
--- a/includes/api/ApiMain.php
+++ b/includes/api/ApiMain.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Sep 4, 2006
*
@@ -31,6 +30,7 @@ if (!defined('MEDIAWIKI')) {
/**
* This is the main API class, used for both external and internal processing.
+ * @addtogroup API
*/
class ApiMain extends ApiBase {
@@ -336,7 +336,7 @@ class ApiMain extends ApiBase {
public function getVersion() {
$vers = array ();
- $vers[] = __CLASS__ . ': $Id: ApiMain.php 17987 2006-11-29 05:45:03Z nickj $';
+ $vers[] = __CLASS__ . ': $Id: ApiMain.php 21402 2007-04-20 08:55:14Z nickj $';
$vers[] = ApiBase :: getBaseVersion();
$vers[] = ApiFormatBase :: getBaseVersion();
$vers[] = ApiQueryBase :: getBaseVersion();
@@ -346,8 +346,9 @@ class ApiMain extends ApiBase {
}
/**
-* @desc This exception will be thrown when dieUsage is called to stop module execution.
-*/
+ * This exception will be thrown when dieUsage is called to stop module execution.
+ * @addtogroup API
+ */
class UsageException extends Exception {
private $mCodestr;
diff --git a/includes/api/ApiOpenSearch.php b/includes/api/ApiOpenSearch.php
index a5a13a7b..77f8b889 100644
--- a/includes/api/ApiOpenSearch.php
+++ b/includes/api/ApiOpenSearch.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Oct 13, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ("ApiBase.php");
}
+/**
+ * @addtogroup API
+ */
class ApiOpenSearch extends ApiBase {
public function __construct($main, $action) {
@@ -103,7 +105,7 @@ class ApiOpenSearch extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiOpenSearch.php 17880 2006-11-23 08:25:56Z nickj $';
+ return __CLASS__ . ': $Id: ApiOpenSearch.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/api/ApiPageSet.php b/includes/api/ApiPageSet.php
index 4728a9f8..dea87b88 100644
--- a/includes/api/ApiPageSet.php
+++ b/includes/api/ApiPageSet.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Sep 24, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiQueryBase.php');
}
+/**
+ * @addtogroup API
+ */
class ApiPageSet extends ApiQueryBase {
private $mAllPages; // [ns][dbkey] => page_id or 0 when missing
@@ -308,7 +310,7 @@ class ApiPageSet extends ApiQueryBase {
if($linkBatch->isEmpty())
return;
- $db = & $this->getDB();
+ $db = $this->getDB();
$set = $linkBatch->constructSet('page', $db);
// Get pageIDs data from the `page` table
@@ -331,7 +333,7 @@ class ApiPageSet extends ApiQueryBase {
'page_id' => $pageids
);
- $db = & $this->getDB();
+ $db = $this->getDB();
// Get pageIDs data from the `page` table
$this->profileDBIn();
@@ -406,7 +408,7 @@ class ApiPageSet extends ApiQueryBase {
if(empty($revids))
return;
- $db = & $this->getDB();
+ $db = $this->getDB();
$pageids = array();
$remaining = array_flip($revids);
@@ -438,7 +440,7 @@ class ApiPageSet extends ApiQueryBase {
private function resolvePendingRedirects() {
if($this->mResolveRedirects) {
- $db = & $this->getDB();
+ $db = $this->getDB();
$pageFlds = $this->getPageTableFields();
// Repeat until all redirects have been resolved
@@ -470,7 +472,7 @@ class ApiPageSet extends ApiQueryBase {
private function getRedirectTargets() {
$linkBatch = new LinkBatch();
- $db = & $this->getDB();
+ $db = $this->getDB();
// find redirect targets for all redirect pages
$this->profileDBIn();
@@ -592,7 +594,7 @@ class ApiPageSet extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiPageSet.php 17929 2006-11-25 17:11:58Z tstarling $';
+ return __CLASS__ . ': $Id: ApiPageSet.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
?>
diff --git a/includes/api/ApiQuery.php b/includes/api/ApiQuery.php
index e7b7f351..6ee05085 100644
--- a/includes/api/ApiQuery.php
+++ b/includes/api/ApiQuery.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Sep 7, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiBase.php');
}
+/**
+ * @addtogroup API
+ */
class ApiQuery extends ApiBase {
private $mPropModuleNames, $mListModuleNames, $mMetaModuleNames;
@@ -79,10 +81,10 @@ class ApiQuery extends ApiBase {
$this->mAllowedGenerators = array_merge($this->mListModuleNames, $this->mPropModuleNames);
}
- public function & getDB() {
+ public function getDB() {
if (!isset ($this->mSlaveDB)) {
$this->profileDBIn();
- $this->mSlaveDB = & wfGetDB(DB_SLAVE);
+ $this->mSlaveDB = wfGetDB(DB_SLAVE);
$this->profileDBOut();
}
return $this->mSlaveDB;
@@ -370,7 +372,7 @@ class ApiQuery extends ApiBase {
public function getVersion() {
$psModule = new ApiPageSet($this);
$vers = array ();
- $vers[] = __CLASS__ . ': $Id: ApiQuery.php 17374 2006-11-03 06:53:47Z yurik $';
+ $vers[] = __CLASS__ . ': $Id: ApiQuery.php 21402 2007-04-20 08:55:14Z nickj $';
$vers[] = $psModule->getVersion();
return $vers;
}
diff --git a/includes/api/ApiQueryAllpages.php b/includes/api/ApiQueryAllpages.php
index 9c076e65..494f7707 100644
--- a/includes/api/ApiQueryAllpages.php
+++ b/includes/api/ApiQueryAllpages.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Sep 25, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiQueryBase.php');
}
+/**
+ * @addtogroup API
+ */
class ApiQueryAllpages extends ApiQueryGeneratorBase {
public function __construct($query, $moduleName) {
@@ -49,7 +51,7 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
private function run($resultPageSet = null) {
wfProfileIn($this->getModuleProfileName() . '-getDB');
- $db = & $this->getDB();
+ $db = $this->getDB();
wfProfileOut($this->getModuleProfileName() . '-getDB');
wfProfileIn($this->getModuleProfileName() . '-parseParams');
@@ -167,7 +169,7 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryAllpages.php 17880 2006-11-23 08:25:56Z nickj $';
+ return __CLASS__ . ': $Id: ApiQueryAllpages.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
?>
diff --git a/includes/api/ApiQueryBacklinks.php b/includes/api/ApiQueryBacklinks.php
index 413068f8..1a6783a9 100644
--- a/includes/api/ApiQueryBacklinks.php
+++ b/includes/api/ApiQueryBacklinks.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Oct 16, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ("ApiQueryBase.php");
}
+/**
+ * @addtogroup API
+ */
class ApiQueryBacklinks extends ApiQueryGeneratorBase {
private $rootTitle, $contRedirs, $contLevel, $contTitle, $contID;
@@ -122,7 +124,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
if ($redirect)
$this->addWhereFld('page_is_redirect', 0);
- $db = & $this->getDB();
+ $db = $this->getDB();
if (!is_null($continue)) {
$plfrm = intval($this->contID);
if ($this->contLevel == 0) {
@@ -352,7 +354,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryBacklinks.php 17880 2006-11-23 08:25:56Z nickj $';
+ return __CLASS__ . ': $Id: ApiQueryBacklinks.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/api/ApiQueryBase.php b/includes/api/ApiQueryBase.php
index ae4edf98..da07bb6c 100644
--- a/includes/api/ApiQueryBase.php
+++ b/includes/api/ApiQueryBase.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Sep 7, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiBase.php');
}
+/**
+ * @addtogroup API
+ */
abstract class ApiQueryBase extends ApiBase {
private $mQueryModule, $tables, $where, $fields, $options;
@@ -337,10 +339,13 @@ abstract class ApiQueryBase extends ApiBase {
}
public static function getBaseVersion() {
- return __CLASS__ . ': $Id: ApiQueryBase.php 17987 2006-11-29 05:45:03Z nickj $';
+ return __CLASS__ . ': $Id: ApiQueryBase.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
+/**
+ * @addtogroup API
+ */
abstract class ApiQueryGeneratorBase extends ApiQueryBase {
private $mIsGenerator;
diff --git a/includes/api/ApiQueryInfo.php b/includes/api/ApiQueryInfo.php
index d93d37a2..77489a5f 100644
--- a/includes/api/ApiQueryInfo.php
+++ b/includes/api/ApiQueryInfo.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Sep 25, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiQueryBase.php');
}
+/**
+ * @addtogroup API
+ */
class ApiQueryInfo extends ApiQueryBase {
public function __construct($query, $moduleName) {
@@ -79,7 +81,7 @@ class ApiQueryInfo extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryInfo.php 17929 2006-11-25 17:11:58Z tstarling $';
+ return __CLASS__ . ': $Id: ApiQueryInfo.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
?>
diff --git a/includes/api/ApiQueryLogEvents.php b/includes/api/ApiQueryLogEvents.php
index 243f96fa..d9f23758 100644
--- a/includes/api/ApiQueryLogEvents.php
+++ b/includes/api/ApiQueryLogEvents.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Oct 16, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiQueryBase.php');
}
+/**
+ * @addtogroup API
+ */
class ApiQueryLogEvents extends ApiQueryBase {
public function __construct($query, $moduleName) {
@@ -39,7 +41,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
$limit = $type = $start = $end = $dir = $user = $title = null;
extract($this->extractRequestParams());
- $db = & $this->getDB();
+ $db = $this->getDB();
list($tbl_logging, $tbl_page, $tbl_user) = $db->tableNamesN('logging', 'page', 'user');
@@ -167,7 +169,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryLogEvents.php 17952 2006-11-27 08:36:57Z nickj $';
+ return __CLASS__ . ': $Id: ApiQueryLogEvents.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
?>
diff --git a/includes/api/ApiQueryRecentChanges.php b/includes/api/ApiQueryRecentChanges.php
index 38f51b05..25f7ff3e 100644
--- a/includes/api/ApiQueryRecentChanges.php
+++ b/includes/api/ApiQueryRecentChanges.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Oct 19, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiQueryBase.php');
}
+/**
+ * @addtogroup API
+ */
class ApiQueryRecentChanges extends ApiQueryBase {
public function __construct($query, $moduleName) {
@@ -87,7 +89,7 @@ class ApiQueryRecentChanges extends ApiQueryBase {
$data = array ();
$count = 0;
- $db = & $this->getDB();
+ $db = $this->getDB();
$res = $this->select(__METHOD__);
while ($row = $db->fetchObject($res)) {
if (++ $count > $limit) {
@@ -181,7 +183,7 @@ class ApiQueryRecentChanges extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryRecentChanges.php 17880 2006-11-23 08:25:56Z nickj $';
+ return __CLASS__ . ': $Id: ApiQueryRecentChanges.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/api/ApiQueryRevisions.php b/includes/api/ApiQueryRevisions.php
index e92b92c9..fc5f6241 100644
--- a/includes/api/ApiQueryRevisions.php
+++ b/includes/api/ApiQueryRevisions.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Sep 7, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiQueryBase.php');
}
+/**
+ * @addtogroup API
+ */
class ApiQueryRevisions extends ApiQueryBase {
public function __construct($query, $moduleName) {
@@ -149,7 +151,7 @@ class ApiQueryRevisions extends ApiQueryBase {
$count = 0;
$res = $this->select(__METHOD__);
- $db = & $this->getDB();
+ $db = $this->getDB();
while ($row = $db->fetchObject($res)) {
if (++ $count > $limit) {
@@ -262,7 +264,7 @@ class ApiQueryRevisions extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryRevisions.php 19434 2007-01-18 02:04:11Z brion $';
+ return __CLASS__ . ': $Id: ApiQueryRevisions.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
?>
diff --git a/includes/api/ApiQuerySiteinfo.php b/includes/api/ApiQuerySiteinfo.php
index 9e8c11ff..fa185c97 100644
--- a/includes/api/ApiQuerySiteinfo.php
+++ b/includes/api/ApiQuerySiteinfo.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Sep 25, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiQueryBase.php');
}
+/**
+ * @addtogroup API
+ */
class ApiQuerySiteinfo extends ApiQueryBase {
public function __construct($query, $moduleName) {
@@ -110,7 +112,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQuerySiteinfo.php 17265 2006-10-27 03:50:34Z yurik $';
+ return __CLASS__ . ': $Id: ApiQuerySiteinfo.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/api/ApiQueryUserContributions.php b/includes/api/ApiQueryUserContributions.php
index 4f63cadb..05bfbb20 100644
--- a/includes/api/ApiQueryUserContributions.php
+++ b/includes/api/ApiQueryUserContributions.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Oct 16, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiQueryBase.php');
}
+/**
+ * @addtogroup API
+ */
class ApiQueryContributions extends ApiQueryBase {
public function __construct($query, $moduleName) {
@@ -44,7 +46,7 @@ class ApiQueryContributions extends ApiQueryBase {
extract($this->extractRequestParams());
//Get a database instance
- $db = & $this->getDB();
+ $db = $this->getDB();
if (is_null($user))
$this->dieUsage("User parameter may not be empty", 'param_user');
@@ -169,7 +171,7 @@ class ApiQueryContributions extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryUserContributions.php 17952 2006-11-27 08:36:57Z nickj $';
+ return __CLASS__ . ': $Id: ApiQueryUserContributions.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
?>
diff --git a/includes/api/ApiQueryWatchlist.php b/includes/api/ApiQueryWatchlist.php
index 67564d62..73c31abb 100644
--- a/includes/api/ApiQueryWatchlist.php
+++ b/includes/api/ApiQueryWatchlist.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Sep 25, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiQueryBase.php');
}
+/**
+ * @addtogroup API
+ */
class ApiQueryWatchlist extends ApiQueryGeneratorBase {
public function __construct($query, $moduleName) {
@@ -228,7 +230,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryWatchlist.php 17987 2006-11-29 05:45:03Z nickj $';
+ return __CLASS__ . ': $Id: ApiQueryWatchlist.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
?>
diff --git a/includes/api/ApiResult.php b/includes/api/ApiResult.php
index c9bfcfb9..79fd34a1 100644
--- a/includes/api/ApiResult.php
+++ b/includes/api/ApiResult.php
@@ -1,6 +1,5 @@
<?php
-
/*
* Created on Sep 4, 2006
*
@@ -29,6 +28,9 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiBase.php');
}
+/**
+ * @addtogroup API
+ */
class ApiResult extends ApiBase {
private $mData, $mIsRawMode;
@@ -151,7 +153,7 @@ class ApiResult extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiResult.php 17076 2006-10-18 05:35:24Z yurik $';
+ return __CLASS__ . ': $Id: ApiResult.php 21402 2007-04-20 08:55:14Z nickj $';
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/media/BMP.php b/includes/media/BMP.php
new file mode 100644
index 00000000..9917856a
--- /dev/null
+++ b/includes/media/BMP.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Handler for Microsoft bitmap format (bmp). It inherits most of the methods
+ * from ImageHandler, some of them had to be overriden cause gd does not
+ * support this format.
+ *
+ * @addtogroup Media
+ */
+class BmpHandler extends BitmapHandler {
+
+ /*
+ * Get width and height from the bmp header.
+ */
+ function getImageSize( $image, $filename ) {
+ $f = fopen( $filename, 'r' );
+ if(!$f) return false;
+ $header = fread( $f, 54 );
+ fclose($f);
+
+ // Extract binary form of width and height from the header
+ $w = substr( $header, 18, 4);
+ $h = substr( $header, 22, 4);
+
+ // Convert the unsigned long 32 bits (little endian):
+ $w = unpack( 'V' , $w );
+ $h = unpack( 'V' , $h );
+ return array( $w[1], $h[1] );
+ }
+}
+
+?>
diff --git a/includes/media/Bitmap.php b/includes/media/Bitmap.php
new file mode 100644
index 00000000..3f3aabbf
--- /dev/null
+++ b/includes/media/Bitmap.php
@@ -0,0 +1,236 @@
+<?php
+
+/**
+ * @addtogroup Media
+ */
+class BitmapHandler extends ImageHandler {
+ function normaliseParams( $image, &$params ) {
+ global $wgMaxImageArea;
+ if ( !parent::normaliseParams( $image, $params ) ) {
+ return false;
+ }
+
+ $mimeType = $image->getMimeType();
+ $srcWidth = $image->getWidth( $params['page'] );
+ $srcHeight = $image->getHeight( $params['page'] );
+
+ # Don't thumbnail an image so big that it will fill hard drives and send servers into swap
+ # JPEG has the handy property of allowing thumbnailing without full decompression, so we make
+ # an exception for it.
+ if ( $mimeType !== 'image/jpeg' &&
+ $srcWidth * $srcHeight > $wgMaxImageArea )
+ {
+ return false;
+ }
+
+ # Don't make an image bigger than the source
+ $params['physicalWidth'] = $params['width'];
+ $params['physicalHeight'] = $params['height'];
+
+ if ( $params['physicalWidth'] >= $srcWidth ) {
+ $params['physicalWidth'] = $srcWidth;
+ $params['physicalHeight'] = $srcHeight;
+ return true;
+ }
+
+ return true;
+ }
+
+ function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
+ global $wgUseImageMagick, $wgImageMagickConvertCommand;
+ global $wgCustomConvertCommand;
+ global $wgSharpenParameter, $wgSharpenReductionThreshold;
+
+ if ( !$this->normaliseParams( $image, $params ) ) {
+ return new TransformParameterError( $params );
+ }
+ $physicalWidth = $params['physicalWidth'];
+ $physicalHeight = $params['physicalHeight'];
+ $clientWidth = $params['width'];
+ $clientHeight = $params['height'];
+ $srcWidth = $image->getWidth();
+ $srcHeight = $image->getHeight();
+ $mimeType = $image->getMimeType();
+ $srcPath = $image->getImagePath();
+ $retval = 0;
+ wfDebug( __METHOD__.": creating {$physicalWidth}x{$physicalHeight} thumbnail at $dstPath\n" );
+
+ if ( $physicalWidth == $srcWidth && $physicalHeight == $srcHeight ) {
+ # normaliseParams (or the user) wants us to return the unscaled image
+ wfDebug( __METHOD__.": returning unscaled image\n" );
+ return new ThumbnailImage( $image->getURL(), $clientWidth, $clientHeight, $srcPath );
+ }
+
+ if ( $wgUseImageMagick ) {
+ $scaler = 'im';
+ } elseif ( $wgCustomConvertCommand ) {
+ $scaler = 'custom';
+ } elseif ( function_exists( 'imagecreatetruecolor' ) ) {
+ $scaler = 'gd';
+ } else {
+ $scaler = 'client';
+ }
+
+ if ( $scaler == 'client' ) {
+ # Client-side image scaling, use the source URL
+ # Using the destination URL in a TRANSFORM_LATER request would be incorrect
+ return new ThumbnailImage( $image->getURL(), $clientWidth, $clientHeight, $srcPath );
+ }
+
+ if ( $flags & self::TRANSFORM_LATER ) {
+ return new ThumbnailImage( $dstUrl, $clientWidth, $clientHeight, $dstPath );
+ }
+
+ if ( !wfMkdirParents( dirname( $dstPath ) ) ) {
+ return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight,
+ wfMsg( 'thumbnail_dest_directory' ) );
+ }
+
+ if ( $scaler == 'im' ) {
+ # use ImageMagick
+
+ $sharpen = '';
+ if ( $mimeType == 'image/jpeg' ) {
+ $quality = "-quality 80"; // 80%
+ # Sharpening, see bug 6193
+ if ( ( $physicalWidth + $physicalHeight ) / ( $srcWidth + $srcHeight ) < $wgSharpenReductionThreshold ) {
+ $sharpen = "-sharpen " . wfEscapeShellArg( $wgSharpenParameter );
+ }
+ } elseif ( $mimeType == 'image/png' ) {
+ $quality = "-quality 95"; // zlib 9, adaptive filtering
+ } else {
+ $quality = ''; // default
+ }
+
+ # Specify white background color, will be used for transparent images
+ # in Internet Explorer/Windows instead of default black.
+
+ # Note, we specify "-size {$physicalWidth}" and NOT "-size {$physicalWidth}x{$physicalHeight}".
+ # It seems that ImageMagick has a bug wherein it produces thumbnails of
+ # the wrong size in the second case.
+
+ $cmd = wfEscapeShellArg($wgImageMagickConvertCommand) .
+ " {$quality} -background white -size {$physicalWidth} ".
+ wfEscapeShellArg($srcPath) .
+ // Coalesce is needed to scale animated GIFs properly (bug 1017).
+ ' -coalesce ' .
+ // For the -resize option a "!" is needed to force exact size,
+ // or ImageMagick may decide your ratio is wrong and slice off
+ // a pixel.
+ " -thumbnail " . wfEscapeShellArg( "{$physicalWidth}x{$physicalHeight}!" ) .
+ " -depth 8 $sharpen " .
+ wfEscapeShellArg($dstPath) . " 2>&1";
+ wfDebug( __METHOD__.": running ImageMagick: $cmd\n");
+ wfProfileIn( 'convert' );
+ $err = wfShellExec( $cmd, $retval );
+ wfProfileOut( 'convert' );
+ } elseif( $scaler == 'custom' ) {
+ # Use a custom convert command
+ # Variables: %s %d %w %h
+ $src = wfEscapeShellArg( $srcPath );
+ $dst = wfEscapeShellArg( $dstPath );
+ $cmd = $wgCustomConvertCommand;
+ $cmd = str_replace( '%s', $src, str_replace( '%d', $dst, $cmd ) ); # Filenames
+ $cmd = str_replace( '%h', $physicalHeight, str_replace( '%w', $physicalWidth, $cmd ) ); # Size
+ wfDebug( __METHOD__.": Running custom convert command $cmd\n" );
+ wfProfileIn( 'convert' );
+ $err = wfShellExec( $cmd, $retval );
+ wfProfileOut( 'convert' );
+ } else /* $scaler == 'gd' */ {
+ # Use PHP's builtin GD library functions.
+ #
+ # First find out what kind of file this is, and select the correct
+ # input routine for this.
+
+ $typemap = array(
+ 'image/gif' => array( 'imagecreatefromgif', 'palette', 'imagegif' ),
+ 'image/jpeg' => array( 'imagecreatefromjpeg', 'truecolor', array( __CLASS__, 'imageJpegWrapper' ) ),
+ 'image/png' => array( 'imagecreatefrompng', 'bits', 'imagepng' ),
+ 'image/vnd.wap.wbmp' => array( 'imagecreatefromwbmp', 'palette', 'imagewbmp' ),
+ 'image/xbm' => array( 'imagecreatefromxbm', 'palette', 'imagexbm' ),
+ );
+ if( !isset( $typemap[$mimeType] ) ) {
+ $err = 'Image type not supported';
+ wfDebug( "$err\n" );
+ return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, $err );
+ }
+ list( $loader, $colorStyle, $saveType ) = $typemap[$mimeType];
+
+ if( !function_exists( $loader ) ) {
+ $err = "Incomplete GD library configuration: missing function $loader";
+ wfDebug( "$err\n" );
+ return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, $err );
+ }
+
+ $src_image = call_user_func( $loader, $srcPath );
+ $dst_image = imagecreatetruecolor( $physicalWidth, $physicalHeight );
+ imagecopyresampled( $dst_image, $src_image,
+ 0,0,0,0,
+ $physicalWidth, $physicalHeight, imagesx( $src_image ), imagesy( $src_image ) );
+ call_user_func( $saveType, $dst_image, $dstPath );
+ imagedestroy( $dst_image );
+ imagedestroy( $src_image );
+ $retval = 0;
+ }
+
+ $removed = $this->removeBadFile( $dstPath, $retval );
+ if ( $retval != 0 || $removed ) {
+ wfDebugLog( 'thumbnail',
+ sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
+ wfHostname(), $retval, trim($err), $cmd ) );
+ return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, $err );
+ } else {
+ return new ThumbnailImage( $dstUrl, $clientWidth, $clientHeight, $dstPath );
+ }
+ }
+
+ static function imageJpegWrapper( $dst_image, $thumbPath ) {
+ imageinterlace( $dst_image );
+ imagejpeg( $dst_image, $thumbPath, 95 );
+ }
+
+
+ function getMetadata( $image, $filename ) {
+ global $wgShowEXIF;
+ if( $wgShowEXIF && file_exists( $filename ) ) {
+ $exif = new Exif( $filename );
+ $data = $exif->getFilteredData();
+ if ( $data ) {
+ $data['MEDIAWIKI_EXIF_VERSION'] = Exif::version();
+ return serialize( $data );
+ } else {
+ return '0';
+ }
+ } else {
+ return '';
+ }
+ }
+
+ function getMetadataType( $image ) {
+ return 'exif';
+ }
+
+ function isMetadataValid( $image, $metadata ) {
+ global $wgShowEXIF;
+ if ( !$wgShowEXIF ) {
+ # Metadata disabled and so an empty field is expected
+ return true;
+ }
+ if ( $metadata === '0' ) {
+ # Special value indicating that there is no EXIF data in the file
+ return true;
+ }
+ $exif = @unserialize( $metadata );
+ if ( !isset( $exif['MEDIAWIKI_EXIF_VERSION'] ) ||
+ $exif['MEDIAWIKI_EXIF_VERSION'] != Exif::version() )
+ {
+ # Wrong version
+ wfDebug( __METHOD__.": wrong version\n" );
+ return false;
+ }
+ return true;
+ }
+
+}
+
+?>
diff --git a/includes/media/DjVu.php b/includes/media/DjVu.php
new file mode 100644
index 00000000..3c053a0c
--- /dev/null
+++ b/includes/media/DjVu.php
@@ -0,0 +1,206 @@
+<?php
+
+/**
+ * @addtogroup Media
+ */
+class DjVuHandler extends ImageHandler {
+ function isEnabled() {
+ global $wgDjvuRenderer, $wgDjvuDump, $wgDjvuToXML;
+ if ( !$wgDjvuRenderer || ( !$wgDjvuDump && !$wgDjvuToXML ) ) {
+ wfDebug( "DjVu is disabled, please set \$wgDjvuRenderer and \$wgDjvuDump\n" );
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ function mustRender() { return true; }
+ function isMultiPage() { return true; }
+
+ function validateParam( $name, $value ) {
+ if ( in_array( $name, array( 'width', 'height', 'page' ) ) ) {
+ if ( $value <= 0 ) {
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ function makeParamString( $params ) {
+ $page = isset( $params['page'] ) ? $params['page'] : 1;
+ if ( !isset( $params['width'] ) ) {
+ return false;
+ }
+ return "page{$page}-{$params['width']}px";
+ }
+
+ function parseParamString( $str ) {
+ $m = false;
+ if ( preg_match( '/^page(\d+)-(\d+)px$/', $str, $m ) ) {
+ return array( 'width' => $m[2], 'page' => $m[1] );
+ } else {
+ return false;
+ }
+ }
+
+ function getScriptParams( $params ) {
+ return array(
+ 'width' => $params['width'],
+ 'page' => $params['page'],
+ );
+ }
+
+ function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
+ global $wgDjvuRenderer, $wgDjvuPostProcessor;
+
+ // Fetch XML and check it, to give a more informative error message than the one which
+ // normaliseParams will inevitably give.
+ $xml = $image->getMetadata();
+ if ( !$xml ) {
+ return new MediaTransformError( 'thumbnail_error', @$params['width'], @$params['height'],
+ wfMsg( 'djvu_no_xml' ) );
+ }
+
+ if ( !$this->normaliseParams( $image, $params ) ) {
+ return new TransformParameterError( $params );
+ }
+ $width = $params['width'];
+ $height = $params['height'];
+ $srcPath = $image->getImagePath();
+ $page = $params['page'];
+ $pageCount = $this->pageCount( $image );
+ if ( $page > $this->pageCount( $image ) ) {
+ return new MediaTransformError( 'thumbnail_error', $width, $height, wfMsg( 'djvu_page_error' ) );
+ }
+
+ if ( $flags & self::TRANSFORM_LATER ) {
+ return new ThumbnailImage( $dstUrl, $width, $height, $dstPath );
+ }
+
+ if ( !wfMkdirParents( dirname( $dstPath ) ) ) {
+ return new MediaTransformError( 'thumbnail_error', $width, $height, wfMsg( 'thumbnail_dest_directory' ) );
+ }
+
+ # Use a subshell (brackets) to aggregate stderr from both pipeline commands
+ # before redirecting it to the overall stdout. This works in both Linux and Windows XP.
+ $cmd = '(' . wfEscapeShellArg( $wgDjvuRenderer ) . " -format=ppm -page={$page} -size={$width}x{$height} " .
+ wfEscapeShellArg( $srcPath );
+ if ( $wgDjvuPostProcessor ) {
+ $cmd .= " | {$wgDjvuPostProcessor}";
+ }
+ $cmd .= ' > ' . wfEscapeShellArg($dstPath) . ') 2>&1';
+ wfProfileIn( 'ddjvu' );
+ wfDebug( __METHOD__.": $cmd\n" );
+ $err = wfShellExec( $cmd, $retval );
+ wfProfileOut( 'ddjvu' );
+
+ $removed = $this->removeBadFile( $dstPath, $retval );
+ if ( $retval != 0 || $removed ) {
+ wfDebugLog( 'thumbnail',
+ sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
+ wfHostname(), $retval, trim($err), $cmd ) );
+ return new MediaTransformError( 'thumbnail_error', $width, $height, $err );
+ } else {
+ return new ThumbnailImage( $dstUrl, $width, $height, $dstPath );
+ }
+ }
+
+ /**
+ * Cache an instance of DjVuImage in an Image object, return that instance
+ */
+ function getDjVuImage( $image, $path ) {
+ if ( !$image ) {
+ $deja = new DjVuImage( $path );
+ } elseif ( !isset( $image->dejaImage ) ) {
+ $deja = $image->dejaImage = new DjVuImage( $path );
+ } else {
+ $deja = $image->dejaImage;
+ }
+ return $deja;
+ }
+
+ /**
+ * Cache a document tree for the DjVu XML metadata
+ */
+ function getMetaTree( $image ) {
+ if ( isset( $image->dejaMetaTree ) ) {
+ return $image->dejaMetaTree;
+ }
+
+ $metadata = $image->getMetadata();
+ if ( !$this->isMetadataValid( $image, $metadata ) ) {
+ wfDebug( "DjVu XML metadata is invalid or missing, should have been fixed in upgradeRow\n" );
+ return false;
+ }
+ wfProfileIn( __METHOD__ );
+
+ wfSuppressWarnings();
+ try {
+ $image->dejaMetaTree = new SimpleXMLElement( $metadata );
+ } catch( Exception $e ) {
+ wfDebug( "Bogus multipage XML metadata on '$image->name'\n" );
+ // Set to false rather than null to avoid further attempts
+ $image->dejaMetaTree = false;
+ }
+ wfRestoreWarnings();
+ wfProfileOut( __METHOD__ );
+ return $image->dejaMetaTree;
+ }
+
+ function getImageSize( $image, $path ) {
+ return $this->getDjVuImage( $image, $path )->getImageSize();
+ }
+
+ function getThumbType( $ext, $mime ) {
+ global $wgDjvuOutputExtension;
+ static $mime;
+ if ( !isset( $mime ) ) {
+ $magic = MimeMagic::singleton();
+ $mime = $magic->guessTypesForExtension( $wgDjvuOutputExtension );
+ }
+ return array( $wgDjvuOutputExtension, $mime );
+ }
+
+ function getMetadata( $image, $path ) {
+ wfDebug( "Getting DjVu metadata for $path\n" );
+ return $this->getDjVuImage( $image, $path )->retrieveMetaData();
+ }
+
+ function getMetadataType( $image ) {
+ return 'djvuxml';
+ }
+
+ function isMetadataValid( $image, $metadata ) {
+ return !empty( $metadata ) && $metadata != serialize(array());
+ }
+
+ function pageCount( $image ) {
+ $tree = $this->getMetaTree( $image );
+ if ( !$tree ) {
+ return false;
+ }
+ return count( $tree->xpath( '//OBJECT' ) );
+ }
+
+ function getPageDimensions( $image, $page ) {
+ $tree = $this->getMetaTree( $image );
+ if ( !$tree ) {
+ return false;
+ }
+
+ $o = $tree->BODY[0]->OBJECT[$page-1];
+ if ( $o ) {
+ return array(
+ 'width' => intval( $o['width'] ),
+ 'height' => intval( $o['height'] )
+ );
+ } else {
+ return false;
+ }
+ }
+}
+
+?>
diff --git a/includes/media/Generic.php b/includes/media/Generic.php
new file mode 100644
index 00000000..5254e0ea
--- /dev/null
+++ b/includes/media/Generic.php
@@ -0,0 +1,298 @@
+<?php
+
+/**
+ * Media-handling base classes and generic functionality
+ */
+
+/**
+ * Base media handler class
+ *
+ * @addtogroup Media
+ */
+abstract class MediaHandler {
+ const TRANSFORM_LATER = 1;
+
+ /**
+ * Instance cache
+ */
+ static $handlers = array();
+
+ /**
+ * Get a MediaHandler for a given MIME type from the instance cache
+ */
+ static function getHandler( $type ) {
+ global $wgMediaHandlers;
+ if ( !isset( $wgMediaHandlers[$type] ) ) {
+ wfDebug( __METHOD__ . ": no handler found for $type.\n");
+ return false;
+ }
+ $class = $wgMediaHandlers[$type];
+ if ( !isset( self::$handlers[$class] ) ) {
+ self::$handlers[$class] = new $class;
+ if ( !self::$handlers[$class]->isEnabled() ) {
+ self::$handlers[$class] = false;
+ }
+ }
+ return self::$handlers[$class];
+ }
+
+ /*
+ * Validate a thumbnail parameter at parse time.
+ * Return true to accept the parameter, and false to reject it.
+ * If you return false, the parser will do something quiet and forgiving.
+ */
+ abstract function validateParam( $name, $value );
+
+ /**
+ * Merge a parameter array into a string appropriate for inclusion in filenames
+ */
+ abstract function makeParamString( $params );
+
+ /**
+ * Parse a param string made with makeParamString back into an array
+ */
+ abstract function parseParamString( $str );
+
+ /**
+ * Changes the parameter array as necessary, ready for transformation.
+ * Should be idempotent.
+ * Returns false if the parameters are unacceptable and the transform should fail
+ */
+ abstract function normaliseParams( $image, &$params );
+
+ /**
+ * Get an image size array like that returned by getimagesize(), or false if it
+ * can't be determined.
+ *
+ * @param Image $image The image object, or false if there isn't one
+ * @param string $fileName The filename
+ * @return array
+ */
+ abstract function getImageSize( $image, $path );
+
+ /**
+ * Get handler-specific metadata which will be saved in the img_metadata field.
+ *
+ * @param Image $image The image object, or false if there isn't one
+ * @param string $fileName The filename
+ * @return string
+ */
+ function getMetadata( $image, $path ) { return ''; }
+
+ /**
+ * Get a string describing the type of metadata, for display purposes.
+ */
+ function getMetadataType( $image ) { return false; }
+
+ /**
+ * Check if the metadata string is valid for this handler.
+ * If it returns false, Image will reload the metadata from the file and update the database
+ */
+ function isMetadataValid( $image, $metadata ) { return true; }
+
+ /**
+ * Get a MediaTransformOutput object representing the transformed output. Does not
+ * actually do the transform.
+ *
+ * @param Image $image The image object
+ * @param string $dstPath Filesystem destination path
+ * @param string $dstUrl Destination URL to use in output HTML
+ * @param array $params Arbitrary set of parameters validated by $this->validateParam()
+ */
+ function getTransform( $image, $dstPath, $dstUrl, $params ) {
+ return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
+ }
+
+ /**
+ * Get a MediaTransformOutput object representing the transformed output. Does the
+ * transform unless $flags contains self::TRANSFORM_LATER.
+ *
+ * @param Image $image The image object
+ * @param string $dstPath Filesystem destination path
+ * @param string $dstUrl Destination URL to use in output HTML
+ * @param array $params Arbitrary set of parameters validated by $this->validateParam()
+ * @param integer $flags A bitfield, may contain self::TRANSFORM_LATER
+ */
+ abstract function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 );
+
+ /**
+ * Get the thumbnail extension and MIME type for a given source MIME type
+ * @return array thumbnail extension and MIME type
+ */
+ function getThumbType( $ext, $mime ) {
+ return array( $ext, $mime );
+ }
+
+ /**
+ * True if the handled types can be transformed
+ */
+ function canRender() { return true; }
+ /**
+ * True if handled types cannot be displayed directly in a browser
+ * but can be rendered
+ */
+ function mustRender() { return false; }
+ /**
+ * True if the type has multi-page capabilities
+ */
+ function isMultiPage() { return false; }
+ /**
+ * Page count for a multi-page document, false if unsupported or unknown
+ */
+ function pageCount() { return false; }
+ /**
+ * False if the handler is disabled for all files
+ */
+ function isEnabled() { return true; }
+
+ /**
+ * Get an associative array of page dimensions
+ * Currently "width" and "height" are understood, but this might be
+ * expanded in the future.
+ * Returns false if unknown or if the document is not multi-page.
+ */
+ function getPageDimensions( $image, $page ) {
+ $gis = $this->getImageSize( $image, $image->getImagePath() );
+ return array(
+ 'width' => $gis[0],
+ 'height' => $gis[1]
+ );
+ }
+}
+
+/**
+ * Media handler abstract base class for images
+ *
+ * @addtogroup Media
+ */
+abstract class ImageHandler extends MediaHandler {
+ function validateParam( $name, $value ) {
+ if ( in_array( $name, array( 'width', 'height' ) ) ) {
+ if ( $value <= 0 ) {
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ function makeParamString( $params ) {
+ if ( isset( $params['physicalWidth'] ) ) {
+ $width = $params['physicalWidth'];
+ } else {
+ $width = $params['width'];
+ }
+ # Removed for ProofreadPage
+ #$width = intval( $width );
+ return "{$width}px";
+ }
+
+ function parseParamString( $str ) {
+ $m = false;
+ if ( preg_match( '/^(\d+)px$/', $str, $m ) ) {
+ return array( 'width' => $m[1] );
+ } else {
+ return false;
+ }
+ }
+
+ function getScriptParams( $params ) {
+ return array( 'width' => $params['width'] );
+ }
+
+ function normaliseParams( $image, &$params ) {
+ $mimeType = $image->getMimeType();
+
+ if ( !isset( $params['width'] ) ) {
+ return false;
+ }
+ if ( !isset( $params['page'] ) ) {
+ $params['page'] = 1;
+ }
+ $srcWidth = $image->getWidth( $params['page'] );
+ $srcHeight = $image->getHeight( $params['page'] );
+ if ( isset( $params['height'] ) && $params['height'] != -1 ) {
+ if ( $params['width'] * $srcHeight > $params['height'] * $srcWidth ) {
+ $params['width'] = wfFitBoxWidth( $srcWidth, $srcHeight, $params['height'] );
+ }
+ }
+ $params['height'] = Image::scaleHeight( $srcWidth, $srcHeight, $params['width'] );
+ if ( !$this->validateThumbParams( $params['width'], $params['height'], $srcWidth, $srcHeight, $mimeType ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Get a transform output object without actually doing the transform
+ */
+ function getTransform( $image, $dstPath, $dstUrl, $params ) {
+ return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
+ }
+
+ /**
+ * Validate thumbnail parameters and fill in the correct height
+ *
+ * @param integer &$width Specified width (input/output)
+ * @param integer &$height Height (output only)
+ * @return false to indicate that an error should be returned to the user.
+ */
+ function validateThumbParams( &$width, &$height, $srcWidth, $srcHeight, $mimeType ) {
+ $width = intval( $width );
+
+ # Sanity check $width
+ if( $width <= 0) {
+ wfDebug( __METHOD__.": Invalid destination width: $width\n" );
+ return false;
+ }
+ if ( $srcWidth <= 0 ) {
+ wfDebug( __METHOD__.": Invalid source width: $srcWidth\n" );
+ return false;
+ }
+
+ $height = Image::scaleHeight( $srcWidth, $srcHeight, $width );
+ return true;
+ }
+
+ function getScriptedTransform( $image, $script, $params ) {
+ if ( !$this->normaliseParams( $image, $params ) ) {
+ return false;
+ }
+ $url = $script . '&' . wfArrayToCGI( $this->getScriptParams( $params ) );
+ return new ThumbnailImage( $url, $params['width'], $params['height'] );
+ }
+
+ /**
+ * Check for zero-sized thumbnails. These can be generated when
+ * no disk space is available or some other error occurs
+ *
+ * @param $dstPath The location of the suspect file
+ * @param $retval Return value of some shell process, file will be deleted if this is non-zero
+ * @return true if removed, false otherwise
+ */
+ function removeBadFile( $dstPath, $retval = 0 ) {
+ $removed = false;
+ if( file_exists( $dstPath ) ) {
+ $thumbstat = stat( $dstPath );
+ if( $thumbstat['size'] == 0 || $retval != 0 ) {
+ wfDebugLog( 'thumbnail',
+ sprintf( 'Removing bad %d-byte thumbnail "%s"',
+ $thumbstat['size'], $dstPath ) );
+ unlink( $dstPath );
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function getImageSize( $image, $path ) {
+ wfSuppressWarnings();
+ $gis = getimagesize( $path );
+ wfRestoreWarnings();
+ return $gis;
+ }
+}
+
+?>
diff --git a/includes/media/SVG.php b/includes/media/SVG.php
new file mode 100644
index 00000000..5307e269
--- /dev/null
+++ b/includes/media/SVG.php
@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * @addtogroup Media
+ */
+class SvgHandler extends ImageHandler {
+ function isEnabled() {
+ global $wgSVGConverters, $wgSVGConverter;
+ if ( !isset( $wgSVGConverters[$wgSVGConverter] ) ) {
+ wfDebug( "\$wgSVGConverter is invalid, disabling SVG rendering.\n" );
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ function mustRender() {
+ return true;
+ }
+
+ function normaliseParams( $image, &$params ) {
+ global $wgSVGMaxSize;
+ if ( !parent::normaliseParams( $image, $params ) ) {
+ return false;
+ }
+
+ # Don't make an image bigger than wgMaxSVGSize
+ $params['physicalWidth'] = $params['width'];
+ $params['physicalHeight'] = $params['height'];
+ if ( $params['physicalWidth'] > $wgSVGMaxSize ) {
+ $srcWidth = $image->getWidth( $params['page'] );
+ $srcHeight = $image->getHeight( $params['page'] );
+ $params['physicalWidth'] = $wgSVGMaxSize;
+ $params['physicalHeight'] = Image::scaleHeight( $srcWidth, $srcHeight, $wgSVGMaxSize );
+ }
+ return true;
+ }
+
+ function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
+ global $wgSVGConverters, $wgSVGConverter, $wgSVGConverterPath;
+
+ if ( !$this->normaliseParams( $image, $params ) ) {
+ return new TransformParameterError( $params );
+ }
+ $clientWidth = $params['width'];
+ $clientHeight = $params['height'];
+ $physicalWidth = $params['physicalWidth'];
+ $physicalHeight = $params['physicalHeight'];
+ $srcWidth = $image->getWidth();
+ $srcHeight = $image->getHeight();
+ $srcPath = $image->getImagePath();
+
+ if ( $flags & self::TRANSFORM_LATER ) {
+ return new ThumbnailImage( $dstUrl, $clientWidth, $clientHeight, $dstPath );
+ }
+
+ if ( !wfMkdirParents( dirname( $dstPath ) ) ) {
+ return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight,
+ wfMsg( 'thumbnail_dest_directory' ) );
+ }
+
+ $err = false;
+ if( isset( $wgSVGConverters[$wgSVGConverter] ) ) {
+ $cmd = str_replace(
+ array( '$path/', '$width', '$height', '$input', '$output' ),
+ array( $wgSVGConverterPath ? wfEscapeShellArg( "$wgSVGConverterPath/" ) : "",
+ intval( $physicalWidth ),
+ intval( $physicalHeight ),
+ wfEscapeShellArg( $srcPath ),
+ wfEscapeShellArg( $dstPath ) ),
+ $wgSVGConverters[$wgSVGConverter] ) . " 2>&1";
+ wfProfileIn( 'rsvg' );
+ wfDebug( __METHOD__.": $cmd\n" );
+ $err = wfShellExec( $cmd, $retval );
+ wfProfileOut( 'rsvg' );
+ }
+
+ $removed = $this->removeBadFile( $dstPath, $retval );
+ if ( $retval != 0 || $removed ) {
+ wfDebugLog( 'thumbnail',
+ sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
+ wfHostname(), $retval, trim($err), $cmd ) );
+ return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, $err );
+ } else {
+ return new ThumbnailImage( $dstUrl, $clientWidth, $clientHeight, $dstPath );
+ }
+ }
+
+ function getImageSize( $image, $path ) {
+ return wfGetSVGsize( $path );
+ }
+
+ function getThumbType( $ext, $mime ) {
+ return array( 'png', 'image/png' );
+ }
+}
+?>
diff --git a/includes/memcached-client.php b/includes/memcached-client.php
index 2c5cc6be..1f4bac00 100644
--- a/includes/memcached-client.php
+++ b/includes/memcached-client.php
@@ -91,7 +91,7 @@ define("COMPRESSION_SAVINGS", 0.20);
* memcached client class implemented using (p)fsockopen()
*
* @author Ryan T. Dean <rtdean@cytherianage.net>
- * @package memcached-client
+ * @addtogroup Cache
*/
class memcached
{
diff --git a/includes/mime.info b/includes/mime.info
index 9b05f089..a960f023 100644
--- a/includes/mime.info
+++ b/includes/mime.info
@@ -7,7 +7,7 @@
image/gif [BITMAP]
-image/png [BITMAP]
+image/png image/x-png [BITMAP]
image/ief [BITMAP]
image/jpeg [BITMAP]
image/xbm [BITMAP]
@@ -19,7 +19,7 @@ image/x-portable-graymap image/x-portable-greymap [BITMAP]
image/x-bmp image/bmp application/x-bmp application/bmp [BITMAP]
image/x-photoshop image/psd image/x-psd image/photoshop [BITMAP]
-image/svg image/svg+xml application/svg+xml application/svg [DRAWING]
+image/svg+xml application/svg+xml application/svg image/svg [DRAWING]
application/postscript [DRAWING]
application/x-latex [DRAWING]
application/x-tex [DRAWING]
@@ -36,6 +36,7 @@ audio/x-realaudio [AUDIO]
video/mpeg application/mpeg [VIDEO]
video/ogg [VIDEO]
video/x-sgi-video [VIDEO]
+video/x-flv [VIDEO]
application/ogg application/x-ogg audio/ogg audio/x-ogg video/ogg video/x-ogg [MULTIMEDIA]
diff --git a/includes/mime.types b/includes/mime.types
index 3a7fa39c..19a61517 100644
--- a/includes/mime.types
+++ b/includes/mime.types
@@ -111,6 +111,7 @@ video/mpeg mpeg mpg mpe
video/ogg ogm ogg
video/quicktime qt mov
video/vnd.mpegurl mxu
+video/x-flv flv
video/x-msvideo avi
video/x-ogg ogm ogg
video/x-sgi-movie movie
diff --git a/includes/normal/CleanUpTest.php b/includes/normal/CleanUpTest.php
index 30ec6a95..cc6f0737 100644
--- a/includes/normal/CleanUpTest.php
+++ b/includes/normal/CleanUpTest.php
@@ -17,15 +17,6 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# http://www.gnu.org/copyleft/gpl.html
-/**
- * Additional tests for UtfNormal::cleanUp() function, inclusion
- * regression checks for known problems.
- *
- * Requires PHPUnit.
- *
- * @package UtfNormal
- * @private
- */
if( php_sapi_name() != 'cli' ) {
die( "Run me from the command line please.\n" );
@@ -38,20 +29,20 @@ if( isset( $_SERVER['argv'] ) && in_array( '--icu', $_SERVER['argv'] ) ) {
#ini_set( 'memory_limit', '40M' );
-require_once( 'PHPUnit.php' );
-require_once( 'UtfNormal.php' );
+require_once 'PHPUnit/Framework.php';
+require_once 'PHPUnit/TextUI/TestRunner.php';
+
+require_once 'UtfNormal.php';
/**
- * @package UtfNormal
+ * Additional tests for UtfNormal::cleanUp() function, inclusion
+ * regression checks for known problems.
+ * Requires PHPUnit.
+ *
+ * @addtogroup UtfNormal
+ * @private
*/
-class CleanUpTest extends PHPUnit_TestCase {
- /**
- * @param $name String: FIXME
- */
- function CleanUpTest( $name ) {
- $this->PHPUnit_TestCase( $name );
- }
-
+class CleanUpTest extends PHPUnit_Framework_TestCase {
/** @todo document */
function setUp() {
}
@@ -412,9 +403,8 @@ class CleanUpTest extends PHPUnit_TestCase {
}
-$suite = new PHPUnit_TestSuite( 'CleanUpTest' );
-$result = PHPUnit::run( $suite );
-echo $result->toString();
+$suite = new PHPUnit_Framework_TestSuite( 'CleanUpTest' );
+$result = PHPUnit_TextUI_TestRunner::run( $suite );
if( !$result->wasSuccessful() ) {
exit( -1 );
diff --git a/includes/normal/Makefile b/includes/normal/Makefile
index fcdf2380..887f3ce6 100644
--- a/includes/normal/Makefile
+++ b/includes/normal/Makefile
@@ -1,11 +1,21 @@
.PHONY : all test testutf8 testclean icutest bench icubench clean distclean
-FETCH=wget
-#FETCH=fetch
-BASE=http://www.unicode.org/Public/UNIDATA
+## Latest greatest version of Unicode
+## May cause confusion if running test suite from these files
+## when the data was generated from a previous version.
+#BASE=http://www.unicode.org/Public/UNIDATA
+
+# Explicitly using Unicode 5.0
+BASE=http://www.unicode.org/Public/5.0.0/ucd/
+
+# Can override to php-cli or php5 or whatevah
PHP=php
#PHP=php-cli
+# Some nice tool to grab URLs with
+FETCH=wget
+#FETCH=fetch
+
all : UtfNormalData.inc
UtfNormalData.inc : UtfNormalGenerate.php UtfNormalUtil.php UnicodeData.txt CompositionExclusions.txt NormalizationCorrections.txt DerivedNormalizationProps.txt
@@ -20,7 +30,7 @@ testutf8 : Utf8Test.php UTF-8-test.txt
testclean : CleanUpTest.php
$(PHP) CleanUpTest.php
-bench : UtfNormalData.inc testdata/washington.txt testdata/berlin.txt testdata/tokyo.txt testdata/sociology.txt testdata/bulgakov.txt
+bench : UtfNormalData.inc testdata/washington.txt testdata/berlin.txt testdata/tokyo.txt testdata/young.txt testdata/bulgakov.txt
$(PHP) UtfNormalBench.php
icutest : UtfNormalData.inc NormalizationTest.txt
@@ -28,14 +38,14 @@ icutest : UtfNormalData.inc NormalizationTest.txt
$(PHP) CleanUpTest.php --icu
$(PHP) UtfNormalTest.php --icu
-icubench : UtfNormalData.inc testdata/washington.txt testdata/berlin.txt testdata/tokyo.txt testdata/sociology.txt testdata/bulgakov.txt
+icubench : UtfNormalData.inc testdata/washington.txt testdata/berlin.txt testdata/tokyo.txt testdata/young.txt testdata/bulgakov.txt
$(PHP) UtfNormalBench.php --icu
clean :
- rm -f UtfNormalData.inc
+ rm -f UtfNormalData.inc UtfNormalDataK.inc
distclean : clean
- rm -f CompositionExclusions.txt NormalizationTest.txt NormalizationCorrections.txt UnicodeData.txt DerivedNormalizationProps.txt
+ rm -f CompositionExclusions.txt NormalizationTest.txt NormalizationCorrections.txt UnicodeData.txt DerivedNormalizationProps.txt UTF-8-test.txt
# The Unicode data files...
CompositionExclusions.txt :
@@ -57,16 +67,16 @@ UTF-8-test.txt :
$(FETCH) http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
testdata/berlin.txt :
- mkdir -p testdata && wget -U MediaWiki/test -O testdata/berlin.txt "http://de.wikipedia.org/w/wiki.phtml?title=Berlin&oldid=2775712&action=raw"
+ mkdir -p testdata && wget -U MediaWiki/test -O testdata/berlin.txt "http://de.wikipedia.org/w/index.php?title=Berlin&oldid=2775712&action=raw"
testdata/washington.txt :
- mkdir -p testdata && wget -U MediaWiki/test -O testdata/washington.txt "http://en.wikipedia.org/w/wiki.phtml?title=Washington%2C_DC&oldid=6370218&action=raw"
+ mkdir -p testdata && wget -U MediaWiki/test -O testdata/washington.txt "http://en.wikipedia.org/w/index.php?title=Washington%2C_D.C.&oldid=6370218&action=raw"
testdata/tokyo.txt :
- mkdir -p testdata && wget -U MediaWiki/test -O testdata/tokyo.txt "http://ja.wikipedia.org/w/wiki.phtml?title=%E6%9D%B1%E4%BA%AC%E9%83%BD&oldid=940880&action=raw"
+ mkdir -p testdata && wget -U MediaWiki/test -O testdata/tokyo.txt "http://ja.wikipedia.org/w/index.php?title=%E6%9D%B1%E4%BA%AC%E9%83%BD&oldid=940880&action=raw"
-testdata/sociology.txt :
- mkdir -p testdata && wget -U MediaWiki/test -O testdata/sociology.txt "http://ko.wikipedia.org/w/wiki.phtml?title=%EC%82%AC%ED%9A%8C%ED%95%99&oldid=16409&action=raw"
+testdata/young.txt :
+ mkdir -p testdata && wget -U MediaWiki/test -O testdata/young.txt "http://ko.wikipedia.org/w/index.php?title=%EC%9D%B4%EC%88%98%EC%98%81&oldid=627688&action=raw"
testdata/bulgakov.txt :
- mkdir -p testdata && wget -U MediaWiki/test -O testdata/bulgakov.txt "http://ru.wikipedia.org/w/wiki.phtml?title=%D0%91%D1%83%D0%BB%D0%B3%D0%B0%D0%BA%D0%BE%D0%B2%2C_%D0%A1%D0%B5%D1%80%D0%B3%D0%B5%D0%B9_%D0%9D%D0%B8%D0%BA%D0%BE%D0%BB%D0%B0%D0%B5%D0%B2%D0%B8%D1%87&oldid=17704&action=raw"
+ mkdir -p testdata && wget -U MediaWiki/test -O testdata/bulgakov.txt "http://ru.wikipedia.org/w/index.php?title=%D0%91%D1%83%D0%BB%D0%B3%D0%B0%D0%BA%D0%BE%D0%B2%2C_%D0%A1%D0%B5%D1%80%D0%B3%D0%B5%D0%B9_%D0%9D%D0%B8%D0%BA%D0%BE%D0%BB%D0%B0%D0%B5%D0%B2%D0%B8%D1%87&oldid=17704&action=raw"
diff --git a/includes/normal/README b/includes/normal/README
index f8207a1b..a17aa7da 100644
--- a/includes/normal/README
+++ b/includes/normal/README
@@ -32,6 +32,10 @@ have been changed or you remove it.
data from from the net if necessary. If it reports failure, something is
going wrong!
+You may have to set up PHPUnit first.
+
+$ pear channel-discover pear.phpunit.de
+$ pear install phpunit/PHPUnit
== Benchmarks ==
diff --git a/includes/normal/RandomTest.php b/includes/normal/RandomTest.php
index b86ab7c3..9ccbc01d 100644
--- a/includes/normal/RandomTest.php
+++ b/includes/normal/RandomTest.php
@@ -22,7 +22,7 @@
* UtfNormal::cleanUp() code paths, and checks to see if there's a
* difference. Will run forever until it finds one or you kill it.
*
- * @package UtfNormal
+ * @addtogroup UtfNormal
* @access private
*/
@@ -69,7 +69,7 @@ function showDiffs( $a, $b ) {
$formatter = new TableDiffFormatter();
$funky = $formatter->format( $diffs );
$matches = array();
- preg_match_all( '/<span class="diffchange">(.*?)<\/span>/', $funky, $matches );
+ preg_match_all( '/<(?:ins|del) class="diffchange">(.*?)<\/(?:ins|del)>/', $funky, $matches );
foreach( $matches[1] as $bit ) {
$hex = bin2hex( $bit );
echo "\t$hex\n";
diff --git a/includes/normal/Utf8Test.php b/includes/normal/Utf8Test.php
index 34ab69c8..fc2e7776 100644
--- a/includes/normal/Utf8Test.php
+++ b/includes/normal/Utf8Test.php
@@ -21,7 +21,7 @@
* Runs the UTF-8 decoder test at:
* http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
*
- * @package UtfNormal
+ * @addtogroup UtfNormal
* @access private
*/
diff --git a/includes/normal/UtfNormal.php b/includes/normal/UtfNormal.php
index d8eac7b8..43bbafd8 100644
--- a/includes/normal/UtfNormal.php
+++ b/includes/normal/UtfNormal.php
@@ -17,21 +17,6 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# http://www.gnu.org/copyleft/gpl.html
-/**
- * Unicode normalization routines for working with UTF-8 strings.
- * Currently assumes that input strings are valid UTF-8!
- *
- * Not as fast as I'd like, but should be usable for most purposes.
- * UtfNormal::toNFC() will bail early if given ASCII text or text
- * it can quickly deterimine is already normalized.
- *
- * All functions can be called static.
- *
- * See description of forms at http://www.unicode.org/reports/tr15/
- *
- * @package UtfNormal
- */
-
/** */
require_once dirname(__FILE__).'/UtfNormalUtil.php';
@@ -111,8 +96,18 @@ define( 'UNORM_FCD', 6 );
define( 'NORMALIZE_ICU', function_exists( 'utf8_normalize' ) );
/**
+ * Unicode normalization routines for working with UTF-8 strings.
+ * Currently assumes that input strings are valid UTF-8!
*
- * @package MediaWiki
+ * Not as fast as I'd like, but should be usable for most purposes.
+ * UtfNormal::toNFC() will bail early if given ASCII text or text
+ * it can quickly deterimine is already normalized.
+ *
+ * All functions can be called static.
+ *
+ * See description of forms at http://www.unicode.org/reports/tr15/
+ *
+ * @addtogroup UtfNormal
*/
class UtfNormal {
/**
@@ -124,7 +119,7 @@ class UtfNormal {
*
* @param string $string a UTF-8 string
* @return string a clean, shiny, normalized UTF-8 string
- * @static
+ * @static
*/
static function cleanUp( $string ) {
if( NORMALIZE_ICU ) {
@@ -226,7 +221,7 @@ class UtfNormal {
static function loadData() {
global $utfCombiningClass;
if( !isset( $utfCombiningClass ) ) {
- require_once( 'UtfNormalData.inc' );
+ require_once( dirname(__FILE__) . '/UtfNormalData.inc' );
}
}
@@ -635,7 +630,11 @@ class UtfNormal {
}
if( isset( $utfCombiningClass[$c] ) ) {
$lastClass = $utfCombiningClass[$c];
- @$combiners[$lastClass] .= $c;
+ if( isset( $combiners[$lastClass] ) ) {
+ $combiners[$lastClass] .= $c;
+ } else {
+ $combiners[$lastClass] = $c;
+ }
continue;
}
}
@@ -805,4 +804,4 @@ class UtfNormal {
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/normal/UtfNormalBench.php b/includes/normal/UtfNormalBench.php
index a5eb267e..c394f4d8 100644
--- a/includes/normal/UtfNormalBench.php
+++ b/includes/normal/UtfNormalBench.php
@@ -20,7 +20,7 @@
/**
* Approximate benchmark for some basic operations.
*
- * @package UtfNormal
+ * @addtogroup UtfNormal
* @access private
*/
@@ -43,7 +43,7 @@ $testfiles = array(
'testdata/berlin.txt' => 'German text',
'testdata/bulgakov.txt' => 'Russian text',
'testdata/tokyo.txt' => 'Japanese text',
- 'testdata/sociology.txt' => 'Korean text'
+ 'testdata/young.txt' => 'Korean text'
);
$normalizer = new UtfNormal;
UtfNormal::loadData();
@@ -100,7 +100,11 @@ function benchmarkForm( &$u, &$data, $form ) {
$rate = intval( strlen( $data ) / $delta );
$same = (0 == strcmp( $data, $out ) );
- printf( " %20s %6.1fms %8d bytes/s (%s)\n", $form, $delta*1000.0, $rate, ($same ? 'no change' : 'changed' ) );
+ printf( " %20s %6.1fms %12s bytes/s (%s)\n",
+ $form,
+ $delta*1000.0,
+ number_format( $rate ),
+ ($same ? 'no change' : 'changed' ) );
return $out;
}
diff --git a/includes/normal/UtfNormalData.inc b/includes/normal/UtfNormalData.inc
index 6216d1a3..91c15769 100644
--- a/includes/normal/UtfNormalData.inc
+++ b/includes/normal/UtfNormalData.inc
@@ -2,12 +2,11 @@
/**
* This file was automatically generated -- do not edit!
* Run UtfNormalGenerate.php to create this file again (make clean && make)
- * @package MediaWiki
*/
/** */
global $utfCombiningClass, $utfCanonicalComp, $utfCanonicalDecomp, $utfCheckNFC;
-$utfCombiningClass = unserialize( 'a:384:{s:2:"̀";i:230;s:2:"́";i:230;s:2:"̂";i:230;s:2:"̃";i:230;s:2:"̄";i:230;s:2:"̅";i:230;s:2:"̆";i:230;s:2:"̇";i:230;s:2:"̈";i:230;s:2:"̉";i:230;s:2:"̊";i:230;s:2:"̋";i:230;s:2:"̌";i:230;s:2:"̍";i:230;s:2:"̎";i:230;s:2:"̏";i:230;s:2:"̐";i:230;s:2:"̑";i:230;s:2:"̒";i:230;s:2:"̓";i:230;s:2:"̔";i:230;s:2:"̕";i:232;s:2:"̖";i:220;s:2:"̗";i:220;s:2:"̘";i:220;s:2:"̙";i:220;s:2:"̚";i:232;s:2:"̛";i:216;s:2:"̜";i:220;s:2:"̝";i:220;s:2:"̞";i:220;s:2:"̟";i:220;s:2:"̠";i:220;s:2:"̡";i:202;s:2:"̢";i:202;s:2:"̣";i:220;s:2:"̤";i:220;s:2:"̥";i:220;s:2:"̦";i:220;s:2:"̧";i:202;s:2:"̨";i:202;s:2:"̩";i:220;s:2:"̪";i:220;s:2:"̫";i:220;s:2:"̬";i:220;s:2:"̭";i:220;s:2:"̮";i:220;s:2:"̯";i:220;s:2:"̰";i:220;s:2:"̱";i:220;s:2:"̲";i:220;s:2:"̳";i:220;s:2:"̴";i:1;s:2:"̵";i:1;s:2:"̶";i:1;s:2:"̷";i:1;s:2:"̸";i:1;s:2:"̹";i:220;s:2:"̺";i:220;s:2:"̻";i:220;s:2:"̼";i:220;s:2:"̽";i:230;s:2:"̾";i:230;s:2:"̿";i:230;s:2:"̀";i:230;s:2:"́";i:230;s:2:"͂";i:230;s:2:"̓";i:230;s:2:"̈́";i:230;s:2:"ͅ";i:240;s:2:"͆";i:230;s:2:"͇";i:220;s:2:"͈";i:220;s:2:"͉";i:220;s:2:"͊";i:230;s:2:"͋";i:230;s:2:"͌";i:230;s:2:"͍";i:220;s:2:"͎";i:220;s:2:"͐";i:230;s:2:"͑";i:230;s:2:"͒";i:230;s:2:"͓";i:220;s:2:"͔";i:220;s:2:"͕";i:220;s:2:"͖";i:220;s:2:"͗";i:230;s:2:"͘";i:232;s:2:"͙";i:220;s:2:"͚";i:220;s:2:"͛";i:230;s:2:"͜";i:233;s:2:"͝";i:234;s:2:"͞";i:234;s:2:"͟";i:233;s:2:"͠";i:234;s:2:"͡";i:234;s:2:"͢";i:233;s:2:"ͣ";i:230;s:2:"ͤ";i:230;s:2:"ͥ";i:230;s:2:"ͦ";i:230;s:2:"ͧ";i:230;s:2:"ͨ";i:230;s:2:"ͩ";i:230;s:2:"ͪ";i:230;s:2:"ͫ";i:230;s:2:"ͬ";i:230;s:2:"ͭ";i:230;s:2:"ͮ";i:230;s:2:"ͯ";i:230;s:2:"҃";i:230;s:2:"҄";i:230;s:2:"҅";i:230;s:2:"҆";i:230;s:2:"֑";i:220;s:2:"֒";i:230;s:2:"֓";i:230;s:2:"֔";i:230;s:2:"֕";i:230;s:2:"֖";i:220;s:2:"֗";i:230;s:2:"֘";i:230;s:2:"֙";i:230;s:2:"֚";i:222;s:2:"֛";i:220;s:2:"֜";i:230;s:2:"֝";i:230;s:2:"֞";i:230;s:2:"֟";i:230;s:2:"֠";i:230;s:2:"֡";i:230;s:2:"֢";i:220;s:2:"֣";i:220;s:2:"֤";i:220;s:2:"֥";i:220;s:2:"֦";i:220;s:2:"֧";i:220;s:2:"֨";i:230;s:2:"֩";i:230;s:2:"֪";i:220;s:2:"֫";i:230;s:2:"֬";i:230;s:2:"֭";i:222;s:2:"֮";i:228;s:2:"֯";i:230;s:2:"ְ";i:10;s:2:"ֱ";i:11;s:2:"ֲ";i:12;s:2:"ֳ";i:13;s:2:"ִ";i:14;s:2:"ֵ";i:15;s:2:"ֶ";i:16;s:2:"ַ";i:17;s:2:"ָ";i:18;s:2:"ֹ";i:19;s:2:"ֻ";i:20;s:2:"ּ";i:21;s:2:"ֽ";i:22;s:2:"ֿ";i:23;s:2:"ׁ";i:24;s:2:"ׂ";i:25;s:2:"ׄ";i:230;s:2:"ׅ";i:220;s:2:"ׇ";i:18;s:2:"ؐ";i:230;s:2:"ؑ";i:230;s:2:"ؒ";i:230;s:2:"ؓ";i:230;s:2:"ؔ";i:230;s:2:"ؕ";i:230;s:2:"ً";i:27;s:2:"ٌ";i:28;s:2:"ٍ";i:29;s:2:"َ";i:30;s:2:"ُ";i:31;s:2:"ِ";i:32;s:2:"ّ";i:33;s:2:"ْ";i:34;s:2:"ٓ";i:230;s:2:"ٔ";i:230;s:2:"ٕ";i:220;s:2:"ٖ";i:220;s:2:"ٗ";i:230;s:2:"٘";i:230;s:2:"ٙ";i:230;s:2:"ٚ";i:230;s:2:"ٛ";i:230;s:2:"ٜ";i:220;s:2:"ٝ";i:230;s:2:"ٞ";i:230;s:2:"ٰ";i:35;s:2:"ۖ";i:230;s:2:"ۗ";i:230;s:2:"ۘ";i:230;s:2:"ۙ";i:230;s:2:"ۚ";i:230;s:2:"ۛ";i:230;s:2:"ۜ";i:230;s:2:"۟";i:230;s:2:"۠";i:230;s:2:"ۡ";i:230;s:2:"ۢ";i:230;s:2:"ۣ";i:220;s:2:"ۤ";i:230;s:2:"ۧ";i:230;s:2:"ۨ";i:230;s:2:"۪";i:220;s:2:"۫";i:230;s:2:"۬";i:230;s:2:"ۭ";i:220;s:2:"ܑ";i:36;s:2:"ܰ";i:230;s:2:"ܱ";i:220;s:2:"ܲ";i:230;s:2:"ܳ";i:230;s:2:"ܴ";i:220;s:2:"ܵ";i:230;s:2:"ܶ";i:230;s:2:"ܷ";i:220;s:2:"ܸ";i:220;s:2:"ܹ";i:220;s:2:"ܺ";i:230;s:2:"ܻ";i:220;s:2:"ܼ";i:220;s:2:"ܽ";i:230;s:2:"ܾ";i:220;s:2:"ܿ";i:230;s:2:"݀";i:230;s:2:"݁";i:230;s:2:"݂";i:220;s:2:"݃";i:230;s:2:"݄";i:220;s:2:"݅";i:230;s:2:"݆";i:220;s:2:"݇";i:230;s:2:"݈";i:220;s:2:"݉";i:230;s:2:"݊";i:230;s:3:"़";i:7;s:3:"्";i:9;s:3:"॑";i:230;s:3:"॒";i:220;s:3:"॓";i:230;s:3:"॔";i:230;s:3:"়";i:7;s:3:"্";i:9;s:3:"਼";i:7;s:3:"੍";i:9;s:3:"઼";i:7;s:3:"્";i:9;s:3:"଼";i:7;s:3:"୍";i:9;s:3:"்";i:9;s:3:"్";i:9;s:3:"ౕ";i:84;s:3:"ౖ";i:91;s:3:"಼";i:7;s:3:"್";i:9;s:3:"്";i:9;s:3:"්";i:9;s:3:"ุ";i:103;s:3:"ู";i:103;s:3:"ฺ";i:9;s:3:"่";i:107;s:3:"้";i:107;s:3:"๊";i:107;s:3:"๋";i:107;s:3:"ຸ";i:118;s:3:"ູ";i:118;s:3:"່";i:122;s:3:"້";i:122;s:3:"໊";i:122;s:3:"໋";i:122;s:3:"༘";i:220;s:3:"༙";i:220;s:3:"༵";i:220;s:3:"༷";i:220;s:3:"༹";i:216;s:3:"ཱ";i:129;s:3:"ི";i:130;s:3:"ུ";i:132;s:3:"ེ";i:130;s:3:"ཻ";i:130;s:3:"ོ";i:130;s:3:"ཽ";i:130;s:3:"ྀ";i:130;s:3:"ྂ";i:230;s:3:"ྃ";i:230;s:3:"྄";i:9;s:3:"྆";i:230;s:3:"྇";i:230;s:3:"࿆";i:220;s:3:"့";i:7;s:3:"္";i:9;s:3:"፟";i:230;s:3:"᜔";i:9;s:3:"᜴";i:9;s:3:"្";i:9;s:3:"៝";i:230;s:3:"ᢩ";i:228;s:3:"᤹";i:222;s:3:"᤺";i:230;s:3:"᤻";i:220;s:3:"ᨗ";i:230;s:3:"ᨘ";i:220;s:3:"᷀";i:230;s:3:"᷁";i:230;s:3:"᷂";i:220;s:3:"᷃";i:230;s:3:"⃐";i:230;s:3:"⃑";i:230;s:3:"⃒";i:1;s:3:"⃓";i:1;s:3:"⃔";i:230;s:3:"⃕";i:230;s:3:"⃖";i:230;s:3:"⃗";i:230;s:3:"⃘";i:1;s:3:"⃙";i:1;s:3:"⃚";i:1;s:3:"⃛";i:230;s:3:"⃜";i:230;s:3:"⃡";i:230;s:3:"⃥";i:1;s:3:"⃦";i:1;s:3:"⃧";i:230;s:3:"⃨";i:220;s:3:"⃩";i:230;s:3:"⃪";i:1;s:3:"⃫";i:1;s:3:"〪";i:218;s:3:"〫";i:228;s:3:"〬";i:232;s:3:"〭";i:222;s:3:"〮";i:224;s:3:"〯";i:224;s:3:"゙";i:8;s:3:"゚";i:8;s:3:"꠆";i:9;s:3:"ﬞ";i:26;s:3:"︠";i:230;s:3:"︡";i:230;s:3:"︢";i:230;s:3:"︣";i:230;s:4:"𐨍";i:220;s:4:"𐨏";i:230;s:4:"𐨸";i:230;s:4:"𐨹";i:1;s:4:"𐨺";i:220;s:4:"𐨿";i:9;s:4:"𝅥";i:216;s:4:"𝅦";i:216;s:4:"𝅧";i:1;s:4:"𝅨";i:1;s:4:"𝅩";i:1;s:4:"𝅭";i:226;s:4:"𝅮";i:216;s:4:"𝅯";i:216;s:4:"𝅰";i:216;s:4:"𝅱";i:216;s:4:"𝅲";i:216;s:4:"𝅻";i:220;s:4:"𝅼";i:220;s:4:"𝅽";i:220;s:4:"𝅾";i:220;s:4:"𝅿";i:220;s:4:"𝆀";i:220;s:4:"𝆁";i:220;s:4:"𝆂";i:220;s:4:"𝆅";i:230;s:4:"𝆆";i:230;s:4:"𝆇";i:230;s:4:"𝆈";i:230;s:4:"𝆉";i:230;s:4:"𝆊";i:220;s:4:"𝆋";i:220;s:4:"𝆪";i:230;s:4:"𝆫";i:230;s:4:"𝆬";i:230;s:4:"𝆭";i:230;s:4:"𝉂";i:230;s:4:"𝉃";i:230;s:4:"𝉄";i:230;}' );
-$utfCanonicalComp = unserialize( 'a:1851:{s:3:"À";s:2:"À";s:3:"Á";s:2:"Á";s:3:"Â";s:2:"Â";s:3:"Ã";s:2:"Ã";s:3:"Ä";s:2:"Ä";s:3:"Å";s:2:"Å";s:3:"Ç";s:2:"Ç";s:3:"È";s:2:"È";s:3:"É";s:2:"É";s:3:"Ê";s:2:"Ê";s:3:"Ë";s:2:"Ë";s:3:"Ì";s:2:"Ì";s:3:"Í";s:2:"Í";s:3:"Î";s:2:"Î";s:3:"Ï";s:2:"Ï";s:3:"Ñ";s:2:"Ñ";s:3:"Ò";s:2:"Ò";s:3:"Ó";s:2:"Ó";s:3:"Ô";s:2:"Ô";s:3:"Õ";s:2:"Õ";s:3:"Ö";s:2:"Ö";s:3:"Ù";s:2:"Ù";s:3:"Ú";s:2:"Ú";s:3:"Û";s:2:"Û";s:3:"Ü";s:2:"Ü";s:3:"Ý";s:2:"Ý";s:3:"à";s:2:"à";s:3:"á";s:2:"á";s:3:"â";s:2:"â";s:3:"ã";s:2:"ã";s:3:"ä";s:2:"ä";s:3:"å";s:2:"å";s:3:"ç";s:2:"ç";s:3:"è";s:2:"è";s:3:"é";s:2:"é";s:3:"ê";s:2:"ê";s:3:"ë";s:2:"ë";s:3:"ì";s:2:"ì";s:3:"í";s:2:"í";s:3:"î";s:2:"î";s:3:"ï";s:2:"ï";s:3:"ñ";s:2:"ñ";s:3:"ò";s:2:"ò";s:3:"ó";s:2:"ó";s:3:"ô";s:2:"ô";s:3:"õ";s:2:"õ";s:3:"ö";s:2:"ö";s:3:"ù";s:2:"ù";s:3:"ú";s:2:"ú";s:3:"û";s:2:"û";s:3:"ü";s:2:"ü";s:3:"ý";s:2:"ý";s:3:"ÿ";s:2:"ÿ";s:3:"Ā";s:2:"Ā";s:3:"ā";s:2:"ā";s:3:"Ă";s:2:"Ă";s:3:"ă";s:2:"ă";s:3:"Ą";s:2:"Ą";s:3:"ą";s:2:"ą";s:3:"Ć";s:2:"Ć";s:3:"ć";s:2:"ć";s:3:"Ĉ";s:2:"Ĉ";s:3:"ĉ";s:2:"ĉ";s:3:"Ċ";s:2:"Ċ";s:3:"ċ";s:2:"ċ";s:3:"Č";s:2:"Č";s:3:"č";s:2:"č";s:3:"Ď";s:2:"Ď";s:3:"ď";s:2:"ď";s:3:"Ē";s:2:"Ē";s:3:"ē";s:2:"ē";s:3:"Ĕ";s:2:"Ĕ";s:3:"ĕ";s:2:"ĕ";s:3:"Ė";s:2:"Ė";s:3:"ė";s:2:"ė";s:3:"Ę";s:2:"Ę";s:3:"ę";s:2:"ę";s:3:"Ě";s:2:"Ě";s:3:"ě";s:2:"ě";s:3:"Ĝ";s:2:"Ĝ";s:3:"ĝ";s:2:"ĝ";s:3:"Ğ";s:2:"Ğ";s:3:"ğ";s:2:"ğ";s:3:"Ġ";s:2:"Ġ";s:3:"ġ";s:2:"ġ";s:3:"Ģ";s:2:"Ģ";s:3:"ģ";s:2:"ģ";s:3:"Ĥ";s:2:"Ĥ";s:3:"ĥ";s:2:"ĥ";s:3:"Ĩ";s:2:"Ĩ";s:3:"ĩ";s:2:"ĩ";s:3:"Ī";s:2:"Ī";s:3:"ī";s:2:"ī";s:3:"Ĭ";s:2:"Ĭ";s:3:"ĭ";s:2:"ĭ";s:3:"Į";s:2:"Į";s:3:"į";s:2:"į";s:3:"İ";s:2:"İ";s:3:"Ĵ";s:2:"Ĵ";s:3:"ĵ";s:2:"ĵ";s:3:"Ķ";s:2:"Ķ";s:3:"ķ";s:2:"ķ";s:3:"Ĺ";s:2:"Ĺ";s:3:"ĺ";s:2:"ĺ";s:3:"Ļ";s:2:"Ļ";s:3:"ļ";s:2:"ļ";s:3:"Ľ";s:2:"Ľ";s:3:"ľ";s:2:"ľ";s:3:"Ń";s:2:"Ń";s:3:"ń";s:2:"ń";s:3:"Ņ";s:2:"Ņ";s:3:"ņ";s:2:"ņ";s:3:"Ň";s:2:"Ň";s:3:"ň";s:2:"ň";s:3:"Ō";s:2:"Ō";s:3:"ō";s:2:"ō";s:3:"Ŏ";s:2:"Ŏ";s:3:"ŏ";s:2:"ŏ";s:3:"Ő";s:2:"Ő";s:3:"ő";s:2:"ő";s:3:"Ŕ";s:2:"Ŕ";s:3:"ŕ";s:2:"ŕ";s:3:"Ŗ";s:2:"Ŗ";s:3:"ŗ";s:2:"ŗ";s:3:"Ř";s:2:"Ř";s:3:"ř";s:2:"ř";s:3:"Ś";s:2:"Ś";s:3:"ś";s:2:"ś";s:3:"Ŝ";s:2:"Ŝ";s:3:"ŝ";s:2:"ŝ";s:3:"Ş";s:2:"Ş";s:3:"ş";s:2:"ş";s:3:"Š";s:2:"Š";s:3:"š";s:2:"š";s:3:"Ţ";s:2:"Ţ";s:3:"ţ";s:2:"ţ";s:3:"Ť";s:2:"Ť";s:3:"ť";s:2:"ť";s:3:"Ũ";s:2:"Ũ";s:3:"ũ";s:2:"ũ";s:3:"Ū";s:2:"Ū";s:3:"ū";s:2:"ū";s:3:"Ŭ";s:2:"Ŭ";s:3:"ŭ";s:2:"ŭ";s:3:"Ů";s:2:"Ů";s:3:"ů";s:2:"ů";s:3:"Ű";s:2:"Ű";s:3:"ű";s:2:"ű";s:3:"Ų";s:2:"Ų";s:3:"ų";s:2:"ų";s:3:"Ŵ";s:2:"Ŵ";s:3:"ŵ";s:2:"ŵ";s:3:"Ŷ";s:2:"Ŷ";s:3:"ŷ";s:2:"ŷ";s:3:"Ÿ";s:2:"Ÿ";s:3:"Ź";s:2:"Ź";s:3:"ź";s:2:"ź";s:3:"Ż";s:2:"Ż";s:3:"ż";s:2:"ż";s:3:"Ž";s:2:"Ž";s:3:"ž";s:2:"ž";s:3:"Ơ";s:2:"Ơ";s:3:"ơ";s:2:"ơ";s:3:"Ư";s:2:"Ư";s:3:"ư";s:2:"ư";s:3:"Ǎ";s:2:"Ǎ";s:3:"ǎ";s:2:"ǎ";s:3:"Ǐ";s:2:"Ǐ";s:3:"ǐ";s:2:"ǐ";s:3:"Ǒ";s:2:"Ǒ";s:3:"ǒ";s:2:"ǒ";s:3:"Ǔ";s:2:"Ǔ";s:3:"ǔ";s:2:"ǔ";s:4:"Ǖ";s:2:"Ǖ";s:4:"ǖ";s:2:"ǖ";s:4:"Ǘ";s:2:"Ǘ";s:4:"ǘ";s:2:"ǘ";s:4:"Ǚ";s:2:"Ǚ";s:4:"ǚ";s:2:"ǚ";s:4:"Ǜ";s:2:"Ǜ";s:4:"ǜ";s:2:"ǜ";s:4:"Ǟ";s:2:"Ǟ";s:4:"ǟ";s:2:"ǟ";s:4:"Ǡ";s:2:"Ǡ";s:4:"ǡ";s:2:"ǡ";s:4:"Ǣ";s:2:"Ǣ";s:4:"ǣ";s:2:"ǣ";s:3:"Ǧ";s:2:"Ǧ";s:3:"ǧ";s:2:"ǧ";s:3:"Ǩ";s:2:"Ǩ";s:3:"ǩ";s:2:"ǩ";s:3:"Ǫ";s:2:"Ǫ";s:3:"ǫ";s:2:"ǫ";s:4:"Ǭ";s:2:"Ǭ";s:4:"ǭ";s:2:"ǭ";s:4:"Ǯ";s:2:"Ǯ";s:4:"ǯ";s:2:"ǯ";s:3:"ǰ";s:2:"ǰ";s:3:"Ǵ";s:2:"Ǵ";s:3:"ǵ";s:2:"ǵ";s:3:"Ǹ";s:2:"Ǹ";s:3:"ǹ";s:2:"ǹ";s:4:"Ǻ";s:2:"Ǻ";s:4:"ǻ";s:2:"ǻ";s:4:"Ǽ";s:2:"Ǽ";s:4:"ǽ";s:2:"ǽ";s:4:"Ǿ";s:2:"Ǿ";s:4:"ǿ";s:2:"ǿ";s:3:"Ȁ";s:2:"Ȁ";s:3:"ȁ";s:2:"ȁ";s:3:"Ȃ";s:2:"Ȃ";s:3:"ȃ";s:2:"ȃ";s:3:"Ȅ";s:2:"Ȅ";s:3:"ȅ";s:2:"ȅ";s:3:"Ȇ";s:2:"Ȇ";s:3:"ȇ";s:2:"ȇ";s:3:"Ȉ";s:2:"Ȉ";s:3:"ȉ";s:2:"ȉ";s:3:"Ȋ";s:2:"Ȋ";s:3:"ȋ";s:2:"ȋ";s:3:"Ȍ";s:2:"Ȍ";s:3:"ȍ";s:2:"ȍ";s:3:"Ȏ";s:2:"Ȏ";s:3:"ȏ";s:2:"ȏ";s:3:"Ȑ";s:2:"Ȑ";s:3:"ȑ";s:2:"ȑ";s:3:"Ȓ";s:2:"Ȓ";s:3:"ȓ";s:2:"ȓ";s:3:"Ȕ";s:2:"Ȕ";s:3:"ȕ";s:2:"ȕ";s:3:"Ȗ";s:2:"Ȗ";s:3:"ȗ";s:2:"ȗ";s:3:"Ș";s:2:"Ș";s:3:"ș";s:2:"ș";s:3:"Ț";s:2:"Ț";s:3:"ț";s:2:"ț";s:3:"Ȟ";s:2:"Ȟ";s:3:"ȟ";s:2:"ȟ";s:3:"Ȧ";s:2:"Ȧ";s:3:"ȧ";s:2:"ȧ";s:3:"Ȩ";s:2:"Ȩ";s:3:"ȩ";s:2:"ȩ";s:4:"Ȫ";s:2:"Ȫ";s:4:"ȫ";s:2:"ȫ";s:4:"Ȭ";s:2:"Ȭ";s:4:"ȭ";s:2:"ȭ";s:3:"Ȯ";s:2:"Ȯ";s:3:"ȯ";s:2:"ȯ";s:4:"Ȱ";s:2:"Ȱ";s:4:"ȱ";s:2:"ȱ";s:3:"Ȳ";s:2:"Ȳ";s:3:"ȳ";s:2:"ȳ";s:2:"̀";s:2:"̀";s:2:"́";s:2:"́";s:2:"̓";s:2:"̓";s:4:"̈́";s:2:"̈́";s:2:"ʹ";s:2:"ʹ";s:1:";";s:2:";";s:4:"΅";s:2:"΅";s:4:"Ά";s:2:"Ά";s:2:"·";s:2:"·";s:4:"Έ";s:2:"Έ";s:4:"Ή";s:2:"Ή";s:4:"Ί";s:2:"Ί";s:4:"Ό";s:2:"Ό";s:4:"Ύ";s:2:"Ύ";s:4:"Ώ";s:2:"Ώ";s:4:"ΐ";s:2:"ΐ";s:4:"Ϊ";s:2:"Ϊ";s:4:"Ϋ";s:2:"Ϋ";s:4:"ά";s:2:"ά";s:4:"έ";s:2:"έ";s:4:"ή";s:2:"ή";s:4:"ί";s:2:"ί";s:4:"ΰ";s:2:"ΰ";s:4:"ϊ";s:2:"ϊ";s:4:"ϋ";s:2:"ϋ";s:4:"ό";s:2:"ό";s:4:"ύ";s:2:"ύ";s:4:"ώ";s:2:"ώ";s:4:"ϓ";s:2:"ϓ";s:4:"ϔ";s:2:"ϔ";s:4:"Ѐ";s:2:"Ѐ";s:4:"Ё";s:2:"Ё";s:4:"Ѓ";s:2:"Ѓ";s:4:"Ї";s:2:"Ї";s:4:"Ќ";s:2:"Ќ";s:4:"Ѝ";s:2:"Ѝ";s:4:"Ў";s:2:"Ў";s:4:"Й";s:2:"Й";s:4:"й";s:2:"й";s:4:"ѐ";s:2:"ѐ";s:4:"ё";s:2:"ё";s:4:"ѓ";s:2:"ѓ";s:4:"ї";s:2:"ї";s:4:"ќ";s:2:"ќ";s:4:"ѝ";s:2:"ѝ";s:4:"ў";s:2:"ў";s:4:"Ѷ";s:2:"Ѷ";s:4:"ѷ";s:2:"ѷ";s:4:"Ӂ";s:2:"Ӂ";s:4:"ӂ";s:2:"ӂ";s:4:"Ӑ";s:2:"Ӑ";s:4:"ӑ";s:2:"ӑ";s:4:"Ӓ";s:2:"Ӓ";s:4:"ӓ";s:2:"ӓ";s:4:"Ӗ";s:2:"Ӗ";s:4:"ӗ";s:2:"ӗ";s:4:"Ӛ";s:2:"Ӛ";s:4:"ӛ";s:2:"ӛ";s:4:"Ӝ";s:2:"Ӝ";s:4:"ӝ";s:2:"ӝ";s:4:"Ӟ";s:2:"Ӟ";s:4:"ӟ";s:2:"ӟ";s:4:"Ӣ";s:2:"Ӣ";s:4:"ӣ";s:2:"ӣ";s:4:"Ӥ";s:2:"Ӥ";s:4:"ӥ";s:2:"ӥ";s:4:"Ӧ";s:2:"Ӧ";s:4:"ӧ";s:2:"ӧ";s:4:"Ӫ";s:2:"Ӫ";s:4:"ӫ";s:2:"ӫ";s:4:"Ӭ";s:2:"Ӭ";s:4:"ӭ";s:2:"ӭ";s:4:"Ӯ";s:2:"Ӯ";s:4:"ӯ";s:2:"ӯ";s:4:"Ӱ";s:2:"Ӱ";s:4:"ӱ";s:2:"ӱ";s:4:"Ӳ";s:2:"Ӳ";s:4:"ӳ";s:2:"ӳ";s:4:"Ӵ";s:2:"Ӵ";s:4:"ӵ";s:2:"ӵ";s:4:"Ӹ";s:2:"Ӹ";s:4:"ӹ";s:2:"ӹ";s:4:"آ";s:2:"آ";s:4:"أ";s:2:"أ";s:4:"ؤ";s:2:"ؤ";s:4:"إ";s:2:"إ";s:4:"ئ";s:2:"ئ";s:4:"ۀ";s:2:"ۀ";s:4:"ۂ";s:2:"ۂ";s:4:"ۓ";s:2:"ۓ";s:6:"ऩ";s:3:"ऩ";s:6:"ऱ";s:3:"ऱ";s:6:"ऴ";s:3:"ऴ";s:6:"ো";s:3:"ো";s:6:"ৌ";s:3:"ৌ";s:6:"ୈ";s:3:"ୈ";s:6:"ୋ";s:3:"ୋ";s:6:"ୌ";s:3:"ୌ";s:6:"ஔ";s:3:"ஔ";s:6:"ொ";s:3:"ொ";s:6:"ோ";s:3:"ோ";s:6:"ௌ";s:3:"ௌ";s:6:"ై";s:3:"ై";s:6:"ೀ";s:3:"ೀ";s:6:"ೇ";s:3:"ೇ";s:6:"ೈ";s:3:"ೈ";s:6:"ೊ";s:3:"ೊ";s:6:"ೋ";s:3:"ೋ";s:6:"ൊ";s:3:"ൊ";s:6:"ോ";s:3:"ോ";s:6:"ൌ";s:3:"ൌ";s:6:"ේ";s:3:"ේ";s:6:"ො";s:3:"ො";s:6:"ෝ";s:3:"ෝ";s:6:"ෞ";s:3:"ෞ";s:6:"ཱི";s:3:"ཱི";s:6:"ཱུ";s:3:"ཱུ";s:6:"ཱྀ";s:3:"ཱྀ";s:6:"ဦ";s:3:"ဦ";s:3:"Ḁ";s:3:"Ḁ";s:3:"ḁ";s:3:"ḁ";s:3:"Ḃ";s:3:"Ḃ";s:3:"ḃ";s:3:"ḃ";s:3:"Ḅ";s:3:"Ḅ";s:3:"ḅ";s:3:"ḅ";s:3:"Ḇ";s:3:"Ḇ";s:3:"ḇ";s:3:"ḇ";s:4:"Ḉ";s:3:"Ḉ";s:4:"ḉ";s:3:"ḉ";s:3:"Ḋ";s:3:"Ḋ";s:3:"ḋ";s:3:"ḋ";s:3:"Ḍ";s:3:"Ḍ";s:3:"ḍ";s:3:"ḍ";s:3:"Ḏ";s:3:"Ḏ";s:3:"ḏ";s:3:"ḏ";s:3:"Ḑ";s:3:"Ḑ";s:3:"ḑ";s:3:"ḑ";s:3:"Ḓ";s:3:"Ḓ";s:3:"ḓ";s:3:"ḓ";s:4:"Ḕ";s:3:"Ḕ";s:4:"ḕ";s:3:"ḕ";s:4:"Ḗ";s:3:"Ḗ";s:4:"ḗ";s:3:"ḗ";s:3:"Ḙ";s:3:"Ḙ";s:3:"ḙ";s:3:"ḙ";s:3:"Ḛ";s:3:"Ḛ";s:3:"ḛ";s:3:"ḛ";s:4:"Ḝ";s:3:"Ḝ";s:4:"ḝ";s:3:"ḝ";s:3:"Ḟ";s:3:"Ḟ";s:3:"ḟ";s:3:"ḟ";s:3:"Ḡ";s:3:"Ḡ";s:3:"ḡ";s:3:"ḡ";s:3:"Ḣ";s:3:"Ḣ";s:3:"ḣ";s:3:"ḣ";s:3:"Ḥ";s:3:"Ḥ";s:3:"ḥ";s:3:"ḥ";s:3:"Ḧ";s:3:"Ḧ";s:3:"ḧ";s:3:"ḧ";s:3:"Ḩ";s:3:"Ḩ";s:3:"ḩ";s:3:"ḩ";s:3:"Ḫ";s:3:"Ḫ";s:3:"ḫ";s:3:"ḫ";s:3:"Ḭ";s:3:"Ḭ";s:3:"ḭ";s:3:"ḭ";s:4:"Ḯ";s:3:"Ḯ";s:4:"ḯ";s:3:"ḯ";s:3:"Ḱ";s:3:"Ḱ";s:3:"ḱ";s:3:"ḱ";s:3:"Ḳ";s:3:"Ḳ";s:3:"ḳ";s:3:"ḳ";s:3:"Ḵ";s:3:"Ḵ";s:3:"ḵ";s:3:"ḵ";s:3:"Ḷ";s:3:"Ḷ";s:3:"ḷ";s:3:"ḷ";s:5:"Ḹ";s:3:"Ḹ";s:5:"ḹ";s:3:"ḹ";s:3:"Ḻ";s:3:"Ḻ";s:3:"ḻ";s:3:"ḻ";s:3:"Ḽ";s:3:"Ḽ";s:3:"ḽ";s:3:"ḽ";s:3:"Ḿ";s:3:"Ḿ";s:3:"ḿ";s:3:"ḿ";s:3:"Ṁ";s:3:"Ṁ";s:3:"ṁ";s:3:"ṁ";s:3:"Ṃ";s:3:"Ṃ";s:3:"ṃ";s:3:"ṃ";s:3:"Ṅ";s:3:"Ṅ";s:3:"ṅ";s:3:"ṅ";s:3:"Ṇ";s:3:"Ṇ";s:3:"ṇ";s:3:"ṇ";s:3:"Ṉ";s:3:"Ṉ";s:3:"ṉ";s:3:"ṉ";s:3:"Ṋ";s:3:"Ṋ";s:3:"ṋ";s:3:"ṋ";s:4:"Ṍ";s:3:"Ṍ";s:4:"ṍ";s:3:"ṍ";s:4:"Ṏ";s:3:"Ṏ";s:4:"ṏ";s:3:"ṏ";s:4:"Ṑ";s:3:"Ṑ";s:4:"ṑ";s:3:"ṑ";s:4:"Ṓ";s:3:"Ṓ";s:4:"ṓ";s:3:"ṓ";s:3:"Ṕ";s:3:"Ṕ";s:3:"ṕ";s:3:"ṕ";s:3:"Ṗ";s:3:"Ṗ";s:3:"ṗ";s:3:"ṗ";s:3:"Ṙ";s:3:"Ṙ";s:3:"ṙ";s:3:"ṙ";s:3:"Ṛ";s:3:"Ṛ";s:3:"ṛ";s:3:"ṛ";s:5:"Ṝ";s:3:"Ṝ";s:5:"ṝ";s:3:"ṝ";s:3:"Ṟ";s:3:"Ṟ";s:3:"ṟ";s:3:"ṟ";s:3:"Ṡ";s:3:"Ṡ";s:3:"ṡ";s:3:"ṡ";s:3:"Ṣ";s:3:"Ṣ";s:3:"ṣ";s:3:"ṣ";s:4:"Ṥ";s:3:"Ṥ";s:4:"ṥ";s:3:"ṥ";s:4:"Ṧ";s:3:"Ṧ";s:4:"ṧ";s:3:"ṧ";s:5:"Ṩ";s:3:"Ṩ";s:5:"ṩ";s:3:"ṩ";s:3:"Ṫ";s:3:"Ṫ";s:3:"ṫ";s:3:"ṫ";s:3:"Ṭ";s:3:"Ṭ";s:3:"ṭ";s:3:"ṭ";s:3:"Ṯ";s:3:"Ṯ";s:3:"ṯ";s:3:"ṯ";s:3:"Ṱ";s:3:"Ṱ";s:3:"ṱ";s:3:"ṱ";s:3:"Ṳ";s:3:"Ṳ";s:3:"ṳ";s:3:"ṳ";s:3:"Ṵ";s:3:"Ṵ";s:3:"ṵ";s:3:"ṵ";s:3:"Ṷ";s:3:"Ṷ";s:3:"ṷ";s:3:"ṷ";s:4:"Ṹ";s:3:"Ṹ";s:4:"ṹ";s:3:"ṹ";s:4:"Ṻ";s:3:"Ṻ";s:4:"ṻ";s:3:"ṻ";s:3:"Ṽ";s:3:"Ṽ";s:3:"ṽ";s:3:"ṽ";s:3:"Ṿ";s:3:"Ṿ";s:3:"ṿ";s:3:"ṿ";s:3:"Ẁ";s:3:"Ẁ";s:3:"ẁ";s:3:"ẁ";s:3:"Ẃ";s:3:"Ẃ";s:3:"ẃ";s:3:"ẃ";s:3:"Ẅ";s:3:"Ẅ";s:3:"ẅ";s:3:"ẅ";s:3:"Ẇ";s:3:"Ẇ";s:3:"ẇ";s:3:"ẇ";s:3:"Ẉ";s:3:"Ẉ";s:3:"ẉ";s:3:"ẉ";s:3:"Ẋ";s:3:"Ẋ";s:3:"ẋ";s:3:"ẋ";s:3:"Ẍ";s:3:"Ẍ";s:3:"ẍ";s:3:"ẍ";s:3:"Ẏ";s:3:"Ẏ";s:3:"ẏ";s:3:"ẏ";s:3:"Ẑ";s:3:"Ẑ";s:3:"ẑ";s:3:"ẑ";s:3:"Ẓ";s:3:"Ẓ";s:3:"ẓ";s:3:"ẓ";s:3:"Ẕ";s:3:"Ẕ";s:3:"ẕ";s:3:"ẕ";s:3:"ẖ";s:3:"ẖ";s:3:"ẗ";s:3:"ẗ";s:3:"ẘ";s:3:"ẘ";s:3:"ẙ";s:3:"ẙ";s:4:"ẛ";s:3:"ẛ";s:3:"Ạ";s:3:"Ạ";s:3:"ạ";s:3:"ạ";s:3:"Ả";s:3:"Ả";s:3:"ả";s:3:"ả";s:4:"Ấ";s:3:"Ấ";s:4:"ấ";s:3:"ấ";s:4:"Ầ";s:3:"Ầ";s:4:"ầ";s:3:"ầ";s:4:"Ẩ";s:3:"Ẩ";s:4:"ẩ";s:3:"ẩ";s:4:"Ẫ";s:3:"Ẫ";s:4:"ẫ";s:3:"ẫ";s:5:"Ậ";s:3:"Ậ";s:5:"ậ";s:3:"ậ";s:4:"Ắ";s:3:"Ắ";s:4:"ắ";s:3:"ắ";s:4:"Ằ";s:3:"Ằ";s:4:"ằ";s:3:"ằ";s:4:"Ẳ";s:3:"Ẳ";s:4:"ẳ";s:3:"ẳ";s:4:"Ẵ";s:3:"Ẵ";s:4:"ẵ";s:3:"ẵ";s:5:"Ặ";s:3:"Ặ";s:5:"ặ";s:3:"ặ";s:3:"Ẹ";s:3:"Ẹ";s:3:"ẹ";s:3:"ẹ";s:3:"Ẻ";s:3:"Ẻ";s:3:"ẻ";s:3:"ẻ";s:3:"Ẽ";s:3:"Ẽ";s:3:"ẽ";s:3:"ẽ";s:4:"Ế";s:3:"Ế";s:4:"ế";s:3:"ế";s:4:"Ề";s:3:"Ề";s:4:"ề";s:3:"ề";s:4:"Ể";s:3:"Ể";s:4:"ể";s:3:"ể";s:4:"Ễ";s:3:"Ễ";s:4:"ễ";s:3:"ễ";s:5:"Ệ";s:3:"Ệ";s:5:"ệ";s:3:"ệ";s:3:"Ỉ";s:3:"Ỉ";s:3:"ỉ";s:3:"ỉ";s:3:"Ị";s:3:"Ị";s:3:"ị";s:3:"ị";s:3:"Ọ";s:3:"Ọ";s:3:"ọ";s:3:"ọ";s:3:"Ỏ";s:3:"Ỏ";s:3:"ỏ";s:3:"ỏ";s:4:"Ố";s:3:"Ố";s:4:"ố";s:3:"ố";s:4:"Ồ";s:3:"Ồ";s:4:"ồ";s:3:"ồ";s:4:"Ổ";s:3:"Ổ";s:4:"ổ";s:3:"ổ";s:4:"Ỗ";s:3:"Ỗ";s:4:"ỗ";s:3:"ỗ";s:5:"Ộ";s:3:"Ộ";s:5:"ộ";s:3:"ộ";s:4:"Ớ";s:3:"Ớ";s:4:"ớ";s:3:"ớ";s:4:"Ờ";s:3:"Ờ";s:4:"ờ";s:3:"ờ";s:4:"Ở";s:3:"Ở";s:4:"ở";s:3:"ở";s:4:"Ỡ";s:3:"Ỡ";s:4:"ỡ";s:3:"ỡ";s:4:"Ợ";s:3:"Ợ";s:4:"ợ";s:3:"ợ";s:3:"Ụ";s:3:"Ụ";s:3:"ụ";s:3:"ụ";s:3:"Ủ";s:3:"Ủ";s:3:"ủ";s:3:"ủ";s:4:"Ứ";s:3:"Ứ";s:4:"ứ";s:3:"ứ";s:4:"Ừ";s:3:"Ừ";s:4:"ừ";s:3:"ừ";s:4:"Ử";s:3:"Ử";s:4:"ử";s:3:"ử";s:4:"Ữ";s:3:"Ữ";s:4:"ữ";s:3:"ữ";s:4:"Ự";s:3:"Ự";s:4:"ự";s:3:"ự";s:3:"Ỳ";s:3:"Ỳ";s:3:"ỳ";s:3:"ỳ";s:3:"Ỵ";s:3:"Ỵ";s:3:"ỵ";s:3:"ỵ";s:3:"Ỷ";s:3:"Ỷ";s:3:"ỷ";s:3:"ỷ";s:3:"Ỹ";s:3:"Ỹ";s:3:"ỹ";s:3:"ỹ";s:4:"ἀ";s:3:"ἀ";s:4:"ἁ";s:3:"ἁ";s:5:"ἂ";s:3:"ἂ";s:5:"ἃ";s:3:"ἃ";s:5:"ἄ";s:3:"ἄ";s:5:"ἅ";s:3:"ἅ";s:5:"ἆ";s:3:"ἆ";s:5:"ἇ";s:3:"ἇ";s:4:"Ἀ";s:3:"Ἀ";s:4:"Ἁ";s:3:"Ἁ";s:5:"Ἂ";s:3:"Ἂ";s:5:"Ἃ";s:3:"Ἃ";s:5:"Ἄ";s:3:"Ἄ";s:5:"Ἅ";s:3:"Ἅ";s:5:"Ἆ";s:3:"Ἆ";s:5:"Ἇ";s:3:"Ἇ";s:4:"ἐ";s:3:"ἐ";s:4:"ἑ";s:3:"ἑ";s:5:"ἒ";s:3:"ἒ";s:5:"ἓ";s:3:"ἓ";s:5:"ἔ";s:3:"ἔ";s:5:"ἕ";s:3:"ἕ";s:4:"Ἐ";s:3:"Ἐ";s:4:"Ἑ";s:3:"Ἑ";s:5:"Ἒ";s:3:"Ἒ";s:5:"Ἓ";s:3:"Ἓ";s:5:"Ἔ";s:3:"Ἔ";s:5:"Ἕ";s:3:"Ἕ";s:4:"ἠ";s:3:"ἠ";s:4:"ἡ";s:3:"ἡ";s:5:"ἢ";s:3:"ἢ";s:5:"ἣ";s:3:"ἣ";s:5:"ἤ";s:3:"ἤ";s:5:"ἥ";s:3:"ἥ";s:5:"ἦ";s:3:"ἦ";s:5:"ἧ";s:3:"ἧ";s:4:"Ἠ";s:3:"Ἠ";s:4:"Ἡ";s:3:"Ἡ";s:5:"Ἢ";s:3:"Ἢ";s:5:"Ἣ";s:3:"Ἣ";s:5:"Ἤ";s:3:"Ἤ";s:5:"Ἥ";s:3:"Ἥ";s:5:"Ἦ";s:3:"Ἦ";s:5:"Ἧ";s:3:"Ἧ";s:4:"ἰ";s:3:"ἰ";s:4:"ἱ";s:3:"ἱ";s:5:"ἲ";s:3:"ἲ";s:5:"ἳ";s:3:"ἳ";s:5:"ἴ";s:3:"ἴ";s:5:"ἵ";s:3:"ἵ";s:5:"ἶ";s:3:"ἶ";s:5:"ἷ";s:3:"ἷ";s:4:"Ἰ";s:3:"Ἰ";s:4:"Ἱ";s:3:"Ἱ";s:5:"Ἲ";s:3:"Ἲ";s:5:"Ἳ";s:3:"Ἳ";s:5:"Ἴ";s:3:"Ἴ";s:5:"Ἵ";s:3:"Ἵ";s:5:"Ἶ";s:3:"Ἶ";s:5:"Ἷ";s:3:"Ἷ";s:4:"ὀ";s:3:"ὀ";s:4:"ὁ";s:3:"ὁ";s:5:"ὂ";s:3:"ὂ";s:5:"ὃ";s:3:"ὃ";s:5:"ὄ";s:3:"ὄ";s:5:"ὅ";s:3:"ὅ";s:4:"Ὀ";s:3:"Ὀ";s:4:"Ὁ";s:3:"Ὁ";s:5:"Ὂ";s:3:"Ὂ";s:5:"Ὃ";s:3:"Ὃ";s:5:"Ὄ";s:3:"Ὄ";s:5:"Ὅ";s:3:"Ὅ";s:4:"ὐ";s:3:"ὐ";s:4:"ὑ";s:3:"ὑ";s:5:"ὒ";s:3:"ὒ";s:5:"ὓ";s:3:"ὓ";s:5:"ὔ";s:3:"ὔ";s:5:"ὕ";s:3:"ὕ";s:5:"ὖ";s:3:"ὖ";s:5:"ὗ";s:3:"ὗ";s:4:"Ὑ";s:3:"Ὑ";s:5:"Ὓ";s:3:"Ὓ";s:5:"Ὕ";s:3:"Ὕ";s:5:"Ὗ";s:3:"Ὗ";s:4:"ὠ";s:3:"ὠ";s:4:"ὡ";s:3:"ὡ";s:5:"ὢ";s:3:"ὢ";s:5:"ὣ";s:3:"ὣ";s:5:"ὤ";s:3:"ὤ";s:5:"ὥ";s:3:"ὥ";s:5:"ὦ";s:3:"ὦ";s:5:"ὧ";s:3:"ὧ";s:4:"Ὠ";s:3:"Ὠ";s:4:"Ὡ";s:3:"Ὡ";s:5:"Ὢ";s:3:"Ὢ";s:5:"Ὣ";s:3:"Ὣ";s:5:"Ὤ";s:3:"Ὤ";s:5:"Ὥ";s:3:"Ὥ";s:5:"Ὦ";s:3:"Ὦ";s:5:"Ὧ";s:3:"Ὧ";s:4:"ὰ";s:3:"ὰ";s:2:"ά";s:3:"ά";s:4:"ὲ";s:3:"ὲ";s:2:"έ";s:3:"έ";s:4:"ὴ";s:3:"ὴ";s:2:"ή";s:3:"ή";s:4:"ὶ";s:3:"ὶ";s:2:"ί";s:3:"ί";s:4:"ὸ";s:3:"ὸ";s:2:"ό";s:3:"ό";s:4:"ὺ";s:3:"ὺ";s:2:"ύ";s:3:"ύ";s:4:"ὼ";s:3:"ὼ";s:2:"ώ";s:3:"ώ";s:5:"ᾀ";s:3:"ᾀ";s:5:"ᾁ";s:3:"ᾁ";s:5:"ᾂ";s:3:"ᾂ";s:5:"ᾃ";s:3:"ᾃ";s:5:"ᾄ";s:3:"ᾄ";s:5:"ᾅ";s:3:"ᾅ";s:5:"ᾆ";s:3:"ᾆ";s:5:"ᾇ";s:3:"ᾇ";s:5:"ᾈ";s:3:"ᾈ";s:5:"ᾉ";s:3:"ᾉ";s:5:"ᾊ";s:3:"ᾊ";s:5:"ᾋ";s:3:"ᾋ";s:5:"ᾌ";s:3:"ᾌ";s:5:"ᾍ";s:3:"ᾍ";s:5:"ᾎ";s:3:"ᾎ";s:5:"ᾏ";s:3:"ᾏ";s:5:"ᾐ";s:3:"ᾐ";s:5:"ᾑ";s:3:"ᾑ";s:5:"ᾒ";s:3:"ᾒ";s:5:"ᾓ";s:3:"ᾓ";s:5:"ᾔ";s:3:"ᾔ";s:5:"ᾕ";s:3:"ᾕ";s:5:"ᾖ";s:3:"ᾖ";s:5:"ᾗ";s:3:"ᾗ";s:5:"ᾘ";s:3:"ᾘ";s:5:"ᾙ";s:3:"ᾙ";s:5:"ᾚ";s:3:"ᾚ";s:5:"ᾛ";s:3:"ᾛ";s:5:"ᾜ";s:3:"ᾜ";s:5:"ᾝ";s:3:"ᾝ";s:5:"ᾞ";s:3:"ᾞ";s:5:"ᾟ";s:3:"ᾟ";s:5:"ᾠ";s:3:"ᾠ";s:5:"ᾡ";s:3:"ᾡ";s:5:"ᾢ";s:3:"ᾢ";s:5:"ᾣ";s:3:"ᾣ";s:5:"ᾤ";s:3:"ᾤ";s:5:"ᾥ";s:3:"ᾥ";s:5:"ᾦ";s:3:"ᾦ";s:5:"ᾧ";s:3:"ᾧ";s:5:"ᾨ";s:3:"ᾨ";s:5:"ᾩ";s:3:"ᾩ";s:5:"ᾪ";s:3:"ᾪ";s:5:"ᾫ";s:3:"ᾫ";s:5:"ᾬ";s:3:"ᾬ";s:5:"ᾭ";s:3:"ᾭ";s:5:"ᾮ";s:3:"ᾮ";s:5:"ᾯ";s:3:"ᾯ";s:4:"ᾰ";s:3:"ᾰ";s:4:"ᾱ";s:3:"ᾱ";s:5:"ᾲ";s:3:"ᾲ";s:4:"ᾳ";s:3:"ᾳ";s:4:"ᾴ";s:3:"ᾴ";s:4:"ᾶ";s:3:"ᾶ";s:5:"ᾷ";s:3:"ᾷ";s:4:"Ᾰ";s:3:"Ᾰ";s:4:"Ᾱ";s:3:"Ᾱ";s:4:"Ὰ";s:3:"Ὰ";s:2:"Ά";s:3:"Ά";s:4:"ᾼ";s:3:"ᾼ";s:2:"ι";s:3:"ι";s:4:"῁";s:3:"῁";s:5:"ῂ";s:3:"ῂ";s:4:"ῃ";s:3:"ῃ";s:4:"ῄ";s:3:"ῄ";s:4:"ῆ";s:3:"ῆ";s:5:"ῇ";s:3:"ῇ";s:4:"Ὲ";s:3:"Ὲ";s:2:"Έ";s:3:"Έ";s:4:"Ὴ";s:3:"Ὴ";s:2:"Ή";s:3:"Ή";s:4:"ῌ";s:3:"ῌ";s:5:"῍";s:3:"῍";s:5:"῎";s:3:"῎";s:5:"῏";s:3:"῏";s:4:"ῐ";s:3:"ῐ";s:4:"ῑ";s:3:"ῑ";s:4:"ῒ";s:3:"ῒ";s:2:"ΐ";s:3:"ΐ";s:4:"ῖ";s:3:"ῖ";s:4:"ῗ";s:3:"ῗ";s:4:"Ῐ";s:3:"Ῐ";s:4:"Ῑ";s:3:"Ῑ";s:4:"Ὶ";s:3:"Ὶ";s:2:"Ί";s:3:"Ί";s:5:"῝";s:3:"῝";s:5:"῞";s:3:"῞";s:5:"῟";s:3:"῟";s:4:"ῠ";s:3:"ῠ";s:4:"ῡ";s:3:"ῡ";s:4:"ῢ";s:3:"ῢ";s:2:"ΰ";s:3:"ΰ";s:4:"ῤ";s:3:"ῤ";s:4:"ῥ";s:3:"ῥ";s:4:"ῦ";s:3:"ῦ";s:4:"ῧ";s:3:"ῧ";s:4:"Ῠ";s:3:"Ῠ";s:4:"Ῡ";s:3:"Ῡ";s:4:"Ὺ";s:3:"Ὺ";s:2:"Ύ";s:3:"Ύ";s:4:"Ῥ";s:3:"Ῥ";s:4:"῭";s:3:"῭";s:2:"΅";s:3:"΅";s:1:"`";s:3:"`";s:5:"ῲ";s:3:"ῲ";s:4:"ῳ";s:3:"ῳ";s:4:"ῴ";s:3:"ῴ";s:4:"ῶ";s:3:"ῶ";s:5:"ῷ";s:3:"ῷ";s:4:"Ὸ";s:3:"Ὸ";s:2:"Ό";s:3:"Ό";s:4:"Ὼ";s:3:"Ὼ";s:2:"Ώ";s:3:"Ώ";s:4:"ῼ";s:3:"ῼ";s:2:"´";s:3:"´";s:3:" ";s:3:" ";s:3:" ";s:3:" ";s:2:"Ω";s:3:"Ω";s:1:"K";s:3:"K";s:2:"Å";s:3:"Å";s:5:"↚";s:3:"↚";s:5:"↛";s:3:"↛";s:5:"↮";s:3:"↮";s:5:"⇍";s:3:"⇍";s:5:"⇎";s:3:"⇎";s:5:"⇏";s:3:"⇏";s:5:"∄";s:3:"∄";s:5:"∉";s:3:"∉";s:5:"∌";s:3:"∌";s:5:"∤";s:3:"∤";s:5:"∦";s:3:"∦";s:5:"≁";s:3:"≁";s:5:"≄";s:3:"≄";s:5:"≇";s:3:"≇";s:5:"≉";s:3:"≉";s:3:"≠";s:3:"≠";s:5:"≢";s:3:"≢";s:5:"≭";s:3:"≭";s:3:"≮";s:3:"≮";s:3:"≯";s:3:"≯";s:5:"≰";s:3:"≰";s:5:"≱";s:3:"≱";s:5:"≴";s:3:"≴";s:5:"≵";s:3:"≵";s:5:"≸";s:3:"≸";s:5:"≹";s:3:"≹";s:5:"⊀";s:3:"⊀";s:5:"⊁";s:3:"⊁";s:5:"⊄";s:3:"⊄";s:5:"⊅";s:3:"⊅";s:5:"⊈";s:3:"⊈";s:5:"⊉";s:3:"⊉";s:5:"⊬";s:3:"⊬";s:5:"⊭";s:3:"⊭";s:5:"⊮";s:3:"⊮";s:5:"⊯";s:3:"⊯";s:5:"⋠";s:3:"⋠";s:5:"⋡";s:3:"⋡";s:5:"⋢";s:3:"⋢";s:5:"⋣";s:3:"⋣";s:5:"⋪";s:3:"⋪";s:5:"⋫";s:3:"⋫";s:5:"⋬";s:3:"⋬";s:5:"⋭";s:3:"⋭";s:3:"〈";s:3:"〈";s:3:"〉";s:3:"〉";s:6:"が";s:3:"が";s:6:"ぎ";s:3:"ぎ";s:6:"ぐ";s:3:"ぐ";s:6:"げ";s:3:"げ";s:6:"ご";s:3:"ご";s:6:"ざ";s:3:"ざ";s:6:"じ";s:3:"じ";s:6:"ず";s:3:"ず";s:6:"ぜ";s:3:"ぜ";s:6:"ぞ";s:3:"ぞ";s:6:"だ";s:3:"だ";s:6:"ぢ";s:3:"ぢ";s:6:"づ";s:3:"づ";s:6:"で";s:3:"で";s:6:"ど";s:3:"ど";s:6:"ば";s:3:"ば";s:6:"ぱ";s:3:"ぱ";s:6:"び";s:3:"び";s:6:"ぴ";s:3:"ぴ";s:6:"ぶ";s:3:"ぶ";s:6:"ぷ";s:3:"ぷ";s:6:"べ";s:3:"べ";s:6:"ぺ";s:3:"ぺ";s:6:"ぼ";s:3:"ぼ";s:6:"ぽ";s:3:"ぽ";s:6:"ゔ";s:3:"ゔ";s:6:"ゞ";s:3:"ゞ";s:6:"ガ";s:3:"ガ";s:6:"ギ";s:3:"ギ";s:6:"グ";s:3:"グ";s:6:"ゲ";s:3:"ゲ";s:6:"ゴ";s:3:"ゴ";s:6:"ザ";s:3:"ザ";s:6:"ジ";s:3:"ジ";s:6:"ズ";s:3:"ズ";s:6:"ゼ";s:3:"ゼ";s:6:"ゾ";s:3:"ゾ";s:6:"ダ";s:3:"ダ";s:6:"ヂ";s:3:"ヂ";s:6:"ヅ";s:3:"ヅ";s:6:"デ";s:3:"デ";s:6:"ド";s:3:"ド";s:6:"バ";s:3:"バ";s:6:"パ";s:3:"パ";s:6:"ビ";s:3:"ビ";s:6:"ピ";s:3:"ピ";s:6:"ブ";s:3:"ブ";s:6:"プ";s:3:"プ";s:6:"ベ";s:3:"ベ";s:6:"ペ";s:3:"ペ";s:6:"ボ";s:3:"ボ";s:6:"ポ";s:3:"ポ";s:6:"ヴ";s:3:"ヴ";s:6:"ヷ";s:3:"ヷ";s:6:"ヸ";s:3:"ヸ";s:6:"ヹ";s:3:"ヹ";s:6:"ヺ";s:3:"ヺ";s:6:"ヾ";s:3:"ヾ";s:3:"豈";s:3:"豈";s:3:"更";s:3:"更";s:3:"車";s:3:"車";s:3:"賈";s:3:"賈";s:3:"滑";s:3:"滑";s:3:"串";s:3:"串";s:3:"句";s:3:"句";s:3:"龜";s:3:"龜";s:3:"契";s:3:"契";s:3:"金";s:3:"金";s:3:"喇";s:3:"喇";s:3:"奈";s:3:"奈";s:3:"懶";s:4:"懶";s:3:"癩";s:3:"癩";s:3:"羅";s:3:"羅";s:3:"蘿";s:3:"蘿";s:3:"螺";s:3:"螺";s:3:"裸";s:3:"裸";s:3:"邏";s:3:"邏";s:3:"樂";s:3:"樂";s:3:"洛";s:3:"洛";s:3:"烙";s:3:"烙";s:3:"珞";s:3:"珞";s:3:"落";s:3:"落";s:3:"酪";s:3:"酪";s:3:"駱";s:3:"駱";s:3:"亂";s:3:"亂";s:3:"卵";s:3:"卵";s:3:"欄";s:3:"欄";s:3:"爛";s:3:"爛";s:3:"蘭";s:3:"蘭";s:3:"鸞";s:3:"鸞";s:3:"嵐";s:3:"嵐";s:3:"濫";s:3:"濫";s:3:"藍";s:3:"藍";s:3:"襤";s:3:"襤";s:3:"拉";s:3:"拉";s:3:"臘";s:3:"臘";s:3:"蠟";s:3:"蠟";s:3:"廊";s:4:"廊";s:3:"朗";s:4:"朗";s:3:"浪";s:3:"浪";s:3:"狼";s:3:"狼";s:3:"郎";s:3:"郎";s:3:"來";s:3:"來";s:3:"冷";s:3:"冷";s:3:"勞";s:3:"勞";s:3:"擄";s:3:"擄";s:3:"櫓";s:3:"櫓";s:3:"爐";s:3:"爐";s:3:"盧";s:3:"盧";s:3:"老";s:3:"老";s:3:"蘆";s:3:"蘆";s:3:"虜";s:4:"虜";s:3:"路";s:3:"路";s:3:"露";s:3:"露";s:3:"魯";s:3:"魯";s:3:"鷺";s:3:"鷺";s:3:"碌";s:4:"碌";s:3:"祿";s:3:"祿";s:3:"綠";s:3:"綠";s:3:"菉";s:3:"菉";s:3:"錄";s:3:"錄";s:3:"鹿";s:3:"鹿";s:3:"論";s:3:"論";s:3:"壟";s:3:"壟";s:3:"弄";s:3:"弄";s:3:"籠";s:3:"籠";s:3:"聾";s:3:"聾";s:3:"牢";s:3:"牢";s:3:"磊";s:3:"磊";s:3:"賂";s:3:"賂";s:3:"雷";s:3:"雷";s:3:"壘";s:3:"壘";s:3:"屢";s:3:"屢";s:3:"樓";s:3:"樓";s:3:"淚";s:3:"淚";s:3:"漏";s:3:"漏";s:3:"累";s:3:"累";s:3:"縷";s:3:"縷";s:3:"陋";s:3:"陋";s:3:"勒";s:3:"勒";s:3:"肋";s:3:"肋";s:3:"凜";s:3:"凜";s:3:"凌";s:3:"凌";s:3:"稜";s:3:"稜";s:3:"綾";s:3:"綾";s:3:"菱";s:3:"菱";s:3:"陵";s:3:"陵";s:3:"讀";s:3:"讀";s:3:"拏";s:3:"拏";s:3:"諾";s:3:"諾";s:3:"丹";s:3:"丹";s:3:"寧";s:4:"寧";s:3:"怒";s:3:"怒";s:3:"率";s:3:"率";s:3:"異";s:4:"異";s:3:"北";s:4:"北";s:3:"磻";s:3:"磻";s:3:"便";s:3:"便";s:3:"復";s:3:"復";s:3:"不";s:3:"不";s:3:"泌";s:3:"泌";s:3:"數";s:3:"數";s:3:"索";s:3:"索";s:3:"參";s:3:"參";s:3:"塞";s:3:"塞";s:3:"省";s:3:"省";s:3:"葉";s:3:"葉";s:3:"說";s:3:"說";s:3:"殺";s:4:"殺";s:3:"辰";s:3:"辰";s:3:"沈";s:3:"沈";s:3:"拾";s:3:"拾";s:3:"若";s:4:"若";s:3:"掠";s:3:"掠";s:3:"略";s:3:"略";s:3:"亮";s:3:"亮";s:3:"兩";s:3:"兩";s:3:"凉";s:3:"凉";s:3:"梁";s:3:"梁";s:3:"糧";s:3:"糧";s:3:"良";s:3:"良";s:3:"諒";s:3:"諒";s:3:"量";s:3:"量";s:3:"勵";s:3:"勵";s:3:"呂";s:3:"呂";s:3:"女";s:3:"女";s:3:"廬";s:3:"廬";s:3:"旅";s:3:"旅";s:3:"濾";s:3:"濾";s:3:"礪";s:3:"礪";s:3:"閭";s:3:"閭";s:3:"驪";s:3:"驪";s:3:"麗";s:3:"麗";s:3:"黎";s:3:"黎";s:3:"力";s:3:"力";s:3:"曆";s:3:"曆";s:3:"歷";s:3:"歷";s:3:"轢";s:3:"轢";s:3:"年";s:3:"年";s:3:"憐";s:3:"憐";s:3:"戀";s:3:"戀";s:3:"撚";s:3:"撚";s:3:"漣";s:3:"漣";s:3:"煉";s:3:"煉";s:3:"璉";s:3:"璉";s:3:"秊";s:3:"秊";s:3:"練";s:3:"練";s:3:"聯";s:3:"聯";s:3:"輦";s:3:"輦";s:3:"蓮";s:3:"蓮";s:3:"連";s:3:"連";s:3:"鍊";s:3:"鍊";s:3:"列";s:3:"列";s:3:"劣";s:3:"劣";s:3:"咽";s:3:"咽";s:3:"烈";s:3:"烈";s:3:"裂";s:3:"裂";s:3:"廉";s:3:"廉";s:3:"念";s:3:"念";s:3:"捻";s:3:"捻";s:3:"殮";s:3:"殮";s:3:"簾";s:3:"簾";s:3:"獵";s:3:"獵";s:3:"令";s:3:"令";s:3:"囹";s:3:"囹";s:3:"嶺";s:3:"嶺";s:3:"怜";s:3:"怜";s:3:"玲";s:3:"玲";s:3:"瑩";s:3:"瑩";s:3:"羚";s:3:"羚";s:3:"聆";s:3:"聆";s:3:"鈴";s:3:"鈴";s:3:"零";s:3:"零";s:3:"靈";s:3:"靈";s:3:"領";s:3:"領";s:3:"例";s:3:"例";s:3:"禮";s:3:"禮";s:3:"醴";s:3:"醴";s:3:"隸";s:3:"隸";s:3:"惡";s:3:"惡";s:3:"了";s:3:"了";s:3:"僚";s:3:"僚";s:3:"寮";s:3:"寮";s:3:"尿";s:3:"尿";s:3:"料";s:3:"料";s:3:"燎";s:3:"燎";s:3:"療";s:3:"療";s:3:"蓼";s:3:"蓼";s:3:"遼";s:3:"遼";s:3:"龍";s:3:"龍";s:3:"暈";s:3:"暈";s:3:"阮";s:3:"阮";s:3:"劉";s:3:"劉";s:3:"杻";s:3:"杻";s:3:"柳";s:3:"柳";s:3:"流";s:4:"流";s:3:"溜";s:3:"溜";s:3:"琉";s:3:"琉";s:3:"留";s:3:"留";s:3:"硫";s:3:"硫";s:3:"紐";s:3:"紐";s:3:"類";s:3:"類";s:3:"六";s:3:"六";s:3:"戮";s:3:"戮";s:3:"陸";s:3:"陸";s:3:"倫";s:3:"倫";s:3:"崙";s:3:"崙";s:3:"淪";s:3:"淪";s:3:"輪";s:3:"輪";s:3:"律";s:3:"律";s:3:"慄";s:3:"慄";s:3:"栗";s:3:"栗";s:3:"隆";s:3:"隆";s:3:"利";s:3:"利";s:3:"吏";s:3:"吏";s:3:"履";s:3:"履";s:3:"易";s:3:"易";s:3:"李";s:3:"李";s:3:"梨";s:3:"梨";s:3:"泥";s:3:"泥";s:3:"理";s:3:"理";s:3:"痢";s:3:"痢";s:3:"罹";s:3:"罹";s:3:"裏";s:3:"裏";s:3:"裡";s:3:"裡";s:3:"里";s:3:"里";s:3:"離";s:3:"離";s:3:"匿";s:3:"匿";s:3:"溺";s:3:"溺";s:3:"吝";s:3:"吝";s:3:"燐";s:3:"燐";s:3:"璘";s:3:"璘";s:3:"藺";s:3:"藺";s:3:"隣";s:3:"隣";s:3:"鱗";s:3:"鱗";s:3:"麟";s:3:"麟";s:3:"林";s:3:"林";s:3:"淋";s:3:"淋";s:3:"臨";s:3:"臨";s:3:"立";s:3:"立";s:3:"笠";s:3:"笠";s:3:"粒";s:3:"粒";s:3:"狀";s:3:"狀";s:3:"炙";s:3:"炙";s:3:"識";s:3:"識";s:3:"什";s:3:"什";s:3:"茶";s:3:"茶";s:3:"刺";s:3:"刺";s:3:"切";s:4:"切";s:3:"度";s:3:"度";s:3:"拓";s:3:"拓";s:3:"糖";s:3:"糖";s:3:"宅";s:3:"宅";s:3:"洞";s:3:"洞";s:3:"暴";s:3:"暴";s:3:"輻";s:3:"輻";s:3:"行";s:3:"行";s:3:"降";s:3:"降";s:3:"見";s:3:"見";s:3:"廓";s:3:"廓";s:3:"兀";s:3:"兀";s:3:"嗀";s:3:"嗀";s:3:"塚";s:3:"塚";s:3:"晴";s:3:"晴";s:3:"凞";s:3:"凞";s:3:"猪";s:3:"猪";s:3:"益";s:3:"益";s:3:"礼";s:3:"礼";s:3:"神";s:3:"神";s:3:"祥";s:3:"祥";s:3:"福";s:4:"福";s:3:"靖";s:3:"靖";s:3:"精";s:3:"精";s:3:"羽";s:3:"羽";s:3:"蘒";s:3:"蘒";s:3:"諸";s:3:"諸";s:3:"逸";s:3:"逸";s:3:"都";s:3:"都";s:3:"飯";s:3:"飯";s:3:"飼";s:3:"飼";s:3:"館";s:3:"館";s:3:"鶴";s:3:"鶴";s:3:"侮";s:4:"侮";s:3:"僧";s:4:"僧";s:3:"免";s:4:"免";s:3:"勉";s:4:"勉";s:3:"勤";s:4:"勤";s:3:"卑";s:4:"卑";s:3:"喝";s:3:"喝";s:3:"嘆";s:4:"嘆";s:3:"器";s:3:"器";s:3:"塀";s:3:"塀";s:3:"墨";s:3:"墨";s:3:"層";s:3:"層";s:3:"屮";s:4:"屮";s:3:"悔";s:4:"悔";s:3:"慨";s:3:"慨";s:3:"憎";s:4:"憎";s:3:"懲";s:4:"懲";s:3:"敏";s:4:"敏";s:3:"既";s:3:"既";s:3:"暑";s:4:"暑";s:3:"梅";s:4:"梅";s:3:"海";s:4:"海";s:3:"渚";s:3:"渚";s:3:"漢";s:3:"漢";s:3:"煮";s:3:"煮";s:3:"爫";s:3:"爫";s:3:"琢";s:3:"琢";s:3:"碑";s:3:"碑";s:3:"社";s:3:"社";s:3:"祉";s:3:"祉";s:3:"祈";s:3:"祈";s:3:"祐";s:3:"祐";s:3:"祖";s:4:"祖";s:3:"祝";s:3:"祝";s:3:"禍";s:3:"禍";s:3:"禎";s:3:"禎";s:3:"穀";s:4:"穀";s:3:"突";s:3:"突";s:3:"節";s:3:"節";s:3:"縉";s:3:"縉";s:3:"繁";s:3:"繁";s:3:"署";s:3:"署";s:3:"者";s:4:"者";s:3:"臭";s:3:"臭";s:3:"艹";s:3:"艹";s:3:"著";s:4:"著";s:3:"褐";s:3:"褐";s:3:"視";s:3:"視";s:3:"謁";s:3:"謁";s:3:"謹";s:3:"謹";s:3:"賓";s:3:"賓";s:3:"贈";s:3:"贈";s:3:"辶";s:3:"辶";s:3:"難";s:3:"難";s:3:"響";s:3:"響";s:3:"頻";s:3:"頻";s:3:"並";s:3:"並";s:3:"况";s:4:"况";s:3:"全";s:3:"全";s:3:"侀";s:3:"侀";s:3:"充";s:3:"充";s:3:"冀";s:3:"冀";s:3:"勇";s:4:"勇";s:3:"勺";s:4:"勺";s:3:"啕";s:3:"啕";s:3:"喙";s:4:"喙";s:3:"嗢";s:3:"嗢";s:3:"墳";s:3:"墳";s:3:"奄";s:3:"奄";s:3:"奔";s:3:"奔";s:3:"婢";s:3:"婢";s:3:"嬨";s:3:"嬨";s:3:"廒";s:3:"廒";s:3:"廙";s:3:"廙";s:3:"彩";s:3:"彩";s:3:"徭";s:3:"徭";s:3:"惘";s:3:"惘";s:3:"慎";s:4:"慎";s:3:"愈";s:3:"愈";s:3:"慠";s:3:"慠";s:3:"戴";s:3:"戴";s:3:"揄";s:3:"揄";s:3:"搜";s:3:"搜";s:3:"摒";s:3:"摒";s:3:"敖";s:3:"敖";s:3:"望";s:4:"望";s:3:"杖";s:3:"杖";s:3:"歹";s:3:"歹";s:3:"滛";s:3:"滛";s:3:"滋";s:4:"滋";s:3:"瀞";s:4:"瀞";s:3:"瞧";s:3:"瞧";s:3:"爵";s:4:"爵";s:3:"犯";s:3:"犯";s:3:"瑱";s:4:"瑱";s:3:"甆";s:3:"甆";s:3:"画";s:3:"画";s:3:"瘝";s:3:"瘝";s:3:"瘟";s:3:"瘟";s:3:"盛";s:3:"盛";s:3:"直";s:4:"直";s:3:"睊";s:4:"睊";s:3:"着";s:3:"着";s:3:"磌";s:4:"磌";s:3:"窱";s:3:"窱";s:3:"类";s:3:"类";s:3:"絛";s:3:"絛";s:3:"缾";s:3:"缾";s:3:"荒";s:3:"荒";s:3:"華";s:3:"華";s:3:"蝹";s:4:"蝹";s:3:"襁";s:3:"襁";s:3:"覆";s:3:"覆";s:3:"調";s:3:"調";s:3:"請";s:3:"請";s:3:"諭";s:4:"諭";s:3:"變";s:4:"變";s:3:"輸";s:4:"輸";s:3:"遲";s:3:"遲";s:3:"醙";s:3:"醙";s:3:"鉶";s:3:"鉶";s:3:"陼";s:3:"陼";s:3:"韛";s:3:"韛";s:3:"頋";s:4:"頋";s:3:"鬒";s:4:"鬒";s:4:"𢡊";s:3:"𢡊";s:4:"𢡄";s:3:"𢡄";s:4:"𣏕";s:3:"𣏕";s:3:"㮝";s:4:"㮝";s:3:"䀘";s:3:"䀘";s:3:"䀹";s:4:"䀹";s:4:"𥉉";s:3:"𥉉";s:4:"𥳐";s:3:"𥳐";s:4:"𧻓";s:3:"𧻓";s:3:"齃";s:3:"齃";s:3:"龎";s:3:"龎";s:3:"丽";s:4:"丽";s:3:"丸";s:4:"丸";s:3:"乁";s:4:"乁";s:4:"𠄢";s:4:"𠄢";s:3:"你";s:4:"你";s:3:"侻";s:4:"侻";s:3:"倂";s:4:"倂";s:3:"偺";s:4:"偺";s:3:"備";s:4:"備";s:3:"像";s:4:"像";s:3:"㒞";s:4:"㒞";s:4:"𠘺";s:4:"𠘺";s:3:"兔";s:4:"兔";s:3:"兤";s:4:"兤";s:3:"具";s:4:"具";s:4:"𠔜";s:4:"𠔜";s:3:"㒹";s:4:"㒹";s:3:"內";s:4:"內";s:3:"再";s:4:"再";s:4:"𠕋";s:4:"𠕋";s:3:"冗";s:4:"冗";s:3:"冤";s:4:"冤";s:3:"仌";s:4:"仌";s:3:"冬";s:4:"冬";s:4:"𩇟";s:4:"𩇟";s:3:"凵";s:4:"凵";s:3:"刃";s:4:"刃";s:3:"㓟";s:4:"㓟";s:3:"刻";s:4:"刻";s:3:"剆";s:4:"剆";s:3:"割";s:4:"割";s:3:"剷";s:4:"剷";s:3:"㔕";s:4:"㔕";s:3:"包";s:4:"包";s:3:"匆";s:4:"匆";s:3:"卉";s:4:"卉";s:3:"博";s:4:"博";s:3:"即";s:4:"即";s:3:"卽";s:4:"卽";s:3:"卿";s:4:"卿";s:4:"𠨬";s:4:"𠨬";s:3:"灰";s:4:"灰";s:3:"及";s:4:"及";s:3:"叟";s:4:"叟";s:4:"𠭣";s:4:"𠭣";s:3:"叫";s:4:"叫";s:3:"叱";s:4:"叱";s:3:"吆";s:4:"吆";s:3:"咞";s:4:"咞";s:3:"吸";s:4:"吸";s:3:"呈";s:4:"呈";s:3:"周";s:4:"周";s:3:"咢";s:4:"咢";s:3:"哶";s:4:"哶";s:3:"唐";s:4:"唐";s:3:"啓";s:4:"啓";s:3:"啣";s:4:"啣";s:3:"善";s:4:"善";s:3:"喫";s:4:"喫";s:3:"喳";s:4:"喳";s:3:"嗂";s:4:"嗂";s:3:"圖";s:4:"圖";s:3:"圗";s:4:"圗";s:3:"噑";s:4:"噑";s:3:"噴";s:4:"噴";s:3:"壮";s:4:"壮";s:3:"城";s:4:"城";s:3:"埴";s:4:"埴";s:3:"堍";s:4:"堍";s:3:"型";s:4:"型";s:3:"堲";s:4:"堲";s:3:"報";s:4:"報";s:3:"墬";s:4:"墬";s:4:"𡓤";s:4:"𡓤";s:3:"売";s:4:"売";s:3:"壷";s:4:"壷";s:3:"夆";s:4:"夆";s:3:"多";s:4:"多";s:3:"夢";s:4:"夢";s:3:"奢";s:4:"奢";s:4:"𡚨";s:4:"𡚨";s:4:"𡛪";s:4:"𡛪";s:3:"姬";s:4:"姬";s:3:"娛";s:4:"娛";s:3:"娧";s:4:"娧";s:3:"姘";s:4:"姘";s:3:"婦";s:4:"婦";s:3:"㛮";s:4:"㛮";s:3:"㛼";s:4:"㛼";s:3:"嬈";s:4:"嬈";s:3:"嬾";s:4:"嬾";s:4:"𡧈";s:4:"𡧈";s:3:"寃";s:4:"寃";s:3:"寘";s:4:"寘";s:3:"寳";s:4:"寳";s:4:"𡬘";s:4:"𡬘";s:3:"寿";s:4:"寿";s:3:"将";s:4:"将";s:3:"当";s:4:"当";s:3:"尢";s:4:"尢";s:3:"㞁";s:4:"㞁";s:3:"屠";s:4:"屠";s:3:"峀";s:4:"峀";s:3:"岍";s:4:"岍";s:4:"𡷤";s:4:"𡷤";s:3:"嵃";s:4:"嵃";s:4:"𡷦";s:4:"𡷦";s:3:"嵮";s:4:"嵮";s:3:"嵫";s:4:"嵫";s:3:"嵼";s:4:"嵼";s:3:"巡";s:4:"巡";s:3:"巢";s:4:"巢";s:3:"㠯";s:4:"㠯";s:3:"巽";s:4:"巽";s:3:"帨";s:4:"帨";s:3:"帽";s:4:"帽";s:3:"幩";s:4:"幩";s:3:"㡢";s:4:"㡢";s:4:"𢆃";s:4:"𢆃";s:3:"㡼";s:4:"㡼";s:3:"庰";s:4:"庰";s:3:"庳";s:4:"庳";s:3:"庶";s:4:"庶";s:4:"𪎒";s:4:"𪎒";s:3:"廾";s:4:"廾";s:4:"𢌱";s:4:"𢌱";s:3:"舁";s:4:"舁";s:3:"弢";s:4:"弢";s:3:"㣇";s:4:"㣇";s:4:"𣊸";s:4:"𣊸";s:4:"𦇚";s:4:"𦇚";s:3:"形";s:4:"形";s:3:"彫";s:4:"彫";s:3:"㣣";s:4:"㣣";s:3:"徚";s:4:"徚";s:3:"忍";s:4:"忍";s:3:"志";s:4:"志";s:3:"忹";s:4:"忹";s:3:"悁";s:4:"悁";s:3:"㤺";s:4:"㤺";s:3:"㤜";s:4:"㤜";s:4:"𢛔";s:4:"𢛔";s:3:"惇";s:4:"惇";s:3:"慈";s:4:"慈";s:3:"慌";s:4:"慌";s:3:"慺";s:4:"慺";s:3:"憲";s:4:"憲";s:3:"憤";s:4:"憤";s:3:"憯";s:4:"憯";s:3:"懞";s:4:"懞";s:3:"成";s:4:"成";s:3:"戛";s:4:"戛";s:3:"扝";s:4:"扝";s:3:"抱";s:4:"抱";s:3:"拔";s:4:"拔";s:3:"捐";s:4:"捐";s:4:"𢬌";s:4:"𢬌";s:3:"挽";s:4:"挽";s:3:"拼";s:4:"拼";s:3:"捨";s:4:"捨";s:3:"掃";s:4:"掃";s:3:"揤";s:4:"揤";s:4:"𢯱";s:4:"𢯱";s:3:"搢";s:4:"搢";s:3:"揅";s:4:"揅";s:3:"掩";s:4:"掩";s:3:"㨮";s:4:"㨮";s:3:"摩";s:4:"摩";s:3:"摾";s:4:"摾";s:3:"撝";s:4:"撝";s:3:"摷";s:4:"摷";s:3:"㩬";s:4:"㩬";s:3:"敬";s:4:"敬";s:4:"𣀊";s:4:"𣀊";s:3:"旣";s:4:"旣";s:3:"書";s:4:"書";s:3:"晉";s:4:"晉";s:3:"㬙";s:4:"㬙";s:3:"㬈";s:4:"㬈";s:3:"㫤";s:4:"㫤";s:3:"冒";s:4:"冒";s:3:"冕";s:4:"冕";s:3:"最";s:4:"最";s:3:"暜";s:4:"暜";s:3:"肭";s:4:"肭";s:3:"䏙";s:4:"䏙";s:3:"朡";s:4:"朡";s:3:"杞";s:4:"杞";s:3:"杓";s:4:"杓";s:4:"𣏃";s:4:"𣏃";s:3:"㭉";s:4:"㭉";s:3:"柺";s:4:"柺";s:3:"枅";s:4:"枅";s:3:"桒";s:4:"桒";s:4:"𣑭";s:4:"𣑭";s:3:"梎";s:4:"梎";s:3:"栟";s:4:"栟";s:3:"椔";s:4:"椔";s:3:"楂";s:4:"楂";s:3:"榣";s:4:"榣";s:3:"槪";s:4:"槪";s:3:"檨";s:4:"檨";s:4:"𣚣";s:4:"𣚣";s:3:"櫛";s:4:"櫛";s:3:"㰘";s:4:"㰘";s:3:"次";s:4:"次";s:4:"𣢧";s:4:"𣢧";s:3:"歔";s:4:"歔";s:3:"㱎";s:4:"㱎";s:3:"歲";s:4:"歲";s:3:"殟";s:4:"殟";s:3:"殻";s:4:"殻";s:4:"𣪍";s:4:"𣪍";s:4:"𡴋";s:4:"𡴋";s:4:"𣫺";s:4:"𣫺";s:3:"汎";s:4:"汎";s:4:"𣲼";s:4:"𣲼";s:3:"沿";s:4:"沿";s:3:"泍";s:4:"泍";s:3:"汧";s:4:"汧";s:3:"洖";s:4:"洖";s:3:"派";s:4:"派";s:3:"浩";s:4:"浩";s:3:"浸";s:4:"浸";s:3:"涅";s:4:"涅";s:4:"𣴞";s:4:"𣴞";s:3:"洴";s:4:"洴";s:3:"港";s:4:"港";s:3:"湮";s:4:"湮";s:3:"㴳";s:4:"㴳";s:3:"滇";s:4:"滇";s:4:"𣻑";s:4:"𣻑";s:3:"淹";s:4:"淹";s:3:"潮";s:4:"潮";s:4:"𣽞";s:4:"𣽞";s:4:"𣾎";s:4:"𣾎";s:3:"濆";s:4:"濆";s:3:"瀹";s:4:"瀹";s:3:"瀛";s:4:"瀛";s:3:"㶖";s:4:"㶖";s:3:"灊";s:4:"灊";s:3:"災";s:4:"災";s:3:"灷";s:4:"灷";s:3:"炭";s:4:"炭";s:4:"𠔥";s:4:"𠔥";s:3:"煅";s:4:"煅";s:4:"𤉣";s:4:"𤉣";s:3:"熜";s:4:"熜";s:4:"𤎫";s:4:"𤎫";s:3:"爨";s:4:"爨";s:3:"牐";s:4:"牐";s:4:"𤘈";s:4:"𤘈";s:3:"犀";s:4:"犀";s:3:"犕";s:4:"犕";s:4:"𤜵";s:4:"𤜵";s:4:"𤠔";s:4:"𤠔";s:3:"獺";s:4:"獺";s:3:"王";s:4:"王";s:3:"㺬";s:4:"㺬";s:3:"玥";s:4:"玥";s:3:"㺸";s:4:"㺸";s:3:"瑇";s:4:"瑇";s:3:"瑜";s:4:"瑜";s:3:"璅";s:4:"璅";s:3:"瓊";s:4:"瓊";s:3:"㼛";s:4:"㼛";s:3:"甤";s:4:"甤";s:4:"𤰶";s:4:"𤰶";s:3:"甾";s:4:"甾";s:4:"𤲒";s:4:"𤲒";s:4:"𢆟";s:4:"𢆟";s:3:"瘐";s:4:"瘐";s:4:"𤾡";s:4:"𤾡";s:4:"𤾸";s:4:"𤾸";s:4:"𥁄";s:4:"𥁄";s:3:"㿼";s:4:"㿼";s:3:"䀈";s:4:"䀈";s:4:"𥃳";s:4:"𥃳";s:4:"𥃲";s:4:"𥃲";s:4:"𥄙";s:4:"𥄙";s:4:"𥄳";s:4:"𥄳";s:3:"眞";s:4:"眞";s:3:"真";s:4:"真";s:3:"瞋";s:4:"瞋";s:3:"䁆";s:4:"䁆";s:3:"䂖";s:4:"䂖";s:4:"𥐝";s:4:"𥐝";s:3:"硎";s:4:"硎";s:3:"䃣";s:4:"䃣";s:4:"𥘦";s:4:"𥘦";s:4:"𥚚";s:4:"𥚚";s:4:"𥛅";s:4:"𥛅";s:3:"秫";s:4:"秫";s:3:"䄯";s:4:"䄯";s:3:"穊";s:4:"穊";s:3:"穏";s:4:"穏";s:4:"𥥼";s:4:"𥥼";s:4:"𥪧";s:4:"𥪧";s:3:"竮";s:4:"竮";s:3:"䈂";s:4:"䈂";s:4:"𥮫";s:4:"𥮫";s:3:"篆";s:4:"篆";s:3:"築";s:4:"築";s:3:"䈧";s:4:"䈧";s:4:"𥲀";s:4:"𥲀";s:3:"糒";s:4:"糒";s:3:"䊠";s:4:"䊠";s:3:"糨";s:4:"糨";s:3:"糣";s:4:"糣";s:3:"紀";s:4:"紀";s:4:"𥾆";s:4:"𥾆";s:3:"絣";s:4:"絣";s:3:"䌁";s:4:"䌁";s:3:"緇";s:4:"緇";s:3:"縂";s:4:"縂";s:3:"繅";s:4:"繅";s:3:"䌴";s:4:"䌴";s:4:"𦈨";s:4:"𦈨";s:4:"𦉇";s:4:"𦉇";s:3:"䍙";s:4:"䍙";s:4:"𦋙";s:4:"𦋙";s:3:"罺";s:4:"罺";s:4:"𦌾";s:4:"𦌾";s:3:"羕";s:4:"羕";s:3:"翺";s:4:"翺";s:4:"𦓚";s:4:"𦓚";s:4:"𦔣";s:4:"𦔣";s:3:"聠";s:4:"聠";s:4:"𦖨";s:4:"𦖨";s:3:"聰";s:4:"聰";s:4:"𣍟";s:4:"𣍟";s:3:"䏕";s:4:"䏕";s:3:"育";s:4:"育";s:3:"脃";s:4:"脃";s:3:"䐋";s:4:"䐋";s:3:"脾";s:4:"脾";s:3:"媵";s:4:"媵";s:4:"𦞧";s:4:"𦞧";s:4:"𦞵";s:4:"𦞵";s:4:"𣎓";s:4:"𣎓";s:4:"𣎜";s:4:"𣎜";s:3:"舄";s:4:"舄";s:3:"辞";s:4:"辞";s:3:"䑫";s:4:"䑫";s:3:"芑";s:4:"芑";s:3:"芋";s:4:"芋";s:3:"芝";s:4:"芝";s:3:"劳";s:4:"劳";s:3:"花";s:4:"花";s:3:"芳";s:4:"芳";s:3:"芽";s:4:"芽";s:3:"苦";s:4:"苦";s:4:"𦬼";s:4:"𦬼";s:3:"茝";s:4:"茝";s:3:"荣";s:4:"荣";s:3:"莭";s:4:"莭";s:3:"茣";s:4:"茣";s:3:"莽";s:4:"莽";s:3:"菧";s:4:"菧";s:3:"荓";s:4:"荓";s:3:"菊";s:4:"菊";s:3:"菌";s:4:"菌";s:3:"菜";s:4:"菜";s:4:"𦰶";s:4:"𦰶";s:4:"𦵫";s:4:"𦵫";s:4:"𦳕";s:4:"𦳕";s:3:"䔫";s:4:"䔫";s:3:"蓱";s:4:"蓱";s:3:"蓳";s:4:"蓳";s:3:"蔖";s:4:"蔖";s:4:"𧏊";s:4:"𧏊";s:3:"蕤";s:4:"蕤";s:4:"𦼬";s:4:"𦼬";s:3:"䕝";s:4:"䕝";s:3:"䕡";s:4:"䕡";s:4:"𦾱";s:4:"𦾱";s:4:"𧃒";s:4:"𧃒";s:3:"䕫";s:4:"䕫";s:3:"虐";s:4:"虐";s:3:"虧";s:4:"虧";s:3:"虩";s:4:"虩";s:3:"蚩";s:4:"蚩";s:3:"蚈";s:4:"蚈";s:3:"蜎";s:4:"蜎";s:3:"蛢";s:4:"蛢";s:3:"蜨";s:4:"蜨";s:3:"蝫";s:4:"蝫";s:3:"螆";s:4:"螆";s:3:"䗗";s:4:"䗗";s:3:"蟡";s:4:"蟡";s:3:"蠁";s:4:"蠁";s:3:"䗹";s:4:"䗹";s:3:"衠";s:4:"衠";s:3:"衣";s:4:"衣";s:4:"𧙧";s:4:"𧙧";s:3:"裗";s:4:"裗";s:3:"裞";s:4:"裞";s:3:"䘵";s:4:"䘵";s:3:"裺";s:4:"裺";s:3:"㒻";s:4:"㒻";s:4:"𧢮";s:4:"𧢮";s:4:"𧥦";s:4:"𧥦";s:3:"䚾";s:4:"䚾";s:3:"䛇";s:4:"䛇";s:3:"誠";s:4:"誠";s:3:"豕";s:4:"豕";s:4:"𧲨";s:4:"𧲨";s:3:"貫";s:4:"貫";s:3:"賁";s:4:"賁";s:3:"贛";s:4:"贛";s:3:"起";s:4:"起";s:4:"𧼯";s:4:"𧼯";s:4:"𠠄";s:4:"𠠄";s:3:"跋";s:4:"跋";s:3:"趼";s:4:"趼";s:3:"跰";s:4:"跰";s:4:"𠣞";s:4:"𠣞";s:3:"軔";s:4:"軔";s:4:"𨗒";s:4:"𨗒";s:4:"𨗭";s:4:"𨗭";s:3:"邔";s:4:"邔";s:3:"郱";s:4:"郱";s:3:"鄑";s:4:"鄑";s:4:"𨜮";s:4:"𨜮";s:3:"鄛";s:4:"鄛";s:3:"鈸";s:4:"鈸";s:3:"鋗";s:4:"鋗";s:3:"鋘";s:4:"鋘";s:3:"鉼";s:4:"鉼";s:3:"鏹";s:4:"鏹";s:3:"鐕";s:4:"鐕";s:4:"𨯺";s:4:"𨯺";s:3:"開";s:4:"開";s:3:"䦕";s:4:"䦕";s:3:"閷";s:4:"閷";s:4:"𨵷";s:4:"𨵷";s:3:"䧦";s:4:"䧦";s:3:"雃";s:4:"雃";s:3:"嶲";s:4:"嶲";s:3:"霣";s:4:"霣";s:4:"𩅅";s:4:"𩅅";s:4:"𩈚";s:4:"𩈚";s:3:"䩮";s:4:"䩮";s:3:"䩶";s:4:"䩶";s:3:"韠";s:4:"韠";s:4:"𩐊";s:4:"𩐊";s:3:"䪲";s:4:"䪲";s:4:"𩒖";s:4:"𩒖";s:3:"頩";s:4:"頩";s:4:"𩖶";s:4:"𩖶";s:3:"飢";s:4:"飢";s:3:"䬳";s:4:"䬳";s:3:"餩";s:4:"餩";s:3:"馧";s:4:"馧";s:3:"駂";s:4:"駂";s:3:"駾";s:4:"駾";s:3:"䯎";s:4:"䯎";s:4:"𩬰";s:4:"𩬰";s:3:"鱀";s:4:"鱀";s:3:"鳽";s:4:"鳽";s:3:"䳎";s:4:"䳎";s:3:"䳭";s:4:"䳭";s:3:"鵧";s:4:"鵧";s:4:"𪃎";s:4:"𪃎";s:3:"䳸";s:4:"䳸";s:4:"𪄅";s:4:"𪄅";s:4:"𪈎";s:4:"𪈎";s:4:"𪊑";s:4:"𪊑";s:3:"麻";s:4:"麻";s:3:"䵖";s:4:"䵖";s:3:"黹";s:4:"黹";s:3:"黾";s:4:"黾";s:3:"鼅";s:4:"鼅";s:3:"鼏";s:4:"鼏";s:3:"鼖";s:4:"鼖";s:3:"鼻";s:4:"鼻";s:4:"𪘀";s:4:"𪘀";}' );
-$utfCanonicalDecomp = unserialize( 'a:2032:{s:2:"À";s:3:"À";s:2:"Á";s:3:"Á";s:2:"Â";s:3:"Â";s:2:"Ã";s:3:"Ã";s:2:"Ä";s:3:"Ä";s:2:"Å";s:3:"Å";s:2:"Ç";s:3:"Ç";s:2:"È";s:3:"È";s:2:"É";s:3:"É";s:2:"Ê";s:3:"Ê";s:2:"Ë";s:3:"Ë";s:2:"Ì";s:3:"Ì";s:2:"Í";s:3:"Í";s:2:"Î";s:3:"Î";s:2:"Ï";s:3:"Ï";s:2:"Ñ";s:3:"Ñ";s:2:"Ò";s:3:"Ò";s:2:"Ó";s:3:"Ó";s:2:"Ô";s:3:"Ô";s:2:"Õ";s:3:"Õ";s:2:"Ö";s:3:"Ö";s:2:"Ù";s:3:"Ù";s:2:"Ú";s:3:"Ú";s:2:"Û";s:3:"Û";s:2:"Ü";s:3:"Ü";s:2:"Ý";s:3:"Ý";s:2:"à";s:3:"à";s:2:"á";s:3:"á";s:2:"â";s:3:"â";s:2:"ã";s:3:"ã";s:2:"ä";s:3:"ä";s:2:"å";s:3:"å";s:2:"ç";s:3:"ç";s:2:"è";s:3:"è";s:2:"é";s:3:"é";s:2:"ê";s:3:"ê";s:2:"ë";s:3:"ë";s:2:"ì";s:3:"ì";s:2:"í";s:3:"í";s:2:"î";s:3:"î";s:2:"ï";s:3:"ï";s:2:"ñ";s:3:"ñ";s:2:"ò";s:3:"ò";s:2:"ó";s:3:"ó";s:2:"ô";s:3:"ô";s:2:"õ";s:3:"õ";s:2:"ö";s:3:"ö";s:2:"ù";s:3:"ù";s:2:"ú";s:3:"ú";s:2:"û";s:3:"û";s:2:"ü";s:3:"ü";s:2:"ý";s:3:"ý";s:2:"ÿ";s:3:"ÿ";s:2:"Ā";s:3:"Ā";s:2:"ā";s:3:"ā";s:2:"Ă";s:3:"Ă";s:2:"ă";s:3:"ă";s:2:"Ą";s:3:"Ą";s:2:"ą";s:3:"ą";s:2:"Ć";s:3:"Ć";s:2:"ć";s:3:"ć";s:2:"Ĉ";s:3:"Ĉ";s:2:"ĉ";s:3:"ĉ";s:2:"Ċ";s:3:"Ċ";s:2:"ċ";s:3:"ċ";s:2:"Č";s:3:"Č";s:2:"č";s:3:"č";s:2:"Ď";s:3:"Ď";s:2:"ď";s:3:"ď";s:2:"Ē";s:3:"Ē";s:2:"ē";s:3:"ē";s:2:"Ĕ";s:3:"Ĕ";s:2:"ĕ";s:3:"ĕ";s:2:"Ė";s:3:"Ė";s:2:"ė";s:3:"ė";s:2:"Ę";s:3:"Ę";s:2:"ę";s:3:"ę";s:2:"Ě";s:3:"Ě";s:2:"ě";s:3:"ě";s:2:"Ĝ";s:3:"Ĝ";s:2:"ĝ";s:3:"ĝ";s:2:"Ğ";s:3:"Ğ";s:2:"ğ";s:3:"ğ";s:2:"Ġ";s:3:"Ġ";s:2:"ġ";s:3:"ġ";s:2:"Ģ";s:3:"Ģ";s:2:"ģ";s:3:"ģ";s:2:"Ĥ";s:3:"Ĥ";s:2:"ĥ";s:3:"ĥ";s:2:"Ĩ";s:3:"Ĩ";s:2:"ĩ";s:3:"ĩ";s:2:"Ī";s:3:"Ī";s:2:"ī";s:3:"ī";s:2:"Ĭ";s:3:"Ĭ";s:2:"ĭ";s:3:"ĭ";s:2:"Į";s:3:"Į";s:2:"į";s:3:"į";s:2:"İ";s:3:"İ";s:2:"Ĵ";s:3:"Ĵ";s:2:"ĵ";s:3:"ĵ";s:2:"Ķ";s:3:"Ķ";s:2:"ķ";s:3:"ķ";s:2:"Ĺ";s:3:"Ĺ";s:2:"ĺ";s:3:"ĺ";s:2:"Ļ";s:3:"Ļ";s:2:"ļ";s:3:"ļ";s:2:"Ľ";s:3:"Ľ";s:2:"ľ";s:3:"ľ";s:2:"Ń";s:3:"Ń";s:2:"ń";s:3:"ń";s:2:"Ņ";s:3:"Ņ";s:2:"ņ";s:3:"ņ";s:2:"Ň";s:3:"Ň";s:2:"ň";s:3:"ň";s:2:"Ō";s:3:"Ō";s:2:"ō";s:3:"ō";s:2:"Ŏ";s:3:"Ŏ";s:2:"ŏ";s:3:"ŏ";s:2:"Ő";s:3:"Ő";s:2:"ő";s:3:"ő";s:2:"Ŕ";s:3:"Ŕ";s:2:"ŕ";s:3:"ŕ";s:2:"Ŗ";s:3:"Ŗ";s:2:"ŗ";s:3:"ŗ";s:2:"Ř";s:3:"Ř";s:2:"ř";s:3:"ř";s:2:"Ś";s:3:"Ś";s:2:"ś";s:3:"ś";s:2:"Ŝ";s:3:"Ŝ";s:2:"ŝ";s:3:"ŝ";s:2:"Ş";s:3:"Ş";s:2:"ş";s:3:"ş";s:2:"Š";s:3:"Š";s:2:"š";s:3:"š";s:2:"Ţ";s:3:"Ţ";s:2:"ţ";s:3:"ţ";s:2:"Ť";s:3:"Ť";s:2:"ť";s:3:"ť";s:2:"Ũ";s:3:"Ũ";s:2:"ũ";s:3:"ũ";s:2:"Ū";s:3:"Ū";s:2:"ū";s:3:"ū";s:2:"Ŭ";s:3:"Ŭ";s:2:"ŭ";s:3:"ŭ";s:2:"Ů";s:3:"Ů";s:2:"ů";s:3:"ů";s:2:"Ű";s:3:"Ű";s:2:"ű";s:3:"ű";s:2:"Ų";s:3:"Ų";s:2:"ų";s:3:"ų";s:2:"Ŵ";s:3:"Ŵ";s:2:"ŵ";s:3:"ŵ";s:2:"Ŷ";s:3:"Ŷ";s:2:"ŷ";s:3:"ŷ";s:2:"Ÿ";s:3:"Ÿ";s:2:"Ź";s:3:"Ź";s:2:"ź";s:3:"ź";s:2:"Ż";s:3:"Ż";s:2:"ż";s:3:"ż";s:2:"Ž";s:3:"Ž";s:2:"ž";s:3:"ž";s:2:"Ơ";s:3:"Ơ";s:2:"ơ";s:3:"ơ";s:2:"Ư";s:3:"Ư";s:2:"ư";s:3:"ư";s:2:"Ǎ";s:3:"Ǎ";s:2:"ǎ";s:3:"ǎ";s:2:"Ǐ";s:3:"Ǐ";s:2:"ǐ";s:3:"ǐ";s:2:"Ǒ";s:3:"Ǒ";s:2:"ǒ";s:3:"ǒ";s:2:"Ǔ";s:3:"Ǔ";s:2:"ǔ";s:3:"ǔ";s:2:"Ǖ";s:5:"Ǖ";s:2:"ǖ";s:5:"ǖ";s:2:"Ǘ";s:5:"Ǘ";s:2:"ǘ";s:5:"ǘ";s:2:"Ǚ";s:5:"Ǚ";s:2:"ǚ";s:5:"ǚ";s:2:"Ǜ";s:5:"Ǜ";s:2:"ǜ";s:5:"ǜ";s:2:"Ǟ";s:5:"Ǟ";s:2:"ǟ";s:5:"ǟ";s:2:"Ǡ";s:5:"Ǡ";s:2:"ǡ";s:5:"ǡ";s:2:"Ǣ";s:4:"Ǣ";s:2:"ǣ";s:4:"ǣ";s:2:"Ǧ";s:3:"Ǧ";s:2:"ǧ";s:3:"ǧ";s:2:"Ǩ";s:3:"Ǩ";s:2:"ǩ";s:3:"ǩ";s:2:"Ǫ";s:3:"Ǫ";s:2:"ǫ";s:3:"ǫ";s:2:"Ǭ";s:5:"Ǭ";s:2:"ǭ";s:5:"ǭ";s:2:"Ǯ";s:4:"Ǯ";s:2:"ǯ";s:4:"ǯ";s:2:"ǰ";s:3:"ǰ";s:2:"Ǵ";s:3:"Ǵ";s:2:"ǵ";s:3:"ǵ";s:2:"Ǹ";s:3:"Ǹ";s:2:"ǹ";s:3:"ǹ";s:2:"Ǻ";s:5:"Ǻ";s:2:"ǻ";s:5:"ǻ";s:2:"Ǽ";s:4:"Ǽ";s:2:"ǽ";s:4:"ǽ";s:2:"Ǿ";s:4:"Ǿ";s:2:"ǿ";s:4:"ǿ";s:2:"Ȁ";s:3:"Ȁ";s:2:"ȁ";s:3:"ȁ";s:2:"Ȃ";s:3:"Ȃ";s:2:"ȃ";s:3:"ȃ";s:2:"Ȅ";s:3:"Ȅ";s:2:"ȅ";s:3:"ȅ";s:2:"Ȇ";s:3:"Ȇ";s:2:"ȇ";s:3:"ȇ";s:2:"Ȉ";s:3:"Ȉ";s:2:"ȉ";s:3:"ȉ";s:2:"Ȋ";s:3:"Ȋ";s:2:"ȋ";s:3:"ȋ";s:2:"Ȍ";s:3:"Ȍ";s:2:"ȍ";s:3:"ȍ";s:2:"Ȏ";s:3:"Ȏ";s:2:"ȏ";s:3:"ȏ";s:2:"Ȑ";s:3:"Ȑ";s:2:"ȑ";s:3:"ȑ";s:2:"Ȓ";s:3:"Ȓ";s:2:"ȓ";s:3:"ȓ";s:2:"Ȕ";s:3:"Ȕ";s:2:"ȕ";s:3:"ȕ";s:2:"Ȗ";s:3:"Ȗ";s:2:"ȗ";s:3:"ȗ";s:2:"Ș";s:3:"Ș";s:2:"ș";s:3:"ș";s:2:"Ț";s:3:"Ț";s:2:"ț";s:3:"ț";s:2:"Ȟ";s:3:"Ȟ";s:2:"ȟ";s:3:"ȟ";s:2:"Ȧ";s:3:"Ȧ";s:2:"ȧ";s:3:"ȧ";s:2:"Ȩ";s:3:"Ȩ";s:2:"ȩ";s:3:"ȩ";s:2:"Ȫ";s:5:"Ȫ";s:2:"ȫ";s:5:"ȫ";s:2:"Ȭ";s:5:"Ȭ";s:2:"ȭ";s:5:"ȭ";s:2:"Ȯ";s:3:"Ȯ";s:2:"ȯ";s:3:"ȯ";s:2:"Ȱ";s:5:"Ȱ";s:2:"ȱ";s:5:"ȱ";s:2:"Ȳ";s:3:"Ȳ";s:2:"ȳ";s:3:"ȳ";s:2:"̀";s:2:"̀";s:2:"́";s:2:"́";s:2:"̓";s:2:"̓";s:2:"̈́";s:4:"̈́";s:2:"ʹ";s:2:"ʹ";s:2:";";s:1:";";s:2:"΅";s:4:"΅";s:2:"Ά";s:4:"Ά";s:2:"·";s:2:"·";s:2:"Έ";s:4:"Έ";s:2:"Ή";s:4:"Ή";s:2:"Ί";s:4:"Ί";s:2:"Ό";s:4:"Ό";s:2:"Ύ";s:4:"Ύ";s:2:"Ώ";s:4:"Ώ";s:2:"ΐ";s:6:"ΐ";s:2:"Ϊ";s:4:"Ϊ";s:2:"Ϋ";s:4:"Ϋ";s:2:"ά";s:4:"ά";s:2:"έ";s:4:"έ";s:2:"ή";s:4:"ή";s:2:"ί";s:4:"ί";s:2:"ΰ";s:6:"ΰ";s:2:"ϊ";s:4:"ϊ";s:2:"ϋ";s:4:"ϋ";s:2:"ό";s:4:"ό";s:2:"ύ";s:4:"ύ";s:2:"ώ";s:4:"ώ";s:2:"ϓ";s:4:"ϓ";s:2:"ϔ";s:4:"ϔ";s:2:"Ѐ";s:4:"Ѐ";s:2:"Ё";s:4:"Ё";s:2:"Ѓ";s:4:"Ѓ";s:2:"Ї";s:4:"Ї";s:2:"Ќ";s:4:"Ќ";s:2:"Ѝ";s:4:"Ѝ";s:2:"Ў";s:4:"Ў";s:2:"Й";s:4:"Й";s:2:"й";s:4:"й";s:2:"ѐ";s:4:"ѐ";s:2:"ё";s:4:"ё";s:2:"ѓ";s:4:"ѓ";s:2:"ї";s:4:"ї";s:2:"ќ";s:4:"ќ";s:2:"ѝ";s:4:"ѝ";s:2:"ў";s:4:"ў";s:2:"Ѷ";s:4:"Ѷ";s:2:"ѷ";s:4:"ѷ";s:2:"Ӂ";s:4:"Ӂ";s:2:"ӂ";s:4:"ӂ";s:2:"Ӑ";s:4:"Ӑ";s:2:"ӑ";s:4:"ӑ";s:2:"Ӓ";s:4:"Ӓ";s:2:"ӓ";s:4:"ӓ";s:2:"Ӗ";s:4:"Ӗ";s:2:"ӗ";s:4:"ӗ";s:2:"Ӛ";s:4:"Ӛ";s:2:"ӛ";s:4:"ӛ";s:2:"Ӝ";s:4:"Ӝ";s:2:"ӝ";s:4:"ӝ";s:2:"Ӟ";s:4:"Ӟ";s:2:"ӟ";s:4:"ӟ";s:2:"Ӣ";s:4:"Ӣ";s:2:"ӣ";s:4:"ӣ";s:2:"Ӥ";s:4:"Ӥ";s:2:"ӥ";s:4:"ӥ";s:2:"Ӧ";s:4:"Ӧ";s:2:"ӧ";s:4:"ӧ";s:2:"Ӫ";s:4:"Ӫ";s:2:"ӫ";s:4:"ӫ";s:2:"Ӭ";s:4:"Ӭ";s:2:"ӭ";s:4:"ӭ";s:2:"Ӯ";s:4:"Ӯ";s:2:"ӯ";s:4:"ӯ";s:2:"Ӱ";s:4:"Ӱ";s:2:"ӱ";s:4:"ӱ";s:2:"Ӳ";s:4:"Ӳ";s:2:"ӳ";s:4:"ӳ";s:2:"Ӵ";s:4:"Ӵ";s:2:"ӵ";s:4:"ӵ";s:2:"Ӹ";s:4:"Ӹ";s:2:"ӹ";s:4:"ӹ";s:2:"آ";s:4:"آ";s:2:"أ";s:4:"أ";s:2:"ؤ";s:4:"ؤ";s:2:"إ";s:4:"إ";s:2:"ئ";s:4:"ئ";s:2:"ۀ";s:4:"ۀ";s:2:"ۂ";s:4:"ۂ";s:2:"ۓ";s:4:"ۓ";s:3:"ऩ";s:6:"ऩ";s:3:"ऱ";s:6:"ऱ";s:3:"ऴ";s:6:"ऴ";s:3:"क़";s:6:"क़";s:3:"ख़";s:6:"ख़";s:3:"ग़";s:6:"ग़";s:3:"ज़";s:6:"ज़";s:3:"ड़";s:6:"ड़";s:3:"ढ़";s:6:"ढ़";s:3:"फ़";s:6:"फ़";s:3:"य़";s:6:"य़";s:3:"ো";s:6:"ো";s:3:"ৌ";s:6:"ৌ";s:3:"ড়";s:6:"ড়";s:3:"ঢ়";s:6:"ঢ়";s:3:"য়";s:6:"য়";s:3:"ਲ਼";s:6:"ਲ਼";s:3:"ਸ਼";s:6:"ਸ਼";s:3:"ਖ਼";s:6:"ਖ਼";s:3:"ਗ਼";s:6:"ਗ਼";s:3:"ਜ਼";s:6:"ਜ਼";s:3:"ਫ਼";s:6:"ਫ਼";s:3:"ୈ";s:6:"ୈ";s:3:"ୋ";s:6:"ୋ";s:3:"ୌ";s:6:"ୌ";s:3:"ଡ଼";s:6:"ଡ଼";s:3:"ଢ଼";s:6:"ଢ଼";s:3:"ஔ";s:6:"ஔ";s:3:"ொ";s:6:"ொ";s:3:"ோ";s:6:"ோ";s:3:"ௌ";s:6:"ௌ";s:3:"ై";s:6:"ై";s:3:"ೀ";s:6:"ೀ";s:3:"ೇ";s:6:"ೇ";s:3:"ೈ";s:6:"ೈ";s:3:"ೊ";s:6:"ೊ";s:3:"ೋ";s:9:"ೋ";s:3:"ൊ";s:6:"ൊ";s:3:"ോ";s:6:"ോ";s:3:"ൌ";s:6:"ൌ";s:3:"ේ";s:6:"ේ";s:3:"ො";s:6:"ො";s:3:"ෝ";s:9:"ෝ";s:3:"ෞ";s:6:"ෞ";s:3:"གྷ";s:6:"གྷ";s:3:"ཌྷ";s:6:"ཌྷ";s:3:"དྷ";s:6:"དྷ";s:3:"བྷ";s:6:"བྷ";s:3:"ཛྷ";s:6:"ཛྷ";s:3:"ཀྵ";s:6:"ཀྵ";s:3:"ཱི";s:6:"ཱི";s:3:"ཱུ";s:6:"ཱུ";s:3:"ྲྀ";s:6:"ྲྀ";s:3:"ླྀ";s:6:"ླྀ";s:3:"ཱྀ";s:6:"ཱྀ";s:3:"ྒྷ";s:6:"ྒྷ";s:3:"ྜྷ";s:6:"ྜྷ";s:3:"ྡྷ";s:6:"ྡྷ";s:3:"ྦྷ";s:6:"ྦྷ";s:3:"ྫྷ";s:6:"ྫྷ";s:3:"ྐྵ";s:6:"ྐྵ";s:3:"ဦ";s:6:"ဦ";s:3:"Ḁ";s:3:"Ḁ";s:3:"ḁ";s:3:"ḁ";s:3:"Ḃ";s:3:"Ḃ";s:3:"ḃ";s:3:"ḃ";s:3:"Ḅ";s:3:"Ḅ";s:3:"ḅ";s:3:"ḅ";s:3:"Ḇ";s:3:"Ḇ";s:3:"ḇ";s:3:"ḇ";s:3:"Ḉ";s:5:"Ḉ";s:3:"ḉ";s:5:"ḉ";s:3:"Ḋ";s:3:"Ḋ";s:3:"ḋ";s:3:"ḋ";s:3:"Ḍ";s:3:"Ḍ";s:3:"ḍ";s:3:"ḍ";s:3:"Ḏ";s:3:"Ḏ";s:3:"ḏ";s:3:"ḏ";s:3:"Ḑ";s:3:"Ḑ";s:3:"ḑ";s:3:"ḑ";s:3:"Ḓ";s:3:"Ḓ";s:3:"ḓ";s:3:"ḓ";s:3:"Ḕ";s:5:"Ḕ";s:3:"ḕ";s:5:"ḕ";s:3:"Ḗ";s:5:"Ḗ";s:3:"ḗ";s:5:"ḗ";s:3:"Ḙ";s:3:"Ḙ";s:3:"ḙ";s:3:"ḙ";s:3:"Ḛ";s:3:"Ḛ";s:3:"ḛ";s:3:"ḛ";s:3:"Ḝ";s:5:"Ḝ";s:3:"ḝ";s:5:"ḝ";s:3:"Ḟ";s:3:"Ḟ";s:3:"ḟ";s:3:"ḟ";s:3:"Ḡ";s:3:"Ḡ";s:3:"ḡ";s:3:"ḡ";s:3:"Ḣ";s:3:"Ḣ";s:3:"ḣ";s:3:"ḣ";s:3:"Ḥ";s:3:"Ḥ";s:3:"ḥ";s:3:"ḥ";s:3:"Ḧ";s:3:"Ḧ";s:3:"ḧ";s:3:"ḧ";s:3:"Ḩ";s:3:"Ḩ";s:3:"ḩ";s:3:"ḩ";s:3:"Ḫ";s:3:"Ḫ";s:3:"ḫ";s:3:"ḫ";s:3:"Ḭ";s:3:"Ḭ";s:3:"ḭ";s:3:"ḭ";s:3:"Ḯ";s:5:"Ḯ";s:3:"ḯ";s:5:"ḯ";s:3:"Ḱ";s:3:"Ḱ";s:3:"ḱ";s:3:"ḱ";s:3:"Ḳ";s:3:"Ḳ";s:3:"ḳ";s:3:"ḳ";s:3:"Ḵ";s:3:"Ḵ";s:3:"ḵ";s:3:"ḵ";s:3:"Ḷ";s:3:"Ḷ";s:3:"ḷ";s:3:"ḷ";s:3:"Ḹ";s:5:"Ḹ";s:3:"ḹ";s:5:"ḹ";s:3:"Ḻ";s:3:"Ḻ";s:3:"ḻ";s:3:"ḻ";s:3:"Ḽ";s:3:"Ḽ";s:3:"ḽ";s:3:"ḽ";s:3:"Ḿ";s:3:"Ḿ";s:3:"ḿ";s:3:"ḿ";s:3:"Ṁ";s:3:"Ṁ";s:3:"ṁ";s:3:"ṁ";s:3:"Ṃ";s:3:"Ṃ";s:3:"ṃ";s:3:"ṃ";s:3:"Ṅ";s:3:"Ṅ";s:3:"ṅ";s:3:"ṅ";s:3:"Ṇ";s:3:"Ṇ";s:3:"ṇ";s:3:"ṇ";s:3:"Ṉ";s:3:"Ṉ";s:3:"ṉ";s:3:"ṉ";s:3:"Ṋ";s:3:"Ṋ";s:3:"ṋ";s:3:"ṋ";s:3:"Ṍ";s:5:"Ṍ";s:3:"ṍ";s:5:"ṍ";s:3:"Ṏ";s:5:"Ṏ";s:3:"ṏ";s:5:"ṏ";s:3:"Ṑ";s:5:"Ṑ";s:3:"ṑ";s:5:"ṑ";s:3:"Ṓ";s:5:"Ṓ";s:3:"ṓ";s:5:"ṓ";s:3:"Ṕ";s:3:"Ṕ";s:3:"ṕ";s:3:"ṕ";s:3:"Ṗ";s:3:"Ṗ";s:3:"ṗ";s:3:"ṗ";s:3:"Ṙ";s:3:"Ṙ";s:3:"ṙ";s:3:"ṙ";s:3:"Ṛ";s:3:"Ṛ";s:3:"ṛ";s:3:"ṛ";s:3:"Ṝ";s:5:"Ṝ";s:3:"ṝ";s:5:"ṝ";s:3:"Ṟ";s:3:"Ṟ";s:3:"ṟ";s:3:"ṟ";s:3:"Ṡ";s:3:"Ṡ";s:3:"ṡ";s:3:"ṡ";s:3:"Ṣ";s:3:"Ṣ";s:3:"ṣ";s:3:"ṣ";s:3:"Ṥ";s:5:"Ṥ";s:3:"ṥ";s:5:"ṥ";s:3:"Ṧ";s:5:"Ṧ";s:3:"ṧ";s:5:"ṧ";s:3:"Ṩ";s:5:"Ṩ";s:3:"ṩ";s:5:"ṩ";s:3:"Ṫ";s:3:"Ṫ";s:3:"ṫ";s:3:"ṫ";s:3:"Ṭ";s:3:"Ṭ";s:3:"ṭ";s:3:"ṭ";s:3:"Ṯ";s:3:"Ṯ";s:3:"ṯ";s:3:"ṯ";s:3:"Ṱ";s:3:"Ṱ";s:3:"ṱ";s:3:"ṱ";s:3:"Ṳ";s:3:"Ṳ";s:3:"ṳ";s:3:"ṳ";s:3:"Ṵ";s:3:"Ṵ";s:3:"ṵ";s:3:"ṵ";s:3:"Ṷ";s:3:"Ṷ";s:3:"ṷ";s:3:"ṷ";s:3:"Ṹ";s:5:"Ṹ";s:3:"ṹ";s:5:"ṹ";s:3:"Ṻ";s:5:"Ṻ";s:3:"ṻ";s:5:"ṻ";s:3:"Ṽ";s:3:"Ṽ";s:3:"ṽ";s:3:"ṽ";s:3:"Ṿ";s:3:"Ṿ";s:3:"ṿ";s:3:"ṿ";s:3:"Ẁ";s:3:"Ẁ";s:3:"ẁ";s:3:"ẁ";s:3:"Ẃ";s:3:"Ẃ";s:3:"ẃ";s:3:"ẃ";s:3:"Ẅ";s:3:"Ẅ";s:3:"ẅ";s:3:"ẅ";s:3:"Ẇ";s:3:"Ẇ";s:3:"ẇ";s:3:"ẇ";s:3:"Ẉ";s:3:"Ẉ";s:3:"ẉ";s:3:"ẉ";s:3:"Ẋ";s:3:"Ẋ";s:3:"ẋ";s:3:"ẋ";s:3:"Ẍ";s:3:"Ẍ";s:3:"ẍ";s:3:"ẍ";s:3:"Ẏ";s:3:"Ẏ";s:3:"ẏ";s:3:"ẏ";s:3:"Ẑ";s:3:"Ẑ";s:3:"ẑ";s:3:"ẑ";s:3:"Ẓ";s:3:"Ẓ";s:3:"ẓ";s:3:"ẓ";s:3:"Ẕ";s:3:"Ẕ";s:3:"ẕ";s:3:"ẕ";s:3:"ẖ";s:3:"ẖ";s:3:"ẗ";s:3:"ẗ";s:3:"ẘ";s:3:"ẘ";s:3:"ẙ";s:3:"ẙ";s:3:"ẛ";s:4:"ẛ";s:3:"Ạ";s:3:"Ạ";s:3:"ạ";s:3:"ạ";s:3:"Ả";s:3:"Ả";s:3:"ả";s:3:"ả";s:3:"Ấ";s:5:"Ấ";s:3:"ấ";s:5:"ấ";s:3:"Ầ";s:5:"Ầ";s:3:"ầ";s:5:"ầ";s:3:"Ẩ";s:5:"Ẩ";s:3:"ẩ";s:5:"ẩ";s:3:"Ẫ";s:5:"Ẫ";s:3:"ẫ";s:5:"ẫ";s:3:"Ậ";s:5:"Ậ";s:3:"ậ";s:5:"ậ";s:3:"Ắ";s:5:"Ắ";s:3:"ắ";s:5:"ắ";s:3:"Ằ";s:5:"Ằ";s:3:"ằ";s:5:"ằ";s:3:"Ẳ";s:5:"Ẳ";s:3:"ẳ";s:5:"ẳ";s:3:"Ẵ";s:5:"Ẵ";s:3:"ẵ";s:5:"ẵ";s:3:"Ặ";s:5:"Ặ";s:3:"ặ";s:5:"ặ";s:3:"Ẹ";s:3:"Ẹ";s:3:"ẹ";s:3:"ẹ";s:3:"Ẻ";s:3:"Ẻ";s:3:"ẻ";s:3:"ẻ";s:3:"Ẽ";s:3:"Ẽ";s:3:"ẽ";s:3:"ẽ";s:3:"Ế";s:5:"Ế";s:3:"ế";s:5:"ế";s:3:"Ề";s:5:"Ề";s:3:"ề";s:5:"ề";s:3:"Ể";s:5:"Ể";s:3:"ể";s:5:"ể";s:3:"Ễ";s:5:"Ễ";s:3:"ễ";s:5:"ễ";s:3:"Ệ";s:5:"Ệ";s:3:"ệ";s:5:"ệ";s:3:"Ỉ";s:3:"Ỉ";s:3:"ỉ";s:3:"ỉ";s:3:"Ị";s:3:"Ị";s:3:"ị";s:3:"ị";s:3:"Ọ";s:3:"Ọ";s:3:"ọ";s:3:"ọ";s:3:"Ỏ";s:3:"Ỏ";s:3:"ỏ";s:3:"ỏ";s:3:"Ố";s:5:"Ố";s:3:"ố";s:5:"ố";s:3:"Ồ";s:5:"Ồ";s:3:"ồ";s:5:"ồ";s:3:"Ổ";s:5:"Ổ";s:3:"ổ";s:5:"ổ";s:3:"Ỗ";s:5:"Ỗ";s:3:"ỗ";s:5:"ỗ";s:3:"Ộ";s:5:"Ộ";s:3:"ộ";s:5:"ộ";s:3:"Ớ";s:5:"Ớ";s:3:"ớ";s:5:"ớ";s:3:"Ờ";s:5:"Ờ";s:3:"ờ";s:5:"ờ";s:3:"Ở";s:5:"Ở";s:3:"ở";s:5:"ở";s:3:"Ỡ";s:5:"Ỡ";s:3:"ỡ";s:5:"ỡ";s:3:"Ợ";s:5:"Ợ";s:3:"ợ";s:5:"ợ";s:3:"Ụ";s:3:"Ụ";s:3:"ụ";s:3:"ụ";s:3:"Ủ";s:3:"Ủ";s:3:"ủ";s:3:"ủ";s:3:"Ứ";s:5:"Ứ";s:3:"ứ";s:5:"ứ";s:3:"Ừ";s:5:"Ừ";s:3:"ừ";s:5:"ừ";s:3:"Ử";s:5:"Ử";s:3:"ử";s:5:"ử";s:3:"Ữ";s:5:"Ữ";s:3:"ữ";s:5:"ữ";s:3:"Ự";s:5:"Ự";s:3:"ự";s:5:"ự";s:3:"Ỳ";s:3:"Ỳ";s:3:"ỳ";s:3:"ỳ";s:3:"Ỵ";s:3:"Ỵ";s:3:"ỵ";s:3:"ỵ";s:3:"Ỷ";s:3:"Ỷ";s:3:"ỷ";s:3:"ỷ";s:3:"Ỹ";s:3:"Ỹ";s:3:"ỹ";s:3:"ỹ";s:3:"ἀ";s:4:"ἀ";s:3:"ἁ";s:4:"ἁ";s:3:"ἂ";s:6:"ἂ";s:3:"ἃ";s:6:"ἃ";s:3:"ἄ";s:6:"ἄ";s:3:"ἅ";s:6:"ἅ";s:3:"ἆ";s:6:"ἆ";s:3:"ἇ";s:6:"ἇ";s:3:"Ἀ";s:4:"Ἀ";s:3:"Ἁ";s:4:"Ἁ";s:3:"Ἂ";s:6:"Ἂ";s:3:"Ἃ";s:6:"Ἃ";s:3:"Ἄ";s:6:"Ἄ";s:3:"Ἅ";s:6:"Ἅ";s:3:"Ἆ";s:6:"Ἆ";s:3:"Ἇ";s:6:"Ἇ";s:3:"ἐ";s:4:"ἐ";s:3:"ἑ";s:4:"ἑ";s:3:"ἒ";s:6:"ἒ";s:3:"ἓ";s:6:"ἓ";s:3:"ἔ";s:6:"ἔ";s:3:"ἕ";s:6:"ἕ";s:3:"Ἐ";s:4:"Ἐ";s:3:"Ἑ";s:4:"Ἑ";s:3:"Ἒ";s:6:"Ἒ";s:3:"Ἓ";s:6:"Ἓ";s:3:"Ἔ";s:6:"Ἔ";s:3:"Ἕ";s:6:"Ἕ";s:3:"ἠ";s:4:"ἠ";s:3:"ἡ";s:4:"ἡ";s:3:"ἢ";s:6:"ἢ";s:3:"ἣ";s:6:"ἣ";s:3:"ἤ";s:6:"ἤ";s:3:"ἥ";s:6:"ἥ";s:3:"ἦ";s:6:"ἦ";s:3:"ἧ";s:6:"ἧ";s:3:"Ἠ";s:4:"Ἠ";s:3:"Ἡ";s:4:"Ἡ";s:3:"Ἢ";s:6:"Ἢ";s:3:"Ἣ";s:6:"Ἣ";s:3:"Ἤ";s:6:"Ἤ";s:3:"Ἥ";s:6:"Ἥ";s:3:"Ἦ";s:6:"Ἦ";s:3:"Ἧ";s:6:"Ἧ";s:3:"ἰ";s:4:"ἰ";s:3:"ἱ";s:4:"ἱ";s:3:"ἲ";s:6:"ἲ";s:3:"ἳ";s:6:"ἳ";s:3:"ἴ";s:6:"ἴ";s:3:"ἵ";s:6:"ἵ";s:3:"ἶ";s:6:"ἶ";s:3:"ἷ";s:6:"ἷ";s:3:"Ἰ";s:4:"Ἰ";s:3:"Ἱ";s:4:"Ἱ";s:3:"Ἲ";s:6:"Ἲ";s:3:"Ἳ";s:6:"Ἳ";s:3:"Ἴ";s:6:"Ἴ";s:3:"Ἵ";s:6:"Ἵ";s:3:"Ἶ";s:6:"Ἶ";s:3:"Ἷ";s:6:"Ἷ";s:3:"ὀ";s:4:"ὀ";s:3:"ὁ";s:4:"ὁ";s:3:"ὂ";s:6:"ὂ";s:3:"ὃ";s:6:"ὃ";s:3:"ὄ";s:6:"ὄ";s:3:"ὅ";s:6:"ὅ";s:3:"Ὀ";s:4:"Ὀ";s:3:"Ὁ";s:4:"Ὁ";s:3:"Ὂ";s:6:"Ὂ";s:3:"Ὃ";s:6:"Ὃ";s:3:"Ὄ";s:6:"Ὄ";s:3:"Ὅ";s:6:"Ὅ";s:3:"ὐ";s:4:"ὐ";s:3:"ὑ";s:4:"ὑ";s:3:"ὒ";s:6:"ὒ";s:3:"ὓ";s:6:"ὓ";s:3:"ὔ";s:6:"ὔ";s:3:"ὕ";s:6:"ὕ";s:3:"ὖ";s:6:"ὖ";s:3:"ὗ";s:6:"ὗ";s:3:"Ὑ";s:4:"Ὑ";s:3:"Ὓ";s:6:"Ὓ";s:3:"Ὕ";s:6:"Ὕ";s:3:"Ὗ";s:6:"Ὗ";s:3:"ὠ";s:4:"ὠ";s:3:"ὡ";s:4:"ὡ";s:3:"ὢ";s:6:"ὢ";s:3:"ὣ";s:6:"ὣ";s:3:"ὤ";s:6:"ὤ";s:3:"ὥ";s:6:"ὥ";s:3:"ὦ";s:6:"ὦ";s:3:"ὧ";s:6:"ὧ";s:3:"Ὠ";s:4:"Ὠ";s:3:"Ὡ";s:4:"Ὡ";s:3:"Ὢ";s:6:"Ὢ";s:3:"Ὣ";s:6:"Ὣ";s:3:"Ὤ";s:6:"Ὤ";s:3:"Ὥ";s:6:"Ὥ";s:3:"Ὦ";s:6:"Ὦ";s:3:"Ὧ";s:6:"Ὧ";s:3:"ὰ";s:4:"ὰ";s:3:"ά";s:4:"ά";s:3:"ὲ";s:4:"ὲ";s:3:"έ";s:4:"έ";s:3:"ὴ";s:4:"ὴ";s:3:"ή";s:4:"ή";s:3:"ὶ";s:4:"ὶ";s:3:"ί";s:4:"ί";s:3:"ὸ";s:4:"ὸ";s:3:"ό";s:4:"ό";s:3:"ὺ";s:4:"ὺ";s:3:"ύ";s:4:"ύ";s:3:"ὼ";s:4:"ὼ";s:3:"ώ";s:4:"ώ";s:3:"ᾀ";s:6:"ᾀ";s:3:"ᾁ";s:6:"ᾁ";s:3:"ᾂ";s:8:"ᾂ";s:3:"ᾃ";s:8:"ᾃ";s:3:"ᾄ";s:8:"ᾄ";s:3:"ᾅ";s:8:"ᾅ";s:3:"ᾆ";s:8:"ᾆ";s:3:"ᾇ";s:8:"ᾇ";s:3:"ᾈ";s:6:"ᾈ";s:3:"ᾉ";s:6:"ᾉ";s:3:"ᾊ";s:8:"ᾊ";s:3:"ᾋ";s:8:"ᾋ";s:3:"ᾌ";s:8:"ᾌ";s:3:"ᾍ";s:8:"ᾍ";s:3:"ᾎ";s:8:"ᾎ";s:3:"ᾏ";s:8:"ᾏ";s:3:"ᾐ";s:6:"ᾐ";s:3:"ᾑ";s:6:"ᾑ";s:3:"ᾒ";s:8:"ᾒ";s:3:"ᾓ";s:8:"ᾓ";s:3:"ᾔ";s:8:"ᾔ";s:3:"ᾕ";s:8:"ᾕ";s:3:"ᾖ";s:8:"ᾖ";s:3:"ᾗ";s:8:"ᾗ";s:3:"ᾘ";s:6:"ᾘ";s:3:"ᾙ";s:6:"ᾙ";s:3:"ᾚ";s:8:"ᾚ";s:3:"ᾛ";s:8:"ᾛ";s:3:"ᾜ";s:8:"ᾜ";s:3:"ᾝ";s:8:"ᾝ";s:3:"ᾞ";s:8:"ᾞ";s:3:"ᾟ";s:8:"ᾟ";s:3:"ᾠ";s:6:"ᾠ";s:3:"ᾡ";s:6:"ᾡ";s:3:"ᾢ";s:8:"ᾢ";s:3:"ᾣ";s:8:"ᾣ";s:3:"ᾤ";s:8:"ᾤ";s:3:"ᾥ";s:8:"ᾥ";s:3:"ᾦ";s:8:"ᾦ";s:3:"ᾧ";s:8:"ᾧ";s:3:"ᾨ";s:6:"ᾨ";s:3:"ᾩ";s:6:"ᾩ";s:3:"ᾪ";s:8:"ᾪ";s:3:"ᾫ";s:8:"ᾫ";s:3:"ᾬ";s:8:"ᾬ";s:3:"ᾭ";s:8:"ᾭ";s:3:"ᾮ";s:8:"ᾮ";s:3:"ᾯ";s:8:"ᾯ";s:3:"ᾰ";s:4:"ᾰ";s:3:"ᾱ";s:4:"ᾱ";s:3:"ᾲ";s:6:"ᾲ";s:3:"ᾳ";s:4:"ᾳ";s:3:"ᾴ";s:6:"ᾴ";s:3:"ᾶ";s:4:"ᾶ";s:3:"ᾷ";s:6:"ᾷ";s:3:"Ᾰ";s:4:"Ᾰ";s:3:"Ᾱ";s:4:"Ᾱ";s:3:"Ὰ";s:4:"Ὰ";s:3:"Ά";s:4:"Ά";s:3:"ᾼ";s:4:"ᾼ";s:3:"ι";s:2:"ι";s:3:"῁";s:4:"῁";s:3:"ῂ";s:6:"ῂ";s:3:"ῃ";s:4:"ῃ";s:3:"ῄ";s:6:"ῄ";s:3:"ῆ";s:4:"ῆ";s:3:"ῇ";s:6:"ῇ";s:3:"Ὲ";s:4:"Ὲ";s:3:"Έ";s:4:"Έ";s:3:"Ὴ";s:4:"Ὴ";s:3:"Ή";s:4:"Ή";s:3:"ῌ";s:4:"ῌ";s:3:"῍";s:5:"῍";s:3:"῎";s:5:"῎";s:3:"῏";s:5:"῏";s:3:"ῐ";s:4:"ῐ";s:3:"ῑ";s:4:"ῑ";s:3:"ῒ";s:6:"ῒ";s:3:"ΐ";s:6:"ΐ";s:3:"ῖ";s:4:"ῖ";s:3:"ῗ";s:6:"ῗ";s:3:"Ῐ";s:4:"Ῐ";s:3:"Ῑ";s:4:"Ῑ";s:3:"Ὶ";s:4:"Ὶ";s:3:"Ί";s:4:"Ί";s:3:"῝";s:5:"῝";s:3:"῞";s:5:"῞";s:3:"῟";s:5:"῟";s:3:"ῠ";s:4:"ῠ";s:3:"ῡ";s:4:"ῡ";s:3:"ῢ";s:6:"ῢ";s:3:"ΰ";s:6:"ΰ";s:3:"ῤ";s:4:"ῤ";s:3:"ῥ";s:4:"ῥ";s:3:"ῦ";s:4:"ῦ";s:3:"ῧ";s:6:"ῧ";s:3:"Ῠ";s:4:"Ῠ";s:3:"Ῡ";s:4:"Ῡ";s:3:"Ὺ";s:4:"Ὺ";s:3:"Ύ";s:4:"Ύ";s:3:"Ῥ";s:4:"Ῥ";s:3:"῭";s:4:"῭";s:3:"΅";s:4:"΅";s:3:"`";s:1:"`";s:3:"ῲ";s:6:"ῲ";s:3:"ῳ";s:4:"ῳ";s:3:"ῴ";s:6:"ῴ";s:3:"ῶ";s:4:"ῶ";s:3:"ῷ";s:6:"ῷ";s:3:"Ὸ";s:4:"Ὸ";s:3:"Ό";s:4:"Ό";s:3:"Ὼ";s:4:"Ὼ";s:3:"Ώ";s:4:"Ώ";s:3:"ῼ";s:4:"ῼ";s:3:"´";s:2:"´";s:3:" ";s:3:" ";s:3:" ";s:3:" ";s:3:"Ω";s:2:"Ω";s:3:"K";s:1:"K";s:3:"Å";s:3:"Å";s:3:"↚";s:5:"↚";s:3:"↛";s:5:"↛";s:3:"↮";s:5:"↮";s:3:"⇍";s:5:"⇍";s:3:"⇎";s:5:"⇎";s:3:"⇏";s:5:"⇏";s:3:"∄";s:5:"∄";s:3:"∉";s:5:"∉";s:3:"∌";s:5:"∌";s:3:"∤";s:5:"∤";s:3:"∦";s:5:"∦";s:3:"≁";s:5:"≁";s:3:"≄";s:5:"≄";s:3:"≇";s:5:"≇";s:3:"≉";s:5:"≉";s:3:"≠";s:3:"≠";s:3:"≢";s:5:"≢";s:3:"≭";s:5:"≭";s:3:"≮";s:3:"≮";s:3:"≯";s:3:"≯";s:3:"≰";s:5:"≰";s:3:"≱";s:5:"≱";s:3:"≴";s:5:"≴";s:3:"≵";s:5:"≵";s:3:"≸";s:5:"≸";s:3:"≹";s:5:"≹";s:3:"⊀";s:5:"⊀";s:3:"⊁";s:5:"⊁";s:3:"⊄";s:5:"⊄";s:3:"⊅";s:5:"⊅";s:3:"⊈";s:5:"⊈";s:3:"⊉";s:5:"⊉";s:3:"⊬";s:5:"⊬";s:3:"⊭";s:5:"⊭";s:3:"⊮";s:5:"⊮";s:3:"⊯";s:5:"⊯";s:3:"⋠";s:5:"⋠";s:3:"⋡";s:5:"⋡";s:3:"⋢";s:5:"⋢";s:3:"⋣";s:5:"⋣";s:3:"⋪";s:5:"⋪";s:3:"⋫";s:5:"⋫";s:3:"⋬";s:5:"⋬";s:3:"⋭";s:5:"⋭";s:3:"〈";s:3:"〈";s:3:"〉";s:3:"〉";s:3:"⫝̸";s:5:"⫝̸";s:3:"が";s:6:"が";s:3:"ぎ";s:6:"ぎ";s:3:"ぐ";s:6:"ぐ";s:3:"げ";s:6:"げ";s:3:"ご";s:6:"ご";s:3:"ざ";s:6:"ざ";s:3:"じ";s:6:"じ";s:3:"ず";s:6:"ず";s:3:"ぜ";s:6:"ぜ";s:3:"ぞ";s:6:"ぞ";s:3:"だ";s:6:"だ";s:3:"ぢ";s:6:"ぢ";s:3:"づ";s:6:"づ";s:3:"で";s:6:"で";s:3:"ど";s:6:"ど";s:3:"ば";s:6:"ば";s:3:"ぱ";s:6:"ぱ";s:3:"び";s:6:"び";s:3:"ぴ";s:6:"ぴ";s:3:"ぶ";s:6:"ぶ";s:3:"ぷ";s:6:"ぷ";s:3:"べ";s:6:"べ";s:3:"ぺ";s:6:"ぺ";s:3:"ぼ";s:6:"ぼ";s:3:"ぽ";s:6:"ぽ";s:3:"ゔ";s:6:"ゔ";s:3:"ゞ";s:6:"ゞ";s:3:"ガ";s:6:"ガ";s:3:"ギ";s:6:"ギ";s:3:"グ";s:6:"グ";s:3:"ゲ";s:6:"ゲ";s:3:"ゴ";s:6:"ゴ";s:3:"ザ";s:6:"ザ";s:3:"ジ";s:6:"ジ";s:3:"ズ";s:6:"ズ";s:3:"ゼ";s:6:"ゼ";s:3:"ゾ";s:6:"ゾ";s:3:"ダ";s:6:"ダ";s:3:"ヂ";s:6:"ヂ";s:3:"ヅ";s:6:"ヅ";s:3:"デ";s:6:"デ";s:3:"ド";s:6:"ド";s:3:"バ";s:6:"バ";s:3:"パ";s:6:"パ";s:3:"ビ";s:6:"ビ";s:3:"ピ";s:6:"ピ";s:3:"ブ";s:6:"ブ";s:3:"プ";s:6:"プ";s:3:"ベ";s:6:"ベ";s:3:"ペ";s:6:"ペ";s:3:"ボ";s:6:"ボ";s:3:"ポ";s:6:"ポ";s:3:"ヴ";s:6:"ヴ";s:3:"ヷ";s:6:"ヷ";s:3:"ヸ";s:6:"ヸ";s:3:"ヹ";s:6:"ヹ";s:3:"ヺ";s:6:"ヺ";s:3:"ヾ";s:6:"ヾ";s:3:"豈";s:3:"豈";s:3:"更";s:3:"更";s:3:"車";s:3:"車";s:3:"賈";s:3:"賈";s:3:"滑";s:3:"滑";s:3:"串";s:3:"串";s:3:"句";s:3:"句";s:3:"龜";s:3:"龜";s:3:"龜";s:3:"龜";s:3:"契";s:3:"契";s:3:"金";s:3:"金";s:3:"喇";s:3:"喇";s:3:"奈";s:3:"奈";s:3:"懶";s:3:"懶";s:3:"癩";s:3:"癩";s:3:"羅";s:3:"羅";s:3:"蘿";s:3:"蘿";s:3:"螺";s:3:"螺";s:3:"裸";s:3:"裸";s:3:"邏";s:3:"邏";s:3:"樂";s:3:"樂";s:3:"洛";s:3:"洛";s:3:"烙";s:3:"烙";s:3:"珞";s:3:"珞";s:3:"落";s:3:"落";s:3:"酪";s:3:"酪";s:3:"駱";s:3:"駱";s:3:"亂";s:3:"亂";s:3:"卵";s:3:"卵";s:3:"欄";s:3:"欄";s:3:"爛";s:3:"爛";s:3:"蘭";s:3:"蘭";s:3:"鸞";s:3:"鸞";s:3:"嵐";s:3:"嵐";s:3:"濫";s:3:"濫";s:3:"藍";s:3:"藍";s:3:"襤";s:3:"襤";s:3:"拉";s:3:"拉";s:3:"臘";s:3:"臘";s:3:"蠟";s:3:"蠟";s:3:"廊";s:3:"廊";s:3:"朗";s:3:"朗";s:3:"浪";s:3:"浪";s:3:"狼";s:3:"狼";s:3:"郎";s:3:"郎";s:3:"來";s:3:"來";s:3:"冷";s:3:"冷";s:3:"勞";s:3:"勞";s:3:"擄";s:3:"擄";s:3:"櫓";s:3:"櫓";s:3:"爐";s:3:"爐";s:3:"盧";s:3:"盧";s:3:"老";s:3:"老";s:3:"蘆";s:3:"蘆";s:3:"虜";s:3:"虜";s:3:"路";s:3:"路";s:3:"露";s:3:"露";s:3:"魯";s:3:"魯";s:3:"鷺";s:3:"鷺";s:3:"碌";s:3:"碌";s:3:"祿";s:3:"祿";s:3:"綠";s:3:"綠";s:3:"菉";s:3:"菉";s:3:"錄";s:3:"錄";s:3:"鹿";s:3:"鹿";s:3:"論";s:3:"論";s:3:"壟";s:3:"壟";s:3:"弄";s:3:"弄";s:3:"籠";s:3:"籠";s:3:"聾";s:3:"聾";s:3:"牢";s:3:"牢";s:3:"磊";s:3:"磊";s:3:"賂";s:3:"賂";s:3:"雷";s:3:"雷";s:3:"壘";s:3:"壘";s:3:"屢";s:3:"屢";s:3:"樓";s:3:"樓";s:3:"淚";s:3:"淚";s:3:"漏";s:3:"漏";s:3:"累";s:3:"累";s:3:"縷";s:3:"縷";s:3:"陋";s:3:"陋";s:3:"勒";s:3:"勒";s:3:"肋";s:3:"肋";s:3:"凜";s:3:"凜";s:3:"凌";s:3:"凌";s:3:"稜";s:3:"稜";s:3:"綾";s:3:"綾";s:3:"菱";s:3:"菱";s:3:"陵";s:3:"陵";s:3:"讀";s:3:"讀";s:3:"拏";s:3:"拏";s:3:"樂";s:3:"樂";s:3:"諾";s:3:"諾";s:3:"丹";s:3:"丹";s:3:"寧";s:3:"寧";s:3:"怒";s:3:"怒";s:3:"率";s:3:"率";s:3:"異";s:3:"異";s:3:"北";s:3:"北";s:3:"磻";s:3:"磻";s:3:"便";s:3:"便";s:3:"復";s:3:"復";s:3:"不";s:3:"不";s:3:"泌";s:3:"泌";s:3:"數";s:3:"數";s:3:"索";s:3:"索";s:3:"參";s:3:"參";s:3:"塞";s:3:"塞";s:3:"省";s:3:"省";s:3:"葉";s:3:"葉";s:3:"說";s:3:"說";s:3:"殺";s:3:"殺";s:3:"辰";s:3:"辰";s:3:"沈";s:3:"沈";s:3:"拾";s:3:"拾";s:3:"若";s:3:"若";s:3:"掠";s:3:"掠";s:3:"略";s:3:"略";s:3:"亮";s:3:"亮";s:3:"兩";s:3:"兩";s:3:"凉";s:3:"凉";s:3:"梁";s:3:"梁";s:3:"糧";s:3:"糧";s:3:"良";s:3:"良";s:3:"諒";s:3:"諒";s:3:"量";s:3:"量";s:3:"勵";s:3:"勵";s:3:"呂";s:3:"呂";s:3:"女";s:3:"女";s:3:"廬";s:3:"廬";s:3:"旅";s:3:"旅";s:3:"濾";s:3:"濾";s:3:"礪";s:3:"礪";s:3:"閭";s:3:"閭";s:3:"驪";s:3:"驪";s:3:"麗";s:3:"麗";s:3:"黎";s:3:"黎";s:3:"力";s:3:"力";s:3:"曆";s:3:"曆";s:3:"歷";s:3:"歷";s:3:"轢";s:3:"轢";s:3:"年";s:3:"年";s:3:"憐";s:3:"憐";s:3:"戀";s:3:"戀";s:3:"撚";s:3:"撚";s:3:"漣";s:3:"漣";s:3:"煉";s:3:"煉";s:3:"璉";s:3:"璉";s:3:"秊";s:3:"秊";s:3:"練";s:3:"練";s:3:"聯";s:3:"聯";s:3:"輦";s:3:"輦";s:3:"蓮";s:3:"蓮";s:3:"連";s:3:"連";s:3:"鍊";s:3:"鍊";s:3:"列";s:3:"列";s:3:"劣";s:3:"劣";s:3:"咽";s:3:"咽";s:3:"烈";s:3:"烈";s:3:"裂";s:3:"裂";s:3:"說";s:3:"說";s:3:"廉";s:3:"廉";s:3:"念";s:3:"念";s:3:"捻";s:3:"捻";s:3:"殮";s:3:"殮";s:3:"簾";s:3:"簾";s:3:"獵";s:3:"獵";s:3:"令";s:3:"令";s:3:"囹";s:3:"囹";s:3:"寧";s:3:"寧";s:3:"嶺";s:3:"嶺";s:3:"怜";s:3:"怜";s:3:"玲";s:3:"玲";s:3:"瑩";s:3:"瑩";s:3:"羚";s:3:"羚";s:3:"聆";s:3:"聆";s:3:"鈴";s:3:"鈴";s:3:"零";s:3:"零";s:3:"靈";s:3:"靈";s:3:"領";s:3:"領";s:3:"例";s:3:"例";s:3:"禮";s:3:"禮";s:3:"醴";s:3:"醴";s:3:"隸";s:3:"隸";s:3:"惡";s:3:"惡";s:3:"了";s:3:"了";s:3:"僚";s:3:"僚";s:3:"寮";s:3:"寮";s:3:"尿";s:3:"尿";s:3:"料";s:3:"料";s:3:"樂";s:3:"樂";s:3:"燎";s:3:"燎";s:3:"療";s:3:"療";s:3:"蓼";s:3:"蓼";s:3:"遼";s:3:"遼";s:3:"龍";s:3:"龍";s:3:"暈";s:3:"暈";s:3:"阮";s:3:"阮";s:3:"劉";s:3:"劉";s:3:"杻";s:3:"杻";s:3:"柳";s:3:"柳";s:3:"流";s:3:"流";s:3:"溜";s:3:"溜";s:3:"琉";s:3:"琉";s:3:"留";s:3:"留";s:3:"硫";s:3:"硫";s:3:"紐";s:3:"紐";s:3:"類";s:3:"類";s:3:"六";s:3:"六";s:3:"戮";s:3:"戮";s:3:"陸";s:3:"陸";s:3:"倫";s:3:"倫";s:3:"崙";s:3:"崙";s:3:"淪";s:3:"淪";s:3:"輪";s:3:"輪";s:3:"律";s:3:"律";s:3:"慄";s:3:"慄";s:3:"栗";s:3:"栗";s:3:"率";s:3:"率";s:3:"隆";s:3:"隆";s:3:"利";s:3:"利";s:3:"吏";s:3:"吏";s:3:"履";s:3:"履";s:3:"易";s:3:"易";s:3:"李";s:3:"李";s:3:"梨";s:3:"梨";s:3:"泥";s:3:"泥";s:3:"理";s:3:"理";s:3:"痢";s:3:"痢";s:3:"罹";s:3:"罹";s:3:"裏";s:3:"裏";s:3:"裡";s:3:"裡";s:3:"里";s:3:"里";s:3:"離";s:3:"離";s:3:"匿";s:3:"匿";s:3:"溺";s:3:"溺";s:3:"吝";s:3:"吝";s:3:"燐";s:3:"燐";s:3:"璘";s:3:"璘";s:3:"藺";s:3:"藺";s:3:"隣";s:3:"隣";s:3:"鱗";s:3:"鱗";s:3:"麟";s:3:"麟";s:3:"林";s:3:"林";s:3:"淋";s:3:"淋";s:3:"臨";s:3:"臨";s:3:"立";s:3:"立";s:3:"笠";s:3:"笠";s:3:"粒";s:3:"粒";s:3:"狀";s:3:"狀";s:3:"炙";s:3:"炙";s:3:"識";s:3:"識";s:3:"什";s:3:"什";s:3:"茶";s:3:"茶";s:3:"刺";s:3:"刺";s:3:"切";s:3:"切";s:3:"度";s:3:"度";s:3:"拓";s:3:"拓";s:3:"糖";s:3:"糖";s:3:"宅";s:3:"宅";s:3:"洞";s:3:"洞";s:3:"暴";s:3:"暴";s:3:"輻";s:3:"輻";s:3:"行";s:3:"行";s:3:"降";s:3:"降";s:3:"見";s:3:"見";s:3:"廓";s:3:"廓";s:3:"兀";s:3:"兀";s:3:"嗀";s:3:"嗀";s:3:"塚";s:3:"塚";s:3:"晴";s:3:"晴";s:3:"凞";s:3:"凞";s:3:"猪";s:3:"猪";s:3:"益";s:3:"益";s:3:"礼";s:3:"礼";s:3:"神";s:3:"神";s:3:"祥";s:3:"祥";s:3:"福";s:3:"福";s:3:"靖";s:3:"靖";s:3:"精";s:3:"精";s:3:"羽";s:3:"羽";s:3:"蘒";s:3:"蘒";s:3:"諸";s:3:"諸";s:3:"逸";s:3:"逸";s:3:"都";s:3:"都";s:3:"飯";s:3:"飯";s:3:"飼";s:3:"飼";s:3:"館";s:3:"館";s:3:"鶴";s:3:"鶴";s:3:"侮";s:3:"侮";s:3:"僧";s:3:"僧";s:3:"免";s:3:"免";s:3:"勉";s:3:"勉";s:3:"勤";s:3:"勤";s:3:"卑";s:3:"卑";s:3:"喝";s:3:"喝";s:3:"嘆";s:3:"嘆";s:3:"器";s:3:"器";s:3:"塀";s:3:"塀";s:3:"墨";s:3:"墨";s:3:"層";s:3:"層";s:3:"屮";s:3:"屮";s:3:"悔";s:3:"悔";s:3:"慨";s:3:"慨";s:3:"憎";s:3:"憎";s:3:"懲";s:3:"懲";s:3:"敏";s:3:"敏";s:3:"既";s:3:"既";s:3:"暑";s:3:"暑";s:3:"梅";s:3:"梅";s:3:"海";s:3:"海";s:3:"渚";s:3:"渚";s:3:"漢";s:3:"漢";s:3:"煮";s:3:"煮";s:3:"爫";s:3:"爫";s:3:"琢";s:3:"琢";s:3:"碑";s:3:"碑";s:3:"社";s:3:"社";s:3:"祉";s:3:"祉";s:3:"祈";s:3:"祈";s:3:"祐";s:3:"祐";s:3:"祖";s:3:"祖";s:3:"祝";s:3:"祝";s:3:"禍";s:3:"禍";s:3:"禎";s:3:"禎";s:3:"穀";s:3:"穀";s:3:"突";s:3:"突";s:3:"節";s:3:"節";s:3:"練";s:3:"練";s:3:"縉";s:3:"縉";s:3:"繁";s:3:"繁";s:3:"署";s:3:"署";s:3:"者";s:3:"者";s:3:"臭";s:3:"臭";s:3:"艹";s:3:"艹";s:3:"艹";s:3:"艹";s:3:"著";s:3:"著";s:3:"褐";s:3:"褐";s:3:"視";s:3:"視";s:3:"謁";s:3:"謁";s:3:"謹";s:3:"謹";s:3:"賓";s:3:"賓";s:3:"贈";s:3:"贈";s:3:"辶";s:3:"辶";s:3:"逸";s:3:"逸";s:3:"難";s:3:"難";s:3:"響";s:3:"響";s:3:"頻";s:3:"頻";s:3:"並";s:3:"並";s:3:"况";s:3:"况";s:3:"全";s:3:"全";s:3:"侀";s:3:"侀";s:3:"充";s:3:"充";s:3:"冀";s:3:"冀";s:3:"勇";s:3:"勇";s:3:"勺";s:3:"勺";s:3:"喝";s:3:"喝";s:3:"啕";s:3:"啕";s:3:"喙";s:3:"喙";s:3:"嗢";s:3:"嗢";s:3:"塚";s:3:"塚";s:3:"墳";s:3:"墳";s:3:"奄";s:3:"奄";s:3:"奔";s:3:"奔";s:3:"婢";s:3:"婢";s:3:"嬨";s:3:"嬨";s:3:"廒";s:3:"廒";s:3:"廙";s:3:"廙";s:3:"彩";s:3:"彩";s:3:"徭";s:3:"徭";s:3:"惘";s:3:"惘";s:3:"慎";s:3:"慎";s:3:"愈";s:3:"愈";s:3:"憎";s:3:"憎";s:3:"慠";s:3:"慠";s:3:"懲";s:3:"懲";s:3:"戴";s:3:"戴";s:3:"揄";s:3:"揄";s:3:"搜";s:3:"搜";s:3:"摒";s:3:"摒";s:3:"敖";s:3:"敖";s:3:"晴";s:3:"晴";s:3:"朗";s:3:"朗";s:3:"望";s:3:"望";s:3:"杖";s:3:"杖";s:3:"歹";s:3:"歹";s:3:"殺";s:3:"殺";s:3:"流";s:3:"流";s:3:"滛";s:3:"滛";s:3:"滋";s:3:"滋";s:3:"漢";s:3:"漢";s:3:"瀞";s:3:"瀞";s:3:"煮";s:3:"煮";s:3:"瞧";s:3:"瞧";s:3:"爵";s:3:"爵";s:3:"犯";s:3:"犯";s:3:"猪";s:3:"猪";s:3:"瑱";s:3:"瑱";s:3:"甆";s:3:"甆";s:3:"画";s:3:"画";s:3:"瘝";s:3:"瘝";s:3:"瘟";s:3:"瘟";s:3:"益";s:3:"益";s:3:"盛";s:3:"盛";s:3:"直";s:3:"直";s:3:"睊";s:3:"睊";s:3:"着";s:3:"着";s:3:"磌";s:3:"磌";s:3:"窱";s:3:"窱";s:3:"節";s:3:"節";s:3:"类";s:3:"类";s:3:"絛";s:3:"絛";s:3:"練";s:3:"練";s:3:"缾";s:3:"缾";s:3:"者";s:3:"者";s:3:"荒";s:3:"荒";s:3:"華";s:3:"華";s:3:"蝹";s:3:"蝹";s:3:"襁";s:3:"襁";s:3:"覆";s:3:"覆";s:3:"視";s:3:"視";s:3:"調";s:3:"調";s:3:"諸";s:3:"諸";s:3:"請";s:3:"請";s:3:"謁";s:3:"謁";s:3:"諾";s:3:"諾";s:3:"諭";s:3:"諭";s:3:"謹";s:3:"謹";s:3:"變";s:3:"變";s:3:"贈";s:3:"贈";s:3:"輸";s:3:"輸";s:3:"遲";s:3:"遲";s:3:"醙";s:3:"醙";s:3:"鉶";s:3:"鉶";s:3:"陼";s:3:"陼";s:3:"難";s:3:"難";s:3:"靖";s:3:"靖";s:3:"韛";s:3:"韛";s:3:"響";s:3:"響";s:3:"頋";s:3:"頋";s:3:"頻";s:3:"頻";s:3:"鬒";s:3:"鬒";s:3:"龜";s:3:"龜";s:3:"𢡊";s:4:"𢡊";s:3:"𢡄";s:4:"𢡄";s:3:"𣏕";s:4:"𣏕";s:3:"㮝";s:3:"㮝";s:3:"䀘";s:3:"䀘";s:3:"䀹";s:3:"䀹";s:3:"𥉉";s:4:"𥉉";s:3:"𥳐";s:4:"𥳐";s:3:"𧻓";s:4:"𧻓";s:3:"齃";s:3:"齃";s:3:"龎";s:3:"龎";s:3:"יִ";s:4:"יִ";s:3:"ײַ";s:4:"ײַ";s:3:"שׁ";s:4:"שׁ";s:3:"שׂ";s:4:"שׂ";s:3:"שּׁ";s:6:"שּׁ";s:3:"שּׂ";s:6:"שּׂ";s:3:"אַ";s:4:"אַ";s:3:"אָ";s:4:"אָ";s:3:"אּ";s:4:"אּ";s:3:"בּ";s:4:"בּ";s:3:"גּ";s:4:"גּ";s:3:"דּ";s:4:"דּ";s:3:"הּ";s:4:"הּ";s:3:"וּ";s:4:"וּ";s:3:"זּ";s:4:"זּ";s:3:"טּ";s:4:"טּ";s:3:"יּ";s:4:"יּ";s:3:"ךּ";s:4:"ךּ";s:3:"כּ";s:4:"כּ";s:3:"לּ";s:4:"לּ";s:3:"מּ";s:4:"מּ";s:3:"נּ";s:4:"נּ";s:3:"סּ";s:4:"סּ";s:3:"ףּ";s:4:"ףּ";s:3:"פּ";s:4:"פּ";s:3:"צּ";s:4:"צּ";s:3:"קּ";s:4:"קּ";s:3:"רּ";s:4:"רּ";s:3:"שּ";s:4:"שּ";s:3:"תּ";s:4:"תּ";s:3:"וֹ";s:4:"וֹ";s:3:"בֿ";s:4:"בֿ";s:3:"כֿ";s:4:"כֿ";s:3:"פֿ";s:4:"פֿ";s:4:"𝅗𝅥";s:8:"𝅗𝅥";s:4:"𝅘𝅥";s:8:"𝅘𝅥";s:4:"𝅘𝅥𝅮";s:12:"𝅘𝅥𝅮";s:4:"𝅘𝅥𝅯";s:12:"𝅘𝅥𝅯";s:4:"𝅘𝅥𝅰";s:12:"𝅘𝅥𝅰";s:4:"𝅘𝅥𝅱";s:12:"𝅘𝅥𝅱";s:4:"𝅘𝅥𝅲";s:12:"𝅘𝅥𝅲";s:4:"𝆹𝅥";s:8:"𝆹𝅥";s:4:"𝆺𝅥";s:8:"𝆺𝅥";s:4:"𝆹𝅥𝅮";s:12:"𝆹𝅥𝅮";s:4:"𝆺𝅥𝅮";s:12:"𝆺𝅥𝅮";s:4:"𝆹𝅥𝅯";s:12:"𝆹𝅥𝅯";s:4:"𝆺𝅥𝅯";s:12:"𝆺𝅥𝅯";s:4:"丽";s:3:"丽";s:4:"丸";s:3:"丸";s:4:"乁";s:3:"乁";s:4:"𠄢";s:4:"𠄢";s:4:"你";s:3:"你";s:4:"侮";s:3:"侮";s:4:"侻";s:3:"侻";s:4:"倂";s:3:"倂";s:4:"偺";s:3:"偺";s:4:"備";s:3:"備";s:4:"僧";s:3:"僧";s:4:"像";s:3:"像";s:4:"㒞";s:3:"㒞";s:4:"𠘺";s:4:"𠘺";s:4:"免";s:3:"免";s:4:"兔";s:3:"兔";s:4:"兤";s:3:"兤";s:4:"具";s:3:"具";s:4:"𠔜";s:4:"𠔜";s:4:"㒹";s:3:"㒹";s:4:"內";s:3:"內";s:4:"再";s:3:"再";s:4:"𠕋";s:4:"𠕋";s:4:"冗";s:3:"冗";s:4:"冤";s:3:"冤";s:4:"仌";s:3:"仌";s:4:"冬";s:3:"冬";s:4:"况";s:3:"况";s:4:"𩇟";s:4:"𩇟";s:4:"凵";s:3:"凵";s:4:"刃";s:3:"刃";s:4:"㓟";s:3:"㓟";s:4:"刻";s:3:"刻";s:4:"剆";s:3:"剆";s:4:"割";s:3:"割";s:4:"剷";s:3:"剷";s:4:"㔕";s:3:"㔕";s:4:"勇";s:3:"勇";s:4:"勉";s:3:"勉";s:4:"勤";s:3:"勤";s:4:"勺";s:3:"勺";s:4:"包";s:3:"包";s:4:"匆";s:3:"匆";s:4:"北";s:3:"北";s:4:"卉";s:3:"卉";s:4:"卑";s:3:"卑";s:4:"博";s:3:"博";s:4:"即";s:3:"即";s:4:"卽";s:3:"卽";s:4:"卿";s:3:"卿";s:4:"卿";s:3:"卿";s:4:"卿";s:3:"卿";s:4:"𠨬";s:4:"𠨬";s:4:"灰";s:3:"灰";s:4:"及";s:3:"及";s:4:"叟";s:3:"叟";s:4:"𠭣";s:4:"𠭣";s:4:"叫";s:3:"叫";s:4:"叱";s:3:"叱";s:4:"吆";s:3:"吆";s:4:"咞";s:3:"咞";s:4:"吸";s:3:"吸";s:4:"呈";s:3:"呈";s:4:"周";s:3:"周";s:4:"咢";s:3:"咢";s:4:"哶";s:3:"哶";s:4:"唐";s:3:"唐";s:4:"啓";s:3:"啓";s:4:"啣";s:3:"啣";s:4:"善";s:3:"善";s:4:"善";s:3:"善";s:4:"喙";s:3:"喙";s:4:"喫";s:3:"喫";s:4:"喳";s:3:"喳";s:4:"嗂";s:3:"嗂";s:4:"圖";s:3:"圖";s:4:"嘆";s:3:"嘆";s:4:"圗";s:3:"圗";s:4:"噑";s:3:"噑";s:4:"噴";s:3:"噴";s:4:"切";s:3:"切";s:4:"壮";s:3:"壮";s:4:"城";s:3:"城";s:4:"埴";s:3:"埴";s:4:"堍";s:3:"堍";s:4:"型";s:3:"型";s:4:"堲";s:3:"堲";s:4:"報";s:3:"報";s:4:"墬";s:3:"墬";s:4:"𡓤";s:4:"𡓤";s:4:"売";s:3:"売";s:4:"壷";s:3:"壷";s:4:"夆";s:3:"夆";s:4:"多";s:3:"多";s:4:"夢";s:3:"夢";s:4:"奢";s:3:"奢";s:4:"𡚨";s:4:"𡚨";s:4:"𡛪";s:4:"𡛪";s:4:"姬";s:3:"姬";s:4:"娛";s:3:"娛";s:4:"娧";s:3:"娧";s:4:"姘";s:3:"姘";s:4:"婦";s:3:"婦";s:4:"㛮";s:3:"㛮";s:4:"㛼";s:3:"㛼";s:4:"嬈";s:3:"嬈";s:4:"嬾";s:3:"嬾";s:4:"嬾";s:3:"嬾";s:4:"𡧈";s:4:"𡧈";s:4:"寃";s:3:"寃";s:4:"寘";s:3:"寘";s:4:"寧";s:3:"寧";s:4:"寳";s:3:"寳";s:4:"𡬘";s:4:"𡬘";s:4:"寿";s:3:"寿";s:4:"将";s:3:"将";s:4:"当";s:3:"当";s:4:"尢";s:3:"尢";s:4:"㞁";s:3:"㞁";s:4:"屠";s:3:"屠";s:4:"屮";s:3:"屮";s:4:"峀";s:3:"峀";s:4:"岍";s:3:"岍";s:4:"𡷤";s:4:"𡷤";s:4:"嵃";s:3:"嵃";s:4:"𡷦";s:4:"𡷦";s:4:"嵮";s:3:"嵮";s:4:"嵫";s:3:"嵫";s:4:"嵼";s:3:"嵼";s:4:"巡";s:3:"巡";s:4:"巢";s:3:"巢";s:4:"㠯";s:3:"㠯";s:4:"巽";s:3:"巽";s:4:"帨";s:3:"帨";s:4:"帽";s:3:"帽";s:4:"幩";s:3:"幩";s:4:"㡢";s:3:"㡢";s:4:"𢆃";s:4:"𢆃";s:4:"㡼";s:3:"㡼";s:4:"庰";s:3:"庰";s:4:"庳";s:3:"庳";s:4:"庶";s:3:"庶";s:4:"廊";s:3:"廊";s:4:"𪎒";s:4:"𪎒";s:4:"廾";s:3:"廾";s:4:"𢌱";s:4:"𢌱";s:4:"𢌱";s:4:"𢌱";s:4:"舁";s:3:"舁";s:4:"弢";s:3:"弢";s:4:"弢";s:3:"弢";s:4:"㣇";s:3:"㣇";s:4:"𣊸";s:4:"𣊸";s:4:"𦇚";s:4:"𦇚";s:4:"形";s:3:"形";s:4:"彫";s:3:"彫";s:4:"㣣";s:3:"㣣";s:4:"徚";s:3:"徚";s:4:"忍";s:3:"忍";s:4:"志";s:3:"志";s:4:"忹";s:3:"忹";s:4:"悁";s:3:"悁";s:4:"㤺";s:3:"㤺";s:4:"㤜";s:3:"㤜";s:4:"悔";s:3:"悔";s:4:"𢛔";s:4:"𢛔";s:4:"惇";s:3:"惇";s:4:"慈";s:3:"慈";s:4:"慌";s:3:"慌";s:4:"慎";s:3:"慎";s:4:"慌";s:3:"慌";s:4:"慺";s:3:"慺";s:4:"憎";s:3:"憎";s:4:"憲";s:3:"憲";s:4:"憤";s:3:"憤";s:4:"憯";s:3:"憯";s:4:"懞";s:3:"懞";s:4:"懲";s:3:"懲";s:4:"懶";s:3:"懶";s:4:"成";s:3:"成";s:4:"戛";s:3:"戛";s:4:"扝";s:3:"扝";s:4:"抱";s:3:"抱";s:4:"拔";s:3:"拔";s:4:"捐";s:3:"捐";s:4:"𢬌";s:4:"𢬌";s:4:"挽";s:3:"挽";s:4:"拼";s:3:"拼";s:4:"捨";s:3:"捨";s:4:"掃";s:3:"掃";s:4:"揤";s:3:"揤";s:4:"𢯱";s:4:"𢯱";s:4:"搢";s:3:"搢";s:4:"揅";s:3:"揅";s:4:"掩";s:3:"掩";s:4:"㨮";s:3:"㨮";s:4:"摩";s:3:"摩";s:4:"摾";s:3:"摾";s:4:"撝";s:3:"撝";s:4:"摷";s:3:"摷";s:4:"㩬";s:3:"㩬";s:4:"敏";s:3:"敏";s:4:"敬";s:3:"敬";s:4:"𣀊";s:4:"𣀊";s:4:"旣";s:3:"旣";s:4:"書";s:3:"書";s:4:"晉";s:3:"晉";s:4:"㬙";s:3:"㬙";s:4:"暑";s:3:"暑";s:4:"㬈";s:3:"㬈";s:4:"㫤";s:3:"㫤";s:4:"冒";s:3:"冒";s:4:"冕";s:3:"冕";s:4:"最";s:3:"最";s:4:"暜";s:3:"暜";s:4:"肭";s:3:"肭";s:4:"䏙";s:3:"䏙";s:4:"朗";s:3:"朗";s:4:"望";s:3:"望";s:4:"朡";s:3:"朡";s:4:"杞";s:3:"杞";s:4:"杓";s:3:"杓";s:4:"𣏃";s:4:"𣏃";s:4:"㭉";s:3:"㭉";s:4:"柺";s:3:"柺";s:4:"枅";s:3:"枅";s:4:"桒";s:3:"桒";s:4:"梅";s:3:"梅";s:4:"𣑭";s:4:"𣑭";s:4:"梎";s:3:"梎";s:4:"栟";s:3:"栟";s:4:"椔";s:3:"椔";s:4:"㮝";s:3:"㮝";s:4:"楂";s:3:"楂";s:4:"榣";s:3:"榣";s:4:"槪";s:3:"槪";s:4:"檨";s:3:"檨";s:4:"𣚣";s:4:"𣚣";s:4:"櫛";s:3:"櫛";s:4:"㰘";s:3:"㰘";s:4:"次";s:3:"次";s:4:"𣢧";s:4:"𣢧";s:4:"歔";s:3:"歔";s:4:"㱎";s:3:"㱎";s:4:"歲";s:3:"歲";s:4:"殟";s:3:"殟";s:4:"殺";s:3:"殺";s:4:"殻";s:3:"殻";s:4:"𣪍";s:4:"𣪍";s:4:"𡴋";s:4:"𡴋";s:4:"𣫺";s:4:"𣫺";s:4:"汎";s:3:"汎";s:4:"𣲼";s:4:"𣲼";s:4:"沿";s:3:"沿";s:4:"泍";s:3:"泍";s:4:"汧";s:3:"汧";s:4:"洖";s:3:"洖";s:4:"派";s:3:"派";s:4:"海";s:3:"海";s:4:"流";s:3:"流";s:4:"浩";s:3:"浩";s:4:"浸";s:3:"浸";s:4:"涅";s:3:"涅";s:4:"𣴞";s:4:"𣴞";s:4:"洴";s:3:"洴";s:4:"港";s:3:"港";s:4:"湮";s:3:"湮";s:4:"㴳";s:3:"㴳";s:4:"滋";s:3:"滋";s:4:"滇";s:3:"滇";s:4:"𣻑";s:4:"𣻑";s:4:"淹";s:3:"淹";s:4:"潮";s:3:"潮";s:4:"𣽞";s:4:"𣽞";s:4:"𣾎";s:4:"𣾎";s:4:"濆";s:3:"濆";s:4:"瀹";s:3:"瀹";s:4:"瀞";s:3:"瀞";s:4:"瀛";s:3:"瀛";s:4:"㶖";s:3:"㶖";s:4:"灊";s:3:"灊";s:4:"災";s:3:"災";s:4:"灷";s:3:"灷";s:4:"炭";s:3:"炭";s:4:"𠔥";s:4:"𠔥";s:4:"煅";s:3:"煅";s:4:"𤉣";s:4:"𤉣";s:4:"熜";s:3:"熜";s:4:"𤎫";s:4:"𤎫";s:4:"爨";s:3:"爨";s:4:"爵";s:3:"爵";s:4:"牐";s:3:"牐";s:4:"𤘈";s:4:"𤘈";s:4:"犀";s:3:"犀";s:4:"犕";s:3:"犕";s:4:"𤜵";s:4:"𤜵";s:4:"𤠔";s:4:"𤠔";s:4:"獺";s:3:"獺";s:4:"王";s:3:"王";s:4:"㺬";s:3:"㺬";s:4:"玥";s:3:"玥";s:4:"㺸";s:3:"㺸";s:4:"㺸";s:3:"㺸";s:4:"瑇";s:3:"瑇";s:4:"瑜";s:3:"瑜";s:4:"瑱";s:3:"瑱";s:4:"璅";s:3:"璅";s:4:"瓊";s:3:"瓊";s:4:"㼛";s:3:"㼛";s:4:"甤";s:3:"甤";s:4:"𤰶";s:4:"𤰶";s:4:"甾";s:3:"甾";s:4:"𤲒";s:4:"𤲒";s:4:"異";s:3:"異";s:4:"𢆟";s:4:"𢆟";s:4:"瘐";s:3:"瘐";s:4:"𤾡";s:4:"𤾡";s:4:"𤾸";s:4:"𤾸";s:4:"𥁄";s:4:"𥁄";s:4:"㿼";s:3:"㿼";s:4:"䀈";s:3:"䀈";s:4:"直";s:3:"直";s:4:"𥃳";s:4:"𥃳";s:4:"𥃲";s:4:"𥃲";s:4:"𥄙";s:4:"𥄙";s:4:"𥄳";s:4:"𥄳";s:4:"眞";s:3:"眞";s:4:"真";s:3:"真";s:4:"真";s:3:"真";s:4:"睊";s:3:"睊";s:4:"䀹";s:3:"䀹";s:4:"瞋";s:3:"瞋";s:4:"䁆";s:3:"䁆";s:4:"䂖";s:3:"䂖";s:4:"𥐝";s:4:"𥐝";s:4:"硎";s:3:"硎";s:4:"碌";s:3:"碌";s:4:"磌";s:3:"磌";s:4:"䃣";s:3:"䃣";s:4:"𥘦";s:4:"𥘦";s:4:"祖";s:3:"祖";s:4:"𥚚";s:4:"𥚚";s:4:"𥛅";s:4:"𥛅";s:4:"福";s:3:"福";s:4:"秫";s:3:"秫";s:4:"䄯";s:3:"䄯";s:4:"穀";s:3:"穀";s:4:"穊";s:3:"穊";s:4:"穏";s:3:"穏";s:4:"𥥼";s:4:"𥥼";s:4:"𥪧";s:4:"𥪧";s:4:"𥪧";s:4:"𥪧";s:4:"竮";s:3:"竮";s:4:"䈂";s:3:"䈂";s:4:"𥮫";s:4:"𥮫";s:4:"篆";s:3:"篆";s:4:"築";s:3:"築";s:4:"䈧";s:3:"䈧";s:4:"𥲀";s:4:"𥲀";s:4:"糒";s:3:"糒";s:4:"䊠";s:3:"䊠";s:4:"糨";s:3:"糨";s:4:"糣";s:3:"糣";s:4:"紀";s:3:"紀";s:4:"𥾆";s:4:"𥾆";s:4:"絣";s:3:"絣";s:4:"䌁";s:3:"䌁";s:4:"緇";s:3:"緇";s:4:"縂";s:3:"縂";s:4:"繅";s:3:"繅";s:4:"䌴";s:3:"䌴";s:4:"𦈨";s:4:"𦈨";s:4:"𦉇";s:4:"𦉇";s:4:"䍙";s:3:"䍙";s:4:"𦋙";s:4:"𦋙";s:4:"罺";s:3:"罺";s:4:"𦌾";s:4:"𦌾";s:4:"羕";s:3:"羕";s:4:"翺";s:3:"翺";s:4:"者";s:3:"者";s:4:"𦓚";s:4:"𦓚";s:4:"𦔣";s:4:"𦔣";s:4:"聠";s:3:"聠";s:4:"𦖨";s:4:"𦖨";s:4:"聰";s:3:"聰";s:4:"𣍟";s:4:"𣍟";s:4:"䏕";s:3:"䏕";s:4:"育";s:3:"育";s:4:"脃";s:3:"脃";s:4:"䐋";s:3:"䐋";s:4:"脾";s:3:"脾";s:4:"媵";s:3:"媵";s:4:"𦞧";s:4:"𦞧";s:4:"𦞵";s:4:"𦞵";s:4:"𣎓";s:4:"𣎓";s:4:"𣎜";s:4:"𣎜";s:4:"舁";s:3:"舁";s:4:"舄";s:3:"舄";s:4:"辞";s:3:"辞";s:4:"䑫";s:3:"䑫";s:4:"芑";s:3:"芑";s:4:"芋";s:3:"芋";s:4:"芝";s:3:"芝";s:4:"劳";s:3:"劳";s:4:"花";s:3:"花";s:4:"芳";s:3:"芳";s:4:"芽";s:3:"芽";s:4:"苦";s:3:"苦";s:4:"𦬼";s:4:"𦬼";s:4:"若";s:3:"若";s:4:"茝";s:3:"茝";s:4:"荣";s:3:"荣";s:4:"莭";s:3:"莭";s:4:"茣";s:3:"茣";s:4:"莽";s:3:"莽";s:4:"菧";s:3:"菧";s:4:"著";s:3:"著";s:4:"荓";s:3:"荓";s:4:"菊";s:3:"菊";s:4:"菌";s:3:"菌";s:4:"菜";s:3:"菜";s:4:"𦰶";s:4:"𦰶";s:4:"𦵫";s:4:"𦵫";s:4:"𦳕";s:4:"𦳕";s:4:"䔫";s:3:"䔫";s:4:"蓱";s:3:"蓱";s:4:"蓳";s:3:"蓳";s:4:"蔖";s:3:"蔖";s:4:"𧏊";s:4:"𧏊";s:4:"蕤";s:3:"蕤";s:4:"𦼬";s:4:"𦼬";s:4:"䕝";s:3:"䕝";s:4:"䕡";s:3:"䕡";s:4:"𦾱";s:4:"𦾱";s:4:"𧃒";s:4:"𧃒";s:4:"䕫";s:3:"䕫";s:4:"虐";s:3:"虐";s:4:"虜";s:3:"虜";s:4:"虧";s:3:"虧";s:4:"虩";s:3:"虩";s:4:"蚩";s:3:"蚩";s:4:"蚈";s:3:"蚈";s:4:"蜎";s:3:"蜎";s:4:"蛢";s:3:"蛢";s:4:"蝹";s:3:"蝹";s:4:"蜨";s:3:"蜨";s:4:"蝫";s:3:"蝫";s:4:"螆";s:3:"螆";s:4:"䗗";s:3:"䗗";s:4:"蟡";s:3:"蟡";s:4:"蠁";s:3:"蠁";s:4:"䗹";s:3:"䗹";s:4:"衠";s:3:"衠";s:4:"衣";s:3:"衣";s:4:"𧙧";s:4:"𧙧";s:4:"裗";s:3:"裗";s:4:"裞";s:3:"裞";s:4:"䘵";s:3:"䘵";s:4:"裺";s:3:"裺";s:4:"㒻";s:3:"㒻";s:4:"𧢮";s:4:"𧢮";s:4:"𧥦";s:4:"𧥦";s:4:"䚾";s:3:"䚾";s:4:"䛇";s:3:"䛇";s:4:"誠";s:3:"誠";s:4:"諭";s:3:"諭";s:4:"變";s:3:"變";s:4:"豕";s:3:"豕";s:4:"𧲨";s:4:"𧲨";s:4:"貫";s:3:"貫";s:4:"賁";s:3:"賁";s:4:"贛";s:3:"贛";s:4:"起";s:3:"起";s:4:"𧼯";s:4:"𧼯";s:4:"𠠄";s:4:"𠠄";s:4:"跋";s:3:"跋";s:4:"趼";s:3:"趼";s:4:"跰";s:3:"跰";s:4:"𠣞";s:4:"𠣞";s:4:"軔";s:3:"軔";s:4:"輸";s:3:"輸";s:4:"𨗒";s:4:"𨗒";s:4:"𨗭";s:4:"𨗭";s:4:"邔";s:3:"邔";s:4:"郱";s:3:"郱";s:4:"鄑";s:3:"鄑";s:4:"𨜮";s:4:"𨜮";s:4:"鄛";s:3:"鄛";s:4:"鈸";s:3:"鈸";s:4:"鋗";s:3:"鋗";s:4:"鋘";s:3:"鋘";s:4:"鉼";s:3:"鉼";s:4:"鏹";s:3:"鏹";s:4:"鐕";s:3:"鐕";s:4:"𨯺";s:4:"𨯺";s:4:"開";s:3:"開";s:4:"䦕";s:3:"䦕";s:4:"閷";s:3:"閷";s:4:"𨵷";s:4:"𨵷";s:4:"䧦";s:3:"䧦";s:4:"雃";s:3:"雃";s:4:"嶲";s:3:"嶲";s:4:"霣";s:3:"霣";s:4:"𩅅";s:4:"𩅅";s:4:"𩈚";s:4:"𩈚";s:4:"䩮";s:3:"䩮";s:4:"䩶";s:3:"䩶";s:4:"韠";s:3:"韠";s:4:"𩐊";s:4:"𩐊";s:4:"䪲";s:3:"䪲";s:4:"𩒖";s:4:"𩒖";s:4:"頋";s:3:"頋";s:4:"頋";s:3:"頋";s:4:"頩";s:3:"頩";s:4:"𩖶";s:4:"𩖶";s:4:"飢";s:3:"飢";s:4:"䬳";s:3:"䬳";s:4:"餩";s:3:"餩";s:4:"馧";s:3:"馧";s:4:"駂";s:3:"駂";s:4:"駾";s:3:"駾";s:4:"䯎";s:3:"䯎";s:4:"𩬰";s:4:"𩬰";s:4:"鬒";s:3:"鬒";s:4:"鱀";s:3:"鱀";s:4:"鳽";s:3:"鳽";s:4:"䳎";s:3:"䳎";s:4:"䳭";s:3:"䳭";s:4:"鵧";s:3:"鵧";s:4:"𪃎";s:4:"𪃎";s:4:"䳸";s:3:"䳸";s:4:"𪄅";s:4:"𪄅";s:4:"𪈎";s:4:"𪈎";s:4:"𪊑";s:4:"𪊑";s:4:"麻";s:3:"麻";s:4:"䵖";s:3:"䵖";s:4:"黹";s:3:"黹";s:4:"黾";s:3:"黾";s:4:"鼅";s:3:"鼅";s:4:"鼏";s:3:"鼏";s:4:"鼖";s:3:"鼖";s:4:"鼻";s:3:"鼻";s:4:"𪘀";s:4:"𪘀";}' );
-$utfCheckNFC = unserialize( 'a:1216:{s:2:"̀";s:1:"N";s:2:"́";s:1:"N";s:2:"̓";s:1:"N";s:2:"̈́";s:1:"N";s:2:"ʹ";s:1:"N";s:2:";";s:1:"N";s:2:"·";s:1:"N";s:3:"क़";s:1:"N";s:3:"ख़";s:1:"N";s:3:"ग़";s:1:"N";s:3:"ज़";s:1:"N";s:3:"ड़";s:1:"N";s:3:"ढ़";s:1:"N";s:3:"फ़";s:1:"N";s:3:"य़";s:1:"N";s:3:"ড়";s:1:"N";s:3:"ঢ়";s:1:"N";s:3:"য়";s:1:"N";s:3:"ਲ਼";s:1:"N";s:3:"ਸ਼";s:1:"N";s:3:"ਖ਼";s:1:"N";s:3:"ਗ਼";s:1:"N";s:3:"ਜ਼";s:1:"N";s:3:"ਫ਼";s:1:"N";s:3:"ଡ଼";s:1:"N";s:3:"ଢ଼";s:1:"N";s:3:"གྷ";s:1:"N";s:3:"ཌྷ";s:1:"N";s:3:"དྷ";s:1:"N";s:3:"བྷ";s:1:"N";s:3:"ཛྷ";s:1:"N";s:3:"ཀྵ";s:1:"N";s:3:"ཱི";s:1:"N";s:3:"ཱུ";s:1:"N";s:3:"ྲྀ";s:1:"N";s:3:"ླྀ";s:1:"N";s:3:"ཱྀ";s:1:"N";s:3:"ྒྷ";s:1:"N";s:3:"ྜྷ";s:1:"N";s:3:"ྡྷ";s:1:"N";s:3:"ྦྷ";s:1:"N";s:3:"ྫྷ";s:1:"N";s:3:"ྐྵ";s:1:"N";s:3:"ά";s:1:"N";s:3:"έ";s:1:"N";s:3:"ή";s:1:"N";s:3:"ί";s:1:"N";s:3:"ό";s:1:"N";s:3:"ύ";s:1:"N";s:3:"ώ";s:1:"N";s:3:"Ά";s:1:"N";s:3:"ι";s:1:"N";s:3:"Έ";s:1:"N";s:3:"Ή";s:1:"N";s:3:"ΐ";s:1:"N";s:3:"Ί";s:1:"N";s:3:"ΰ";s:1:"N";s:3:"Ύ";s:1:"N";s:3:"΅";s:1:"N";s:3:"`";s:1:"N";s:3:"Ό";s:1:"N";s:3:"Ώ";s:1:"N";s:3:"´";s:1:"N";s:3:" ";s:1:"N";s:3:" ";s:1:"N";s:3:"Ω";s:1:"N";s:3:"K";s:1:"N";s:3:"Å";s:1:"N";s:3:"〈";s:1:"N";s:3:"〉";s:1:"N";s:3:"⫝̸";s:1:"N";s:3:"豈";s:1:"N";s:3:"更";s:1:"N";s:3:"車";s:1:"N";s:3:"賈";s:1:"N";s:3:"滑";s:1:"N";s:3:"串";s:1:"N";s:3:"句";s:1:"N";s:3:"龜";s:1:"N";s:3:"龜";s:1:"N";s:3:"契";s:1:"N";s:3:"金";s:1:"N";s:3:"喇";s:1:"N";s:3:"奈";s:1:"N";s:3:"懶";s:1:"N";s:3:"癩";s:1:"N";s:3:"羅";s:1:"N";s:3:"蘿";s:1:"N";s:3:"螺";s:1:"N";s:3:"裸";s:1:"N";s:3:"邏";s:1:"N";s:3:"樂";s:1:"N";s:3:"洛";s:1:"N";s:3:"烙";s:1:"N";s:3:"珞";s:1:"N";s:3:"落";s:1:"N";s:3:"酪";s:1:"N";s:3:"駱";s:1:"N";s:3:"亂";s:1:"N";s:3:"卵";s:1:"N";s:3:"欄";s:1:"N";s:3:"爛";s:1:"N";s:3:"蘭";s:1:"N";s:3:"鸞";s:1:"N";s:3:"嵐";s:1:"N";s:3:"濫";s:1:"N";s:3:"藍";s:1:"N";s:3:"襤";s:1:"N";s:3:"拉";s:1:"N";s:3:"臘";s:1:"N";s:3:"蠟";s:1:"N";s:3:"廊";s:1:"N";s:3:"朗";s:1:"N";s:3:"浪";s:1:"N";s:3:"狼";s:1:"N";s:3:"郎";s:1:"N";s:3:"來";s:1:"N";s:3:"冷";s:1:"N";s:3:"勞";s:1:"N";s:3:"擄";s:1:"N";s:3:"櫓";s:1:"N";s:3:"爐";s:1:"N";s:3:"盧";s:1:"N";s:3:"老";s:1:"N";s:3:"蘆";s:1:"N";s:3:"虜";s:1:"N";s:3:"路";s:1:"N";s:3:"露";s:1:"N";s:3:"魯";s:1:"N";s:3:"鷺";s:1:"N";s:3:"碌";s:1:"N";s:3:"祿";s:1:"N";s:3:"綠";s:1:"N";s:3:"菉";s:1:"N";s:3:"錄";s:1:"N";s:3:"鹿";s:1:"N";s:3:"論";s:1:"N";s:3:"壟";s:1:"N";s:3:"弄";s:1:"N";s:3:"籠";s:1:"N";s:3:"聾";s:1:"N";s:3:"牢";s:1:"N";s:3:"磊";s:1:"N";s:3:"賂";s:1:"N";s:3:"雷";s:1:"N";s:3:"壘";s:1:"N";s:3:"屢";s:1:"N";s:3:"樓";s:1:"N";s:3:"淚";s:1:"N";s:3:"漏";s:1:"N";s:3:"累";s:1:"N";s:3:"縷";s:1:"N";s:3:"陋";s:1:"N";s:3:"勒";s:1:"N";s:3:"肋";s:1:"N";s:3:"凜";s:1:"N";s:3:"凌";s:1:"N";s:3:"稜";s:1:"N";s:3:"綾";s:1:"N";s:3:"菱";s:1:"N";s:3:"陵";s:1:"N";s:3:"讀";s:1:"N";s:3:"拏";s:1:"N";s:3:"樂";s:1:"N";s:3:"諾";s:1:"N";s:3:"丹";s:1:"N";s:3:"寧";s:1:"N";s:3:"怒";s:1:"N";s:3:"率";s:1:"N";s:3:"異";s:1:"N";s:3:"北";s:1:"N";s:3:"磻";s:1:"N";s:3:"便";s:1:"N";s:3:"復";s:1:"N";s:3:"不";s:1:"N";s:3:"泌";s:1:"N";s:3:"數";s:1:"N";s:3:"索";s:1:"N";s:3:"參";s:1:"N";s:3:"塞";s:1:"N";s:3:"省";s:1:"N";s:3:"葉";s:1:"N";s:3:"說";s:1:"N";s:3:"殺";s:1:"N";s:3:"辰";s:1:"N";s:3:"沈";s:1:"N";s:3:"拾";s:1:"N";s:3:"若";s:1:"N";s:3:"掠";s:1:"N";s:3:"略";s:1:"N";s:3:"亮";s:1:"N";s:3:"兩";s:1:"N";s:3:"凉";s:1:"N";s:3:"梁";s:1:"N";s:3:"糧";s:1:"N";s:3:"良";s:1:"N";s:3:"諒";s:1:"N";s:3:"量";s:1:"N";s:3:"勵";s:1:"N";s:3:"呂";s:1:"N";s:3:"女";s:1:"N";s:3:"廬";s:1:"N";s:3:"旅";s:1:"N";s:3:"濾";s:1:"N";s:3:"礪";s:1:"N";s:3:"閭";s:1:"N";s:3:"驪";s:1:"N";s:3:"麗";s:1:"N";s:3:"黎";s:1:"N";s:3:"力";s:1:"N";s:3:"曆";s:1:"N";s:3:"歷";s:1:"N";s:3:"轢";s:1:"N";s:3:"年";s:1:"N";s:3:"憐";s:1:"N";s:3:"戀";s:1:"N";s:3:"撚";s:1:"N";s:3:"漣";s:1:"N";s:3:"煉";s:1:"N";s:3:"璉";s:1:"N";s:3:"秊";s:1:"N";s:3:"練";s:1:"N";s:3:"聯";s:1:"N";s:3:"輦";s:1:"N";s:3:"蓮";s:1:"N";s:3:"連";s:1:"N";s:3:"鍊";s:1:"N";s:3:"列";s:1:"N";s:3:"劣";s:1:"N";s:3:"咽";s:1:"N";s:3:"烈";s:1:"N";s:3:"裂";s:1:"N";s:3:"說";s:1:"N";s:3:"廉";s:1:"N";s:3:"念";s:1:"N";s:3:"捻";s:1:"N";s:3:"殮";s:1:"N";s:3:"簾";s:1:"N";s:3:"獵";s:1:"N";s:3:"令";s:1:"N";s:3:"囹";s:1:"N";s:3:"寧";s:1:"N";s:3:"嶺";s:1:"N";s:3:"怜";s:1:"N";s:3:"玲";s:1:"N";s:3:"瑩";s:1:"N";s:3:"羚";s:1:"N";s:3:"聆";s:1:"N";s:3:"鈴";s:1:"N";s:3:"零";s:1:"N";s:3:"靈";s:1:"N";s:3:"領";s:1:"N";s:3:"例";s:1:"N";s:3:"禮";s:1:"N";s:3:"醴";s:1:"N";s:3:"隸";s:1:"N";s:3:"惡";s:1:"N";s:3:"了";s:1:"N";s:3:"僚";s:1:"N";s:3:"寮";s:1:"N";s:3:"尿";s:1:"N";s:3:"料";s:1:"N";s:3:"樂";s:1:"N";s:3:"燎";s:1:"N";s:3:"療";s:1:"N";s:3:"蓼";s:1:"N";s:3:"遼";s:1:"N";s:3:"龍";s:1:"N";s:3:"暈";s:1:"N";s:3:"阮";s:1:"N";s:3:"劉";s:1:"N";s:3:"杻";s:1:"N";s:3:"柳";s:1:"N";s:3:"流";s:1:"N";s:3:"溜";s:1:"N";s:3:"琉";s:1:"N";s:3:"留";s:1:"N";s:3:"硫";s:1:"N";s:3:"紐";s:1:"N";s:3:"類";s:1:"N";s:3:"六";s:1:"N";s:3:"戮";s:1:"N";s:3:"陸";s:1:"N";s:3:"倫";s:1:"N";s:3:"崙";s:1:"N";s:3:"淪";s:1:"N";s:3:"輪";s:1:"N";s:3:"律";s:1:"N";s:3:"慄";s:1:"N";s:3:"栗";s:1:"N";s:3:"率";s:1:"N";s:3:"隆";s:1:"N";s:3:"利";s:1:"N";s:3:"吏";s:1:"N";s:3:"履";s:1:"N";s:3:"易";s:1:"N";s:3:"李";s:1:"N";s:3:"梨";s:1:"N";s:3:"泥";s:1:"N";s:3:"理";s:1:"N";s:3:"痢";s:1:"N";s:3:"罹";s:1:"N";s:3:"裏";s:1:"N";s:3:"裡";s:1:"N";s:3:"里";s:1:"N";s:3:"離";s:1:"N";s:3:"匿";s:1:"N";s:3:"溺";s:1:"N";s:3:"吝";s:1:"N";s:3:"燐";s:1:"N";s:3:"璘";s:1:"N";s:3:"藺";s:1:"N";s:3:"隣";s:1:"N";s:3:"鱗";s:1:"N";s:3:"麟";s:1:"N";s:3:"林";s:1:"N";s:3:"淋";s:1:"N";s:3:"臨";s:1:"N";s:3:"立";s:1:"N";s:3:"笠";s:1:"N";s:3:"粒";s:1:"N";s:3:"狀";s:1:"N";s:3:"炙";s:1:"N";s:3:"識";s:1:"N";s:3:"什";s:1:"N";s:3:"茶";s:1:"N";s:3:"刺";s:1:"N";s:3:"切";s:1:"N";s:3:"度";s:1:"N";s:3:"拓";s:1:"N";s:3:"糖";s:1:"N";s:3:"宅";s:1:"N";s:3:"洞";s:1:"N";s:3:"暴";s:1:"N";s:3:"輻";s:1:"N";s:3:"行";s:1:"N";s:3:"降";s:1:"N";s:3:"見";s:1:"N";s:3:"廓";s:1:"N";s:3:"兀";s:1:"N";s:3:"嗀";s:1:"N";s:3:"塚";s:1:"N";s:3:"晴";s:1:"N";s:3:"凞";s:1:"N";s:3:"猪";s:1:"N";s:3:"益";s:1:"N";s:3:"礼";s:1:"N";s:3:"神";s:1:"N";s:3:"祥";s:1:"N";s:3:"福";s:1:"N";s:3:"靖";s:1:"N";s:3:"精";s:1:"N";s:3:"羽";s:1:"N";s:3:"蘒";s:1:"N";s:3:"諸";s:1:"N";s:3:"逸";s:1:"N";s:3:"都";s:1:"N";s:3:"飯";s:1:"N";s:3:"飼";s:1:"N";s:3:"館";s:1:"N";s:3:"鶴";s:1:"N";s:3:"侮";s:1:"N";s:3:"僧";s:1:"N";s:3:"免";s:1:"N";s:3:"勉";s:1:"N";s:3:"勤";s:1:"N";s:3:"卑";s:1:"N";s:3:"喝";s:1:"N";s:3:"嘆";s:1:"N";s:3:"器";s:1:"N";s:3:"塀";s:1:"N";s:3:"墨";s:1:"N";s:3:"層";s:1:"N";s:3:"屮";s:1:"N";s:3:"悔";s:1:"N";s:3:"慨";s:1:"N";s:3:"憎";s:1:"N";s:3:"懲";s:1:"N";s:3:"敏";s:1:"N";s:3:"既";s:1:"N";s:3:"暑";s:1:"N";s:3:"梅";s:1:"N";s:3:"海";s:1:"N";s:3:"渚";s:1:"N";s:3:"漢";s:1:"N";s:3:"煮";s:1:"N";s:3:"爫";s:1:"N";s:3:"琢";s:1:"N";s:3:"碑";s:1:"N";s:3:"社";s:1:"N";s:3:"祉";s:1:"N";s:3:"祈";s:1:"N";s:3:"祐";s:1:"N";s:3:"祖";s:1:"N";s:3:"祝";s:1:"N";s:3:"禍";s:1:"N";s:3:"禎";s:1:"N";s:3:"穀";s:1:"N";s:3:"突";s:1:"N";s:3:"節";s:1:"N";s:3:"練";s:1:"N";s:3:"縉";s:1:"N";s:3:"繁";s:1:"N";s:3:"署";s:1:"N";s:3:"者";s:1:"N";s:3:"臭";s:1:"N";s:3:"艹";s:1:"N";s:3:"艹";s:1:"N";s:3:"著";s:1:"N";s:3:"褐";s:1:"N";s:3:"視";s:1:"N";s:3:"謁";s:1:"N";s:3:"謹";s:1:"N";s:3:"賓";s:1:"N";s:3:"贈";s:1:"N";s:3:"辶";s:1:"N";s:3:"逸";s:1:"N";s:3:"難";s:1:"N";s:3:"響";s:1:"N";s:3:"頻";s:1:"N";s:3:"並";s:1:"N";s:3:"况";s:1:"N";s:3:"全";s:1:"N";s:3:"侀";s:1:"N";s:3:"充";s:1:"N";s:3:"冀";s:1:"N";s:3:"勇";s:1:"N";s:3:"勺";s:1:"N";s:3:"喝";s:1:"N";s:3:"啕";s:1:"N";s:3:"喙";s:1:"N";s:3:"嗢";s:1:"N";s:3:"塚";s:1:"N";s:3:"墳";s:1:"N";s:3:"奄";s:1:"N";s:3:"奔";s:1:"N";s:3:"婢";s:1:"N";s:3:"嬨";s:1:"N";s:3:"廒";s:1:"N";s:3:"廙";s:1:"N";s:3:"彩";s:1:"N";s:3:"徭";s:1:"N";s:3:"惘";s:1:"N";s:3:"慎";s:1:"N";s:3:"愈";s:1:"N";s:3:"憎";s:1:"N";s:3:"慠";s:1:"N";s:3:"懲";s:1:"N";s:3:"戴";s:1:"N";s:3:"揄";s:1:"N";s:3:"搜";s:1:"N";s:3:"摒";s:1:"N";s:3:"敖";s:1:"N";s:3:"晴";s:1:"N";s:3:"朗";s:1:"N";s:3:"望";s:1:"N";s:3:"杖";s:1:"N";s:3:"歹";s:1:"N";s:3:"殺";s:1:"N";s:3:"流";s:1:"N";s:3:"滛";s:1:"N";s:3:"滋";s:1:"N";s:3:"漢";s:1:"N";s:3:"瀞";s:1:"N";s:3:"煮";s:1:"N";s:3:"瞧";s:1:"N";s:3:"爵";s:1:"N";s:3:"犯";s:1:"N";s:3:"猪";s:1:"N";s:3:"瑱";s:1:"N";s:3:"甆";s:1:"N";s:3:"画";s:1:"N";s:3:"瘝";s:1:"N";s:3:"瘟";s:1:"N";s:3:"益";s:1:"N";s:3:"盛";s:1:"N";s:3:"直";s:1:"N";s:3:"睊";s:1:"N";s:3:"着";s:1:"N";s:3:"磌";s:1:"N";s:3:"窱";s:1:"N";s:3:"節";s:1:"N";s:3:"类";s:1:"N";s:3:"絛";s:1:"N";s:3:"練";s:1:"N";s:3:"缾";s:1:"N";s:3:"者";s:1:"N";s:3:"荒";s:1:"N";s:3:"華";s:1:"N";s:3:"蝹";s:1:"N";s:3:"襁";s:1:"N";s:3:"覆";s:1:"N";s:3:"視";s:1:"N";s:3:"調";s:1:"N";s:3:"諸";s:1:"N";s:3:"請";s:1:"N";s:3:"謁";s:1:"N";s:3:"諾";s:1:"N";s:3:"諭";s:1:"N";s:3:"謹";s:1:"N";s:3:"變";s:1:"N";s:3:"贈";s:1:"N";s:3:"輸";s:1:"N";s:3:"遲";s:1:"N";s:3:"醙";s:1:"N";s:3:"鉶";s:1:"N";s:3:"陼";s:1:"N";s:3:"難";s:1:"N";s:3:"靖";s:1:"N";s:3:"韛";s:1:"N";s:3:"響";s:1:"N";s:3:"頋";s:1:"N";s:3:"頻";s:1:"N";s:3:"鬒";s:1:"N";s:3:"龜";s:1:"N";s:3:"𢡊";s:1:"N";s:3:"𢡄";s:1:"N";s:3:"𣏕";s:1:"N";s:3:"㮝";s:1:"N";s:3:"䀘";s:1:"N";s:3:"䀹";s:1:"N";s:3:"𥉉";s:1:"N";s:3:"𥳐";s:1:"N";s:3:"𧻓";s:1:"N";s:3:"齃";s:1:"N";s:3:"龎";s:1:"N";s:3:"יִ";s:1:"N";s:3:"ײַ";s:1:"N";s:3:"שׁ";s:1:"N";s:3:"שׂ";s:1:"N";s:3:"שּׁ";s:1:"N";s:3:"שּׂ";s:1:"N";s:3:"אַ";s:1:"N";s:3:"אָ";s:1:"N";s:3:"אּ";s:1:"N";s:3:"בּ";s:1:"N";s:3:"גּ";s:1:"N";s:3:"דּ";s:1:"N";s:3:"הּ";s:1:"N";s:3:"וּ";s:1:"N";s:3:"זּ";s:1:"N";s:3:"טּ";s:1:"N";s:3:"יּ";s:1:"N";s:3:"ךּ";s:1:"N";s:3:"כּ";s:1:"N";s:3:"לּ";s:1:"N";s:3:"מּ";s:1:"N";s:3:"נּ";s:1:"N";s:3:"סּ";s:1:"N";s:3:"ףּ";s:1:"N";s:3:"פּ";s:1:"N";s:3:"צּ";s:1:"N";s:3:"קּ";s:1:"N";s:3:"רּ";s:1:"N";s:3:"שּ";s:1:"N";s:3:"תּ";s:1:"N";s:3:"וֹ";s:1:"N";s:3:"בֿ";s:1:"N";s:3:"כֿ";s:1:"N";s:3:"פֿ";s:1:"N";s:4:"𝅗𝅥";s:1:"N";s:4:"𝅘𝅥";s:1:"N";s:4:"𝅘𝅥𝅮";s:1:"N";s:4:"𝅘𝅥𝅯";s:1:"N";s:4:"𝅘𝅥𝅰";s:1:"N";s:4:"𝅘𝅥𝅱";s:1:"N";s:4:"𝅘𝅥𝅲";s:1:"N";s:4:"𝆹𝅥";s:1:"N";s:4:"𝆺𝅥";s:1:"N";s:4:"𝆹𝅥𝅮";s:1:"N";s:4:"𝆺𝅥𝅮";s:1:"N";s:4:"𝆹𝅥𝅯";s:1:"N";s:4:"𝆺𝅥𝅯";s:1:"N";s:4:"丽";s:1:"N";s:4:"丸";s:1:"N";s:4:"乁";s:1:"N";s:4:"𠄢";s:1:"N";s:4:"你";s:1:"N";s:4:"侮";s:1:"N";s:4:"侻";s:1:"N";s:4:"倂";s:1:"N";s:4:"偺";s:1:"N";s:4:"備";s:1:"N";s:4:"僧";s:1:"N";s:4:"像";s:1:"N";s:4:"㒞";s:1:"N";s:4:"𠘺";s:1:"N";s:4:"免";s:1:"N";s:4:"兔";s:1:"N";s:4:"兤";s:1:"N";s:4:"具";s:1:"N";s:4:"𠔜";s:1:"N";s:4:"㒹";s:1:"N";s:4:"內";s:1:"N";s:4:"再";s:1:"N";s:4:"𠕋";s:1:"N";s:4:"冗";s:1:"N";s:4:"冤";s:1:"N";s:4:"仌";s:1:"N";s:4:"冬";s:1:"N";s:4:"况";s:1:"N";s:4:"𩇟";s:1:"N";s:4:"凵";s:1:"N";s:4:"刃";s:1:"N";s:4:"㓟";s:1:"N";s:4:"刻";s:1:"N";s:4:"剆";s:1:"N";s:4:"割";s:1:"N";s:4:"剷";s:1:"N";s:4:"㔕";s:1:"N";s:4:"勇";s:1:"N";s:4:"勉";s:1:"N";s:4:"勤";s:1:"N";s:4:"勺";s:1:"N";s:4:"包";s:1:"N";s:4:"匆";s:1:"N";s:4:"北";s:1:"N";s:4:"卉";s:1:"N";s:4:"卑";s:1:"N";s:4:"博";s:1:"N";s:4:"即";s:1:"N";s:4:"卽";s:1:"N";s:4:"卿";s:1:"N";s:4:"卿";s:1:"N";s:4:"卿";s:1:"N";s:4:"𠨬";s:1:"N";s:4:"灰";s:1:"N";s:4:"及";s:1:"N";s:4:"叟";s:1:"N";s:4:"𠭣";s:1:"N";s:4:"叫";s:1:"N";s:4:"叱";s:1:"N";s:4:"吆";s:1:"N";s:4:"咞";s:1:"N";s:4:"吸";s:1:"N";s:4:"呈";s:1:"N";s:4:"周";s:1:"N";s:4:"咢";s:1:"N";s:4:"哶";s:1:"N";s:4:"唐";s:1:"N";s:4:"啓";s:1:"N";s:4:"啣";s:1:"N";s:4:"善";s:1:"N";s:4:"善";s:1:"N";s:4:"喙";s:1:"N";s:4:"喫";s:1:"N";s:4:"喳";s:1:"N";s:4:"嗂";s:1:"N";s:4:"圖";s:1:"N";s:4:"嘆";s:1:"N";s:4:"圗";s:1:"N";s:4:"噑";s:1:"N";s:4:"噴";s:1:"N";s:4:"切";s:1:"N";s:4:"壮";s:1:"N";s:4:"城";s:1:"N";s:4:"埴";s:1:"N";s:4:"堍";s:1:"N";s:4:"型";s:1:"N";s:4:"堲";s:1:"N";s:4:"報";s:1:"N";s:4:"墬";s:1:"N";s:4:"𡓤";s:1:"N";s:4:"売";s:1:"N";s:4:"壷";s:1:"N";s:4:"夆";s:1:"N";s:4:"多";s:1:"N";s:4:"夢";s:1:"N";s:4:"奢";s:1:"N";s:4:"𡚨";s:1:"N";s:4:"𡛪";s:1:"N";s:4:"姬";s:1:"N";s:4:"娛";s:1:"N";s:4:"娧";s:1:"N";s:4:"姘";s:1:"N";s:4:"婦";s:1:"N";s:4:"㛮";s:1:"N";s:4:"㛼";s:1:"N";s:4:"嬈";s:1:"N";s:4:"嬾";s:1:"N";s:4:"嬾";s:1:"N";s:4:"𡧈";s:1:"N";s:4:"寃";s:1:"N";s:4:"寘";s:1:"N";s:4:"寧";s:1:"N";s:4:"寳";s:1:"N";s:4:"𡬘";s:1:"N";s:4:"寿";s:1:"N";s:4:"将";s:1:"N";s:4:"当";s:1:"N";s:4:"尢";s:1:"N";s:4:"㞁";s:1:"N";s:4:"屠";s:1:"N";s:4:"屮";s:1:"N";s:4:"峀";s:1:"N";s:4:"岍";s:1:"N";s:4:"𡷤";s:1:"N";s:4:"嵃";s:1:"N";s:4:"𡷦";s:1:"N";s:4:"嵮";s:1:"N";s:4:"嵫";s:1:"N";s:4:"嵼";s:1:"N";s:4:"巡";s:1:"N";s:4:"巢";s:1:"N";s:4:"㠯";s:1:"N";s:4:"巽";s:1:"N";s:4:"帨";s:1:"N";s:4:"帽";s:1:"N";s:4:"幩";s:1:"N";s:4:"㡢";s:1:"N";s:4:"𢆃";s:1:"N";s:4:"㡼";s:1:"N";s:4:"庰";s:1:"N";s:4:"庳";s:1:"N";s:4:"庶";s:1:"N";s:4:"廊";s:1:"N";s:4:"𪎒";s:1:"N";s:4:"廾";s:1:"N";s:4:"𢌱";s:1:"N";s:4:"𢌱";s:1:"N";s:4:"舁";s:1:"N";s:4:"弢";s:1:"N";s:4:"弢";s:1:"N";s:4:"㣇";s:1:"N";s:4:"𣊸";s:1:"N";s:4:"𦇚";s:1:"N";s:4:"形";s:1:"N";s:4:"彫";s:1:"N";s:4:"㣣";s:1:"N";s:4:"徚";s:1:"N";s:4:"忍";s:1:"N";s:4:"志";s:1:"N";s:4:"忹";s:1:"N";s:4:"悁";s:1:"N";s:4:"㤺";s:1:"N";s:4:"㤜";s:1:"N";s:4:"悔";s:1:"N";s:4:"𢛔";s:1:"N";s:4:"惇";s:1:"N";s:4:"慈";s:1:"N";s:4:"慌";s:1:"N";s:4:"慎";s:1:"N";s:4:"慌";s:1:"N";s:4:"慺";s:1:"N";s:4:"憎";s:1:"N";s:4:"憲";s:1:"N";s:4:"憤";s:1:"N";s:4:"憯";s:1:"N";s:4:"懞";s:1:"N";s:4:"懲";s:1:"N";s:4:"懶";s:1:"N";s:4:"成";s:1:"N";s:4:"戛";s:1:"N";s:4:"扝";s:1:"N";s:4:"抱";s:1:"N";s:4:"拔";s:1:"N";s:4:"捐";s:1:"N";s:4:"𢬌";s:1:"N";s:4:"挽";s:1:"N";s:4:"拼";s:1:"N";s:4:"捨";s:1:"N";s:4:"掃";s:1:"N";s:4:"揤";s:1:"N";s:4:"𢯱";s:1:"N";s:4:"搢";s:1:"N";s:4:"揅";s:1:"N";s:4:"掩";s:1:"N";s:4:"㨮";s:1:"N";s:4:"摩";s:1:"N";s:4:"摾";s:1:"N";s:4:"撝";s:1:"N";s:4:"摷";s:1:"N";s:4:"㩬";s:1:"N";s:4:"敏";s:1:"N";s:4:"敬";s:1:"N";s:4:"𣀊";s:1:"N";s:4:"旣";s:1:"N";s:4:"書";s:1:"N";s:4:"晉";s:1:"N";s:4:"㬙";s:1:"N";s:4:"暑";s:1:"N";s:4:"㬈";s:1:"N";s:4:"㫤";s:1:"N";s:4:"冒";s:1:"N";s:4:"冕";s:1:"N";s:4:"最";s:1:"N";s:4:"暜";s:1:"N";s:4:"肭";s:1:"N";s:4:"䏙";s:1:"N";s:4:"朗";s:1:"N";s:4:"望";s:1:"N";s:4:"朡";s:1:"N";s:4:"杞";s:1:"N";s:4:"杓";s:1:"N";s:4:"𣏃";s:1:"N";s:4:"㭉";s:1:"N";s:4:"柺";s:1:"N";s:4:"枅";s:1:"N";s:4:"桒";s:1:"N";s:4:"梅";s:1:"N";s:4:"𣑭";s:1:"N";s:4:"梎";s:1:"N";s:4:"栟";s:1:"N";s:4:"椔";s:1:"N";s:4:"㮝";s:1:"N";s:4:"楂";s:1:"N";s:4:"榣";s:1:"N";s:4:"槪";s:1:"N";s:4:"檨";s:1:"N";s:4:"𣚣";s:1:"N";s:4:"櫛";s:1:"N";s:4:"㰘";s:1:"N";s:4:"次";s:1:"N";s:4:"𣢧";s:1:"N";s:4:"歔";s:1:"N";s:4:"㱎";s:1:"N";s:4:"歲";s:1:"N";s:4:"殟";s:1:"N";s:4:"殺";s:1:"N";s:4:"殻";s:1:"N";s:4:"𣪍";s:1:"N";s:4:"𡴋";s:1:"N";s:4:"𣫺";s:1:"N";s:4:"汎";s:1:"N";s:4:"𣲼";s:1:"N";s:4:"沿";s:1:"N";s:4:"泍";s:1:"N";s:4:"汧";s:1:"N";s:4:"洖";s:1:"N";s:4:"派";s:1:"N";s:4:"海";s:1:"N";s:4:"流";s:1:"N";s:4:"浩";s:1:"N";s:4:"浸";s:1:"N";s:4:"涅";s:1:"N";s:4:"𣴞";s:1:"N";s:4:"洴";s:1:"N";s:4:"港";s:1:"N";s:4:"湮";s:1:"N";s:4:"㴳";s:1:"N";s:4:"滋";s:1:"N";s:4:"滇";s:1:"N";s:4:"𣻑";s:1:"N";s:4:"淹";s:1:"N";s:4:"潮";s:1:"N";s:4:"𣽞";s:1:"N";s:4:"𣾎";s:1:"N";s:4:"濆";s:1:"N";s:4:"瀹";s:1:"N";s:4:"瀞";s:1:"N";s:4:"瀛";s:1:"N";s:4:"㶖";s:1:"N";s:4:"灊";s:1:"N";s:4:"災";s:1:"N";s:4:"灷";s:1:"N";s:4:"炭";s:1:"N";s:4:"𠔥";s:1:"N";s:4:"煅";s:1:"N";s:4:"𤉣";s:1:"N";s:4:"熜";s:1:"N";s:4:"𤎫";s:1:"N";s:4:"爨";s:1:"N";s:4:"爵";s:1:"N";s:4:"牐";s:1:"N";s:4:"𤘈";s:1:"N";s:4:"犀";s:1:"N";s:4:"犕";s:1:"N";s:4:"𤜵";s:1:"N";s:4:"𤠔";s:1:"N";s:4:"獺";s:1:"N";s:4:"王";s:1:"N";s:4:"㺬";s:1:"N";s:4:"玥";s:1:"N";s:4:"㺸";s:1:"N";s:4:"㺸";s:1:"N";s:4:"瑇";s:1:"N";s:4:"瑜";s:1:"N";s:4:"瑱";s:1:"N";s:4:"璅";s:1:"N";s:4:"瓊";s:1:"N";s:4:"㼛";s:1:"N";s:4:"甤";s:1:"N";s:4:"𤰶";s:1:"N";s:4:"甾";s:1:"N";s:4:"𤲒";s:1:"N";s:4:"異";s:1:"N";s:4:"𢆟";s:1:"N";s:4:"瘐";s:1:"N";s:4:"𤾡";s:1:"N";s:4:"𤾸";s:1:"N";s:4:"𥁄";s:1:"N";s:4:"㿼";s:1:"N";s:4:"䀈";s:1:"N";s:4:"直";s:1:"N";s:4:"𥃳";s:1:"N";s:4:"𥃲";s:1:"N";s:4:"𥄙";s:1:"N";s:4:"𥄳";s:1:"N";s:4:"眞";s:1:"N";s:4:"真";s:1:"N";s:4:"真";s:1:"N";s:4:"睊";s:1:"N";s:4:"䀹";s:1:"N";s:4:"瞋";s:1:"N";s:4:"䁆";s:1:"N";s:4:"䂖";s:1:"N";s:4:"𥐝";s:1:"N";s:4:"硎";s:1:"N";s:4:"碌";s:1:"N";s:4:"磌";s:1:"N";s:4:"䃣";s:1:"N";s:4:"𥘦";s:1:"N";s:4:"祖";s:1:"N";s:4:"𥚚";s:1:"N";s:4:"𥛅";s:1:"N";s:4:"福";s:1:"N";s:4:"秫";s:1:"N";s:4:"䄯";s:1:"N";s:4:"穀";s:1:"N";s:4:"穊";s:1:"N";s:4:"穏";s:1:"N";s:4:"𥥼";s:1:"N";s:4:"𥪧";s:1:"N";s:4:"𥪧";s:1:"N";s:4:"竮";s:1:"N";s:4:"䈂";s:1:"N";s:4:"𥮫";s:1:"N";s:4:"篆";s:1:"N";s:4:"築";s:1:"N";s:4:"䈧";s:1:"N";s:4:"𥲀";s:1:"N";s:4:"糒";s:1:"N";s:4:"䊠";s:1:"N";s:4:"糨";s:1:"N";s:4:"糣";s:1:"N";s:4:"紀";s:1:"N";s:4:"𥾆";s:1:"N";s:4:"絣";s:1:"N";s:4:"䌁";s:1:"N";s:4:"緇";s:1:"N";s:4:"縂";s:1:"N";s:4:"繅";s:1:"N";s:4:"䌴";s:1:"N";s:4:"𦈨";s:1:"N";s:4:"𦉇";s:1:"N";s:4:"䍙";s:1:"N";s:4:"𦋙";s:1:"N";s:4:"罺";s:1:"N";s:4:"𦌾";s:1:"N";s:4:"羕";s:1:"N";s:4:"翺";s:1:"N";s:4:"者";s:1:"N";s:4:"𦓚";s:1:"N";s:4:"𦔣";s:1:"N";s:4:"聠";s:1:"N";s:4:"𦖨";s:1:"N";s:4:"聰";s:1:"N";s:4:"𣍟";s:1:"N";s:4:"䏕";s:1:"N";s:4:"育";s:1:"N";s:4:"脃";s:1:"N";s:4:"䐋";s:1:"N";s:4:"脾";s:1:"N";s:4:"媵";s:1:"N";s:4:"𦞧";s:1:"N";s:4:"𦞵";s:1:"N";s:4:"𣎓";s:1:"N";s:4:"𣎜";s:1:"N";s:4:"舁";s:1:"N";s:4:"舄";s:1:"N";s:4:"辞";s:1:"N";s:4:"䑫";s:1:"N";s:4:"芑";s:1:"N";s:4:"芋";s:1:"N";s:4:"芝";s:1:"N";s:4:"劳";s:1:"N";s:4:"花";s:1:"N";s:4:"芳";s:1:"N";s:4:"芽";s:1:"N";s:4:"苦";s:1:"N";s:4:"𦬼";s:1:"N";s:4:"若";s:1:"N";s:4:"茝";s:1:"N";s:4:"荣";s:1:"N";s:4:"莭";s:1:"N";s:4:"茣";s:1:"N";s:4:"莽";s:1:"N";s:4:"菧";s:1:"N";s:4:"著";s:1:"N";s:4:"荓";s:1:"N";s:4:"菊";s:1:"N";s:4:"菌";s:1:"N";s:4:"菜";s:1:"N";s:4:"𦰶";s:1:"N";s:4:"𦵫";s:1:"N";s:4:"𦳕";s:1:"N";s:4:"䔫";s:1:"N";s:4:"蓱";s:1:"N";s:4:"蓳";s:1:"N";s:4:"蔖";s:1:"N";s:4:"𧏊";s:1:"N";s:4:"蕤";s:1:"N";s:4:"𦼬";s:1:"N";s:4:"䕝";s:1:"N";s:4:"䕡";s:1:"N";s:4:"𦾱";s:1:"N";s:4:"𧃒";s:1:"N";s:4:"䕫";s:1:"N";s:4:"虐";s:1:"N";s:4:"虜";s:1:"N";s:4:"虧";s:1:"N";s:4:"虩";s:1:"N";s:4:"蚩";s:1:"N";s:4:"蚈";s:1:"N";s:4:"蜎";s:1:"N";s:4:"蛢";s:1:"N";s:4:"蝹";s:1:"N";s:4:"蜨";s:1:"N";s:4:"蝫";s:1:"N";s:4:"螆";s:1:"N";s:4:"䗗";s:1:"N";s:4:"蟡";s:1:"N";s:4:"蠁";s:1:"N";s:4:"䗹";s:1:"N";s:4:"衠";s:1:"N";s:4:"衣";s:1:"N";s:4:"𧙧";s:1:"N";s:4:"裗";s:1:"N";s:4:"裞";s:1:"N";s:4:"䘵";s:1:"N";s:4:"裺";s:1:"N";s:4:"㒻";s:1:"N";s:4:"𧢮";s:1:"N";s:4:"𧥦";s:1:"N";s:4:"䚾";s:1:"N";s:4:"䛇";s:1:"N";s:4:"誠";s:1:"N";s:4:"諭";s:1:"N";s:4:"變";s:1:"N";s:4:"豕";s:1:"N";s:4:"𧲨";s:1:"N";s:4:"貫";s:1:"N";s:4:"賁";s:1:"N";s:4:"贛";s:1:"N";s:4:"起";s:1:"N";s:4:"𧼯";s:1:"N";s:4:"𠠄";s:1:"N";s:4:"跋";s:1:"N";s:4:"趼";s:1:"N";s:4:"跰";s:1:"N";s:4:"𠣞";s:1:"N";s:4:"軔";s:1:"N";s:4:"輸";s:1:"N";s:4:"𨗒";s:1:"N";s:4:"𨗭";s:1:"N";s:4:"邔";s:1:"N";s:4:"郱";s:1:"N";s:4:"鄑";s:1:"N";s:4:"𨜮";s:1:"N";s:4:"鄛";s:1:"N";s:4:"鈸";s:1:"N";s:4:"鋗";s:1:"N";s:4:"鋘";s:1:"N";s:4:"鉼";s:1:"N";s:4:"鏹";s:1:"N";s:4:"鐕";s:1:"N";s:4:"𨯺";s:1:"N";s:4:"開";s:1:"N";s:4:"䦕";s:1:"N";s:4:"閷";s:1:"N";s:4:"𨵷";s:1:"N";s:4:"䧦";s:1:"N";s:4:"雃";s:1:"N";s:4:"嶲";s:1:"N";s:4:"霣";s:1:"N";s:4:"𩅅";s:1:"N";s:4:"𩈚";s:1:"N";s:4:"䩮";s:1:"N";s:4:"䩶";s:1:"N";s:4:"韠";s:1:"N";s:4:"𩐊";s:1:"N";s:4:"䪲";s:1:"N";s:4:"𩒖";s:1:"N";s:4:"頋";s:1:"N";s:4:"頋";s:1:"N";s:4:"頩";s:1:"N";s:4:"𩖶";s:1:"N";s:4:"飢";s:1:"N";s:4:"䬳";s:1:"N";s:4:"餩";s:1:"N";s:4:"馧";s:1:"N";s:4:"駂";s:1:"N";s:4:"駾";s:1:"N";s:4:"䯎";s:1:"N";s:4:"𩬰";s:1:"N";s:4:"鬒";s:1:"N";s:4:"鱀";s:1:"N";s:4:"鳽";s:1:"N";s:4:"䳎";s:1:"N";s:4:"䳭";s:1:"N";s:4:"鵧";s:1:"N";s:4:"𪃎";s:1:"N";s:4:"䳸";s:1:"N";s:4:"𪄅";s:1:"N";s:4:"𪈎";s:1:"N";s:4:"𪊑";s:1:"N";s:4:"麻";s:1:"N";s:4:"䵖";s:1:"N";s:4:"黹";s:1:"N";s:4:"黾";s:1:"N";s:4:"鼅";s:1:"N";s:4:"鼏";s:1:"N";s:4:"鼖";s:1:"N";s:4:"鼻";s:1:"N";s:4:"𪘀";s:1:"N";s:2:"̀";s:1:"M";s:2:"́";s:1:"M";s:2:"̂";s:1:"M";s:2:"̃";s:1:"M";s:2:"̄";s:1:"M";s:2:"̆";s:1:"M";s:2:"̇";s:1:"M";s:2:"̈";s:1:"M";s:2:"̉";s:1:"M";s:2:"̊";s:1:"M";s:2:"̋";s:1:"M";s:2:"̌";s:1:"M";s:2:"̏";s:1:"M";s:2:"̑";s:1:"M";s:2:"̓";s:1:"M";s:2:"̔";s:1:"M";s:2:"̛";s:1:"M";s:2:"̣";s:1:"M";s:2:"̤";s:1:"M";s:2:"̥";s:1:"M";s:2:"̦";s:1:"M";s:2:"̧";s:1:"M";s:2:"̨";s:1:"M";s:2:"̭";s:1:"M";s:2:"̮";s:1:"M";s:2:"̰";s:1:"M";s:2:"̱";s:1:"M";s:2:"̸";s:1:"M";s:2:"͂";s:1:"M";s:2:"ͅ";s:1:"M";s:2:"ٓ";s:1:"M";s:2:"ٔ";s:1:"M";s:2:"ٕ";s:1:"M";s:3:"़";s:1:"M";s:3:"া";s:1:"M";s:3:"ৗ";s:1:"M";s:3:"ା";s:1:"M";s:3:"ୖ";s:1:"M";s:3:"ୗ";s:1:"M";s:3:"ா";s:1:"M";s:3:"ௗ";s:1:"M";s:3:"ౖ";s:1:"M";s:3:"ೂ";s:1:"M";s:3:"ೕ";s:1:"M";s:3:"ೖ";s:1:"M";s:3:"ാ";s:1:"M";s:3:"ൗ";s:1:"M";s:3:"්";s:1:"M";s:3:"ා";s:1:"M";s:3:"ෟ";s:1:"M";s:3:"ီ";s:1:"M";s:3:"ᅡ";s:1:"M";s:3:"ᅢ";s:1:"M";s:3:"ᅣ";s:1:"M";s:3:"ᅤ";s:1:"M";s:3:"ᅥ";s:1:"M";s:3:"ᅦ";s:1:"M";s:3:"ᅧ";s:1:"M";s:3:"ᅨ";s:1:"M";s:3:"ᅩ";s:1:"M";s:3:"ᅪ";s:1:"M";s:3:"ᅫ";s:1:"M";s:3:"ᅬ";s:1:"M";s:3:"ᅭ";s:1:"M";s:3:"ᅮ";s:1:"M";s:3:"ᅯ";s:1:"M";s:3:"ᅰ";s:1:"M";s:3:"ᅱ";s:1:"M";s:3:"ᅲ";s:1:"M";s:3:"ᅳ";s:1:"M";s:3:"ᅴ";s:1:"M";s:3:"ᅵ";s:1:"M";s:3:"ᆨ";s:1:"M";s:3:"ᆩ";s:1:"M";s:3:"ᆪ";s:1:"M";s:3:"ᆫ";s:1:"M";s:3:"ᆬ";s:1:"M";s:3:"ᆭ";s:1:"M";s:3:"ᆮ";s:1:"M";s:3:"ᆯ";s:1:"M";s:3:"ᆰ";s:1:"M";s:3:"ᆱ";s:1:"M";s:3:"ᆲ";s:1:"M";s:3:"ᆳ";s:1:"M";s:3:"ᆴ";s:1:"M";s:3:"ᆵ";s:1:"M";s:3:"ᆶ";s:1:"M";s:3:"ᆷ";s:1:"M";s:3:"ᆸ";s:1:"M";s:3:"ᆹ";s:1:"M";s:3:"ᆺ";s:1:"M";s:3:"ᆻ";s:1:"M";s:3:"ᆼ";s:1:"M";s:3:"ᆽ";s:1:"M";s:3:"ᆾ";s:1:"M";s:3:"ᆿ";s:1:"M";s:3:"ᇀ";s:1:"M";s:3:"ᇁ";s:1:"M";s:3:"ᇂ";s:1:"M";s:3:"゙";s:1:"M";s:3:"゚";s:1:"M";}' );
+$utfCombiningClass = unserialize( 'a:418:{s:2:"̀";i:230;s:2:"́";i:230;s:2:"̂";i:230;s:2:"̃";i:230;s:2:"̄";i:230;s:2:"̅";i:230;s:2:"̆";i:230;s:2:"̇";i:230;s:2:"̈";i:230;s:2:"̉";i:230;s:2:"̊";i:230;s:2:"̋";i:230;s:2:"̌";i:230;s:2:"̍";i:230;s:2:"̎";i:230;s:2:"̏";i:230;s:2:"̐";i:230;s:2:"̑";i:230;s:2:"̒";i:230;s:2:"̓";i:230;s:2:"̔";i:230;s:2:"̕";i:232;s:2:"̖";i:220;s:2:"̗";i:220;s:2:"̘";i:220;s:2:"̙";i:220;s:2:"̚";i:232;s:2:"̛";i:216;s:2:"̜";i:220;s:2:"̝";i:220;s:2:"̞";i:220;s:2:"̟";i:220;s:2:"̠";i:220;s:2:"̡";i:202;s:2:"̢";i:202;s:2:"̣";i:220;s:2:"̤";i:220;s:2:"̥";i:220;s:2:"̦";i:220;s:2:"̧";i:202;s:2:"̨";i:202;s:2:"̩";i:220;s:2:"̪";i:220;s:2:"̫";i:220;s:2:"̬";i:220;s:2:"̭";i:220;s:2:"̮";i:220;s:2:"̯";i:220;s:2:"̰";i:220;s:2:"̱";i:220;s:2:"̲";i:220;s:2:"̳";i:220;s:2:"̴";i:1;s:2:"̵";i:1;s:2:"̶";i:1;s:2:"̷";i:1;s:2:"̸";i:1;s:2:"̹";i:220;s:2:"̺";i:220;s:2:"̻";i:220;s:2:"̼";i:220;s:2:"̽";i:230;s:2:"̾";i:230;s:2:"̿";i:230;s:2:"̀";i:230;s:2:"́";i:230;s:2:"͂";i:230;s:2:"̓";i:230;s:2:"̈́";i:230;s:2:"ͅ";i:240;s:2:"͆";i:230;s:2:"͇";i:220;s:2:"͈";i:220;s:2:"͉";i:220;s:2:"͊";i:230;s:2:"͋";i:230;s:2:"͌";i:230;s:2:"͍";i:220;s:2:"͎";i:220;s:2:"͐";i:230;s:2:"͑";i:230;s:2:"͒";i:230;s:2:"͓";i:220;s:2:"͔";i:220;s:2:"͕";i:220;s:2:"͖";i:220;s:2:"͗";i:230;s:2:"͘";i:232;s:2:"͙";i:220;s:2:"͚";i:220;s:2:"͛";i:230;s:2:"͜";i:233;s:2:"͝";i:234;s:2:"͞";i:234;s:2:"͟";i:233;s:2:"͠";i:234;s:2:"͡";i:234;s:2:"͢";i:233;s:2:"ͣ";i:230;s:2:"ͤ";i:230;s:2:"ͥ";i:230;s:2:"ͦ";i:230;s:2:"ͧ";i:230;s:2:"ͨ";i:230;s:2:"ͩ";i:230;s:2:"ͪ";i:230;s:2:"ͫ";i:230;s:2:"ͬ";i:230;s:2:"ͭ";i:230;s:2:"ͮ";i:230;s:2:"ͯ";i:230;s:2:"҃";i:230;s:2:"҄";i:230;s:2:"҅";i:230;s:2:"҆";i:230;s:2:"֑";i:220;s:2:"֒";i:230;s:2:"֓";i:230;s:2:"֔";i:230;s:2:"֕";i:230;s:2:"֖";i:220;s:2:"֗";i:230;s:2:"֘";i:230;s:2:"֙";i:230;s:2:"֚";i:222;s:2:"֛";i:220;s:2:"֜";i:230;s:2:"֝";i:230;s:2:"֞";i:230;s:2:"֟";i:230;s:2:"֠";i:230;s:2:"֡";i:230;s:2:"֢";i:220;s:2:"֣";i:220;s:2:"֤";i:220;s:2:"֥";i:220;s:2:"֦";i:220;s:2:"֧";i:220;s:2:"֨";i:230;s:2:"֩";i:230;s:2:"֪";i:220;s:2:"֫";i:230;s:2:"֬";i:230;s:2:"֭";i:222;s:2:"֮";i:228;s:2:"֯";i:230;s:2:"ְ";i:10;s:2:"ֱ";i:11;s:2:"ֲ";i:12;s:2:"ֳ";i:13;s:2:"ִ";i:14;s:2:"ֵ";i:15;s:2:"ֶ";i:16;s:2:"ַ";i:17;s:2:"ָ";i:18;s:2:"ֹ";i:19;s:2:"ֺ";i:19;s:2:"ֻ";i:20;s:2:"ּ";i:21;s:2:"ֽ";i:22;s:2:"ֿ";i:23;s:2:"ׁ";i:24;s:2:"ׂ";i:25;s:2:"ׄ";i:230;s:2:"ׅ";i:220;s:2:"ׇ";i:18;s:2:"ؐ";i:230;s:2:"ؑ";i:230;s:2:"ؒ";i:230;s:2:"ؓ";i:230;s:2:"ؔ";i:230;s:2:"ؕ";i:230;s:2:"ً";i:27;s:2:"ٌ";i:28;s:2:"ٍ";i:29;s:2:"َ";i:30;s:2:"ُ";i:31;s:2:"ِ";i:32;s:2:"ّ";i:33;s:2:"ْ";i:34;s:2:"ٓ";i:230;s:2:"ٔ";i:230;s:2:"ٕ";i:220;s:2:"ٖ";i:220;s:2:"ٗ";i:230;s:2:"٘";i:230;s:2:"ٙ";i:230;s:2:"ٚ";i:230;s:2:"ٛ";i:230;s:2:"ٜ";i:220;s:2:"ٝ";i:230;s:2:"ٞ";i:230;s:2:"ٰ";i:35;s:2:"ۖ";i:230;s:2:"ۗ";i:230;s:2:"ۘ";i:230;s:2:"ۙ";i:230;s:2:"ۚ";i:230;s:2:"ۛ";i:230;s:2:"ۜ";i:230;s:2:"۟";i:230;s:2:"۠";i:230;s:2:"ۡ";i:230;s:2:"ۢ";i:230;s:2:"ۣ";i:220;s:2:"ۤ";i:230;s:2:"ۧ";i:230;s:2:"ۨ";i:230;s:2:"۪";i:220;s:2:"۫";i:230;s:2:"۬";i:230;s:2:"ۭ";i:220;s:2:"ܑ";i:36;s:2:"ܰ";i:230;s:2:"ܱ";i:220;s:2:"ܲ";i:230;s:2:"ܳ";i:230;s:2:"ܴ";i:220;s:2:"ܵ";i:230;s:2:"ܶ";i:230;s:2:"ܷ";i:220;s:2:"ܸ";i:220;s:2:"ܹ";i:220;s:2:"ܺ";i:230;s:2:"ܻ";i:220;s:2:"ܼ";i:220;s:2:"ܽ";i:230;s:2:"ܾ";i:220;s:2:"ܿ";i:230;s:2:"݀";i:230;s:2:"݁";i:230;s:2:"݂";i:220;s:2:"݃";i:230;s:2:"݄";i:220;s:2:"݅";i:230;s:2:"݆";i:220;s:2:"݇";i:230;s:2:"݈";i:220;s:2:"݉";i:230;s:2:"݊";i:230;s:2:"߫";i:230;s:2:"߬";i:230;s:2:"߭";i:230;s:2:"߮";i:230;s:2:"߯";i:230;s:2:"߰";i:230;s:2:"߱";i:230;s:2:"߲";i:220;s:2:"߳";i:230;s:3:"़";i:7;s:3:"्";i:9;s:3:"॑";i:230;s:3:"॒";i:220;s:3:"॓";i:230;s:3:"॔";i:230;s:3:"়";i:7;s:3:"্";i:9;s:3:"਼";i:7;s:3:"੍";i:9;s:3:"઼";i:7;s:3:"્";i:9;s:3:"଼";i:7;s:3:"୍";i:9;s:3:"்";i:9;s:3:"్";i:9;s:3:"ౕ";i:84;s:3:"ౖ";i:91;s:3:"಼";i:7;s:3:"್";i:9;s:3:"്";i:9;s:3:"්";i:9;s:3:"ุ";i:103;s:3:"ู";i:103;s:3:"ฺ";i:9;s:3:"่";i:107;s:3:"้";i:107;s:3:"๊";i:107;s:3:"๋";i:107;s:3:"ຸ";i:118;s:3:"ູ";i:118;s:3:"່";i:122;s:3:"້";i:122;s:3:"໊";i:122;s:3:"໋";i:122;s:3:"༘";i:220;s:3:"༙";i:220;s:3:"༵";i:220;s:3:"༷";i:220;s:3:"༹";i:216;s:3:"ཱ";i:129;s:3:"ི";i:130;s:3:"ུ";i:132;s:3:"ེ";i:130;s:3:"ཻ";i:130;s:3:"ོ";i:130;s:3:"ཽ";i:130;s:3:"ྀ";i:130;s:3:"ྂ";i:230;s:3:"ྃ";i:230;s:3:"྄";i:9;s:3:"྆";i:230;s:3:"྇";i:230;s:3:"࿆";i:220;s:3:"့";i:7;s:3:"္";i:9;s:3:"፟";i:230;s:3:"᜔";i:9;s:3:"᜴";i:9;s:3:"្";i:9;s:3:"៝";i:230;s:3:"ᢩ";i:228;s:3:"᤹";i:222;s:3:"᤺";i:230;s:3:"᤻";i:220;s:3:"ᨗ";i:230;s:3:"ᨘ";i:220;s:3:"᬴";i:7;s:3:"᭄";i:9;s:3:"᭫";i:230;s:3:"᭬";i:220;s:3:"᭭";i:230;s:3:"᭮";i:230;s:3:"᭯";i:230;s:3:"᭰";i:230;s:3:"᭱";i:230;s:3:"᭲";i:230;s:3:"᭳";i:230;s:3:"᷀";i:230;s:3:"᷁";i:230;s:3:"᷂";i:220;s:3:"᷃";i:230;s:3:"᷄";i:230;s:3:"᷅";i:230;s:3:"᷆";i:230;s:3:"᷇";i:230;s:3:"᷈";i:230;s:3:"᷉";i:230;s:3:"᷊";i:220;s:3:"᷾";i:230;s:3:"᷿";i:220;s:3:"⃐";i:230;s:3:"⃑";i:230;s:3:"⃒";i:1;s:3:"⃓";i:1;s:3:"⃔";i:230;s:3:"⃕";i:230;s:3:"⃖";i:230;s:3:"⃗";i:230;s:3:"⃘";i:1;s:3:"⃙";i:1;s:3:"⃚";i:1;s:3:"⃛";i:230;s:3:"⃜";i:230;s:3:"⃡";i:230;s:3:"⃥";i:1;s:3:"⃦";i:1;s:3:"⃧";i:230;s:3:"⃨";i:220;s:3:"⃩";i:230;s:3:"⃪";i:1;s:3:"⃫";i:1;s:3:"⃬";i:220;s:3:"⃭";i:220;s:3:"⃮";i:220;s:3:"⃯";i:220;s:3:"〪";i:218;s:3:"〫";i:228;s:3:"〬";i:232;s:3:"〭";i:222;s:3:"〮";i:224;s:3:"〯";i:224;s:3:"゙";i:8;s:3:"゚";i:8;s:3:"꠆";i:9;s:3:"ﬞ";i:26;s:3:"︠";i:230;s:3:"︡";i:230;s:3:"︢";i:230;s:3:"︣";i:230;s:4:"𐨍";i:220;s:4:"𐨏";i:230;s:4:"𐨸";i:230;s:4:"𐨹";i:1;s:4:"𐨺";i:220;s:4:"𐨿";i:9;s:4:"𝅥";i:216;s:4:"𝅦";i:216;s:4:"𝅧";i:1;s:4:"𝅨";i:1;s:4:"𝅩";i:1;s:4:"𝅭";i:226;s:4:"𝅮";i:216;s:4:"𝅯";i:216;s:4:"𝅰";i:216;s:4:"𝅱";i:216;s:4:"𝅲";i:216;s:4:"𝅻";i:220;s:4:"𝅼";i:220;s:4:"𝅽";i:220;s:4:"𝅾";i:220;s:4:"𝅿";i:220;s:4:"𝆀";i:220;s:4:"𝆁";i:220;s:4:"𝆂";i:220;s:4:"𝆅";i:230;s:4:"𝆆";i:230;s:4:"𝆇";i:230;s:4:"𝆈";i:230;s:4:"𝆉";i:230;s:4:"𝆊";i:220;s:4:"𝆋";i:220;s:4:"𝆪";i:230;s:4:"𝆫";i:230;s:4:"𝆬";i:230;s:4:"𝆭";i:230;s:4:"𝉂";i:230;s:4:"𝉃";i:230;s:4:"𝉄";i:230;}' );
+$utfCanonicalComp = unserialize( 'a:1862:{s:3:"À";s:2:"À";s:3:"Á";s:2:"Á";s:3:"Â";s:2:"Â";s:3:"Ã";s:2:"Ã";s:3:"Ä";s:2:"Ä";s:3:"Å";s:2:"Å";s:3:"Ç";s:2:"Ç";s:3:"È";s:2:"È";s:3:"É";s:2:"É";s:3:"Ê";s:2:"Ê";s:3:"Ë";s:2:"Ë";s:3:"Ì";s:2:"Ì";s:3:"Í";s:2:"Í";s:3:"Î";s:2:"Î";s:3:"Ï";s:2:"Ï";s:3:"Ñ";s:2:"Ñ";s:3:"Ò";s:2:"Ò";s:3:"Ó";s:2:"Ó";s:3:"Ô";s:2:"Ô";s:3:"Õ";s:2:"Õ";s:3:"Ö";s:2:"Ö";s:3:"Ù";s:2:"Ù";s:3:"Ú";s:2:"Ú";s:3:"Û";s:2:"Û";s:3:"Ü";s:2:"Ü";s:3:"Ý";s:2:"Ý";s:3:"à";s:2:"à";s:3:"á";s:2:"á";s:3:"â";s:2:"â";s:3:"ã";s:2:"ã";s:3:"ä";s:2:"ä";s:3:"å";s:2:"å";s:3:"ç";s:2:"ç";s:3:"è";s:2:"è";s:3:"é";s:2:"é";s:3:"ê";s:2:"ê";s:3:"ë";s:2:"ë";s:3:"ì";s:2:"ì";s:3:"í";s:2:"í";s:3:"î";s:2:"î";s:3:"ï";s:2:"ï";s:3:"ñ";s:2:"ñ";s:3:"ò";s:2:"ò";s:3:"ó";s:2:"ó";s:3:"ô";s:2:"ô";s:3:"õ";s:2:"õ";s:3:"ö";s:2:"ö";s:3:"ù";s:2:"ù";s:3:"ú";s:2:"ú";s:3:"û";s:2:"û";s:3:"ü";s:2:"ü";s:3:"ý";s:2:"ý";s:3:"ÿ";s:2:"ÿ";s:3:"Ā";s:2:"Ā";s:3:"ā";s:2:"ā";s:3:"Ă";s:2:"Ă";s:3:"ă";s:2:"ă";s:3:"Ą";s:2:"Ą";s:3:"ą";s:2:"ą";s:3:"Ć";s:2:"Ć";s:3:"ć";s:2:"ć";s:3:"Ĉ";s:2:"Ĉ";s:3:"ĉ";s:2:"ĉ";s:3:"Ċ";s:2:"Ċ";s:3:"ċ";s:2:"ċ";s:3:"Č";s:2:"Č";s:3:"č";s:2:"č";s:3:"Ď";s:2:"Ď";s:3:"ď";s:2:"ď";s:3:"Ē";s:2:"Ē";s:3:"ē";s:2:"ē";s:3:"Ĕ";s:2:"Ĕ";s:3:"ĕ";s:2:"ĕ";s:3:"Ė";s:2:"Ė";s:3:"ė";s:2:"ė";s:3:"Ę";s:2:"Ę";s:3:"ę";s:2:"ę";s:3:"Ě";s:2:"Ě";s:3:"ě";s:2:"ě";s:3:"Ĝ";s:2:"Ĝ";s:3:"ĝ";s:2:"ĝ";s:3:"Ğ";s:2:"Ğ";s:3:"ğ";s:2:"ğ";s:3:"Ġ";s:2:"Ġ";s:3:"ġ";s:2:"ġ";s:3:"Ģ";s:2:"Ģ";s:3:"ģ";s:2:"ģ";s:3:"Ĥ";s:2:"Ĥ";s:3:"ĥ";s:2:"ĥ";s:3:"Ĩ";s:2:"Ĩ";s:3:"ĩ";s:2:"ĩ";s:3:"Ī";s:2:"Ī";s:3:"ī";s:2:"ī";s:3:"Ĭ";s:2:"Ĭ";s:3:"ĭ";s:2:"ĭ";s:3:"Į";s:2:"Į";s:3:"į";s:2:"į";s:3:"İ";s:2:"İ";s:3:"Ĵ";s:2:"Ĵ";s:3:"ĵ";s:2:"ĵ";s:3:"Ķ";s:2:"Ķ";s:3:"ķ";s:2:"ķ";s:3:"Ĺ";s:2:"Ĺ";s:3:"ĺ";s:2:"ĺ";s:3:"Ļ";s:2:"Ļ";s:3:"ļ";s:2:"ļ";s:3:"Ľ";s:2:"Ľ";s:3:"ľ";s:2:"ľ";s:3:"Ń";s:2:"Ń";s:3:"ń";s:2:"ń";s:3:"Ņ";s:2:"Ņ";s:3:"ņ";s:2:"ņ";s:3:"Ň";s:2:"Ň";s:3:"ň";s:2:"ň";s:3:"Ō";s:2:"Ō";s:3:"ō";s:2:"ō";s:3:"Ŏ";s:2:"Ŏ";s:3:"ŏ";s:2:"ŏ";s:3:"Ő";s:2:"Ő";s:3:"ő";s:2:"ő";s:3:"Ŕ";s:2:"Ŕ";s:3:"ŕ";s:2:"ŕ";s:3:"Ŗ";s:2:"Ŗ";s:3:"ŗ";s:2:"ŗ";s:3:"Ř";s:2:"Ř";s:3:"ř";s:2:"ř";s:3:"Ś";s:2:"Ś";s:3:"ś";s:2:"ś";s:3:"Ŝ";s:2:"Ŝ";s:3:"ŝ";s:2:"ŝ";s:3:"Ş";s:2:"Ş";s:3:"ş";s:2:"ş";s:3:"Š";s:2:"Š";s:3:"š";s:2:"š";s:3:"Ţ";s:2:"Ţ";s:3:"ţ";s:2:"ţ";s:3:"Ť";s:2:"Ť";s:3:"ť";s:2:"ť";s:3:"Ũ";s:2:"Ũ";s:3:"ũ";s:2:"ũ";s:3:"Ū";s:2:"Ū";s:3:"ū";s:2:"ū";s:3:"Ŭ";s:2:"Ŭ";s:3:"ŭ";s:2:"ŭ";s:3:"Ů";s:2:"Ů";s:3:"ů";s:2:"ů";s:3:"Ű";s:2:"Ű";s:3:"ű";s:2:"ű";s:3:"Ų";s:2:"Ų";s:3:"ų";s:2:"ų";s:3:"Ŵ";s:2:"Ŵ";s:3:"ŵ";s:2:"ŵ";s:3:"Ŷ";s:2:"Ŷ";s:3:"ŷ";s:2:"ŷ";s:3:"Ÿ";s:2:"Ÿ";s:3:"Ź";s:2:"Ź";s:3:"ź";s:2:"ź";s:3:"Ż";s:2:"Ż";s:3:"ż";s:2:"ż";s:3:"Ž";s:2:"Ž";s:3:"ž";s:2:"ž";s:3:"Ơ";s:2:"Ơ";s:3:"ơ";s:2:"ơ";s:3:"Ư";s:2:"Ư";s:3:"ư";s:2:"ư";s:3:"Ǎ";s:2:"Ǎ";s:3:"ǎ";s:2:"ǎ";s:3:"Ǐ";s:2:"Ǐ";s:3:"ǐ";s:2:"ǐ";s:3:"Ǒ";s:2:"Ǒ";s:3:"ǒ";s:2:"ǒ";s:3:"Ǔ";s:2:"Ǔ";s:3:"ǔ";s:2:"ǔ";s:4:"Ǖ";s:2:"Ǖ";s:4:"ǖ";s:2:"ǖ";s:4:"Ǘ";s:2:"Ǘ";s:4:"ǘ";s:2:"ǘ";s:4:"Ǚ";s:2:"Ǚ";s:4:"ǚ";s:2:"ǚ";s:4:"Ǜ";s:2:"Ǜ";s:4:"ǜ";s:2:"ǜ";s:4:"Ǟ";s:2:"Ǟ";s:4:"ǟ";s:2:"ǟ";s:4:"Ǡ";s:2:"Ǡ";s:4:"ǡ";s:2:"ǡ";s:4:"Ǣ";s:2:"Ǣ";s:4:"ǣ";s:2:"ǣ";s:3:"Ǧ";s:2:"Ǧ";s:3:"ǧ";s:2:"ǧ";s:3:"Ǩ";s:2:"Ǩ";s:3:"ǩ";s:2:"ǩ";s:3:"Ǫ";s:2:"Ǫ";s:3:"ǫ";s:2:"ǫ";s:4:"Ǭ";s:2:"Ǭ";s:4:"ǭ";s:2:"ǭ";s:4:"Ǯ";s:2:"Ǯ";s:4:"ǯ";s:2:"ǯ";s:3:"ǰ";s:2:"ǰ";s:3:"Ǵ";s:2:"Ǵ";s:3:"ǵ";s:2:"ǵ";s:3:"Ǹ";s:2:"Ǹ";s:3:"ǹ";s:2:"ǹ";s:4:"Ǻ";s:2:"Ǻ";s:4:"ǻ";s:2:"ǻ";s:4:"Ǽ";s:2:"Ǽ";s:4:"ǽ";s:2:"ǽ";s:4:"Ǿ";s:2:"Ǿ";s:4:"ǿ";s:2:"ǿ";s:3:"Ȁ";s:2:"Ȁ";s:3:"ȁ";s:2:"ȁ";s:3:"Ȃ";s:2:"Ȃ";s:3:"ȃ";s:2:"ȃ";s:3:"Ȅ";s:2:"Ȅ";s:3:"ȅ";s:2:"ȅ";s:3:"Ȇ";s:2:"Ȇ";s:3:"ȇ";s:2:"ȇ";s:3:"Ȉ";s:2:"Ȉ";s:3:"ȉ";s:2:"ȉ";s:3:"Ȋ";s:2:"Ȋ";s:3:"ȋ";s:2:"ȋ";s:3:"Ȍ";s:2:"Ȍ";s:3:"ȍ";s:2:"ȍ";s:3:"Ȏ";s:2:"Ȏ";s:3:"ȏ";s:2:"ȏ";s:3:"Ȑ";s:2:"Ȑ";s:3:"ȑ";s:2:"ȑ";s:3:"Ȓ";s:2:"Ȓ";s:3:"ȓ";s:2:"ȓ";s:3:"Ȕ";s:2:"Ȕ";s:3:"ȕ";s:2:"ȕ";s:3:"Ȗ";s:2:"Ȗ";s:3:"ȗ";s:2:"ȗ";s:3:"Ș";s:2:"Ș";s:3:"ș";s:2:"ș";s:3:"Ț";s:2:"Ț";s:3:"ț";s:2:"ț";s:3:"Ȟ";s:2:"Ȟ";s:3:"ȟ";s:2:"ȟ";s:3:"Ȧ";s:2:"Ȧ";s:3:"ȧ";s:2:"ȧ";s:3:"Ȩ";s:2:"Ȩ";s:3:"ȩ";s:2:"ȩ";s:4:"Ȫ";s:2:"Ȫ";s:4:"ȫ";s:2:"ȫ";s:4:"Ȭ";s:2:"Ȭ";s:4:"ȭ";s:2:"ȭ";s:3:"Ȯ";s:2:"Ȯ";s:3:"ȯ";s:2:"ȯ";s:4:"Ȱ";s:2:"Ȱ";s:4:"ȱ";s:2:"ȱ";s:3:"Ȳ";s:2:"Ȳ";s:3:"ȳ";s:2:"ȳ";s:2:"̀";s:2:"̀";s:2:"́";s:2:"́";s:2:"̓";s:2:"̓";s:4:"̈́";s:2:"̈́";s:2:"ʹ";s:2:"ʹ";s:1:";";s:2:";";s:4:"΅";s:2:"΅";s:4:"Ά";s:2:"Ά";s:2:"·";s:2:"·";s:4:"Έ";s:2:"Έ";s:4:"Ή";s:2:"Ή";s:4:"Ί";s:2:"Ί";s:4:"Ό";s:2:"Ό";s:4:"Ύ";s:2:"Ύ";s:4:"Ώ";s:2:"Ώ";s:4:"ΐ";s:2:"ΐ";s:4:"Ϊ";s:2:"Ϊ";s:4:"Ϋ";s:2:"Ϋ";s:4:"ά";s:2:"ά";s:4:"έ";s:2:"έ";s:4:"ή";s:2:"ή";s:4:"ί";s:2:"ί";s:4:"ΰ";s:2:"ΰ";s:4:"ϊ";s:2:"ϊ";s:4:"ϋ";s:2:"ϋ";s:4:"ό";s:2:"ό";s:4:"ύ";s:2:"ύ";s:4:"ώ";s:2:"ώ";s:4:"ϓ";s:2:"ϓ";s:4:"ϔ";s:2:"ϔ";s:4:"Ѐ";s:2:"Ѐ";s:4:"Ё";s:2:"Ё";s:4:"Ѓ";s:2:"Ѓ";s:4:"Ї";s:2:"Ї";s:4:"Ќ";s:2:"Ќ";s:4:"Ѝ";s:2:"Ѝ";s:4:"Ў";s:2:"Ў";s:4:"Й";s:2:"Й";s:4:"й";s:2:"й";s:4:"ѐ";s:2:"ѐ";s:4:"ё";s:2:"ё";s:4:"ѓ";s:2:"ѓ";s:4:"ї";s:2:"ї";s:4:"ќ";s:2:"ќ";s:4:"ѝ";s:2:"ѝ";s:4:"ў";s:2:"ў";s:4:"Ѷ";s:2:"Ѷ";s:4:"ѷ";s:2:"ѷ";s:4:"Ӂ";s:2:"Ӂ";s:4:"ӂ";s:2:"ӂ";s:4:"Ӑ";s:2:"Ӑ";s:4:"ӑ";s:2:"ӑ";s:4:"Ӓ";s:2:"Ӓ";s:4:"ӓ";s:2:"ӓ";s:4:"Ӗ";s:2:"Ӗ";s:4:"ӗ";s:2:"ӗ";s:4:"Ӛ";s:2:"Ӛ";s:4:"ӛ";s:2:"ӛ";s:4:"Ӝ";s:2:"Ӝ";s:4:"ӝ";s:2:"ӝ";s:4:"Ӟ";s:2:"Ӟ";s:4:"ӟ";s:2:"ӟ";s:4:"Ӣ";s:2:"Ӣ";s:4:"ӣ";s:2:"ӣ";s:4:"Ӥ";s:2:"Ӥ";s:4:"ӥ";s:2:"ӥ";s:4:"Ӧ";s:2:"Ӧ";s:4:"ӧ";s:2:"ӧ";s:4:"Ӫ";s:2:"Ӫ";s:4:"ӫ";s:2:"ӫ";s:4:"Ӭ";s:2:"Ӭ";s:4:"ӭ";s:2:"ӭ";s:4:"Ӯ";s:2:"Ӯ";s:4:"ӯ";s:2:"ӯ";s:4:"Ӱ";s:2:"Ӱ";s:4:"ӱ";s:2:"ӱ";s:4:"Ӳ";s:2:"Ӳ";s:4:"ӳ";s:2:"ӳ";s:4:"Ӵ";s:2:"Ӵ";s:4:"ӵ";s:2:"ӵ";s:4:"Ӹ";s:2:"Ӹ";s:4:"ӹ";s:2:"ӹ";s:4:"آ";s:2:"آ";s:4:"أ";s:2:"أ";s:4:"ؤ";s:2:"ؤ";s:4:"إ";s:2:"إ";s:4:"ئ";s:2:"ئ";s:4:"ۀ";s:2:"ۀ";s:4:"ۂ";s:2:"ۂ";s:4:"ۓ";s:2:"ۓ";s:6:"ऩ";s:3:"ऩ";s:6:"ऱ";s:3:"ऱ";s:6:"ऴ";s:3:"ऴ";s:6:"ো";s:3:"ো";s:6:"ৌ";s:3:"ৌ";s:6:"ୈ";s:3:"ୈ";s:6:"ୋ";s:3:"ୋ";s:6:"ୌ";s:3:"ୌ";s:6:"ஔ";s:3:"ஔ";s:6:"ொ";s:3:"ொ";s:6:"ோ";s:3:"ோ";s:6:"ௌ";s:3:"ௌ";s:6:"ై";s:3:"ై";s:6:"ೀ";s:3:"ೀ";s:6:"ೇ";s:3:"ೇ";s:6:"ೈ";s:3:"ೈ";s:6:"ೊ";s:3:"ೊ";s:6:"ೋ";s:3:"ೋ";s:6:"ൊ";s:3:"ൊ";s:6:"ോ";s:3:"ോ";s:6:"ൌ";s:3:"ൌ";s:6:"ේ";s:3:"ේ";s:6:"ො";s:3:"ො";s:6:"ෝ";s:3:"ෝ";s:6:"ෞ";s:3:"ෞ";s:6:"ཱི";s:3:"ཱི";s:6:"ཱུ";s:3:"ཱུ";s:6:"ཱྀ";s:3:"ཱྀ";s:6:"ဦ";s:3:"ဦ";s:6:"ᬆ";s:3:"ᬆ";s:6:"ᬈ";s:3:"ᬈ";s:6:"ᬊ";s:3:"ᬊ";s:6:"ᬌ";s:3:"ᬌ";s:6:"ᬎ";s:3:"ᬎ";s:6:"ᬒ";s:3:"ᬒ";s:6:"ᬻ";s:3:"ᬻ";s:6:"ᬽ";s:3:"ᬽ";s:6:"ᭀ";s:3:"ᭀ";s:6:"ᭁ";s:3:"ᭁ";s:6:"ᭃ";s:3:"ᭃ";s:3:"Ḁ";s:3:"Ḁ";s:3:"ḁ";s:3:"ḁ";s:3:"Ḃ";s:3:"Ḃ";s:3:"ḃ";s:3:"ḃ";s:3:"Ḅ";s:3:"Ḅ";s:3:"ḅ";s:3:"ḅ";s:3:"Ḇ";s:3:"Ḇ";s:3:"ḇ";s:3:"ḇ";s:4:"Ḉ";s:3:"Ḉ";s:4:"ḉ";s:3:"ḉ";s:3:"Ḋ";s:3:"Ḋ";s:3:"ḋ";s:3:"ḋ";s:3:"Ḍ";s:3:"Ḍ";s:3:"ḍ";s:3:"ḍ";s:3:"Ḏ";s:3:"Ḏ";s:3:"ḏ";s:3:"ḏ";s:3:"Ḑ";s:3:"Ḑ";s:3:"ḑ";s:3:"ḑ";s:3:"Ḓ";s:3:"Ḓ";s:3:"ḓ";s:3:"ḓ";s:4:"Ḕ";s:3:"Ḕ";s:4:"ḕ";s:3:"ḕ";s:4:"Ḗ";s:3:"Ḗ";s:4:"ḗ";s:3:"ḗ";s:3:"Ḙ";s:3:"Ḙ";s:3:"ḙ";s:3:"ḙ";s:3:"Ḛ";s:3:"Ḛ";s:3:"ḛ";s:3:"ḛ";s:4:"Ḝ";s:3:"Ḝ";s:4:"ḝ";s:3:"ḝ";s:3:"Ḟ";s:3:"Ḟ";s:3:"ḟ";s:3:"ḟ";s:3:"Ḡ";s:3:"Ḡ";s:3:"ḡ";s:3:"ḡ";s:3:"Ḣ";s:3:"Ḣ";s:3:"ḣ";s:3:"ḣ";s:3:"Ḥ";s:3:"Ḥ";s:3:"ḥ";s:3:"ḥ";s:3:"Ḧ";s:3:"Ḧ";s:3:"ḧ";s:3:"ḧ";s:3:"Ḩ";s:3:"Ḩ";s:3:"ḩ";s:3:"ḩ";s:3:"Ḫ";s:3:"Ḫ";s:3:"ḫ";s:3:"ḫ";s:3:"Ḭ";s:3:"Ḭ";s:3:"ḭ";s:3:"ḭ";s:4:"Ḯ";s:3:"Ḯ";s:4:"ḯ";s:3:"ḯ";s:3:"Ḱ";s:3:"Ḱ";s:3:"ḱ";s:3:"ḱ";s:3:"Ḳ";s:3:"Ḳ";s:3:"ḳ";s:3:"ḳ";s:3:"Ḵ";s:3:"Ḵ";s:3:"ḵ";s:3:"ḵ";s:3:"Ḷ";s:3:"Ḷ";s:3:"ḷ";s:3:"ḷ";s:5:"Ḹ";s:3:"Ḹ";s:5:"ḹ";s:3:"ḹ";s:3:"Ḻ";s:3:"Ḻ";s:3:"ḻ";s:3:"ḻ";s:3:"Ḽ";s:3:"Ḽ";s:3:"ḽ";s:3:"ḽ";s:3:"Ḿ";s:3:"Ḿ";s:3:"ḿ";s:3:"ḿ";s:3:"Ṁ";s:3:"Ṁ";s:3:"ṁ";s:3:"ṁ";s:3:"Ṃ";s:3:"Ṃ";s:3:"ṃ";s:3:"ṃ";s:3:"Ṅ";s:3:"Ṅ";s:3:"ṅ";s:3:"ṅ";s:3:"Ṇ";s:3:"Ṇ";s:3:"ṇ";s:3:"ṇ";s:3:"Ṉ";s:3:"Ṉ";s:3:"ṉ";s:3:"ṉ";s:3:"Ṋ";s:3:"Ṋ";s:3:"ṋ";s:3:"ṋ";s:4:"Ṍ";s:3:"Ṍ";s:4:"ṍ";s:3:"ṍ";s:4:"Ṏ";s:3:"Ṏ";s:4:"ṏ";s:3:"ṏ";s:4:"Ṑ";s:3:"Ṑ";s:4:"ṑ";s:3:"ṑ";s:4:"Ṓ";s:3:"Ṓ";s:4:"ṓ";s:3:"ṓ";s:3:"Ṕ";s:3:"Ṕ";s:3:"ṕ";s:3:"ṕ";s:3:"Ṗ";s:3:"Ṗ";s:3:"ṗ";s:3:"ṗ";s:3:"Ṙ";s:3:"Ṙ";s:3:"ṙ";s:3:"ṙ";s:3:"Ṛ";s:3:"Ṛ";s:3:"ṛ";s:3:"ṛ";s:5:"Ṝ";s:3:"Ṝ";s:5:"ṝ";s:3:"ṝ";s:3:"Ṟ";s:3:"Ṟ";s:3:"ṟ";s:3:"ṟ";s:3:"Ṡ";s:3:"Ṡ";s:3:"ṡ";s:3:"ṡ";s:3:"Ṣ";s:3:"Ṣ";s:3:"ṣ";s:3:"ṣ";s:4:"Ṥ";s:3:"Ṥ";s:4:"ṥ";s:3:"ṥ";s:4:"Ṧ";s:3:"Ṧ";s:4:"ṧ";s:3:"ṧ";s:5:"Ṩ";s:3:"Ṩ";s:5:"ṩ";s:3:"ṩ";s:3:"Ṫ";s:3:"Ṫ";s:3:"ṫ";s:3:"ṫ";s:3:"Ṭ";s:3:"Ṭ";s:3:"ṭ";s:3:"ṭ";s:3:"Ṯ";s:3:"Ṯ";s:3:"ṯ";s:3:"ṯ";s:3:"Ṱ";s:3:"Ṱ";s:3:"ṱ";s:3:"ṱ";s:3:"Ṳ";s:3:"Ṳ";s:3:"ṳ";s:3:"ṳ";s:3:"Ṵ";s:3:"Ṵ";s:3:"ṵ";s:3:"ṵ";s:3:"Ṷ";s:3:"Ṷ";s:3:"ṷ";s:3:"ṷ";s:4:"Ṹ";s:3:"Ṹ";s:4:"ṹ";s:3:"ṹ";s:4:"Ṻ";s:3:"Ṻ";s:4:"ṻ";s:3:"ṻ";s:3:"Ṽ";s:3:"Ṽ";s:3:"ṽ";s:3:"ṽ";s:3:"Ṿ";s:3:"Ṿ";s:3:"ṿ";s:3:"ṿ";s:3:"Ẁ";s:3:"Ẁ";s:3:"ẁ";s:3:"ẁ";s:3:"Ẃ";s:3:"Ẃ";s:3:"ẃ";s:3:"ẃ";s:3:"Ẅ";s:3:"Ẅ";s:3:"ẅ";s:3:"ẅ";s:3:"Ẇ";s:3:"Ẇ";s:3:"ẇ";s:3:"ẇ";s:3:"Ẉ";s:3:"Ẉ";s:3:"ẉ";s:3:"ẉ";s:3:"Ẋ";s:3:"Ẋ";s:3:"ẋ";s:3:"ẋ";s:3:"Ẍ";s:3:"Ẍ";s:3:"ẍ";s:3:"ẍ";s:3:"Ẏ";s:3:"Ẏ";s:3:"ẏ";s:3:"ẏ";s:3:"Ẑ";s:3:"Ẑ";s:3:"ẑ";s:3:"ẑ";s:3:"Ẓ";s:3:"Ẓ";s:3:"ẓ";s:3:"ẓ";s:3:"Ẕ";s:3:"Ẕ";s:3:"ẕ";s:3:"ẕ";s:3:"ẖ";s:3:"ẖ";s:3:"ẗ";s:3:"ẗ";s:3:"ẘ";s:3:"ẘ";s:3:"ẙ";s:3:"ẙ";s:4:"ẛ";s:3:"ẛ";s:3:"Ạ";s:3:"Ạ";s:3:"ạ";s:3:"ạ";s:3:"Ả";s:3:"Ả";s:3:"ả";s:3:"ả";s:4:"Ấ";s:3:"Ấ";s:4:"ấ";s:3:"ấ";s:4:"Ầ";s:3:"Ầ";s:4:"ầ";s:3:"ầ";s:4:"Ẩ";s:3:"Ẩ";s:4:"ẩ";s:3:"ẩ";s:4:"Ẫ";s:3:"Ẫ";s:4:"ẫ";s:3:"ẫ";s:5:"Ậ";s:3:"Ậ";s:5:"ậ";s:3:"ậ";s:4:"Ắ";s:3:"Ắ";s:4:"ắ";s:3:"ắ";s:4:"Ằ";s:3:"Ằ";s:4:"ằ";s:3:"ằ";s:4:"Ẳ";s:3:"Ẳ";s:4:"ẳ";s:3:"ẳ";s:4:"Ẵ";s:3:"Ẵ";s:4:"ẵ";s:3:"ẵ";s:5:"Ặ";s:3:"Ặ";s:5:"ặ";s:3:"ặ";s:3:"Ẹ";s:3:"Ẹ";s:3:"ẹ";s:3:"ẹ";s:3:"Ẻ";s:3:"Ẻ";s:3:"ẻ";s:3:"ẻ";s:3:"Ẽ";s:3:"Ẽ";s:3:"ẽ";s:3:"ẽ";s:4:"Ế";s:3:"Ế";s:4:"ế";s:3:"ế";s:4:"Ề";s:3:"Ề";s:4:"ề";s:3:"ề";s:4:"Ể";s:3:"Ể";s:4:"ể";s:3:"ể";s:4:"Ễ";s:3:"Ễ";s:4:"ễ";s:3:"ễ";s:5:"Ệ";s:3:"Ệ";s:5:"ệ";s:3:"ệ";s:3:"Ỉ";s:3:"Ỉ";s:3:"ỉ";s:3:"ỉ";s:3:"Ị";s:3:"Ị";s:3:"ị";s:3:"ị";s:3:"Ọ";s:3:"Ọ";s:3:"ọ";s:3:"ọ";s:3:"Ỏ";s:3:"Ỏ";s:3:"ỏ";s:3:"ỏ";s:4:"Ố";s:3:"Ố";s:4:"ố";s:3:"ố";s:4:"Ồ";s:3:"Ồ";s:4:"ồ";s:3:"ồ";s:4:"Ổ";s:3:"Ổ";s:4:"ổ";s:3:"ổ";s:4:"Ỗ";s:3:"Ỗ";s:4:"ỗ";s:3:"ỗ";s:5:"Ộ";s:3:"Ộ";s:5:"ộ";s:3:"ộ";s:4:"Ớ";s:3:"Ớ";s:4:"ớ";s:3:"ớ";s:4:"Ờ";s:3:"Ờ";s:4:"ờ";s:3:"ờ";s:4:"Ở";s:3:"Ở";s:4:"ở";s:3:"ở";s:4:"Ỡ";s:3:"Ỡ";s:4:"ỡ";s:3:"ỡ";s:4:"Ợ";s:3:"Ợ";s:4:"ợ";s:3:"ợ";s:3:"Ụ";s:3:"Ụ";s:3:"ụ";s:3:"ụ";s:3:"Ủ";s:3:"Ủ";s:3:"ủ";s:3:"ủ";s:4:"Ứ";s:3:"Ứ";s:4:"ứ";s:3:"ứ";s:4:"Ừ";s:3:"Ừ";s:4:"ừ";s:3:"ừ";s:4:"Ử";s:3:"Ử";s:4:"ử";s:3:"ử";s:4:"Ữ";s:3:"Ữ";s:4:"ữ";s:3:"ữ";s:4:"Ự";s:3:"Ự";s:4:"ự";s:3:"ự";s:3:"Ỳ";s:3:"Ỳ";s:3:"ỳ";s:3:"ỳ";s:3:"Ỵ";s:3:"Ỵ";s:3:"ỵ";s:3:"ỵ";s:3:"Ỷ";s:3:"Ỷ";s:3:"ỷ";s:3:"ỷ";s:3:"Ỹ";s:3:"Ỹ";s:3:"ỹ";s:3:"ỹ";s:4:"ἀ";s:3:"ἀ";s:4:"ἁ";s:3:"ἁ";s:5:"ἂ";s:3:"ἂ";s:5:"ἃ";s:3:"ἃ";s:5:"ἄ";s:3:"ἄ";s:5:"ἅ";s:3:"ἅ";s:5:"ἆ";s:3:"ἆ";s:5:"ἇ";s:3:"ἇ";s:4:"Ἀ";s:3:"Ἀ";s:4:"Ἁ";s:3:"Ἁ";s:5:"Ἂ";s:3:"Ἂ";s:5:"Ἃ";s:3:"Ἃ";s:5:"Ἄ";s:3:"Ἄ";s:5:"Ἅ";s:3:"Ἅ";s:5:"Ἆ";s:3:"Ἆ";s:5:"Ἇ";s:3:"Ἇ";s:4:"ἐ";s:3:"ἐ";s:4:"ἑ";s:3:"ἑ";s:5:"ἒ";s:3:"ἒ";s:5:"ἓ";s:3:"ἓ";s:5:"ἔ";s:3:"ἔ";s:5:"ἕ";s:3:"ἕ";s:4:"Ἐ";s:3:"Ἐ";s:4:"Ἑ";s:3:"Ἑ";s:5:"Ἒ";s:3:"Ἒ";s:5:"Ἓ";s:3:"Ἓ";s:5:"Ἔ";s:3:"Ἔ";s:5:"Ἕ";s:3:"Ἕ";s:4:"ἠ";s:3:"ἠ";s:4:"ἡ";s:3:"ἡ";s:5:"ἢ";s:3:"ἢ";s:5:"ἣ";s:3:"ἣ";s:5:"ἤ";s:3:"ἤ";s:5:"ἥ";s:3:"ἥ";s:5:"ἦ";s:3:"ἦ";s:5:"ἧ";s:3:"ἧ";s:4:"Ἠ";s:3:"Ἠ";s:4:"Ἡ";s:3:"Ἡ";s:5:"Ἢ";s:3:"Ἢ";s:5:"Ἣ";s:3:"Ἣ";s:5:"Ἤ";s:3:"Ἤ";s:5:"Ἥ";s:3:"Ἥ";s:5:"Ἦ";s:3:"Ἦ";s:5:"Ἧ";s:3:"Ἧ";s:4:"ἰ";s:3:"ἰ";s:4:"ἱ";s:3:"ἱ";s:5:"ἲ";s:3:"ἲ";s:5:"ἳ";s:3:"ἳ";s:5:"ἴ";s:3:"ἴ";s:5:"ἵ";s:3:"ἵ";s:5:"ἶ";s:3:"ἶ";s:5:"ἷ";s:3:"ἷ";s:4:"Ἰ";s:3:"Ἰ";s:4:"Ἱ";s:3:"Ἱ";s:5:"Ἲ";s:3:"Ἲ";s:5:"Ἳ";s:3:"Ἳ";s:5:"Ἴ";s:3:"Ἴ";s:5:"Ἵ";s:3:"Ἵ";s:5:"Ἶ";s:3:"Ἶ";s:5:"Ἷ";s:3:"Ἷ";s:4:"ὀ";s:3:"ὀ";s:4:"ὁ";s:3:"ὁ";s:5:"ὂ";s:3:"ὂ";s:5:"ὃ";s:3:"ὃ";s:5:"ὄ";s:3:"ὄ";s:5:"ὅ";s:3:"ὅ";s:4:"Ὀ";s:3:"Ὀ";s:4:"Ὁ";s:3:"Ὁ";s:5:"Ὂ";s:3:"Ὂ";s:5:"Ὃ";s:3:"Ὃ";s:5:"Ὄ";s:3:"Ὄ";s:5:"Ὅ";s:3:"Ὅ";s:4:"ὐ";s:3:"ὐ";s:4:"ὑ";s:3:"ὑ";s:5:"ὒ";s:3:"ὒ";s:5:"ὓ";s:3:"ὓ";s:5:"ὔ";s:3:"ὔ";s:5:"ὕ";s:3:"ὕ";s:5:"ὖ";s:3:"ὖ";s:5:"ὗ";s:3:"ὗ";s:4:"Ὑ";s:3:"Ὑ";s:5:"Ὓ";s:3:"Ὓ";s:5:"Ὕ";s:3:"Ὕ";s:5:"Ὗ";s:3:"Ὗ";s:4:"ὠ";s:3:"ὠ";s:4:"ὡ";s:3:"ὡ";s:5:"ὢ";s:3:"ὢ";s:5:"ὣ";s:3:"ὣ";s:5:"ὤ";s:3:"ὤ";s:5:"ὥ";s:3:"ὥ";s:5:"ὦ";s:3:"ὦ";s:5:"ὧ";s:3:"ὧ";s:4:"Ὠ";s:3:"Ὠ";s:4:"Ὡ";s:3:"Ὡ";s:5:"Ὢ";s:3:"Ὢ";s:5:"Ὣ";s:3:"Ὣ";s:5:"Ὤ";s:3:"Ὤ";s:5:"Ὥ";s:3:"Ὥ";s:5:"Ὦ";s:3:"Ὦ";s:5:"Ὧ";s:3:"Ὧ";s:4:"ὰ";s:3:"ὰ";s:2:"ά";s:3:"ά";s:4:"ὲ";s:3:"ὲ";s:2:"έ";s:3:"έ";s:4:"ὴ";s:3:"ὴ";s:2:"ή";s:3:"ή";s:4:"ὶ";s:3:"ὶ";s:2:"ί";s:3:"ί";s:4:"ὸ";s:3:"ὸ";s:2:"ό";s:3:"ό";s:4:"ὺ";s:3:"ὺ";s:2:"ύ";s:3:"ύ";s:4:"ὼ";s:3:"ὼ";s:2:"ώ";s:3:"ώ";s:5:"ᾀ";s:3:"ᾀ";s:5:"ᾁ";s:3:"ᾁ";s:5:"ᾂ";s:3:"ᾂ";s:5:"ᾃ";s:3:"ᾃ";s:5:"ᾄ";s:3:"ᾄ";s:5:"ᾅ";s:3:"ᾅ";s:5:"ᾆ";s:3:"ᾆ";s:5:"ᾇ";s:3:"ᾇ";s:5:"ᾈ";s:3:"ᾈ";s:5:"ᾉ";s:3:"ᾉ";s:5:"ᾊ";s:3:"ᾊ";s:5:"ᾋ";s:3:"ᾋ";s:5:"ᾌ";s:3:"ᾌ";s:5:"ᾍ";s:3:"ᾍ";s:5:"ᾎ";s:3:"ᾎ";s:5:"ᾏ";s:3:"ᾏ";s:5:"ᾐ";s:3:"ᾐ";s:5:"ᾑ";s:3:"ᾑ";s:5:"ᾒ";s:3:"ᾒ";s:5:"ᾓ";s:3:"ᾓ";s:5:"ᾔ";s:3:"ᾔ";s:5:"ᾕ";s:3:"ᾕ";s:5:"ᾖ";s:3:"ᾖ";s:5:"ᾗ";s:3:"ᾗ";s:5:"ᾘ";s:3:"ᾘ";s:5:"ᾙ";s:3:"ᾙ";s:5:"ᾚ";s:3:"ᾚ";s:5:"ᾛ";s:3:"ᾛ";s:5:"ᾜ";s:3:"ᾜ";s:5:"ᾝ";s:3:"ᾝ";s:5:"ᾞ";s:3:"ᾞ";s:5:"ᾟ";s:3:"ᾟ";s:5:"ᾠ";s:3:"ᾠ";s:5:"ᾡ";s:3:"ᾡ";s:5:"ᾢ";s:3:"ᾢ";s:5:"ᾣ";s:3:"ᾣ";s:5:"ᾤ";s:3:"ᾤ";s:5:"ᾥ";s:3:"ᾥ";s:5:"ᾦ";s:3:"ᾦ";s:5:"ᾧ";s:3:"ᾧ";s:5:"ᾨ";s:3:"ᾨ";s:5:"ᾩ";s:3:"ᾩ";s:5:"ᾪ";s:3:"ᾪ";s:5:"ᾫ";s:3:"ᾫ";s:5:"ᾬ";s:3:"ᾬ";s:5:"ᾭ";s:3:"ᾭ";s:5:"ᾮ";s:3:"ᾮ";s:5:"ᾯ";s:3:"ᾯ";s:4:"ᾰ";s:3:"ᾰ";s:4:"ᾱ";s:3:"ᾱ";s:5:"ᾲ";s:3:"ᾲ";s:4:"ᾳ";s:3:"ᾳ";s:4:"ᾴ";s:3:"ᾴ";s:4:"ᾶ";s:3:"ᾶ";s:5:"ᾷ";s:3:"ᾷ";s:4:"Ᾰ";s:3:"Ᾰ";s:4:"Ᾱ";s:3:"Ᾱ";s:4:"Ὰ";s:3:"Ὰ";s:2:"Ά";s:3:"Ά";s:4:"ᾼ";s:3:"ᾼ";s:2:"ι";s:3:"ι";s:4:"῁";s:3:"῁";s:5:"ῂ";s:3:"ῂ";s:4:"ῃ";s:3:"ῃ";s:4:"ῄ";s:3:"ῄ";s:4:"ῆ";s:3:"ῆ";s:5:"ῇ";s:3:"ῇ";s:4:"Ὲ";s:3:"Ὲ";s:2:"Έ";s:3:"Έ";s:4:"Ὴ";s:3:"Ὴ";s:2:"Ή";s:3:"Ή";s:4:"ῌ";s:3:"ῌ";s:5:"῍";s:3:"῍";s:5:"῎";s:3:"῎";s:5:"῏";s:3:"῏";s:4:"ῐ";s:3:"ῐ";s:4:"ῑ";s:3:"ῑ";s:4:"ῒ";s:3:"ῒ";s:2:"ΐ";s:3:"ΐ";s:4:"ῖ";s:3:"ῖ";s:4:"ῗ";s:3:"ῗ";s:4:"Ῐ";s:3:"Ῐ";s:4:"Ῑ";s:3:"Ῑ";s:4:"Ὶ";s:3:"Ὶ";s:2:"Ί";s:3:"Ί";s:5:"῝";s:3:"῝";s:5:"῞";s:3:"῞";s:5:"῟";s:3:"῟";s:4:"ῠ";s:3:"ῠ";s:4:"ῡ";s:3:"ῡ";s:4:"ῢ";s:3:"ῢ";s:2:"ΰ";s:3:"ΰ";s:4:"ῤ";s:3:"ῤ";s:4:"ῥ";s:3:"ῥ";s:4:"ῦ";s:3:"ῦ";s:4:"ῧ";s:3:"ῧ";s:4:"Ῠ";s:3:"Ῠ";s:4:"Ῡ";s:3:"Ῡ";s:4:"Ὺ";s:3:"Ὺ";s:2:"Ύ";s:3:"Ύ";s:4:"Ῥ";s:3:"Ῥ";s:4:"῭";s:3:"῭";s:2:"΅";s:3:"΅";s:1:"`";s:3:"`";s:5:"ῲ";s:3:"ῲ";s:4:"ῳ";s:3:"ῳ";s:4:"ῴ";s:3:"ῴ";s:4:"ῶ";s:3:"ῶ";s:5:"ῷ";s:3:"ῷ";s:4:"Ὸ";s:3:"Ὸ";s:2:"Ό";s:3:"Ό";s:4:"Ὼ";s:3:"Ὼ";s:2:"Ώ";s:3:"Ώ";s:4:"ῼ";s:3:"ῼ";s:2:"´";s:3:"´";s:3:" ";s:3:" ";s:3:" ";s:3:" ";s:2:"Ω";s:3:"Ω";s:1:"K";s:3:"K";s:2:"Å";s:3:"Å";s:5:"↚";s:3:"↚";s:5:"↛";s:3:"↛";s:5:"↮";s:3:"↮";s:5:"⇍";s:3:"⇍";s:5:"⇎";s:3:"⇎";s:5:"⇏";s:3:"⇏";s:5:"∄";s:3:"∄";s:5:"∉";s:3:"∉";s:5:"∌";s:3:"∌";s:5:"∤";s:3:"∤";s:5:"∦";s:3:"∦";s:5:"≁";s:3:"≁";s:5:"≄";s:3:"≄";s:5:"≇";s:3:"≇";s:5:"≉";s:3:"≉";s:3:"≠";s:3:"≠";s:5:"≢";s:3:"≢";s:5:"≭";s:3:"≭";s:3:"≮";s:3:"≮";s:3:"≯";s:3:"≯";s:5:"≰";s:3:"≰";s:5:"≱";s:3:"≱";s:5:"≴";s:3:"≴";s:5:"≵";s:3:"≵";s:5:"≸";s:3:"≸";s:5:"≹";s:3:"≹";s:5:"⊀";s:3:"⊀";s:5:"⊁";s:3:"⊁";s:5:"⊄";s:3:"⊄";s:5:"⊅";s:3:"⊅";s:5:"⊈";s:3:"⊈";s:5:"⊉";s:3:"⊉";s:5:"⊬";s:3:"⊬";s:5:"⊭";s:3:"⊭";s:5:"⊮";s:3:"⊮";s:5:"⊯";s:3:"⊯";s:5:"⋠";s:3:"⋠";s:5:"⋡";s:3:"⋡";s:5:"⋢";s:3:"⋢";s:5:"⋣";s:3:"⋣";s:5:"⋪";s:3:"⋪";s:5:"⋫";s:3:"⋫";s:5:"⋬";s:3:"⋬";s:5:"⋭";s:3:"⋭";s:3:"〈";s:3:"〈";s:3:"〉";s:3:"〉";s:6:"が";s:3:"が";s:6:"ぎ";s:3:"ぎ";s:6:"ぐ";s:3:"ぐ";s:6:"げ";s:3:"げ";s:6:"ご";s:3:"ご";s:6:"ざ";s:3:"ざ";s:6:"じ";s:3:"じ";s:6:"ず";s:3:"ず";s:6:"ぜ";s:3:"ぜ";s:6:"ぞ";s:3:"ぞ";s:6:"だ";s:3:"だ";s:6:"ぢ";s:3:"ぢ";s:6:"づ";s:3:"づ";s:6:"で";s:3:"で";s:6:"ど";s:3:"ど";s:6:"ば";s:3:"ば";s:6:"ぱ";s:3:"ぱ";s:6:"び";s:3:"び";s:6:"ぴ";s:3:"ぴ";s:6:"ぶ";s:3:"ぶ";s:6:"ぷ";s:3:"ぷ";s:6:"べ";s:3:"べ";s:6:"ぺ";s:3:"ぺ";s:6:"ぼ";s:3:"ぼ";s:6:"ぽ";s:3:"ぽ";s:6:"ゔ";s:3:"ゔ";s:6:"ゞ";s:3:"ゞ";s:6:"ガ";s:3:"ガ";s:6:"ギ";s:3:"ギ";s:6:"グ";s:3:"グ";s:6:"ゲ";s:3:"ゲ";s:6:"ゴ";s:3:"ゴ";s:6:"ザ";s:3:"ザ";s:6:"ジ";s:3:"ジ";s:6:"ズ";s:3:"ズ";s:6:"ゼ";s:3:"ゼ";s:6:"ゾ";s:3:"ゾ";s:6:"ダ";s:3:"ダ";s:6:"ヂ";s:3:"ヂ";s:6:"ヅ";s:3:"ヅ";s:6:"デ";s:3:"デ";s:6:"ド";s:3:"ド";s:6:"バ";s:3:"バ";s:6:"パ";s:3:"パ";s:6:"ビ";s:3:"ビ";s:6:"ピ";s:3:"ピ";s:6:"ブ";s:3:"ブ";s:6:"プ";s:3:"プ";s:6:"ベ";s:3:"ベ";s:6:"ペ";s:3:"ペ";s:6:"ボ";s:3:"ボ";s:6:"ポ";s:3:"ポ";s:6:"ヴ";s:3:"ヴ";s:6:"ヷ";s:3:"ヷ";s:6:"ヸ";s:3:"ヸ";s:6:"ヹ";s:3:"ヹ";s:6:"ヺ";s:3:"ヺ";s:6:"ヾ";s:3:"ヾ";s:3:"豈";s:3:"豈";s:3:"更";s:3:"更";s:3:"車";s:3:"車";s:3:"賈";s:3:"賈";s:3:"滑";s:3:"滑";s:3:"串";s:3:"串";s:3:"句";s:3:"句";s:3:"龜";s:3:"龜";s:3:"契";s:3:"契";s:3:"金";s:3:"金";s:3:"喇";s:3:"喇";s:3:"奈";s:3:"奈";s:3:"懶";s:4:"懶";s:3:"癩";s:3:"癩";s:3:"羅";s:3:"羅";s:3:"蘿";s:3:"蘿";s:3:"螺";s:3:"螺";s:3:"裸";s:3:"裸";s:3:"邏";s:3:"邏";s:3:"樂";s:3:"樂";s:3:"洛";s:3:"洛";s:3:"烙";s:3:"烙";s:3:"珞";s:3:"珞";s:3:"落";s:3:"落";s:3:"酪";s:3:"酪";s:3:"駱";s:3:"駱";s:3:"亂";s:3:"亂";s:3:"卵";s:3:"卵";s:3:"欄";s:3:"欄";s:3:"爛";s:3:"爛";s:3:"蘭";s:3:"蘭";s:3:"鸞";s:3:"鸞";s:3:"嵐";s:3:"嵐";s:3:"濫";s:3:"濫";s:3:"藍";s:3:"藍";s:3:"襤";s:3:"襤";s:3:"拉";s:3:"拉";s:3:"臘";s:3:"臘";s:3:"蠟";s:3:"蠟";s:3:"廊";s:4:"廊";s:3:"朗";s:4:"朗";s:3:"浪";s:3:"浪";s:3:"狼";s:3:"狼";s:3:"郎";s:3:"郎";s:3:"來";s:3:"來";s:3:"冷";s:3:"冷";s:3:"勞";s:3:"勞";s:3:"擄";s:3:"擄";s:3:"櫓";s:3:"櫓";s:3:"爐";s:3:"爐";s:3:"盧";s:3:"盧";s:3:"老";s:3:"老";s:3:"蘆";s:3:"蘆";s:3:"虜";s:4:"虜";s:3:"路";s:3:"路";s:3:"露";s:3:"露";s:3:"魯";s:3:"魯";s:3:"鷺";s:3:"鷺";s:3:"碌";s:4:"碌";s:3:"祿";s:3:"祿";s:3:"綠";s:3:"綠";s:3:"菉";s:3:"菉";s:3:"錄";s:3:"錄";s:3:"鹿";s:3:"鹿";s:3:"論";s:3:"論";s:3:"壟";s:3:"壟";s:3:"弄";s:3:"弄";s:3:"籠";s:3:"籠";s:3:"聾";s:3:"聾";s:3:"牢";s:3:"牢";s:3:"磊";s:3:"磊";s:3:"賂";s:3:"賂";s:3:"雷";s:3:"雷";s:3:"壘";s:3:"壘";s:3:"屢";s:3:"屢";s:3:"樓";s:3:"樓";s:3:"淚";s:3:"淚";s:3:"漏";s:3:"漏";s:3:"累";s:3:"累";s:3:"縷";s:3:"縷";s:3:"陋";s:3:"陋";s:3:"勒";s:3:"勒";s:3:"肋";s:3:"肋";s:3:"凜";s:3:"凜";s:3:"凌";s:3:"凌";s:3:"稜";s:3:"稜";s:3:"綾";s:3:"綾";s:3:"菱";s:3:"菱";s:3:"陵";s:3:"陵";s:3:"讀";s:3:"讀";s:3:"拏";s:3:"拏";s:3:"諾";s:3:"諾";s:3:"丹";s:3:"丹";s:3:"寧";s:4:"寧";s:3:"怒";s:3:"怒";s:3:"率";s:3:"率";s:3:"異";s:4:"異";s:3:"北";s:4:"北";s:3:"磻";s:3:"磻";s:3:"便";s:3:"便";s:3:"復";s:3:"復";s:3:"不";s:3:"不";s:3:"泌";s:3:"泌";s:3:"數";s:3:"數";s:3:"索";s:3:"索";s:3:"參";s:3:"參";s:3:"塞";s:3:"塞";s:3:"省";s:3:"省";s:3:"葉";s:3:"葉";s:3:"說";s:3:"說";s:3:"殺";s:4:"殺";s:3:"辰";s:3:"辰";s:3:"沈";s:3:"沈";s:3:"拾";s:3:"拾";s:3:"若";s:4:"若";s:3:"掠";s:3:"掠";s:3:"略";s:3:"略";s:3:"亮";s:3:"亮";s:3:"兩";s:3:"兩";s:3:"凉";s:3:"凉";s:3:"梁";s:3:"梁";s:3:"糧";s:3:"糧";s:3:"良";s:3:"良";s:3:"諒";s:3:"諒";s:3:"量";s:3:"量";s:3:"勵";s:3:"勵";s:3:"呂";s:3:"呂";s:3:"女";s:3:"女";s:3:"廬";s:3:"廬";s:3:"旅";s:3:"旅";s:3:"濾";s:3:"濾";s:3:"礪";s:3:"礪";s:3:"閭";s:3:"閭";s:3:"驪";s:3:"驪";s:3:"麗";s:3:"麗";s:3:"黎";s:3:"黎";s:3:"力";s:3:"力";s:3:"曆";s:3:"曆";s:3:"歷";s:3:"歷";s:3:"轢";s:3:"轢";s:3:"年";s:3:"年";s:3:"憐";s:3:"憐";s:3:"戀";s:3:"戀";s:3:"撚";s:3:"撚";s:3:"漣";s:3:"漣";s:3:"煉";s:3:"煉";s:3:"璉";s:3:"璉";s:3:"秊";s:3:"秊";s:3:"練";s:3:"練";s:3:"聯";s:3:"聯";s:3:"輦";s:3:"輦";s:3:"蓮";s:3:"蓮";s:3:"連";s:3:"連";s:3:"鍊";s:3:"鍊";s:3:"列";s:3:"列";s:3:"劣";s:3:"劣";s:3:"咽";s:3:"咽";s:3:"烈";s:3:"烈";s:3:"裂";s:3:"裂";s:3:"廉";s:3:"廉";s:3:"念";s:3:"念";s:3:"捻";s:3:"捻";s:3:"殮";s:3:"殮";s:3:"簾";s:3:"簾";s:3:"獵";s:3:"獵";s:3:"令";s:3:"令";s:3:"囹";s:3:"囹";s:3:"嶺";s:3:"嶺";s:3:"怜";s:3:"怜";s:3:"玲";s:3:"玲";s:3:"瑩";s:3:"瑩";s:3:"羚";s:3:"羚";s:3:"聆";s:3:"聆";s:3:"鈴";s:3:"鈴";s:3:"零";s:3:"零";s:3:"靈";s:3:"靈";s:3:"領";s:3:"領";s:3:"例";s:3:"例";s:3:"禮";s:3:"禮";s:3:"醴";s:3:"醴";s:3:"隸";s:3:"隸";s:3:"惡";s:3:"惡";s:3:"了";s:3:"了";s:3:"僚";s:3:"僚";s:3:"寮";s:3:"寮";s:3:"尿";s:3:"尿";s:3:"料";s:3:"料";s:3:"燎";s:3:"燎";s:3:"療";s:3:"療";s:3:"蓼";s:3:"蓼";s:3:"遼";s:3:"遼";s:3:"龍";s:3:"龍";s:3:"暈";s:3:"暈";s:3:"阮";s:3:"阮";s:3:"劉";s:3:"劉";s:3:"杻";s:3:"杻";s:3:"柳";s:3:"柳";s:3:"流";s:4:"流";s:3:"溜";s:3:"溜";s:3:"琉";s:3:"琉";s:3:"留";s:3:"留";s:3:"硫";s:3:"硫";s:3:"紐";s:3:"紐";s:3:"類";s:3:"類";s:3:"六";s:3:"六";s:3:"戮";s:3:"戮";s:3:"陸";s:3:"陸";s:3:"倫";s:3:"倫";s:3:"崙";s:3:"崙";s:3:"淪";s:3:"淪";s:3:"輪";s:3:"輪";s:3:"律";s:3:"律";s:3:"慄";s:3:"慄";s:3:"栗";s:3:"栗";s:3:"隆";s:3:"隆";s:3:"利";s:3:"利";s:3:"吏";s:3:"吏";s:3:"履";s:3:"履";s:3:"易";s:3:"易";s:3:"李";s:3:"李";s:3:"梨";s:3:"梨";s:3:"泥";s:3:"泥";s:3:"理";s:3:"理";s:3:"痢";s:3:"痢";s:3:"罹";s:3:"罹";s:3:"裏";s:3:"裏";s:3:"裡";s:3:"裡";s:3:"里";s:3:"里";s:3:"離";s:3:"離";s:3:"匿";s:3:"匿";s:3:"溺";s:3:"溺";s:3:"吝";s:3:"吝";s:3:"燐";s:3:"燐";s:3:"璘";s:3:"璘";s:3:"藺";s:3:"藺";s:3:"隣";s:3:"隣";s:3:"鱗";s:3:"鱗";s:3:"麟";s:3:"麟";s:3:"林";s:3:"林";s:3:"淋";s:3:"淋";s:3:"臨";s:3:"臨";s:3:"立";s:3:"立";s:3:"笠";s:3:"笠";s:3:"粒";s:3:"粒";s:3:"狀";s:3:"狀";s:3:"炙";s:3:"炙";s:3:"識";s:3:"識";s:3:"什";s:3:"什";s:3:"茶";s:3:"茶";s:3:"刺";s:3:"刺";s:3:"切";s:4:"切";s:3:"度";s:3:"度";s:3:"拓";s:3:"拓";s:3:"糖";s:3:"糖";s:3:"宅";s:3:"宅";s:3:"洞";s:3:"洞";s:3:"暴";s:3:"暴";s:3:"輻";s:3:"輻";s:3:"行";s:3:"行";s:3:"降";s:3:"降";s:3:"見";s:3:"見";s:3:"廓";s:3:"廓";s:3:"兀";s:3:"兀";s:3:"嗀";s:3:"嗀";s:3:"塚";s:3:"塚";s:3:"晴";s:3:"晴";s:3:"凞";s:3:"凞";s:3:"猪";s:3:"猪";s:3:"益";s:3:"益";s:3:"礼";s:3:"礼";s:3:"神";s:3:"神";s:3:"祥";s:3:"祥";s:3:"福";s:4:"福";s:3:"靖";s:3:"靖";s:3:"精";s:3:"精";s:3:"羽";s:3:"羽";s:3:"蘒";s:3:"蘒";s:3:"諸";s:3:"諸";s:3:"逸";s:3:"逸";s:3:"都";s:3:"都";s:3:"飯";s:3:"飯";s:3:"飼";s:3:"飼";s:3:"館";s:3:"館";s:3:"鶴";s:3:"鶴";s:3:"侮";s:4:"侮";s:3:"僧";s:4:"僧";s:3:"免";s:4:"免";s:3:"勉";s:4:"勉";s:3:"勤";s:4:"勤";s:3:"卑";s:4:"卑";s:3:"喝";s:3:"喝";s:3:"嘆";s:4:"嘆";s:3:"器";s:3:"器";s:3:"塀";s:3:"塀";s:3:"墨";s:3:"墨";s:3:"層";s:3:"層";s:3:"屮";s:4:"屮";s:3:"悔";s:4:"悔";s:3:"慨";s:3:"慨";s:3:"憎";s:4:"憎";s:3:"懲";s:4:"懲";s:3:"敏";s:4:"敏";s:3:"既";s:3:"既";s:3:"暑";s:4:"暑";s:3:"梅";s:4:"梅";s:3:"海";s:4:"海";s:3:"渚";s:3:"渚";s:3:"漢";s:3:"漢";s:3:"煮";s:3:"煮";s:3:"爫";s:3:"爫";s:3:"琢";s:3:"琢";s:3:"碑";s:3:"碑";s:3:"社";s:3:"社";s:3:"祉";s:3:"祉";s:3:"祈";s:3:"祈";s:3:"祐";s:3:"祐";s:3:"祖";s:4:"祖";s:3:"祝";s:3:"祝";s:3:"禍";s:3:"禍";s:3:"禎";s:3:"禎";s:3:"穀";s:4:"穀";s:3:"突";s:3:"突";s:3:"節";s:3:"節";s:3:"縉";s:3:"縉";s:3:"繁";s:3:"繁";s:3:"署";s:3:"署";s:3:"者";s:4:"者";s:3:"臭";s:3:"臭";s:3:"艹";s:3:"艹";s:3:"著";s:4:"著";s:3:"褐";s:3:"褐";s:3:"視";s:3:"視";s:3:"謁";s:3:"謁";s:3:"謹";s:3:"謹";s:3:"賓";s:3:"賓";s:3:"贈";s:3:"贈";s:3:"辶";s:3:"辶";s:3:"難";s:3:"難";s:3:"響";s:3:"響";s:3:"頻";s:3:"頻";s:3:"並";s:3:"並";s:3:"况";s:4:"况";s:3:"全";s:3:"全";s:3:"侀";s:3:"侀";s:3:"充";s:3:"充";s:3:"冀";s:3:"冀";s:3:"勇";s:4:"勇";s:3:"勺";s:4:"勺";s:3:"啕";s:3:"啕";s:3:"喙";s:4:"喙";s:3:"嗢";s:3:"嗢";s:3:"墳";s:3:"墳";s:3:"奄";s:3:"奄";s:3:"奔";s:3:"奔";s:3:"婢";s:3:"婢";s:3:"嬨";s:3:"嬨";s:3:"廒";s:3:"廒";s:3:"廙";s:3:"廙";s:3:"彩";s:3:"彩";s:3:"徭";s:3:"徭";s:3:"惘";s:3:"惘";s:3:"慎";s:4:"慎";s:3:"愈";s:3:"愈";s:3:"慠";s:3:"慠";s:3:"戴";s:3:"戴";s:3:"揄";s:3:"揄";s:3:"搜";s:3:"搜";s:3:"摒";s:3:"摒";s:3:"敖";s:3:"敖";s:3:"望";s:4:"望";s:3:"杖";s:3:"杖";s:3:"歹";s:3:"歹";s:3:"滛";s:3:"滛";s:3:"滋";s:4:"滋";s:3:"瀞";s:4:"瀞";s:3:"瞧";s:3:"瞧";s:3:"爵";s:4:"爵";s:3:"犯";s:3:"犯";s:3:"瑱";s:4:"瑱";s:3:"甆";s:3:"甆";s:3:"画";s:3:"画";s:3:"瘝";s:3:"瘝";s:3:"瘟";s:3:"瘟";s:3:"盛";s:3:"盛";s:3:"直";s:4:"直";s:3:"睊";s:4:"睊";s:3:"着";s:3:"着";s:3:"磌";s:4:"磌";s:3:"窱";s:3:"窱";s:3:"类";s:3:"类";s:3:"絛";s:3:"絛";s:3:"缾";s:3:"缾";s:3:"荒";s:3:"荒";s:3:"華";s:3:"華";s:3:"蝹";s:4:"蝹";s:3:"襁";s:3:"襁";s:3:"覆";s:3:"覆";s:3:"調";s:3:"調";s:3:"請";s:3:"請";s:3:"諭";s:4:"諭";s:3:"變";s:4:"變";s:3:"輸";s:4:"輸";s:3:"遲";s:3:"遲";s:3:"醙";s:3:"醙";s:3:"鉶";s:3:"鉶";s:3:"陼";s:3:"陼";s:3:"韛";s:3:"韛";s:3:"頋";s:4:"頋";s:3:"鬒";s:4:"鬒";s:4:"𢡊";s:3:"𢡊";s:4:"𢡄";s:3:"𢡄";s:4:"𣏕";s:3:"𣏕";s:3:"㮝";s:4:"㮝";s:3:"䀘";s:3:"䀘";s:3:"䀹";s:4:"䀹";s:4:"𥉉";s:3:"𥉉";s:4:"𥳐";s:3:"𥳐";s:4:"𧻓";s:3:"𧻓";s:3:"齃";s:3:"齃";s:3:"龎";s:3:"龎";s:3:"丽";s:4:"丽";s:3:"丸";s:4:"丸";s:3:"乁";s:4:"乁";s:4:"𠄢";s:4:"𠄢";s:3:"你";s:4:"你";s:3:"侻";s:4:"侻";s:3:"倂";s:4:"倂";s:3:"偺";s:4:"偺";s:3:"備";s:4:"備";s:3:"像";s:4:"像";s:3:"㒞";s:4:"㒞";s:4:"𠘺";s:4:"𠘺";s:3:"兔";s:4:"兔";s:3:"兤";s:4:"兤";s:3:"具";s:4:"具";s:4:"𠔜";s:4:"𠔜";s:3:"㒹";s:4:"㒹";s:3:"內";s:4:"內";s:3:"再";s:4:"再";s:4:"𠕋";s:4:"𠕋";s:3:"冗";s:4:"冗";s:3:"冤";s:4:"冤";s:3:"仌";s:4:"仌";s:3:"冬";s:4:"冬";s:4:"𩇟";s:4:"𩇟";s:3:"凵";s:4:"凵";s:3:"刃";s:4:"刃";s:3:"㓟";s:4:"㓟";s:3:"刻";s:4:"刻";s:3:"剆";s:4:"剆";s:3:"割";s:4:"割";s:3:"剷";s:4:"剷";s:3:"㔕";s:4:"㔕";s:3:"包";s:4:"包";s:3:"匆";s:4:"匆";s:3:"卉";s:4:"卉";s:3:"博";s:4:"博";s:3:"即";s:4:"即";s:3:"卽";s:4:"卽";s:3:"卿";s:4:"卿";s:4:"𠨬";s:4:"𠨬";s:3:"灰";s:4:"灰";s:3:"及";s:4:"及";s:3:"叟";s:4:"叟";s:4:"𠭣";s:4:"𠭣";s:3:"叫";s:4:"叫";s:3:"叱";s:4:"叱";s:3:"吆";s:4:"吆";s:3:"咞";s:4:"咞";s:3:"吸";s:4:"吸";s:3:"呈";s:4:"呈";s:3:"周";s:4:"周";s:3:"咢";s:4:"咢";s:3:"哶";s:4:"哶";s:3:"唐";s:4:"唐";s:3:"啓";s:4:"啓";s:3:"啣";s:4:"啣";s:3:"善";s:4:"善";s:3:"喫";s:4:"喫";s:3:"喳";s:4:"喳";s:3:"嗂";s:4:"嗂";s:3:"圖";s:4:"圖";s:3:"圗";s:4:"圗";s:3:"噑";s:4:"噑";s:3:"噴";s:4:"噴";s:3:"壮";s:4:"壮";s:3:"城";s:4:"城";s:3:"埴";s:4:"埴";s:3:"堍";s:4:"堍";s:3:"型";s:4:"型";s:3:"堲";s:4:"堲";s:3:"報";s:4:"報";s:3:"墬";s:4:"墬";s:4:"𡓤";s:4:"𡓤";s:3:"売";s:4:"売";s:3:"壷";s:4:"壷";s:3:"夆";s:4:"夆";s:3:"多";s:4:"多";s:3:"夢";s:4:"夢";s:3:"奢";s:4:"奢";s:4:"𡚨";s:4:"𡚨";s:4:"𡛪";s:4:"𡛪";s:3:"姬";s:4:"姬";s:3:"娛";s:4:"娛";s:3:"娧";s:4:"娧";s:3:"姘";s:4:"姘";s:3:"婦";s:4:"婦";s:3:"㛮";s:4:"㛮";s:3:"㛼";s:4:"㛼";s:3:"嬈";s:4:"嬈";s:3:"嬾";s:4:"嬾";s:4:"𡧈";s:4:"𡧈";s:3:"寃";s:4:"寃";s:3:"寘";s:4:"寘";s:3:"寳";s:4:"寳";s:4:"𡬘";s:4:"𡬘";s:3:"寿";s:4:"寿";s:3:"将";s:4:"将";s:3:"当";s:4:"当";s:3:"尢";s:4:"尢";s:3:"㞁";s:4:"㞁";s:3:"屠";s:4:"屠";s:3:"峀";s:4:"峀";s:3:"岍";s:4:"岍";s:4:"𡷤";s:4:"𡷤";s:3:"嵃";s:4:"嵃";s:4:"𡷦";s:4:"𡷦";s:3:"嵮";s:4:"嵮";s:3:"嵫";s:4:"嵫";s:3:"嵼";s:4:"嵼";s:3:"巡";s:4:"巡";s:3:"巢";s:4:"巢";s:3:"㠯";s:4:"㠯";s:3:"巽";s:4:"巽";s:3:"帨";s:4:"帨";s:3:"帽";s:4:"帽";s:3:"幩";s:4:"幩";s:3:"㡢";s:4:"㡢";s:4:"𢆃";s:4:"𢆃";s:3:"㡼";s:4:"㡼";s:3:"庰";s:4:"庰";s:3:"庳";s:4:"庳";s:3:"庶";s:4:"庶";s:4:"𪎒";s:4:"𪎒";s:3:"廾";s:4:"廾";s:4:"𢌱";s:4:"𢌱";s:3:"舁";s:4:"舁";s:3:"弢";s:4:"弢";s:3:"㣇";s:4:"㣇";s:4:"𣊸";s:4:"𣊸";s:4:"𦇚";s:4:"𦇚";s:3:"形";s:4:"形";s:3:"彫";s:4:"彫";s:3:"㣣";s:4:"㣣";s:3:"徚";s:4:"徚";s:3:"忍";s:4:"忍";s:3:"志";s:4:"志";s:3:"忹";s:4:"忹";s:3:"悁";s:4:"悁";s:3:"㤺";s:4:"㤺";s:3:"㤜";s:4:"㤜";s:4:"𢛔";s:4:"𢛔";s:3:"惇";s:4:"惇";s:3:"慈";s:4:"慈";s:3:"慌";s:4:"慌";s:3:"慺";s:4:"慺";s:3:"憲";s:4:"憲";s:3:"憤";s:4:"憤";s:3:"憯";s:4:"憯";s:3:"懞";s:4:"懞";s:3:"成";s:4:"成";s:3:"戛";s:4:"戛";s:3:"扝";s:4:"扝";s:3:"抱";s:4:"抱";s:3:"拔";s:4:"拔";s:3:"捐";s:4:"捐";s:4:"𢬌";s:4:"𢬌";s:3:"挽";s:4:"挽";s:3:"拼";s:4:"拼";s:3:"捨";s:4:"捨";s:3:"掃";s:4:"掃";s:3:"揤";s:4:"揤";s:4:"𢯱";s:4:"𢯱";s:3:"搢";s:4:"搢";s:3:"揅";s:4:"揅";s:3:"掩";s:4:"掩";s:3:"㨮";s:4:"㨮";s:3:"摩";s:4:"摩";s:3:"摾";s:4:"摾";s:3:"撝";s:4:"撝";s:3:"摷";s:4:"摷";s:3:"㩬";s:4:"㩬";s:3:"敬";s:4:"敬";s:4:"𣀊";s:4:"𣀊";s:3:"旣";s:4:"旣";s:3:"書";s:4:"書";s:3:"晉";s:4:"晉";s:3:"㬙";s:4:"㬙";s:3:"㬈";s:4:"㬈";s:3:"㫤";s:4:"㫤";s:3:"冒";s:4:"冒";s:3:"冕";s:4:"冕";s:3:"最";s:4:"最";s:3:"暜";s:4:"暜";s:3:"肭";s:4:"肭";s:3:"䏙";s:4:"䏙";s:3:"朡";s:4:"朡";s:3:"杞";s:4:"杞";s:3:"杓";s:4:"杓";s:4:"𣏃";s:4:"𣏃";s:3:"㭉";s:4:"㭉";s:3:"柺";s:4:"柺";s:3:"枅";s:4:"枅";s:3:"桒";s:4:"桒";s:4:"𣑭";s:4:"𣑭";s:3:"梎";s:4:"梎";s:3:"栟";s:4:"栟";s:3:"椔";s:4:"椔";s:3:"楂";s:4:"楂";s:3:"榣";s:4:"榣";s:3:"槪";s:4:"槪";s:3:"檨";s:4:"檨";s:4:"𣚣";s:4:"𣚣";s:3:"櫛";s:4:"櫛";s:3:"㰘";s:4:"㰘";s:3:"次";s:4:"次";s:4:"𣢧";s:4:"𣢧";s:3:"歔";s:4:"歔";s:3:"㱎";s:4:"㱎";s:3:"歲";s:4:"歲";s:3:"殟";s:4:"殟";s:3:"殻";s:4:"殻";s:4:"𣪍";s:4:"𣪍";s:4:"𡴋";s:4:"𡴋";s:4:"𣫺";s:4:"𣫺";s:3:"汎";s:4:"汎";s:4:"𣲼";s:4:"𣲼";s:3:"沿";s:4:"沿";s:3:"泍";s:4:"泍";s:3:"汧";s:4:"汧";s:3:"洖";s:4:"洖";s:3:"派";s:4:"派";s:3:"浩";s:4:"浩";s:3:"浸";s:4:"浸";s:3:"涅";s:4:"涅";s:4:"𣴞";s:4:"𣴞";s:3:"洴";s:4:"洴";s:3:"港";s:4:"港";s:3:"湮";s:4:"湮";s:3:"㴳";s:4:"㴳";s:3:"滇";s:4:"滇";s:4:"𣻑";s:4:"𣻑";s:3:"淹";s:4:"淹";s:3:"潮";s:4:"潮";s:4:"𣽞";s:4:"𣽞";s:4:"𣾎";s:4:"𣾎";s:3:"濆";s:4:"濆";s:3:"瀹";s:4:"瀹";s:3:"瀛";s:4:"瀛";s:3:"㶖";s:4:"㶖";s:3:"灊";s:4:"灊";s:3:"災";s:4:"災";s:3:"灷";s:4:"灷";s:3:"炭";s:4:"炭";s:4:"𠔥";s:4:"𠔥";s:3:"煅";s:4:"煅";s:4:"𤉣";s:4:"𤉣";s:3:"熜";s:4:"熜";s:4:"𤎫";s:4:"𤎫";s:3:"爨";s:4:"爨";s:3:"牐";s:4:"牐";s:4:"𤘈";s:4:"𤘈";s:3:"犀";s:4:"犀";s:3:"犕";s:4:"犕";s:4:"𤜵";s:4:"𤜵";s:4:"𤠔";s:4:"𤠔";s:3:"獺";s:4:"獺";s:3:"王";s:4:"王";s:3:"㺬";s:4:"㺬";s:3:"玥";s:4:"玥";s:3:"㺸";s:4:"㺸";s:3:"瑇";s:4:"瑇";s:3:"瑜";s:4:"瑜";s:3:"璅";s:4:"璅";s:3:"瓊";s:4:"瓊";s:3:"㼛";s:4:"㼛";s:3:"甤";s:4:"甤";s:4:"𤰶";s:4:"𤰶";s:3:"甾";s:4:"甾";s:4:"𤲒";s:4:"𤲒";s:4:"𢆟";s:4:"𢆟";s:3:"瘐";s:4:"瘐";s:4:"𤾡";s:4:"𤾡";s:4:"𤾸";s:4:"𤾸";s:4:"𥁄";s:4:"𥁄";s:3:"㿼";s:4:"㿼";s:3:"䀈";s:4:"䀈";s:4:"𥃳";s:4:"𥃳";s:4:"𥃲";s:4:"𥃲";s:4:"𥄙";s:4:"𥄙";s:4:"𥄳";s:4:"𥄳";s:3:"眞";s:4:"眞";s:3:"真";s:4:"真";s:3:"瞋";s:4:"瞋";s:3:"䁆";s:4:"䁆";s:3:"䂖";s:4:"䂖";s:4:"𥐝";s:4:"𥐝";s:3:"硎";s:4:"硎";s:3:"䃣";s:4:"䃣";s:4:"𥘦";s:4:"𥘦";s:4:"𥚚";s:4:"𥚚";s:4:"𥛅";s:4:"𥛅";s:3:"秫";s:4:"秫";s:3:"䄯";s:4:"䄯";s:3:"穊";s:4:"穊";s:3:"穏";s:4:"穏";s:4:"𥥼";s:4:"𥥼";s:4:"𥪧";s:4:"𥪧";s:3:"竮";s:4:"竮";s:3:"䈂";s:4:"䈂";s:4:"𥮫";s:4:"𥮫";s:3:"篆";s:4:"篆";s:3:"築";s:4:"築";s:3:"䈧";s:4:"䈧";s:4:"𥲀";s:4:"𥲀";s:3:"糒";s:4:"糒";s:3:"䊠";s:4:"䊠";s:3:"糨";s:4:"糨";s:3:"糣";s:4:"糣";s:3:"紀";s:4:"紀";s:4:"𥾆";s:4:"𥾆";s:3:"絣";s:4:"絣";s:3:"䌁";s:4:"䌁";s:3:"緇";s:4:"緇";s:3:"縂";s:4:"縂";s:3:"繅";s:4:"繅";s:3:"䌴";s:4:"䌴";s:4:"𦈨";s:4:"𦈨";s:4:"𦉇";s:4:"𦉇";s:3:"䍙";s:4:"䍙";s:4:"𦋙";s:4:"𦋙";s:3:"罺";s:4:"罺";s:4:"𦌾";s:4:"𦌾";s:3:"羕";s:4:"羕";s:3:"翺";s:4:"翺";s:4:"𦓚";s:4:"𦓚";s:4:"𦔣";s:4:"𦔣";s:3:"聠";s:4:"聠";s:4:"𦖨";s:4:"𦖨";s:3:"聰";s:4:"聰";s:4:"𣍟";s:4:"𣍟";s:3:"䏕";s:4:"䏕";s:3:"育";s:4:"育";s:3:"脃";s:4:"脃";s:3:"䐋";s:4:"䐋";s:3:"脾";s:4:"脾";s:3:"媵";s:4:"媵";s:4:"𦞧";s:4:"𦞧";s:4:"𦞵";s:4:"𦞵";s:4:"𣎓";s:4:"𣎓";s:4:"𣎜";s:4:"𣎜";s:3:"舄";s:4:"舄";s:3:"辞";s:4:"辞";s:3:"䑫";s:4:"䑫";s:3:"芑";s:4:"芑";s:3:"芋";s:4:"芋";s:3:"芝";s:4:"芝";s:3:"劳";s:4:"劳";s:3:"花";s:4:"花";s:3:"芳";s:4:"芳";s:3:"芽";s:4:"芽";s:3:"苦";s:4:"苦";s:4:"𦬼";s:4:"𦬼";s:3:"茝";s:4:"茝";s:3:"荣";s:4:"荣";s:3:"莭";s:4:"莭";s:3:"茣";s:4:"茣";s:3:"莽";s:4:"莽";s:3:"菧";s:4:"菧";s:3:"荓";s:4:"荓";s:3:"菊";s:4:"菊";s:3:"菌";s:4:"菌";s:3:"菜";s:4:"菜";s:4:"𦰶";s:4:"𦰶";s:4:"𦵫";s:4:"𦵫";s:4:"𦳕";s:4:"𦳕";s:3:"䔫";s:4:"䔫";s:3:"蓱";s:4:"蓱";s:3:"蓳";s:4:"蓳";s:3:"蔖";s:4:"蔖";s:4:"𧏊";s:4:"𧏊";s:3:"蕤";s:4:"蕤";s:4:"𦼬";s:4:"𦼬";s:3:"䕝";s:4:"䕝";s:3:"䕡";s:4:"䕡";s:4:"𦾱";s:4:"𦾱";s:4:"𧃒";s:4:"𧃒";s:3:"䕫";s:4:"䕫";s:3:"虐";s:4:"虐";s:3:"虧";s:4:"虧";s:3:"虩";s:4:"虩";s:3:"蚩";s:4:"蚩";s:3:"蚈";s:4:"蚈";s:3:"蜎";s:4:"蜎";s:3:"蛢";s:4:"蛢";s:3:"蜨";s:4:"蜨";s:3:"蝫";s:4:"蝫";s:3:"螆";s:4:"螆";s:3:"䗗";s:4:"䗗";s:3:"蟡";s:4:"蟡";s:3:"蠁";s:4:"蠁";s:3:"䗹";s:4:"䗹";s:3:"衠";s:4:"衠";s:3:"衣";s:4:"衣";s:4:"𧙧";s:4:"𧙧";s:3:"裗";s:4:"裗";s:3:"裞";s:4:"裞";s:3:"䘵";s:4:"䘵";s:3:"裺";s:4:"裺";s:3:"㒻";s:4:"㒻";s:4:"𧢮";s:4:"𧢮";s:4:"𧥦";s:4:"𧥦";s:3:"䚾";s:4:"䚾";s:3:"䛇";s:4:"䛇";s:3:"誠";s:4:"誠";s:3:"豕";s:4:"豕";s:4:"𧲨";s:4:"𧲨";s:3:"貫";s:4:"貫";s:3:"賁";s:4:"賁";s:3:"贛";s:4:"贛";s:3:"起";s:4:"起";s:4:"𧼯";s:4:"𧼯";s:4:"𠠄";s:4:"𠠄";s:3:"跋";s:4:"跋";s:3:"趼";s:4:"趼";s:3:"跰";s:4:"跰";s:4:"𠣞";s:4:"𠣞";s:3:"軔";s:4:"軔";s:4:"𨗒";s:4:"𨗒";s:4:"𨗭";s:4:"𨗭";s:3:"邔";s:4:"邔";s:3:"郱";s:4:"郱";s:3:"鄑";s:4:"鄑";s:4:"𨜮";s:4:"𨜮";s:3:"鄛";s:4:"鄛";s:3:"鈸";s:4:"鈸";s:3:"鋗";s:4:"鋗";s:3:"鋘";s:4:"鋘";s:3:"鉼";s:4:"鉼";s:3:"鏹";s:4:"鏹";s:3:"鐕";s:4:"鐕";s:4:"𨯺";s:4:"𨯺";s:3:"開";s:4:"開";s:3:"䦕";s:4:"䦕";s:3:"閷";s:4:"閷";s:4:"𨵷";s:4:"𨵷";s:3:"䧦";s:4:"䧦";s:3:"雃";s:4:"雃";s:3:"嶲";s:4:"嶲";s:3:"霣";s:4:"霣";s:4:"𩅅";s:4:"𩅅";s:4:"𩈚";s:4:"𩈚";s:3:"䩮";s:4:"䩮";s:3:"䩶";s:4:"䩶";s:3:"韠";s:4:"韠";s:4:"𩐊";s:4:"𩐊";s:3:"䪲";s:4:"䪲";s:4:"𩒖";s:4:"𩒖";s:3:"頩";s:4:"頩";s:4:"𩖶";s:4:"𩖶";s:3:"飢";s:4:"飢";s:3:"䬳";s:4:"䬳";s:3:"餩";s:4:"餩";s:3:"馧";s:4:"馧";s:3:"駂";s:4:"駂";s:3:"駾";s:4:"駾";s:3:"䯎";s:4:"䯎";s:4:"𩬰";s:4:"𩬰";s:3:"鱀";s:4:"鱀";s:3:"鳽";s:4:"鳽";s:3:"䳎";s:4:"䳎";s:3:"䳭";s:4:"䳭";s:3:"鵧";s:4:"鵧";s:4:"𪃎";s:4:"𪃎";s:3:"䳸";s:4:"䳸";s:4:"𪄅";s:4:"𪄅";s:4:"𪈎";s:4:"𪈎";s:4:"𪊑";s:4:"𪊑";s:3:"麻";s:4:"麻";s:3:"䵖";s:4:"䵖";s:3:"黹";s:4:"黹";s:3:"黾";s:4:"黾";s:3:"鼅";s:4:"鼅";s:3:"鼏";s:4:"鼏";s:3:"鼖";s:4:"鼖";s:3:"鼻";s:4:"鼻";s:4:"𪘀";s:4:"𪘀";}' );
+$utfCanonicalDecomp = unserialize( 'a:2043:{s:2:"À";s:3:"À";s:2:"Á";s:3:"Á";s:2:"Â";s:3:"Â";s:2:"Ã";s:3:"Ã";s:2:"Ä";s:3:"Ä";s:2:"Å";s:3:"Å";s:2:"Ç";s:3:"Ç";s:2:"È";s:3:"È";s:2:"É";s:3:"É";s:2:"Ê";s:3:"Ê";s:2:"Ë";s:3:"Ë";s:2:"Ì";s:3:"Ì";s:2:"Í";s:3:"Í";s:2:"Î";s:3:"Î";s:2:"Ï";s:3:"Ï";s:2:"Ñ";s:3:"Ñ";s:2:"Ò";s:3:"Ò";s:2:"Ó";s:3:"Ó";s:2:"Ô";s:3:"Ô";s:2:"Õ";s:3:"Õ";s:2:"Ö";s:3:"Ö";s:2:"Ù";s:3:"Ù";s:2:"Ú";s:3:"Ú";s:2:"Û";s:3:"Û";s:2:"Ü";s:3:"Ü";s:2:"Ý";s:3:"Ý";s:2:"à";s:3:"à";s:2:"á";s:3:"á";s:2:"â";s:3:"â";s:2:"ã";s:3:"ã";s:2:"ä";s:3:"ä";s:2:"å";s:3:"å";s:2:"ç";s:3:"ç";s:2:"è";s:3:"è";s:2:"é";s:3:"é";s:2:"ê";s:3:"ê";s:2:"ë";s:3:"ë";s:2:"ì";s:3:"ì";s:2:"í";s:3:"í";s:2:"î";s:3:"î";s:2:"ï";s:3:"ï";s:2:"ñ";s:3:"ñ";s:2:"ò";s:3:"ò";s:2:"ó";s:3:"ó";s:2:"ô";s:3:"ô";s:2:"õ";s:3:"õ";s:2:"ö";s:3:"ö";s:2:"ù";s:3:"ù";s:2:"ú";s:3:"ú";s:2:"û";s:3:"û";s:2:"ü";s:3:"ü";s:2:"ý";s:3:"ý";s:2:"ÿ";s:3:"ÿ";s:2:"Ā";s:3:"Ā";s:2:"ā";s:3:"ā";s:2:"Ă";s:3:"Ă";s:2:"ă";s:3:"ă";s:2:"Ą";s:3:"Ą";s:2:"ą";s:3:"ą";s:2:"Ć";s:3:"Ć";s:2:"ć";s:3:"ć";s:2:"Ĉ";s:3:"Ĉ";s:2:"ĉ";s:3:"ĉ";s:2:"Ċ";s:3:"Ċ";s:2:"ċ";s:3:"ċ";s:2:"Č";s:3:"Č";s:2:"č";s:3:"č";s:2:"Ď";s:3:"Ď";s:2:"ď";s:3:"ď";s:2:"Ē";s:3:"Ē";s:2:"ē";s:3:"ē";s:2:"Ĕ";s:3:"Ĕ";s:2:"ĕ";s:3:"ĕ";s:2:"Ė";s:3:"Ė";s:2:"ė";s:3:"ė";s:2:"Ę";s:3:"Ę";s:2:"ę";s:3:"ę";s:2:"Ě";s:3:"Ě";s:2:"ě";s:3:"ě";s:2:"Ĝ";s:3:"Ĝ";s:2:"ĝ";s:3:"ĝ";s:2:"Ğ";s:3:"Ğ";s:2:"ğ";s:3:"ğ";s:2:"Ġ";s:3:"Ġ";s:2:"ġ";s:3:"ġ";s:2:"Ģ";s:3:"Ģ";s:2:"ģ";s:3:"ģ";s:2:"Ĥ";s:3:"Ĥ";s:2:"ĥ";s:3:"ĥ";s:2:"Ĩ";s:3:"Ĩ";s:2:"ĩ";s:3:"ĩ";s:2:"Ī";s:3:"Ī";s:2:"ī";s:3:"ī";s:2:"Ĭ";s:3:"Ĭ";s:2:"ĭ";s:3:"ĭ";s:2:"Į";s:3:"Į";s:2:"į";s:3:"į";s:2:"İ";s:3:"İ";s:2:"Ĵ";s:3:"Ĵ";s:2:"ĵ";s:3:"ĵ";s:2:"Ķ";s:3:"Ķ";s:2:"ķ";s:3:"ķ";s:2:"Ĺ";s:3:"Ĺ";s:2:"ĺ";s:3:"ĺ";s:2:"Ļ";s:3:"Ļ";s:2:"ļ";s:3:"ļ";s:2:"Ľ";s:3:"Ľ";s:2:"ľ";s:3:"ľ";s:2:"Ń";s:3:"Ń";s:2:"ń";s:3:"ń";s:2:"Ņ";s:3:"Ņ";s:2:"ņ";s:3:"ņ";s:2:"Ň";s:3:"Ň";s:2:"ň";s:3:"ň";s:2:"Ō";s:3:"Ō";s:2:"ō";s:3:"ō";s:2:"Ŏ";s:3:"Ŏ";s:2:"ŏ";s:3:"ŏ";s:2:"Ő";s:3:"Ő";s:2:"ő";s:3:"ő";s:2:"Ŕ";s:3:"Ŕ";s:2:"ŕ";s:3:"ŕ";s:2:"Ŗ";s:3:"Ŗ";s:2:"ŗ";s:3:"ŗ";s:2:"Ř";s:3:"Ř";s:2:"ř";s:3:"ř";s:2:"Ś";s:3:"Ś";s:2:"ś";s:3:"ś";s:2:"Ŝ";s:3:"Ŝ";s:2:"ŝ";s:3:"ŝ";s:2:"Ş";s:3:"Ş";s:2:"ş";s:3:"ş";s:2:"Š";s:3:"Š";s:2:"š";s:3:"š";s:2:"Ţ";s:3:"Ţ";s:2:"ţ";s:3:"ţ";s:2:"Ť";s:3:"Ť";s:2:"ť";s:3:"ť";s:2:"Ũ";s:3:"Ũ";s:2:"ũ";s:3:"ũ";s:2:"Ū";s:3:"Ū";s:2:"ū";s:3:"ū";s:2:"Ŭ";s:3:"Ŭ";s:2:"ŭ";s:3:"ŭ";s:2:"Ů";s:3:"Ů";s:2:"ů";s:3:"ů";s:2:"Ű";s:3:"Ű";s:2:"ű";s:3:"ű";s:2:"Ų";s:3:"Ų";s:2:"ų";s:3:"ų";s:2:"Ŵ";s:3:"Ŵ";s:2:"ŵ";s:3:"ŵ";s:2:"Ŷ";s:3:"Ŷ";s:2:"ŷ";s:3:"ŷ";s:2:"Ÿ";s:3:"Ÿ";s:2:"Ź";s:3:"Ź";s:2:"ź";s:3:"ź";s:2:"Ż";s:3:"Ż";s:2:"ż";s:3:"ż";s:2:"Ž";s:3:"Ž";s:2:"ž";s:3:"ž";s:2:"Ơ";s:3:"Ơ";s:2:"ơ";s:3:"ơ";s:2:"Ư";s:3:"Ư";s:2:"ư";s:3:"ư";s:2:"Ǎ";s:3:"Ǎ";s:2:"ǎ";s:3:"ǎ";s:2:"Ǐ";s:3:"Ǐ";s:2:"ǐ";s:3:"ǐ";s:2:"Ǒ";s:3:"Ǒ";s:2:"ǒ";s:3:"ǒ";s:2:"Ǔ";s:3:"Ǔ";s:2:"ǔ";s:3:"ǔ";s:2:"Ǖ";s:5:"Ǖ";s:2:"ǖ";s:5:"ǖ";s:2:"Ǘ";s:5:"Ǘ";s:2:"ǘ";s:5:"ǘ";s:2:"Ǚ";s:5:"Ǚ";s:2:"ǚ";s:5:"ǚ";s:2:"Ǜ";s:5:"Ǜ";s:2:"ǜ";s:5:"ǜ";s:2:"Ǟ";s:5:"Ǟ";s:2:"ǟ";s:5:"ǟ";s:2:"Ǡ";s:5:"Ǡ";s:2:"ǡ";s:5:"ǡ";s:2:"Ǣ";s:4:"Ǣ";s:2:"ǣ";s:4:"ǣ";s:2:"Ǧ";s:3:"Ǧ";s:2:"ǧ";s:3:"ǧ";s:2:"Ǩ";s:3:"Ǩ";s:2:"ǩ";s:3:"ǩ";s:2:"Ǫ";s:3:"Ǫ";s:2:"ǫ";s:3:"ǫ";s:2:"Ǭ";s:5:"Ǭ";s:2:"ǭ";s:5:"ǭ";s:2:"Ǯ";s:4:"Ǯ";s:2:"ǯ";s:4:"ǯ";s:2:"ǰ";s:3:"ǰ";s:2:"Ǵ";s:3:"Ǵ";s:2:"ǵ";s:3:"ǵ";s:2:"Ǹ";s:3:"Ǹ";s:2:"ǹ";s:3:"ǹ";s:2:"Ǻ";s:5:"Ǻ";s:2:"ǻ";s:5:"ǻ";s:2:"Ǽ";s:4:"Ǽ";s:2:"ǽ";s:4:"ǽ";s:2:"Ǿ";s:4:"Ǿ";s:2:"ǿ";s:4:"ǿ";s:2:"Ȁ";s:3:"Ȁ";s:2:"ȁ";s:3:"ȁ";s:2:"Ȃ";s:3:"Ȃ";s:2:"ȃ";s:3:"ȃ";s:2:"Ȅ";s:3:"Ȅ";s:2:"ȅ";s:3:"ȅ";s:2:"Ȇ";s:3:"Ȇ";s:2:"ȇ";s:3:"ȇ";s:2:"Ȉ";s:3:"Ȉ";s:2:"ȉ";s:3:"ȉ";s:2:"Ȋ";s:3:"Ȋ";s:2:"ȋ";s:3:"ȋ";s:2:"Ȍ";s:3:"Ȍ";s:2:"ȍ";s:3:"ȍ";s:2:"Ȏ";s:3:"Ȏ";s:2:"ȏ";s:3:"ȏ";s:2:"Ȑ";s:3:"Ȑ";s:2:"ȑ";s:3:"ȑ";s:2:"Ȓ";s:3:"Ȓ";s:2:"ȓ";s:3:"ȓ";s:2:"Ȕ";s:3:"Ȕ";s:2:"ȕ";s:3:"ȕ";s:2:"Ȗ";s:3:"Ȗ";s:2:"ȗ";s:3:"ȗ";s:2:"Ș";s:3:"Ș";s:2:"ș";s:3:"ș";s:2:"Ț";s:3:"Ț";s:2:"ț";s:3:"ț";s:2:"Ȟ";s:3:"Ȟ";s:2:"ȟ";s:3:"ȟ";s:2:"Ȧ";s:3:"Ȧ";s:2:"ȧ";s:3:"ȧ";s:2:"Ȩ";s:3:"Ȩ";s:2:"ȩ";s:3:"ȩ";s:2:"Ȫ";s:5:"Ȫ";s:2:"ȫ";s:5:"ȫ";s:2:"Ȭ";s:5:"Ȭ";s:2:"ȭ";s:5:"ȭ";s:2:"Ȯ";s:3:"Ȯ";s:2:"ȯ";s:3:"ȯ";s:2:"Ȱ";s:5:"Ȱ";s:2:"ȱ";s:5:"ȱ";s:2:"Ȳ";s:3:"Ȳ";s:2:"ȳ";s:3:"ȳ";s:2:"̀";s:2:"̀";s:2:"́";s:2:"́";s:2:"̓";s:2:"̓";s:2:"̈́";s:4:"̈́";s:2:"ʹ";s:2:"ʹ";s:2:";";s:1:";";s:2:"΅";s:4:"΅";s:2:"Ά";s:4:"Ά";s:2:"·";s:2:"·";s:2:"Έ";s:4:"Έ";s:2:"Ή";s:4:"Ή";s:2:"Ί";s:4:"Ί";s:2:"Ό";s:4:"Ό";s:2:"Ύ";s:4:"Ύ";s:2:"Ώ";s:4:"Ώ";s:2:"ΐ";s:6:"ΐ";s:2:"Ϊ";s:4:"Ϊ";s:2:"Ϋ";s:4:"Ϋ";s:2:"ά";s:4:"ά";s:2:"έ";s:4:"έ";s:2:"ή";s:4:"ή";s:2:"ί";s:4:"ί";s:2:"ΰ";s:6:"ΰ";s:2:"ϊ";s:4:"ϊ";s:2:"ϋ";s:4:"ϋ";s:2:"ό";s:4:"ό";s:2:"ύ";s:4:"ύ";s:2:"ώ";s:4:"ώ";s:2:"ϓ";s:4:"ϓ";s:2:"ϔ";s:4:"ϔ";s:2:"Ѐ";s:4:"Ѐ";s:2:"Ё";s:4:"Ё";s:2:"Ѓ";s:4:"Ѓ";s:2:"Ї";s:4:"Ї";s:2:"Ќ";s:4:"Ќ";s:2:"Ѝ";s:4:"Ѝ";s:2:"Ў";s:4:"Ў";s:2:"Й";s:4:"Й";s:2:"й";s:4:"й";s:2:"ѐ";s:4:"ѐ";s:2:"ё";s:4:"ё";s:2:"ѓ";s:4:"ѓ";s:2:"ї";s:4:"ї";s:2:"ќ";s:4:"ќ";s:2:"ѝ";s:4:"ѝ";s:2:"ў";s:4:"ў";s:2:"Ѷ";s:4:"Ѷ";s:2:"ѷ";s:4:"ѷ";s:2:"Ӂ";s:4:"Ӂ";s:2:"ӂ";s:4:"ӂ";s:2:"Ӑ";s:4:"Ӑ";s:2:"ӑ";s:4:"ӑ";s:2:"Ӓ";s:4:"Ӓ";s:2:"ӓ";s:4:"ӓ";s:2:"Ӗ";s:4:"Ӗ";s:2:"ӗ";s:4:"ӗ";s:2:"Ӛ";s:4:"Ӛ";s:2:"ӛ";s:4:"ӛ";s:2:"Ӝ";s:4:"Ӝ";s:2:"ӝ";s:4:"ӝ";s:2:"Ӟ";s:4:"Ӟ";s:2:"ӟ";s:4:"ӟ";s:2:"Ӣ";s:4:"Ӣ";s:2:"ӣ";s:4:"ӣ";s:2:"Ӥ";s:4:"Ӥ";s:2:"ӥ";s:4:"ӥ";s:2:"Ӧ";s:4:"Ӧ";s:2:"ӧ";s:4:"ӧ";s:2:"Ӫ";s:4:"Ӫ";s:2:"ӫ";s:4:"ӫ";s:2:"Ӭ";s:4:"Ӭ";s:2:"ӭ";s:4:"ӭ";s:2:"Ӯ";s:4:"Ӯ";s:2:"ӯ";s:4:"ӯ";s:2:"Ӱ";s:4:"Ӱ";s:2:"ӱ";s:4:"ӱ";s:2:"Ӳ";s:4:"Ӳ";s:2:"ӳ";s:4:"ӳ";s:2:"Ӵ";s:4:"Ӵ";s:2:"ӵ";s:4:"ӵ";s:2:"Ӹ";s:4:"Ӹ";s:2:"ӹ";s:4:"ӹ";s:2:"آ";s:4:"آ";s:2:"أ";s:4:"أ";s:2:"ؤ";s:4:"ؤ";s:2:"إ";s:4:"إ";s:2:"ئ";s:4:"ئ";s:2:"ۀ";s:4:"ۀ";s:2:"ۂ";s:4:"ۂ";s:2:"ۓ";s:4:"ۓ";s:3:"ऩ";s:6:"ऩ";s:3:"ऱ";s:6:"ऱ";s:3:"ऴ";s:6:"ऴ";s:3:"क़";s:6:"क़";s:3:"ख़";s:6:"ख़";s:3:"ग़";s:6:"ग़";s:3:"ज़";s:6:"ज़";s:3:"ड़";s:6:"ड़";s:3:"ढ़";s:6:"ढ़";s:3:"फ़";s:6:"फ़";s:3:"य़";s:6:"य़";s:3:"ো";s:6:"ো";s:3:"ৌ";s:6:"ৌ";s:3:"ড়";s:6:"ড়";s:3:"ঢ়";s:6:"ঢ়";s:3:"য়";s:6:"য়";s:3:"ਲ਼";s:6:"ਲ਼";s:3:"ਸ਼";s:6:"ਸ਼";s:3:"ਖ਼";s:6:"ਖ਼";s:3:"ਗ਼";s:6:"ਗ਼";s:3:"ਜ਼";s:6:"ਜ਼";s:3:"ਫ਼";s:6:"ਫ਼";s:3:"ୈ";s:6:"ୈ";s:3:"ୋ";s:6:"ୋ";s:3:"ୌ";s:6:"ୌ";s:3:"ଡ଼";s:6:"ଡ଼";s:3:"ଢ଼";s:6:"ଢ଼";s:3:"ஔ";s:6:"ஔ";s:3:"ொ";s:6:"ொ";s:3:"ோ";s:6:"ோ";s:3:"ௌ";s:6:"ௌ";s:3:"ై";s:6:"ై";s:3:"ೀ";s:6:"ೀ";s:3:"ೇ";s:6:"ೇ";s:3:"ೈ";s:6:"ೈ";s:3:"ೊ";s:6:"ೊ";s:3:"ೋ";s:9:"ೋ";s:3:"ൊ";s:6:"ൊ";s:3:"ോ";s:6:"ോ";s:3:"ൌ";s:6:"ൌ";s:3:"ේ";s:6:"ේ";s:3:"ො";s:6:"ො";s:3:"ෝ";s:9:"ෝ";s:3:"ෞ";s:6:"ෞ";s:3:"གྷ";s:6:"གྷ";s:3:"ཌྷ";s:6:"ཌྷ";s:3:"དྷ";s:6:"དྷ";s:3:"བྷ";s:6:"བྷ";s:3:"ཛྷ";s:6:"ཛྷ";s:3:"ཀྵ";s:6:"ཀྵ";s:3:"ཱི";s:6:"ཱི";s:3:"ཱུ";s:6:"ཱུ";s:3:"ྲྀ";s:6:"ྲྀ";s:3:"ླྀ";s:6:"ླྀ";s:3:"ཱྀ";s:6:"ཱྀ";s:3:"ྒྷ";s:6:"ྒྷ";s:3:"ྜྷ";s:6:"ྜྷ";s:3:"ྡྷ";s:6:"ྡྷ";s:3:"ྦྷ";s:6:"ྦྷ";s:3:"ྫྷ";s:6:"ྫྷ";s:3:"ྐྵ";s:6:"ྐྵ";s:3:"ဦ";s:6:"ဦ";s:3:"ᬆ";s:6:"ᬆ";s:3:"ᬈ";s:6:"ᬈ";s:3:"ᬊ";s:6:"ᬊ";s:3:"ᬌ";s:6:"ᬌ";s:3:"ᬎ";s:6:"ᬎ";s:3:"ᬒ";s:6:"ᬒ";s:3:"ᬻ";s:6:"ᬻ";s:3:"ᬽ";s:6:"ᬽ";s:3:"ᭀ";s:6:"ᭀ";s:3:"ᭁ";s:6:"ᭁ";s:3:"ᭃ";s:6:"ᭃ";s:3:"Ḁ";s:3:"Ḁ";s:3:"ḁ";s:3:"ḁ";s:3:"Ḃ";s:3:"Ḃ";s:3:"ḃ";s:3:"ḃ";s:3:"Ḅ";s:3:"Ḅ";s:3:"ḅ";s:3:"ḅ";s:3:"Ḇ";s:3:"Ḇ";s:3:"ḇ";s:3:"ḇ";s:3:"Ḉ";s:5:"Ḉ";s:3:"ḉ";s:5:"ḉ";s:3:"Ḋ";s:3:"Ḋ";s:3:"ḋ";s:3:"ḋ";s:3:"Ḍ";s:3:"Ḍ";s:3:"ḍ";s:3:"ḍ";s:3:"Ḏ";s:3:"Ḏ";s:3:"ḏ";s:3:"ḏ";s:3:"Ḑ";s:3:"Ḑ";s:3:"ḑ";s:3:"ḑ";s:3:"Ḓ";s:3:"Ḓ";s:3:"ḓ";s:3:"ḓ";s:3:"Ḕ";s:5:"Ḕ";s:3:"ḕ";s:5:"ḕ";s:3:"Ḗ";s:5:"Ḗ";s:3:"ḗ";s:5:"ḗ";s:3:"Ḙ";s:3:"Ḙ";s:3:"ḙ";s:3:"ḙ";s:3:"Ḛ";s:3:"Ḛ";s:3:"ḛ";s:3:"ḛ";s:3:"Ḝ";s:5:"Ḝ";s:3:"ḝ";s:5:"ḝ";s:3:"Ḟ";s:3:"Ḟ";s:3:"ḟ";s:3:"ḟ";s:3:"Ḡ";s:3:"Ḡ";s:3:"ḡ";s:3:"ḡ";s:3:"Ḣ";s:3:"Ḣ";s:3:"ḣ";s:3:"ḣ";s:3:"Ḥ";s:3:"Ḥ";s:3:"ḥ";s:3:"ḥ";s:3:"Ḧ";s:3:"Ḧ";s:3:"ḧ";s:3:"ḧ";s:3:"Ḩ";s:3:"Ḩ";s:3:"ḩ";s:3:"ḩ";s:3:"Ḫ";s:3:"Ḫ";s:3:"ḫ";s:3:"ḫ";s:3:"Ḭ";s:3:"Ḭ";s:3:"ḭ";s:3:"ḭ";s:3:"Ḯ";s:5:"Ḯ";s:3:"ḯ";s:5:"ḯ";s:3:"Ḱ";s:3:"Ḱ";s:3:"ḱ";s:3:"ḱ";s:3:"Ḳ";s:3:"Ḳ";s:3:"ḳ";s:3:"ḳ";s:3:"Ḵ";s:3:"Ḵ";s:3:"ḵ";s:3:"ḵ";s:3:"Ḷ";s:3:"Ḷ";s:3:"ḷ";s:3:"ḷ";s:3:"Ḹ";s:5:"Ḹ";s:3:"ḹ";s:5:"ḹ";s:3:"Ḻ";s:3:"Ḻ";s:3:"ḻ";s:3:"ḻ";s:3:"Ḽ";s:3:"Ḽ";s:3:"ḽ";s:3:"ḽ";s:3:"Ḿ";s:3:"Ḿ";s:3:"ḿ";s:3:"ḿ";s:3:"Ṁ";s:3:"Ṁ";s:3:"ṁ";s:3:"ṁ";s:3:"Ṃ";s:3:"Ṃ";s:3:"ṃ";s:3:"ṃ";s:3:"Ṅ";s:3:"Ṅ";s:3:"ṅ";s:3:"ṅ";s:3:"Ṇ";s:3:"Ṇ";s:3:"ṇ";s:3:"ṇ";s:3:"Ṉ";s:3:"Ṉ";s:3:"ṉ";s:3:"ṉ";s:3:"Ṋ";s:3:"Ṋ";s:3:"ṋ";s:3:"ṋ";s:3:"Ṍ";s:5:"Ṍ";s:3:"ṍ";s:5:"ṍ";s:3:"Ṏ";s:5:"Ṏ";s:3:"ṏ";s:5:"ṏ";s:3:"Ṑ";s:5:"Ṑ";s:3:"ṑ";s:5:"ṑ";s:3:"Ṓ";s:5:"Ṓ";s:3:"ṓ";s:5:"ṓ";s:3:"Ṕ";s:3:"Ṕ";s:3:"ṕ";s:3:"ṕ";s:3:"Ṗ";s:3:"Ṗ";s:3:"ṗ";s:3:"ṗ";s:3:"Ṙ";s:3:"Ṙ";s:3:"ṙ";s:3:"ṙ";s:3:"Ṛ";s:3:"Ṛ";s:3:"ṛ";s:3:"ṛ";s:3:"Ṝ";s:5:"Ṝ";s:3:"ṝ";s:5:"ṝ";s:3:"Ṟ";s:3:"Ṟ";s:3:"ṟ";s:3:"ṟ";s:3:"Ṡ";s:3:"Ṡ";s:3:"ṡ";s:3:"ṡ";s:3:"Ṣ";s:3:"Ṣ";s:3:"ṣ";s:3:"ṣ";s:3:"Ṥ";s:5:"Ṥ";s:3:"ṥ";s:5:"ṥ";s:3:"Ṧ";s:5:"Ṧ";s:3:"ṧ";s:5:"ṧ";s:3:"Ṩ";s:5:"Ṩ";s:3:"ṩ";s:5:"ṩ";s:3:"Ṫ";s:3:"Ṫ";s:3:"ṫ";s:3:"ṫ";s:3:"Ṭ";s:3:"Ṭ";s:3:"ṭ";s:3:"ṭ";s:3:"Ṯ";s:3:"Ṯ";s:3:"ṯ";s:3:"ṯ";s:3:"Ṱ";s:3:"Ṱ";s:3:"ṱ";s:3:"ṱ";s:3:"Ṳ";s:3:"Ṳ";s:3:"ṳ";s:3:"ṳ";s:3:"Ṵ";s:3:"Ṵ";s:3:"ṵ";s:3:"ṵ";s:3:"Ṷ";s:3:"Ṷ";s:3:"ṷ";s:3:"ṷ";s:3:"Ṹ";s:5:"Ṹ";s:3:"ṹ";s:5:"ṹ";s:3:"Ṻ";s:5:"Ṻ";s:3:"ṻ";s:5:"ṻ";s:3:"Ṽ";s:3:"Ṽ";s:3:"ṽ";s:3:"ṽ";s:3:"Ṿ";s:3:"Ṿ";s:3:"ṿ";s:3:"ṿ";s:3:"Ẁ";s:3:"Ẁ";s:3:"ẁ";s:3:"ẁ";s:3:"Ẃ";s:3:"Ẃ";s:3:"ẃ";s:3:"ẃ";s:3:"Ẅ";s:3:"Ẅ";s:3:"ẅ";s:3:"ẅ";s:3:"Ẇ";s:3:"Ẇ";s:3:"ẇ";s:3:"ẇ";s:3:"Ẉ";s:3:"Ẉ";s:3:"ẉ";s:3:"ẉ";s:3:"Ẋ";s:3:"Ẋ";s:3:"ẋ";s:3:"ẋ";s:3:"Ẍ";s:3:"Ẍ";s:3:"ẍ";s:3:"ẍ";s:3:"Ẏ";s:3:"Ẏ";s:3:"ẏ";s:3:"ẏ";s:3:"Ẑ";s:3:"Ẑ";s:3:"ẑ";s:3:"ẑ";s:3:"Ẓ";s:3:"Ẓ";s:3:"ẓ";s:3:"ẓ";s:3:"Ẕ";s:3:"Ẕ";s:3:"ẕ";s:3:"ẕ";s:3:"ẖ";s:3:"ẖ";s:3:"ẗ";s:3:"ẗ";s:3:"ẘ";s:3:"ẘ";s:3:"ẙ";s:3:"ẙ";s:3:"ẛ";s:4:"ẛ";s:3:"Ạ";s:3:"Ạ";s:3:"ạ";s:3:"ạ";s:3:"Ả";s:3:"Ả";s:3:"ả";s:3:"ả";s:3:"Ấ";s:5:"Ấ";s:3:"ấ";s:5:"ấ";s:3:"Ầ";s:5:"Ầ";s:3:"ầ";s:5:"ầ";s:3:"Ẩ";s:5:"Ẩ";s:3:"ẩ";s:5:"ẩ";s:3:"Ẫ";s:5:"Ẫ";s:3:"ẫ";s:5:"ẫ";s:3:"Ậ";s:5:"Ậ";s:3:"ậ";s:5:"ậ";s:3:"Ắ";s:5:"Ắ";s:3:"ắ";s:5:"ắ";s:3:"Ằ";s:5:"Ằ";s:3:"ằ";s:5:"ằ";s:3:"Ẳ";s:5:"Ẳ";s:3:"ẳ";s:5:"ẳ";s:3:"Ẵ";s:5:"Ẵ";s:3:"ẵ";s:5:"ẵ";s:3:"Ặ";s:5:"Ặ";s:3:"ặ";s:5:"ặ";s:3:"Ẹ";s:3:"Ẹ";s:3:"ẹ";s:3:"ẹ";s:3:"Ẻ";s:3:"Ẻ";s:3:"ẻ";s:3:"ẻ";s:3:"Ẽ";s:3:"Ẽ";s:3:"ẽ";s:3:"ẽ";s:3:"Ế";s:5:"Ế";s:3:"ế";s:5:"ế";s:3:"Ề";s:5:"Ề";s:3:"ề";s:5:"ề";s:3:"Ể";s:5:"Ể";s:3:"ể";s:5:"ể";s:3:"Ễ";s:5:"Ễ";s:3:"ễ";s:5:"ễ";s:3:"Ệ";s:5:"Ệ";s:3:"ệ";s:5:"ệ";s:3:"Ỉ";s:3:"Ỉ";s:3:"ỉ";s:3:"ỉ";s:3:"Ị";s:3:"Ị";s:3:"ị";s:3:"ị";s:3:"Ọ";s:3:"Ọ";s:3:"ọ";s:3:"ọ";s:3:"Ỏ";s:3:"Ỏ";s:3:"ỏ";s:3:"ỏ";s:3:"Ố";s:5:"Ố";s:3:"ố";s:5:"ố";s:3:"Ồ";s:5:"Ồ";s:3:"ồ";s:5:"ồ";s:3:"Ổ";s:5:"Ổ";s:3:"ổ";s:5:"ổ";s:3:"Ỗ";s:5:"Ỗ";s:3:"ỗ";s:5:"ỗ";s:3:"Ộ";s:5:"Ộ";s:3:"ộ";s:5:"ộ";s:3:"Ớ";s:5:"Ớ";s:3:"ớ";s:5:"ớ";s:3:"Ờ";s:5:"Ờ";s:3:"ờ";s:5:"ờ";s:3:"Ở";s:5:"Ở";s:3:"ở";s:5:"ở";s:3:"Ỡ";s:5:"Ỡ";s:3:"ỡ";s:5:"ỡ";s:3:"Ợ";s:5:"Ợ";s:3:"ợ";s:5:"ợ";s:3:"Ụ";s:3:"Ụ";s:3:"ụ";s:3:"ụ";s:3:"Ủ";s:3:"Ủ";s:3:"ủ";s:3:"ủ";s:3:"Ứ";s:5:"Ứ";s:3:"ứ";s:5:"ứ";s:3:"Ừ";s:5:"Ừ";s:3:"ừ";s:5:"ừ";s:3:"Ử";s:5:"Ử";s:3:"ử";s:5:"ử";s:3:"Ữ";s:5:"Ữ";s:3:"ữ";s:5:"ữ";s:3:"Ự";s:5:"Ự";s:3:"ự";s:5:"ự";s:3:"Ỳ";s:3:"Ỳ";s:3:"ỳ";s:3:"ỳ";s:3:"Ỵ";s:3:"Ỵ";s:3:"ỵ";s:3:"ỵ";s:3:"Ỷ";s:3:"Ỷ";s:3:"ỷ";s:3:"ỷ";s:3:"Ỹ";s:3:"Ỹ";s:3:"ỹ";s:3:"ỹ";s:3:"ἀ";s:4:"ἀ";s:3:"ἁ";s:4:"ἁ";s:3:"ἂ";s:6:"ἂ";s:3:"ἃ";s:6:"ἃ";s:3:"ἄ";s:6:"ἄ";s:3:"ἅ";s:6:"ἅ";s:3:"ἆ";s:6:"ἆ";s:3:"ἇ";s:6:"ἇ";s:3:"Ἀ";s:4:"Ἀ";s:3:"Ἁ";s:4:"Ἁ";s:3:"Ἂ";s:6:"Ἂ";s:3:"Ἃ";s:6:"Ἃ";s:3:"Ἄ";s:6:"Ἄ";s:3:"Ἅ";s:6:"Ἅ";s:3:"Ἆ";s:6:"Ἆ";s:3:"Ἇ";s:6:"Ἇ";s:3:"ἐ";s:4:"ἐ";s:3:"ἑ";s:4:"ἑ";s:3:"ἒ";s:6:"ἒ";s:3:"ἓ";s:6:"ἓ";s:3:"ἔ";s:6:"ἔ";s:3:"ἕ";s:6:"ἕ";s:3:"Ἐ";s:4:"Ἐ";s:3:"Ἑ";s:4:"Ἑ";s:3:"Ἒ";s:6:"Ἒ";s:3:"Ἓ";s:6:"Ἓ";s:3:"Ἔ";s:6:"Ἔ";s:3:"Ἕ";s:6:"Ἕ";s:3:"ἠ";s:4:"ἠ";s:3:"ἡ";s:4:"ἡ";s:3:"ἢ";s:6:"ἢ";s:3:"ἣ";s:6:"ἣ";s:3:"ἤ";s:6:"ἤ";s:3:"ἥ";s:6:"ἥ";s:3:"ἦ";s:6:"ἦ";s:3:"ἧ";s:6:"ἧ";s:3:"Ἠ";s:4:"Ἠ";s:3:"Ἡ";s:4:"Ἡ";s:3:"Ἢ";s:6:"Ἢ";s:3:"Ἣ";s:6:"Ἣ";s:3:"Ἤ";s:6:"Ἤ";s:3:"Ἥ";s:6:"Ἥ";s:3:"Ἦ";s:6:"Ἦ";s:3:"Ἧ";s:6:"Ἧ";s:3:"ἰ";s:4:"ἰ";s:3:"ἱ";s:4:"ἱ";s:3:"ἲ";s:6:"ἲ";s:3:"ἳ";s:6:"ἳ";s:3:"ἴ";s:6:"ἴ";s:3:"ἵ";s:6:"ἵ";s:3:"ἶ";s:6:"ἶ";s:3:"ἷ";s:6:"ἷ";s:3:"Ἰ";s:4:"Ἰ";s:3:"Ἱ";s:4:"Ἱ";s:3:"Ἲ";s:6:"Ἲ";s:3:"Ἳ";s:6:"Ἳ";s:3:"Ἴ";s:6:"Ἴ";s:3:"Ἵ";s:6:"Ἵ";s:3:"Ἶ";s:6:"Ἶ";s:3:"Ἷ";s:6:"Ἷ";s:3:"ὀ";s:4:"ὀ";s:3:"ὁ";s:4:"ὁ";s:3:"ὂ";s:6:"ὂ";s:3:"ὃ";s:6:"ὃ";s:3:"ὄ";s:6:"ὄ";s:3:"ὅ";s:6:"ὅ";s:3:"Ὀ";s:4:"Ὀ";s:3:"Ὁ";s:4:"Ὁ";s:3:"Ὂ";s:6:"Ὂ";s:3:"Ὃ";s:6:"Ὃ";s:3:"Ὄ";s:6:"Ὄ";s:3:"Ὅ";s:6:"Ὅ";s:3:"ὐ";s:4:"ὐ";s:3:"ὑ";s:4:"ὑ";s:3:"ὒ";s:6:"ὒ";s:3:"ὓ";s:6:"ὓ";s:3:"ὔ";s:6:"ὔ";s:3:"ὕ";s:6:"ὕ";s:3:"ὖ";s:6:"ὖ";s:3:"ὗ";s:6:"ὗ";s:3:"Ὑ";s:4:"Ὑ";s:3:"Ὓ";s:6:"Ὓ";s:3:"Ὕ";s:6:"Ὕ";s:3:"Ὗ";s:6:"Ὗ";s:3:"ὠ";s:4:"ὠ";s:3:"ὡ";s:4:"ὡ";s:3:"ὢ";s:6:"ὢ";s:3:"ὣ";s:6:"ὣ";s:3:"ὤ";s:6:"ὤ";s:3:"ὥ";s:6:"ὥ";s:3:"ὦ";s:6:"ὦ";s:3:"ὧ";s:6:"ὧ";s:3:"Ὠ";s:4:"Ὠ";s:3:"Ὡ";s:4:"Ὡ";s:3:"Ὢ";s:6:"Ὢ";s:3:"Ὣ";s:6:"Ὣ";s:3:"Ὤ";s:6:"Ὤ";s:3:"Ὥ";s:6:"Ὥ";s:3:"Ὦ";s:6:"Ὦ";s:3:"Ὧ";s:6:"Ὧ";s:3:"ὰ";s:4:"ὰ";s:3:"ά";s:4:"ά";s:3:"ὲ";s:4:"ὲ";s:3:"έ";s:4:"έ";s:3:"ὴ";s:4:"ὴ";s:3:"ή";s:4:"ή";s:3:"ὶ";s:4:"ὶ";s:3:"ί";s:4:"ί";s:3:"ὸ";s:4:"ὸ";s:3:"ό";s:4:"ό";s:3:"ὺ";s:4:"ὺ";s:3:"ύ";s:4:"ύ";s:3:"ὼ";s:4:"ὼ";s:3:"ώ";s:4:"ώ";s:3:"ᾀ";s:6:"ᾀ";s:3:"ᾁ";s:6:"ᾁ";s:3:"ᾂ";s:8:"ᾂ";s:3:"ᾃ";s:8:"ᾃ";s:3:"ᾄ";s:8:"ᾄ";s:3:"ᾅ";s:8:"ᾅ";s:3:"ᾆ";s:8:"ᾆ";s:3:"ᾇ";s:8:"ᾇ";s:3:"ᾈ";s:6:"ᾈ";s:3:"ᾉ";s:6:"ᾉ";s:3:"ᾊ";s:8:"ᾊ";s:3:"ᾋ";s:8:"ᾋ";s:3:"ᾌ";s:8:"ᾌ";s:3:"ᾍ";s:8:"ᾍ";s:3:"ᾎ";s:8:"ᾎ";s:3:"ᾏ";s:8:"ᾏ";s:3:"ᾐ";s:6:"ᾐ";s:3:"ᾑ";s:6:"ᾑ";s:3:"ᾒ";s:8:"ᾒ";s:3:"ᾓ";s:8:"ᾓ";s:3:"ᾔ";s:8:"ᾔ";s:3:"ᾕ";s:8:"ᾕ";s:3:"ᾖ";s:8:"ᾖ";s:3:"ᾗ";s:8:"ᾗ";s:3:"ᾘ";s:6:"ᾘ";s:3:"ᾙ";s:6:"ᾙ";s:3:"ᾚ";s:8:"ᾚ";s:3:"ᾛ";s:8:"ᾛ";s:3:"ᾜ";s:8:"ᾜ";s:3:"ᾝ";s:8:"ᾝ";s:3:"ᾞ";s:8:"ᾞ";s:3:"ᾟ";s:8:"ᾟ";s:3:"ᾠ";s:6:"ᾠ";s:3:"ᾡ";s:6:"ᾡ";s:3:"ᾢ";s:8:"ᾢ";s:3:"ᾣ";s:8:"ᾣ";s:3:"ᾤ";s:8:"ᾤ";s:3:"ᾥ";s:8:"ᾥ";s:3:"ᾦ";s:8:"ᾦ";s:3:"ᾧ";s:8:"ᾧ";s:3:"ᾨ";s:6:"ᾨ";s:3:"ᾩ";s:6:"ᾩ";s:3:"ᾪ";s:8:"ᾪ";s:3:"ᾫ";s:8:"ᾫ";s:3:"ᾬ";s:8:"ᾬ";s:3:"ᾭ";s:8:"ᾭ";s:3:"ᾮ";s:8:"ᾮ";s:3:"ᾯ";s:8:"ᾯ";s:3:"ᾰ";s:4:"ᾰ";s:3:"ᾱ";s:4:"ᾱ";s:3:"ᾲ";s:6:"ᾲ";s:3:"ᾳ";s:4:"ᾳ";s:3:"ᾴ";s:6:"ᾴ";s:3:"ᾶ";s:4:"ᾶ";s:3:"ᾷ";s:6:"ᾷ";s:3:"Ᾰ";s:4:"Ᾰ";s:3:"Ᾱ";s:4:"Ᾱ";s:3:"Ὰ";s:4:"Ὰ";s:3:"Ά";s:4:"Ά";s:3:"ᾼ";s:4:"ᾼ";s:3:"ι";s:2:"ι";s:3:"῁";s:4:"῁";s:3:"ῂ";s:6:"ῂ";s:3:"ῃ";s:4:"ῃ";s:3:"ῄ";s:6:"ῄ";s:3:"ῆ";s:4:"ῆ";s:3:"ῇ";s:6:"ῇ";s:3:"Ὲ";s:4:"Ὲ";s:3:"Έ";s:4:"Έ";s:3:"Ὴ";s:4:"Ὴ";s:3:"Ή";s:4:"Ή";s:3:"ῌ";s:4:"ῌ";s:3:"῍";s:5:"῍";s:3:"῎";s:5:"῎";s:3:"῏";s:5:"῏";s:3:"ῐ";s:4:"ῐ";s:3:"ῑ";s:4:"ῑ";s:3:"ῒ";s:6:"ῒ";s:3:"ΐ";s:6:"ΐ";s:3:"ῖ";s:4:"ῖ";s:3:"ῗ";s:6:"ῗ";s:3:"Ῐ";s:4:"Ῐ";s:3:"Ῑ";s:4:"Ῑ";s:3:"Ὶ";s:4:"Ὶ";s:3:"Ί";s:4:"Ί";s:3:"῝";s:5:"῝";s:3:"῞";s:5:"῞";s:3:"῟";s:5:"῟";s:3:"ῠ";s:4:"ῠ";s:3:"ῡ";s:4:"ῡ";s:3:"ῢ";s:6:"ῢ";s:3:"ΰ";s:6:"ΰ";s:3:"ῤ";s:4:"ῤ";s:3:"ῥ";s:4:"ῥ";s:3:"ῦ";s:4:"ῦ";s:3:"ῧ";s:6:"ῧ";s:3:"Ῠ";s:4:"Ῠ";s:3:"Ῡ";s:4:"Ῡ";s:3:"Ὺ";s:4:"Ὺ";s:3:"Ύ";s:4:"Ύ";s:3:"Ῥ";s:4:"Ῥ";s:3:"῭";s:4:"῭";s:3:"΅";s:4:"΅";s:3:"`";s:1:"`";s:3:"ῲ";s:6:"ῲ";s:3:"ῳ";s:4:"ῳ";s:3:"ῴ";s:6:"ῴ";s:3:"ῶ";s:4:"ῶ";s:3:"ῷ";s:6:"ῷ";s:3:"Ὸ";s:4:"Ὸ";s:3:"Ό";s:4:"Ό";s:3:"Ὼ";s:4:"Ὼ";s:3:"Ώ";s:4:"Ώ";s:3:"ῼ";s:4:"ῼ";s:3:"´";s:2:"´";s:3:" ";s:3:" ";s:3:" ";s:3:" ";s:3:"Ω";s:2:"Ω";s:3:"K";s:1:"K";s:3:"Å";s:3:"Å";s:3:"↚";s:5:"↚";s:3:"↛";s:5:"↛";s:3:"↮";s:5:"↮";s:3:"⇍";s:5:"⇍";s:3:"⇎";s:5:"⇎";s:3:"⇏";s:5:"⇏";s:3:"∄";s:5:"∄";s:3:"∉";s:5:"∉";s:3:"∌";s:5:"∌";s:3:"∤";s:5:"∤";s:3:"∦";s:5:"∦";s:3:"≁";s:5:"≁";s:3:"≄";s:5:"≄";s:3:"≇";s:5:"≇";s:3:"≉";s:5:"≉";s:3:"≠";s:3:"≠";s:3:"≢";s:5:"≢";s:3:"≭";s:5:"≭";s:3:"≮";s:3:"≮";s:3:"≯";s:3:"≯";s:3:"≰";s:5:"≰";s:3:"≱";s:5:"≱";s:3:"≴";s:5:"≴";s:3:"≵";s:5:"≵";s:3:"≸";s:5:"≸";s:3:"≹";s:5:"≹";s:3:"⊀";s:5:"⊀";s:3:"⊁";s:5:"⊁";s:3:"⊄";s:5:"⊄";s:3:"⊅";s:5:"⊅";s:3:"⊈";s:5:"⊈";s:3:"⊉";s:5:"⊉";s:3:"⊬";s:5:"⊬";s:3:"⊭";s:5:"⊭";s:3:"⊮";s:5:"⊮";s:3:"⊯";s:5:"⊯";s:3:"⋠";s:5:"⋠";s:3:"⋡";s:5:"⋡";s:3:"⋢";s:5:"⋢";s:3:"⋣";s:5:"⋣";s:3:"⋪";s:5:"⋪";s:3:"⋫";s:5:"⋫";s:3:"⋬";s:5:"⋬";s:3:"⋭";s:5:"⋭";s:3:"〈";s:3:"〈";s:3:"〉";s:3:"〉";s:3:"⫝̸";s:5:"⫝̸";s:3:"が";s:6:"が";s:3:"ぎ";s:6:"ぎ";s:3:"ぐ";s:6:"ぐ";s:3:"げ";s:6:"げ";s:3:"ご";s:6:"ご";s:3:"ざ";s:6:"ざ";s:3:"じ";s:6:"じ";s:3:"ず";s:6:"ず";s:3:"ぜ";s:6:"ぜ";s:3:"ぞ";s:6:"ぞ";s:3:"だ";s:6:"だ";s:3:"ぢ";s:6:"ぢ";s:3:"づ";s:6:"づ";s:3:"で";s:6:"で";s:3:"ど";s:6:"ど";s:3:"ば";s:6:"ば";s:3:"ぱ";s:6:"ぱ";s:3:"び";s:6:"び";s:3:"ぴ";s:6:"ぴ";s:3:"ぶ";s:6:"ぶ";s:3:"ぷ";s:6:"ぷ";s:3:"べ";s:6:"べ";s:3:"ぺ";s:6:"ぺ";s:3:"ぼ";s:6:"ぼ";s:3:"ぽ";s:6:"ぽ";s:3:"ゔ";s:6:"ゔ";s:3:"ゞ";s:6:"ゞ";s:3:"ガ";s:6:"ガ";s:3:"ギ";s:6:"ギ";s:3:"グ";s:6:"グ";s:3:"ゲ";s:6:"ゲ";s:3:"ゴ";s:6:"ゴ";s:3:"ザ";s:6:"ザ";s:3:"ジ";s:6:"ジ";s:3:"ズ";s:6:"ズ";s:3:"ゼ";s:6:"ゼ";s:3:"ゾ";s:6:"ゾ";s:3:"ダ";s:6:"ダ";s:3:"ヂ";s:6:"ヂ";s:3:"ヅ";s:6:"ヅ";s:3:"デ";s:6:"デ";s:3:"ド";s:6:"ド";s:3:"バ";s:6:"バ";s:3:"パ";s:6:"パ";s:3:"ビ";s:6:"ビ";s:3:"ピ";s:6:"ピ";s:3:"ブ";s:6:"ブ";s:3:"プ";s:6:"プ";s:3:"ベ";s:6:"ベ";s:3:"ペ";s:6:"ペ";s:3:"ボ";s:6:"ボ";s:3:"ポ";s:6:"ポ";s:3:"ヴ";s:6:"ヴ";s:3:"ヷ";s:6:"ヷ";s:3:"ヸ";s:6:"ヸ";s:3:"ヹ";s:6:"ヹ";s:3:"ヺ";s:6:"ヺ";s:3:"ヾ";s:6:"ヾ";s:3:"豈";s:3:"豈";s:3:"更";s:3:"更";s:3:"車";s:3:"車";s:3:"賈";s:3:"賈";s:3:"滑";s:3:"滑";s:3:"串";s:3:"串";s:3:"句";s:3:"句";s:3:"龜";s:3:"龜";s:3:"龜";s:3:"龜";s:3:"契";s:3:"契";s:3:"金";s:3:"金";s:3:"喇";s:3:"喇";s:3:"奈";s:3:"奈";s:3:"懶";s:3:"懶";s:3:"癩";s:3:"癩";s:3:"羅";s:3:"羅";s:3:"蘿";s:3:"蘿";s:3:"螺";s:3:"螺";s:3:"裸";s:3:"裸";s:3:"邏";s:3:"邏";s:3:"樂";s:3:"樂";s:3:"洛";s:3:"洛";s:3:"烙";s:3:"烙";s:3:"珞";s:3:"珞";s:3:"落";s:3:"落";s:3:"酪";s:3:"酪";s:3:"駱";s:3:"駱";s:3:"亂";s:3:"亂";s:3:"卵";s:3:"卵";s:3:"欄";s:3:"欄";s:3:"爛";s:3:"爛";s:3:"蘭";s:3:"蘭";s:3:"鸞";s:3:"鸞";s:3:"嵐";s:3:"嵐";s:3:"濫";s:3:"濫";s:3:"藍";s:3:"藍";s:3:"襤";s:3:"襤";s:3:"拉";s:3:"拉";s:3:"臘";s:3:"臘";s:3:"蠟";s:3:"蠟";s:3:"廊";s:3:"廊";s:3:"朗";s:3:"朗";s:3:"浪";s:3:"浪";s:3:"狼";s:3:"狼";s:3:"郎";s:3:"郎";s:3:"來";s:3:"來";s:3:"冷";s:3:"冷";s:3:"勞";s:3:"勞";s:3:"擄";s:3:"擄";s:3:"櫓";s:3:"櫓";s:3:"爐";s:3:"爐";s:3:"盧";s:3:"盧";s:3:"老";s:3:"老";s:3:"蘆";s:3:"蘆";s:3:"虜";s:3:"虜";s:3:"路";s:3:"路";s:3:"露";s:3:"露";s:3:"魯";s:3:"魯";s:3:"鷺";s:3:"鷺";s:3:"碌";s:3:"碌";s:3:"祿";s:3:"祿";s:3:"綠";s:3:"綠";s:3:"菉";s:3:"菉";s:3:"錄";s:3:"錄";s:3:"鹿";s:3:"鹿";s:3:"論";s:3:"論";s:3:"壟";s:3:"壟";s:3:"弄";s:3:"弄";s:3:"籠";s:3:"籠";s:3:"聾";s:3:"聾";s:3:"牢";s:3:"牢";s:3:"磊";s:3:"磊";s:3:"賂";s:3:"賂";s:3:"雷";s:3:"雷";s:3:"壘";s:3:"壘";s:3:"屢";s:3:"屢";s:3:"樓";s:3:"樓";s:3:"淚";s:3:"淚";s:3:"漏";s:3:"漏";s:3:"累";s:3:"累";s:3:"縷";s:3:"縷";s:3:"陋";s:3:"陋";s:3:"勒";s:3:"勒";s:3:"肋";s:3:"肋";s:3:"凜";s:3:"凜";s:3:"凌";s:3:"凌";s:3:"稜";s:3:"稜";s:3:"綾";s:3:"綾";s:3:"菱";s:3:"菱";s:3:"陵";s:3:"陵";s:3:"讀";s:3:"讀";s:3:"拏";s:3:"拏";s:3:"樂";s:3:"樂";s:3:"諾";s:3:"諾";s:3:"丹";s:3:"丹";s:3:"寧";s:3:"寧";s:3:"怒";s:3:"怒";s:3:"率";s:3:"率";s:3:"異";s:3:"異";s:3:"北";s:3:"北";s:3:"磻";s:3:"磻";s:3:"便";s:3:"便";s:3:"復";s:3:"復";s:3:"不";s:3:"不";s:3:"泌";s:3:"泌";s:3:"數";s:3:"數";s:3:"索";s:3:"索";s:3:"參";s:3:"參";s:3:"塞";s:3:"塞";s:3:"省";s:3:"省";s:3:"葉";s:3:"葉";s:3:"說";s:3:"說";s:3:"殺";s:3:"殺";s:3:"辰";s:3:"辰";s:3:"沈";s:3:"沈";s:3:"拾";s:3:"拾";s:3:"若";s:3:"若";s:3:"掠";s:3:"掠";s:3:"略";s:3:"略";s:3:"亮";s:3:"亮";s:3:"兩";s:3:"兩";s:3:"凉";s:3:"凉";s:3:"梁";s:3:"梁";s:3:"糧";s:3:"糧";s:3:"良";s:3:"良";s:3:"諒";s:3:"諒";s:3:"量";s:3:"量";s:3:"勵";s:3:"勵";s:3:"呂";s:3:"呂";s:3:"女";s:3:"女";s:3:"廬";s:3:"廬";s:3:"旅";s:3:"旅";s:3:"濾";s:3:"濾";s:3:"礪";s:3:"礪";s:3:"閭";s:3:"閭";s:3:"驪";s:3:"驪";s:3:"麗";s:3:"麗";s:3:"黎";s:3:"黎";s:3:"力";s:3:"力";s:3:"曆";s:3:"曆";s:3:"歷";s:3:"歷";s:3:"轢";s:3:"轢";s:3:"年";s:3:"年";s:3:"憐";s:3:"憐";s:3:"戀";s:3:"戀";s:3:"撚";s:3:"撚";s:3:"漣";s:3:"漣";s:3:"煉";s:3:"煉";s:3:"璉";s:3:"璉";s:3:"秊";s:3:"秊";s:3:"練";s:3:"練";s:3:"聯";s:3:"聯";s:3:"輦";s:3:"輦";s:3:"蓮";s:3:"蓮";s:3:"連";s:3:"連";s:3:"鍊";s:3:"鍊";s:3:"列";s:3:"列";s:3:"劣";s:3:"劣";s:3:"咽";s:3:"咽";s:3:"烈";s:3:"烈";s:3:"裂";s:3:"裂";s:3:"說";s:3:"說";s:3:"廉";s:3:"廉";s:3:"念";s:3:"念";s:3:"捻";s:3:"捻";s:3:"殮";s:3:"殮";s:3:"簾";s:3:"簾";s:3:"獵";s:3:"獵";s:3:"令";s:3:"令";s:3:"囹";s:3:"囹";s:3:"寧";s:3:"寧";s:3:"嶺";s:3:"嶺";s:3:"怜";s:3:"怜";s:3:"玲";s:3:"玲";s:3:"瑩";s:3:"瑩";s:3:"羚";s:3:"羚";s:3:"聆";s:3:"聆";s:3:"鈴";s:3:"鈴";s:3:"零";s:3:"零";s:3:"靈";s:3:"靈";s:3:"領";s:3:"領";s:3:"例";s:3:"例";s:3:"禮";s:3:"禮";s:3:"醴";s:3:"醴";s:3:"隸";s:3:"隸";s:3:"惡";s:3:"惡";s:3:"了";s:3:"了";s:3:"僚";s:3:"僚";s:3:"寮";s:3:"寮";s:3:"尿";s:3:"尿";s:3:"料";s:3:"料";s:3:"樂";s:3:"樂";s:3:"燎";s:3:"燎";s:3:"療";s:3:"療";s:3:"蓼";s:3:"蓼";s:3:"遼";s:3:"遼";s:3:"龍";s:3:"龍";s:3:"暈";s:3:"暈";s:3:"阮";s:3:"阮";s:3:"劉";s:3:"劉";s:3:"杻";s:3:"杻";s:3:"柳";s:3:"柳";s:3:"流";s:3:"流";s:3:"溜";s:3:"溜";s:3:"琉";s:3:"琉";s:3:"留";s:3:"留";s:3:"硫";s:3:"硫";s:3:"紐";s:3:"紐";s:3:"類";s:3:"類";s:3:"六";s:3:"六";s:3:"戮";s:3:"戮";s:3:"陸";s:3:"陸";s:3:"倫";s:3:"倫";s:3:"崙";s:3:"崙";s:3:"淪";s:3:"淪";s:3:"輪";s:3:"輪";s:3:"律";s:3:"律";s:3:"慄";s:3:"慄";s:3:"栗";s:3:"栗";s:3:"率";s:3:"率";s:3:"隆";s:3:"隆";s:3:"利";s:3:"利";s:3:"吏";s:3:"吏";s:3:"履";s:3:"履";s:3:"易";s:3:"易";s:3:"李";s:3:"李";s:3:"梨";s:3:"梨";s:3:"泥";s:3:"泥";s:3:"理";s:3:"理";s:3:"痢";s:3:"痢";s:3:"罹";s:3:"罹";s:3:"裏";s:3:"裏";s:3:"裡";s:3:"裡";s:3:"里";s:3:"里";s:3:"離";s:3:"離";s:3:"匿";s:3:"匿";s:3:"溺";s:3:"溺";s:3:"吝";s:3:"吝";s:3:"燐";s:3:"燐";s:3:"璘";s:3:"璘";s:3:"藺";s:3:"藺";s:3:"隣";s:3:"隣";s:3:"鱗";s:3:"鱗";s:3:"麟";s:3:"麟";s:3:"林";s:3:"林";s:3:"淋";s:3:"淋";s:3:"臨";s:3:"臨";s:3:"立";s:3:"立";s:3:"笠";s:3:"笠";s:3:"粒";s:3:"粒";s:3:"狀";s:3:"狀";s:3:"炙";s:3:"炙";s:3:"識";s:3:"識";s:3:"什";s:3:"什";s:3:"茶";s:3:"茶";s:3:"刺";s:3:"刺";s:3:"切";s:3:"切";s:3:"度";s:3:"度";s:3:"拓";s:3:"拓";s:3:"糖";s:3:"糖";s:3:"宅";s:3:"宅";s:3:"洞";s:3:"洞";s:3:"暴";s:3:"暴";s:3:"輻";s:3:"輻";s:3:"行";s:3:"行";s:3:"降";s:3:"降";s:3:"見";s:3:"見";s:3:"廓";s:3:"廓";s:3:"兀";s:3:"兀";s:3:"嗀";s:3:"嗀";s:3:"塚";s:3:"塚";s:3:"晴";s:3:"晴";s:3:"凞";s:3:"凞";s:3:"猪";s:3:"猪";s:3:"益";s:3:"益";s:3:"礼";s:3:"礼";s:3:"神";s:3:"神";s:3:"祥";s:3:"祥";s:3:"福";s:3:"福";s:3:"靖";s:3:"靖";s:3:"精";s:3:"精";s:3:"羽";s:3:"羽";s:3:"蘒";s:3:"蘒";s:3:"諸";s:3:"諸";s:3:"逸";s:3:"逸";s:3:"都";s:3:"都";s:3:"飯";s:3:"飯";s:3:"飼";s:3:"飼";s:3:"館";s:3:"館";s:3:"鶴";s:3:"鶴";s:3:"侮";s:3:"侮";s:3:"僧";s:3:"僧";s:3:"免";s:3:"免";s:3:"勉";s:3:"勉";s:3:"勤";s:3:"勤";s:3:"卑";s:3:"卑";s:3:"喝";s:3:"喝";s:3:"嘆";s:3:"嘆";s:3:"器";s:3:"器";s:3:"塀";s:3:"塀";s:3:"墨";s:3:"墨";s:3:"層";s:3:"層";s:3:"屮";s:3:"屮";s:3:"悔";s:3:"悔";s:3:"慨";s:3:"慨";s:3:"憎";s:3:"憎";s:3:"懲";s:3:"懲";s:3:"敏";s:3:"敏";s:3:"既";s:3:"既";s:3:"暑";s:3:"暑";s:3:"梅";s:3:"梅";s:3:"海";s:3:"海";s:3:"渚";s:3:"渚";s:3:"漢";s:3:"漢";s:3:"煮";s:3:"煮";s:3:"爫";s:3:"爫";s:3:"琢";s:3:"琢";s:3:"碑";s:3:"碑";s:3:"社";s:3:"社";s:3:"祉";s:3:"祉";s:3:"祈";s:3:"祈";s:3:"祐";s:3:"祐";s:3:"祖";s:3:"祖";s:3:"祝";s:3:"祝";s:3:"禍";s:3:"禍";s:3:"禎";s:3:"禎";s:3:"穀";s:3:"穀";s:3:"突";s:3:"突";s:3:"節";s:3:"節";s:3:"練";s:3:"練";s:3:"縉";s:3:"縉";s:3:"繁";s:3:"繁";s:3:"署";s:3:"署";s:3:"者";s:3:"者";s:3:"臭";s:3:"臭";s:3:"艹";s:3:"艹";s:3:"艹";s:3:"艹";s:3:"著";s:3:"著";s:3:"褐";s:3:"褐";s:3:"視";s:3:"視";s:3:"謁";s:3:"謁";s:3:"謹";s:3:"謹";s:3:"賓";s:3:"賓";s:3:"贈";s:3:"贈";s:3:"辶";s:3:"辶";s:3:"逸";s:3:"逸";s:3:"難";s:3:"難";s:3:"響";s:3:"響";s:3:"頻";s:3:"頻";s:3:"並";s:3:"並";s:3:"况";s:3:"况";s:3:"全";s:3:"全";s:3:"侀";s:3:"侀";s:3:"充";s:3:"充";s:3:"冀";s:3:"冀";s:3:"勇";s:3:"勇";s:3:"勺";s:3:"勺";s:3:"喝";s:3:"喝";s:3:"啕";s:3:"啕";s:3:"喙";s:3:"喙";s:3:"嗢";s:3:"嗢";s:3:"塚";s:3:"塚";s:3:"墳";s:3:"墳";s:3:"奄";s:3:"奄";s:3:"奔";s:3:"奔";s:3:"婢";s:3:"婢";s:3:"嬨";s:3:"嬨";s:3:"廒";s:3:"廒";s:3:"廙";s:3:"廙";s:3:"彩";s:3:"彩";s:3:"徭";s:3:"徭";s:3:"惘";s:3:"惘";s:3:"慎";s:3:"慎";s:3:"愈";s:3:"愈";s:3:"憎";s:3:"憎";s:3:"慠";s:3:"慠";s:3:"懲";s:3:"懲";s:3:"戴";s:3:"戴";s:3:"揄";s:3:"揄";s:3:"搜";s:3:"搜";s:3:"摒";s:3:"摒";s:3:"敖";s:3:"敖";s:3:"晴";s:3:"晴";s:3:"朗";s:3:"朗";s:3:"望";s:3:"望";s:3:"杖";s:3:"杖";s:3:"歹";s:3:"歹";s:3:"殺";s:3:"殺";s:3:"流";s:3:"流";s:3:"滛";s:3:"滛";s:3:"滋";s:3:"滋";s:3:"漢";s:3:"漢";s:3:"瀞";s:3:"瀞";s:3:"煮";s:3:"煮";s:3:"瞧";s:3:"瞧";s:3:"爵";s:3:"爵";s:3:"犯";s:3:"犯";s:3:"猪";s:3:"猪";s:3:"瑱";s:3:"瑱";s:3:"甆";s:3:"甆";s:3:"画";s:3:"画";s:3:"瘝";s:3:"瘝";s:3:"瘟";s:3:"瘟";s:3:"益";s:3:"益";s:3:"盛";s:3:"盛";s:3:"直";s:3:"直";s:3:"睊";s:3:"睊";s:3:"着";s:3:"着";s:3:"磌";s:3:"磌";s:3:"窱";s:3:"窱";s:3:"節";s:3:"節";s:3:"类";s:3:"类";s:3:"絛";s:3:"絛";s:3:"練";s:3:"練";s:3:"缾";s:3:"缾";s:3:"者";s:3:"者";s:3:"荒";s:3:"荒";s:3:"華";s:3:"華";s:3:"蝹";s:3:"蝹";s:3:"襁";s:3:"襁";s:3:"覆";s:3:"覆";s:3:"視";s:3:"視";s:3:"調";s:3:"調";s:3:"諸";s:3:"諸";s:3:"請";s:3:"請";s:3:"謁";s:3:"謁";s:3:"諾";s:3:"諾";s:3:"諭";s:3:"諭";s:3:"謹";s:3:"謹";s:3:"變";s:3:"變";s:3:"贈";s:3:"贈";s:3:"輸";s:3:"輸";s:3:"遲";s:3:"遲";s:3:"醙";s:3:"醙";s:3:"鉶";s:3:"鉶";s:3:"陼";s:3:"陼";s:3:"難";s:3:"難";s:3:"靖";s:3:"靖";s:3:"韛";s:3:"韛";s:3:"響";s:3:"響";s:3:"頋";s:3:"頋";s:3:"頻";s:3:"頻";s:3:"鬒";s:3:"鬒";s:3:"龜";s:3:"龜";s:3:"𢡊";s:4:"𢡊";s:3:"𢡄";s:4:"𢡄";s:3:"𣏕";s:4:"𣏕";s:3:"㮝";s:3:"㮝";s:3:"䀘";s:3:"䀘";s:3:"䀹";s:3:"䀹";s:3:"𥉉";s:4:"𥉉";s:3:"𥳐";s:4:"𥳐";s:3:"𧻓";s:4:"𧻓";s:3:"齃";s:3:"齃";s:3:"龎";s:3:"龎";s:3:"יִ";s:4:"יִ";s:3:"ײַ";s:4:"ײַ";s:3:"שׁ";s:4:"שׁ";s:3:"שׂ";s:4:"שׂ";s:3:"שּׁ";s:6:"שּׁ";s:3:"שּׂ";s:6:"שּׂ";s:3:"אַ";s:4:"אַ";s:3:"אָ";s:4:"אָ";s:3:"אּ";s:4:"אּ";s:3:"בּ";s:4:"בּ";s:3:"גּ";s:4:"גּ";s:3:"דּ";s:4:"דּ";s:3:"הּ";s:4:"הּ";s:3:"וּ";s:4:"וּ";s:3:"זּ";s:4:"זּ";s:3:"טּ";s:4:"טּ";s:3:"יּ";s:4:"יּ";s:3:"ךּ";s:4:"ךּ";s:3:"כּ";s:4:"כּ";s:3:"לּ";s:4:"לּ";s:3:"מּ";s:4:"מּ";s:3:"נּ";s:4:"נּ";s:3:"סּ";s:4:"סּ";s:3:"ףּ";s:4:"ףּ";s:3:"פּ";s:4:"פּ";s:3:"צּ";s:4:"צּ";s:3:"קּ";s:4:"קּ";s:3:"רּ";s:4:"רּ";s:3:"שּ";s:4:"שּ";s:3:"תּ";s:4:"תּ";s:3:"וֹ";s:4:"וֹ";s:3:"בֿ";s:4:"בֿ";s:3:"כֿ";s:4:"כֿ";s:3:"פֿ";s:4:"פֿ";s:4:"𝅗𝅥";s:8:"𝅗𝅥";s:4:"𝅘𝅥";s:8:"𝅘𝅥";s:4:"𝅘𝅥𝅮";s:12:"𝅘𝅥𝅮";s:4:"𝅘𝅥𝅯";s:12:"𝅘𝅥𝅯";s:4:"𝅘𝅥𝅰";s:12:"𝅘𝅥𝅰";s:4:"𝅘𝅥𝅱";s:12:"𝅘𝅥𝅱";s:4:"𝅘𝅥𝅲";s:12:"𝅘𝅥𝅲";s:4:"𝆹𝅥";s:8:"𝆹𝅥";s:4:"𝆺𝅥";s:8:"𝆺𝅥";s:4:"𝆹𝅥𝅮";s:12:"𝆹𝅥𝅮";s:4:"𝆺𝅥𝅮";s:12:"𝆺𝅥𝅮";s:4:"𝆹𝅥𝅯";s:12:"𝆹𝅥𝅯";s:4:"𝆺𝅥𝅯";s:12:"𝆺𝅥𝅯";s:4:"丽";s:3:"丽";s:4:"丸";s:3:"丸";s:4:"乁";s:3:"乁";s:4:"𠄢";s:4:"𠄢";s:4:"你";s:3:"你";s:4:"侮";s:3:"侮";s:4:"侻";s:3:"侻";s:4:"倂";s:3:"倂";s:4:"偺";s:3:"偺";s:4:"備";s:3:"備";s:4:"僧";s:3:"僧";s:4:"像";s:3:"像";s:4:"㒞";s:3:"㒞";s:4:"𠘺";s:4:"𠘺";s:4:"免";s:3:"免";s:4:"兔";s:3:"兔";s:4:"兤";s:3:"兤";s:4:"具";s:3:"具";s:4:"𠔜";s:4:"𠔜";s:4:"㒹";s:3:"㒹";s:4:"內";s:3:"內";s:4:"再";s:3:"再";s:4:"𠕋";s:4:"𠕋";s:4:"冗";s:3:"冗";s:4:"冤";s:3:"冤";s:4:"仌";s:3:"仌";s:4:"冬";s:3:"冬";s:4:"况";s:3:"况";s:4:"𩇟";s:4:"𩇟";s:4:"凵";s:3:"凵";s:4:"刃";s:3:"刃";s:4:"㓟";s:3:"㓟";s:4:"刻";s:3:"刻";s:4:"剆";s:3:"剆";s:4:"割";s:3:"割";s:4:"剷";s:3:"剷";s:4:"㔕";s:3:"㔕";s:4:"勇";s:3:"勇";s:4:"勉";s:3:"勉";s:4:"勤";s:3:"勤";s:4:"勺";s:3:"勺";s:4:"包";s:3:"包";s:4:"匆";s:3:"匆";s:4:"北";s:3:"北";s:4:"卉";s:3:"卉";s:4:"卑";s:3:"卑";s:4:"博";s:3:"博";s:4:"即";s:3:"即";s:4:"卽";s:3:"卽";s:4:"卿";s:3:"卿";s:4:"卿";s:3:"卿";s:4:"卿";s:3:"卿";s:4:"𠨬";s:4:"𠨬";s:4:"灰";s:3:"灰";s:4:"及";s:3:"及";s:4:"叟";s:3:"叟";s:4:"𠭣";s:4:"𠭣";s:4:"叫";s:3:"叫";s:4:"叱";s:3:"叱";s:4:"吆";s:3:"吆";s:4:"咞";s:3:"咞";s:4:"吸";s:3:"吸";s:4:"呈";s:3:"呈";s:4:"周";s:3:"周";s:4:"咢";s:3:"咢";s:4:"哶";s:3:"哶";s:4:"唐";s:3:"唐";s:4:"啓";s:3:"啓";s:4:"啣";s:3:"啣";s:4:"善";s:3:"善";s:4:"善";s:3:"善";s:4:"喙";s:3:"喙";s:4:"喫";s:3:"喫";s:4:"喳";s:3:"喳";s:4:"嗂";s:3:"嗂";s:4:"圖";s:3:"圖";s:4:"嘆";s:3:"嘆";s:4:"圗";s:3:"圗";s:4:"噑";s:3:"噑";s:4:"噴";s:3:"噴";s:4:"切";s:3:"切";s:4:"壮";s:3:"壮";s:4:"城";s:3:"城";s:4:"埴";s:3:"埴";s:4:"堍";s:3:"堍";s:4:"型";s:3:"型";s:4:"堲";s:3:"堲";s:4:"報";s:3:"報";s:4:"墬";s:3:"墬";s:4:"𡓤";s:4:"𡓤";s:4:"売";s:3:"売";s:4:"壷";s:3:"壷";s:4:"夆";s:3:"夆";s:4:"多";s:3:"多";s:4:"夢";s:3:"夢";s:4:"奢";s:3:"奢";s:4:"𡚨";s:4:"𡚨";s:4:"𡛪";s:4:"𡛪";s:4:"姬";s:3:"姬";s:4:"娛";s:3:"娛";s:4:"娧";s:3:"娧";s:4:"姘";s:3:"姘";s:4:"婦";s:3:"婦";s:4:"㛮";s:3:"㛮";s:4:"㛼";s:3:"㛼";s:4:"嬈";s:3:"嬈";s:4:"嬾";s:3:"嬾";s:4:"嬾";s:3:"嬾";s:4:"𡧈";s:4:"𡧈";s:4:"寃";s:3:"寃";s:4:"寘";s:3:"寘";s:4:"寧";s:3:"寧";s:4:"寳";s:3:"寳";s:4:"𡬘";s:4:"𡬘";s:4:"寿";s:3:"寿";s:4:"将";s:3:"将";s:4:"当";s:3:"当";s:4:"尢";s:3:"尢";s:4:"㞁";s:3:"㞁";s:4:"屠";s:3:"屠";s:4:"屮";s:3:"屮";s:4:"峀";s:3:"峀";s:4:"岍";s:3:"岍";s:4:"𡷤";s:4:"𡷤";s:4:"嵃";s:3:"嵃";s:4:"𡷦";s:4:"𡷦";s:4:"嵮";s:3:"嵮";s:4:"嵫";s:3:"嵫";s:4:"嵼";s:3:"嵼";s:4:"巡";s:3:"巡";s:4:"巢";s:3:"巢";s:4:"㠯";s:3:"㠯";s:4:"巽";s:3:"巽";s:4:"帨";s:3:"帨";s:4:"帽";s:3:"帽";s:4:"幩";s:3:"幩";s:4:"㡢";s:3:"㡢";s:4:"𢆃";s:4:"𢆃";s:4:"㡼";s:3:"㡼";s:4:"庰";s:3:"庰";s:4:"庳";s:3:"庳";s:4:"庶";s:3:"庶";s:4:"廊";s:3:"廊";s:4:"𪎒";s:4:"𪎒";s:4:"廾";s:3:"廾";s:4:"𢌱";s:4:"𢌱";s:4:"𢌱";s:4:"𢌱";s:4:"舁";s:3:"舁";s:4:"弢";s:3:"弢";s:4:"弢";s:3:"弢";s:4:"㣇";s:3:"㣇";s:4:"𣊸";s:4:"𣊸";s:4:"𦇚";s:4:"𦇚";s:4:"形";s:3:"形";s:4:"彫";s:3:"彫";s:4:"㣣";s:3:"㣣";s:4:"徚";s:3:"徚";s:4:"忍";s:3:"忍";s:4:"志";s:3:"志";s:4:"忹";s:3:"忹";s:4:"悁";s:3:"悁";s:4:"㤺";s:3:"㤺";s:4:"㤜";s:3:"㤜";s:4:"悔";s:3:"悔";s:4:"𢛔";s:4:"𢛔";s:4:"惇";s:3:"惇";s:4:"慈";s:3:"慈";s:4:"慌";s:3:"慌";s:4:"慎";s:3:"慎";s:4:"慌";s:3:"慌";s:4:"慺";s:3:"慺";s:4:"憎";s:3:"憎";s:4:"憲";s:3:"憲";s:4:"憤";s:3:"憤";s:4:"憯";s:3:"憯";s:4:"懞";s:3:"懞";s:4:"懲";s:3:"懲";s:4:"懶";s:3:"懶";s:4:"成";s:3:"成";s:4:"戛";s:3:"戛";s:4:"扝";s:3:"扝";s:4:"抱";s:3:"抱";s:4:"拔";s:3:"拔";s:4:"捐";s:3:"捐";s:4:"𢬌";s:4:"𢬌";s:4:"挽";s:3:"挽";s:4:"拼";s:3:"拼";s:4:"捨";s:3:"捨";s:4:"掃";s:3:"掃";s:4:"揤";s:3:"揤";s:4:"𢯱";s:4:"𢯱";s:4:"搢";s:3:"搢";s:4:"揅";s:3:"揅";s:4:"掩";s:3:"掩";s:4:"㨮";s:3:"㨮";s:4:"摩";s:3:"摩";s:4:"摾";s:3:"摾";s:4:"撝";s:3:"撝";s:4:"摷";s:3:"摷";s:4:"㩬";s:3:"㩬";s:4:"敏";s:3:"敏";s:4:"敬";s:3:"敬";s:4:"𣀊";s:4:"𣀊";s:4:"旣";s:3:"旣";s:4:"書";s:3:"書";s:4:"晉";s:3:"晉";s:4:"㬙";s:3:"㬙";s:4:"暑";s:3:"暑";s:4:"㬈";s:3:"㬈";s:4:"㫤";s:3:"㫤";s:4:"冒";s:3:"冒";s:4:"冕";s:3:"冕";s:4:"最";s:3:"最";s:4:"暜";s:3:"暜";s:4:"肭";s:3:"肭";s:4:"䏙";s:3:"䏙";s:4:"朗";s:3:"朗";s:4:"望";s:3:"望";s:4:"朡";s:3:"朡";s:4:"杞";s:3:"杞";s:4:"杓";s:3:"杓";s:4:"𣏃";s:4:"𣏃";s:4:"㭉";s:3:"㭉";s:4:"柺";s:3:"柺";s:4:"枅";s:3:"枅";s:4:"桒";s:3:"桒";s:4:"梅";s:3:"梅";s:4:"𣑭";s:4:"𣑭";s:4:"梎";s:3:"梎";s:4:"栟";s:3:"栟";s:4:"椔";s:3:"椔";s:4:"㮝";s:3:"㮝";s:4:"楂";s:3:"楂";s:4:"榣";s:3:"榣";s:4:"槪";s:3:"槪";s:4:"檨";s:3:"檨";s:4:"𣚣";s:4:"𣚣";s:4:"櫛";s:3:"櫛";s:4:"㰘";s:3:"㰘";s:4:"次";s:3:"次";s:4:"𣢧";s:4:"𣢧";s:4:"歔";s:3:"歔";s:4:"㱎";s:3:"㱎";s:4:"歲";s:3:"歲";s:4:"殟";s:3:"殟";s:4:"殺";s:3:"殺";s:4:"殻";s:3:"殻";s:4:"𣪍";s:4:"𣪍";s:4:"𡴋";s:4:"𡴋";s:4:"𣫺";s:4:"𣫺";s:4:"汎";s:3:"汎";s:4:"𣲼";s:4:"𣲼";s:4:"沿";s:3:"沿";s:4:"泍";s:3:"泍";s:4:"汧";s:3:"汧";s:4:"洖";s:3:"洖";s:4:"派";s:3:"派";s:4:"海";s:3:"海";s:4:"流";s:3:"流";s:4:"浩";s:3:"浩";s:4:"浸";s:3:"浸";s:4:"涅";s:3:"涅";s:4:"𣴞";s:4:"𣴞";s:4:"洴";s:3:"洴";s:4:"港";s:3:"港";s:4:"湮";s:3:"湮";s:4:"㴳";s:3:"㴳";s:4:"滋";s:3:"滋";s:4:"滇";s:3:"滇";s:4:"𣻑";s:4:"𣻑";s:4:"淹";s:3:"淹";s:4:"潮";s:3:"潮";s:4:"𣽞";s:4:"𣽞";s:4:"𣾎";s:4:"𣾎";s:4:"濆";s:3:"濆";s:4:"瀹";s:3:"瀹";s:4:"瀞";s:3:"瀞";s:4:"瀛";s:3:"瀛";s:4:"㶖";s:3:"㶖";s:4:"灊";s:3:"灊";s:4:"災";s:3:"災";s:4:"灷";s:3:"灷";s:4:"炭";s:3:"炭";s:4:"𠔥";s:4:"𠔥";s:4:"煅";s:3:"煅";s:4:"𤉣";s:4:"𤉣";s:4:"熜";s:3:"熜";s:4:"𤎫";s:4:"𤎫";s:4:"爨";s:3:"爨";s:4:"爵";s:3:"爵";s:4:"牐";s:3:"牐";s:4:"𤘈";s:4:"𤘈";s:4:"犀";s:3:"犀";s:4:"犕";s:3:"犕";s:4:"𤜵";s:4:"𤜵";s:4:"𤠔";s:4:"𤠔";s:4:"獺";s:3:"獺";s:4:"王";s:3:"王";s:4:"㺬";s:3:"㺬";s:4:"玥";s:3:"玥";s:4:"㺸";s:3:"㺸";s:4:"㺸";s:3:"㺸";s:4:"瑇";s:3:"瑇";s:4:"瑜";s:3:"瑜";s:4:"瑱";s:3:"瑱";s:4:"璅";s:3:"璅";s:4:"瓊";s:3:"瓊";s:4:"㼛";s:3:"㼛";s:4:"甤";s:3:"甤";s:4:"𤰶";s:4:"𤰶";s:4:"甾";s:3:"甾";s:4:"𤲒";s:4:"𤲒";s:4:"異";s:3:"異";s:4:"𢆟";s:4:"𢆟";s:4:"瘐";s:3:"瘐";s:4:"𤾡";s:4:"𤾡";s:4:"𤾸";s:4:"𤾸";s:4:"𥁄";s:4:"𥁄";s:4:"㿼";s:3:"㿼";s:4:"䀈";s:3:"䀈";s:4:"直";s:3:"直";s:4:"𥃳";s:4:"𥃳";s:4:"𥃲";s:4:"𥃲";s:4:"𥄙";s:4:"𥄙";s:4:"𥄳";s:4:"𥄳";s:4:"眞";s:3:"眞";s:4:"真";s:3:"真";s:4:"真";s:3:"真";s:4:"睊";s:3:"睊";s:4:"䀹";s:3:"䀹";s:4:"瞋";s:3:"瞋";s:4:"䁆";s:3:"䁆";s:4:"䂖";s:3:"䂖";s:4:"𥐝";s:4:"𥐝";s:4:"硎";s:3:"硎";s:4:"碌";s:3:"碌";s:4:"磌";s:3:"磌";s:4:"䃣";s:3:"䃣";s:4:"𥘦";s:4:"𥘦";s:4:"祖";s:3:"祖";s:4:"𥚚";s:4:"𥚚";s:4:"𥛅";s:4:"𥛅";s:4:"福";s:3:"福";s:4:"秫";s:3:"秫";s:4:"䄯";s:3:"䄯";s:4:"穀";s:3:"穀";s:4:"穊";s:3:"穊";s:4:"穏";s:3:"穏";s:4:"𥥼";s:4:"𥥼";s:4:"𥪧";s:4:"𥪧";s:4:"𥪧";s:4:"𥪧";s:4:"竮";s:3:"竮";s:4:"䈂";s:3:"䈂";s:4:"𥮫";s:4:"𥮫";s:4:"篆";s:3:"篆";s:4:"築";s:3:"築";s:4:"䈧";s:3:"䈧";s:4:"𥲀";s:4:"𥲀";s:4:"糒";s:3:"糒";s:4:"䊠";s:3:"䊠";s:4:"糨";s:3:"糨";s:4:"糣";s:3:"糣";s:4:"紀";s:3:"紀";s:4:"𥾆";s:4:"𥾆";s:4:"絣";s:3:"絣";s:4:"䌁";s:3:"䌁";s:4:"緇";s:3:"緇";s:4:"縂";s:3:"縂";s:4:"繅";s:3:"繅";s:4:"䌴";s:3:"䌴";s:4:"𦈨";s:4:"𦈨";s:4:"𦉇";s:4:"𦉇";s:4:"䍙";s:3:"䍙";s:4:"𦋙";s:4:"𦋙";s:4:"罺";s:3:"罺";s:4:"𦌾";s:4:"𦌾";s:4:"羕";s:3:"羕";s:4:"翺";s:3:"翺";s:4:"者";s:3:"者";s:4:"𦓚";s:4:"𦓚";s:4:"𦔣";s:4:"𦔣";s:4:"聠";s:3:"聠";s:4:"𦖨";s:4:"𦖨";s:4:"聰";s:3:"聰";s:4:"𣍟";s:4:"𣍟";s:4:"䏕";s:3:"䏕";s:4:"育";s:3:"育";s:4:"脃";s:3:"脃";s:4:"䐋";s:3:"䐋";s:4:"脾";s:3:"脾";s:4:"媵";s:3:"媵";s:4:"𦞧";s:4:"𦞧";s:4:"𦞵";s:4:"𦞵";s:4:"𣎓";s:4:"𣎓";s:4:"𣎜";s:4:"𣎜";s:4:"舁";s:3:"舁";s:4:"舄";s:3:"舄";s:4:"辞";s:3:"辞";s:4:"䑫";s:3:"䑫";s:4:"芑";s:3:"芑";s:4:"芋";s:3:"芋";s:4:"芝";s:3:"芝";s:4:"劳";s:3:"劳";s:4:"花";s:3:"花";s:4:"芳";s:3:"芳";s:4:"芽";s:3:"芽";s:4:"苦";s:3:"苦";s:4:"𦬼";s:4:"𦬼";s:4:"若";s:3:"若";s:4:"茝";s:3:"茝";s:4:"荣";s:3:"荣";s:4:"莭";s:3:"莭";s:4:"茣";s:3:"茣";s:4:"莽";s:3:"莽";s:4:"菧";s:3:"菧";s:4:"著";s:3:"著";s:4:"荓";s:3:"荓";s:4:"菊";s:3:"菊";s:4:"菌";s:3:"菌";s:4:"菜";s:3:"菜";s:4:"𦰶";s:4:"𦰶";s:4:"𦵫";s:4:"𦵫";s:4:"𦳕";s:4:"𦳕";s:4:"䔫";s:3:"䔫";s:4:"蓱";s:3:"蓱";s:4:"蓳";s:3:"蓳";s:4:"蔖";s:3:"蔖";s:4:"𧏊";s:4:"𧏊";s:4:"蕤";s:3:"蕤";s:4:"𦼬";s:4:"𦼬";s:4:"䕝";s:3:"䕝";s:4:"䕡";s:3:"䕡";s:4:"𦾱";s:4:"𦾱";s:4:"𧃒";s:4:"𧃒";s:4:"䕫";s:3:"䕫";s:4:"虐";s:3:"虐";s:4:"虜";s:3:"虜";s:4:"虧";s:3:"虧";s:4:"虩";s:3:"虩";s:4:"蚩";s:3:"蚩";s:4:"蚈";s:3:"蚈";s:4:"蜎";s:3:"蜎";s:4:"蛢";s:3:"蛢";s:4:"蝹";s:3:"蝹";s:4:"蜨";s:3:"蜨";s:4:"蝫";s:3:"蝫";s:4:"螆";s:3:"螆";s:4:"䗗";s:3:"䗗";s:4:"蟡";s:3:"蟡";s:4:"蠁";s:3:"蠁";s:4:"䗹";s:3:"䗹";s:4:"衠";s:3:"衠";s:4:"衣";s:3:"衣";s:4:"𧙧";s:4:"𧙧";s:4:"裗";s:3:"裗";s:4:"裞";s:3:"裞";s:4:"䘵";s:3:"䘵";s:4:"裺";s:3:"裺";s:4:"㒻";s:3:"㒻";s:4:"𧢮";s:4:"𧢮";s:4:"𧥦";s:4:"𧥦";s:4:"䚾";s:3:"䚾";s:4:"䛇";s:3:"䛇";s:4:"誠";s:3:"誠";s:4:"諭";s:3:"諭";s:4:"變";s:3:"變";s:4:"豕";s:3:"豕";s:4:"𧲨";s:4:"𧲨";s:4:"貫";s:3:"貫";s:4:"賁";s:3:"賁";s:4:"贛";s:3:"贛";s:4:"起";s:3:"起";s:4:"𧼯";s:4:"𧼯";s:4:"𠠄";s:4:"𠠄";s:4:"跋";s:3:"跋";s:4:"趼";s:3:"趼";s:4:"跰";s:3:"跰";s:4:"𠣞";s:4:"𠣞";s:4:"軔";s:3:"軔";s:4:"輸";s:3:"輸";s:4:"𨗒";s:4:"𨗒";s:4:"𨗭";s:4:"𨗭";s:4:"邔";s:3:"邔";s:4:"郱";s:3:"郱";s:4:"鄑";s:3:"鄑";s:4:"𨜮";s:4:"𨜮";s:4:"鄛";s:3:"鄛";s:4:"鈸";s:3:"鈸";s:4:"鋗";s:3:"鋗";s:4:"鋘";s:3:"鋘";s:4:"鉼";s:3:"鉼";s:4:"鏹";s:3:"鏹";s:4:"鐕";s:3:"鐕";s:4:"𨯺";s:4:"𨯺";s:4:"開";s:3:"開";s:4:"䦕";s:3:"䦕";s:4:"閷";s:3:"閷";s:4:"𨵷";s:4:"𨵷";s:4:"䧦";s:3:"䧦";s:4:"雃";s:3:"雃";s:4:"嶲";s:3:"嶲";s:4:"霣";s:3:"霣";s:4:"𩅅";s:4:"𩅅";s:4:"𩈚";s:4:"𩈚";s:4:"䩮";s:3:"䩮";s:4:"䩶";s:3:"䩶";s:4:"韠";s:3:"韠";s:4:"𩐊";s:4:"𩐊";s:4:"䪲";s:3:"䪲";s:4:"𩒖";s:4:"𩒖";s:4:"頋";s:3:"頋";s:4:"頋";s:3:"頋";s:4:"頩";s:3:"頩";s:4:"𩖶";s:4:"𩖶";s:4:"飢";s:3:"飢";s:4:"䬳";s:3:"䬳";s:4:"餩";s:3:"餩";s:4:"馧";s:3:"馧";s:4:"駂";s:3:"駂";s:4:"駾";s:3:"駾";s:4:"䯎";s:3:"䯎";s:4:"𩬰";s:4:"𩬰";s:4:"鬒";s:3:"鬒";s:4:"鱀";s:3:"鱀";s:4:"鳽";s:3:"鳽";s:4:"䳎";s:3:"䳎";s:4:"䳭";s:3:"䳭";s:4:"鵧";s:3:"鵧";s:4:"𪃎";s:4:"𪃎";s:4:"䳸";s:3:"䳸";s:4:"𪄅";s:4:"𪄅";s:4:"𪈎";s:4:"𪈎";s:4:"𪊑";s:4:"𪊑";s:4:"麻";s:3:"麻";s:4:"䵖";s:3:"䵖";s:4:"黹";s:3:"黹";s:4:"黾";s:3:"黾";s:4:"鼅";s:3:"鼅";s:4:"鼏";s:3:"鼏";s:4:"鼖";s:3:"鼖";s:4:"鼻";s:3:"鼻";s:4:"𪘀";s:4:"𪘀";}' );
+$utfCheckNFC = unserialize( 'a:1217:{s:2:"̀";s:1:"N";s:2:"́";s:1:"N";s:2:"̓";s:1:"N";s:2:"̈́";s:1:"N";s:2:"ʹ";s:1:"N";s:2:";";s:1:"N";s:2:"·";s:1:"N";s:3:"क़";s:1:"N";s:3:"ख़";s:1:"N";s:3:"ग़";s:1:"N";s:3:"ज़";s:1:"N";s:3:"ड़";s:1:"N";s:3:"ढ़";s:1:"N";s:3:"फ़";s:1:"N";s:3:"य़";s:1:"N";s:3:"ড়";s:1:"N";s:3:"ঢ়";s:1:"N";s:3:"য়";s:1:"N";s:3:"ਲ਼";s:1:"N";s:3:"ਸ਼";s:1:"N";s:3:"ਖ਼";s:1:"N";s:3:"ਗ਼";s:1:"N";s:3:"ਜ਼";s:1:"N";s:3:"ਫ਼";s:1:"N";s:3:"ଡ଼";s:1:"N";s:3:"ଢ଼";s:1:"N";s:3:"གྷ";s:1:"N";s:3:"ཌྷ";s:1:"N";s:3:"དྷ";s:1:"N";s:3:"བྷ";s:1:"N";s:3:"ཛྷ";s:1:"N";s:3:"ཀྵ";s:1:"N";s:3:"ཱི";s:1:"N";s:3:"ཱུ";s:1:"N";s:3:"ྲྀ";s:1:"N";s:3:"ླྀ";s:1:"N";s:3:"ཱྀ";s:1:"N";s:3:"ྒྷ";s:1:"N";s:3:"ྜྷ";s:1:"N";s:3:"ྡྷ";s:1:"N";s:3:"ྦྷ";s:1:"N";s:3:"ྫྷ";s:1:"N";s:3:"ྐྵ";s:1:"N";s:3:"ά";s:1:"N";s:3:"έ";s:1:"N";s:3:"ή";s:1:"N";s:3:"ί";s:1:"N";s:3:"ό";s:1:"N";s:3:"ύ";s:1:"N";s:3:"ώ";s:1:"N";s:3:"Ά";s:1:"N";s:3:"ι";s:1:"N";s:3:"Έ";s:1:"N";s:3:"Ή";s:1:"N";s:3:"ΐ";s:1:"N";s:3:"Ί";s:1:"N";s:3:"ΰ";s:1:"N";s:3:"Ύ";s:1:"N";s:3:"΅";s:1:"N";s:3:"`";s:1:"N";s:3:"Ό";s:1:"N";s:3:"Ώ";s:1:"N";s:3:"´";s:1:"N";s:3:" ";s:1:"N";s:3:" ";s:1:"N";s:3:"Ω";s:1:"N";s:3:"K";s:1:"N";s:3:"Å";s:1:"N";s:3:"〈";s:1:"N";s:3:"〉";s:1:"N";s:3:"⫝̸";s:1:"N";s:3:"豈";s:1:"N";s:3:"更";s:1:"N";s:3:"車";s:1:"N";s:3:"賈";s:1:"N";s:3:"滑";s:1:"N";s:3:"串";s:1:"N";s:3:"句";s:1:"N";s:3:"龜";s:1:"N";s:3:"龜";s:1:"N";s:3:"契";s:1:"N";s:3:"金";s:1:"N";s:3:"喇";s:1:"N";s:3:"奈";s:1:"N";s:3:"懶";s:1:"N";s:3:"癩";s:1:"N";s:3:"羅";s:1:"N";s:3:"蘿";s:1:"N";s:3:"螺";s:1:"N";s:3:"裸";s:1:"N";s:3:"邏";s:1:"N";s:3:"樂";s:1:"N";s:3:"洛";s:1:"N";s:3:"烙";s:1:"N";s:3:"珞";s:1:"N";s:3:"落";s:1:"N";s:3:"酪";s:1:"N";s:3:"駱";s:1:"N";s:3:"亂";s:1:"N";s:3:"卵";s:1:"N";s:3:"欄";s:1:"N";s:3:"爛";s:1:"N";s:3:"蘭";s:1:"N";s:3:"鸞";s:1:"N";s:3:"嵐";s:1:"N";s:3:"濫";s:1:"N";s:3:"藍";s:1:"N";s:3:"襤";s:1:"N";s:3:"拉";s:1:"N";s:3:"臘";s:1:"N";s:3:"蠟";s:1:"N";s:3:"廊";s:1:"N";s:3:"朗";s:1:"N";s:3:"浪";s:1:"N";s:3:"狼";s:1:"N";s:3:"郎";s:1:"N";s:3:"來";s:1:"N";s:3:"冷";s:1:"N";s:3:"勞";s:1:"N";s:3:"擄";s:1:"N";s:3:"櫓";s:1:"N";s:3:"爐";s:1:"N";s:3:"盧";s:1:"N";s:3:"老";s:1:"N";s:3:"蘆";s:1:"N";s:3:"虜";s:1:"N";s:3:"路";s:1:"N";s:3:"露";s:1:"N";s:3:"魯";s:1:"N";s:3:"鷺";s:1:"N";s:3:"碌";s:1:"N";s:3:"祿";s:1:"N";s:3:"綠";s:1:"N";s:3:"菉";s:1:"N";s:3:"錄";s:1:"N";s:3:"鹿";s:1:"N";s:3:"論";s:1:"N";s:3:"壟";s:1:"N";s:3:"弄";s:1:"N";s:3:"籠";s:1:"N";s:3:"聾";s:1:"N";s:3:"牢";s:1:"N";s:3:"磊";s:1:"N";s:3:"賂";s:1:"N";s:3:"雷";s:1:"N";s:3:"壘";s:1:"N";s:3:"屢";s:1:"N";s:3:"樓";s:1:"N";s:3:"淚";s:1:"N";s:3:"漏";s:1:"N";s:3:"累";s:1:"N";s:3:"縷";s:1:"N";s:3:"陋";s:1:"N";s:3:"勒";s:1:"N";s:3:"肋";s:1:"N";s:3:"凜";s:1:"N";s:3:"凌";s:1:"N";s:3:"稜";s:1:"N";s:3:"綾";s:1:"N";s:3:"菱";s:1:"N";s:3:"陵";s:1:"N";s:3:"讀";s:1:"N";s:3:"拏";s:1:"N";s:3:"樂";s:1:"N";s:3:"諾";s:1:"N";s:3:"丹";s:1:"N";s:3:"寧";s:1:"N";s:3:"怒";s:1:"N";s:3:"率";s:1:"N";s:3:"異";s:1:"N";s:3:"北";s:1:"N";s:3:"磻";s:1:"N";s:3:"便";s:1:"N";s:3:"復";s:1:"N";s:3:"不";s:1:"N";s:3:"泌";s:1:"N";s:3:"數";s:1:"N";s:3:"索";s:1:"N";s:3:"參";s:1:"N";s:3:"塞";s:1:"N";s:3:"省";s:1:"N";s:3:"葉";s:1:"N";s:3:"說";s:1:"N";s:3:"殺";s:1:"N";s:3:"辰";s:1:"N";s:3:"沈";s:1:"N";s:3:"拾";s:1:"N";s:3:"若";s:1:"N";s:3:"掠";s:1:"N";s:3:"略";s:1:"N";s:3:"亮";s:1:"N";s:3:"兩";s:1:"N";s:3:"凉";s:1:"N";s:3:"梁";s:1:"N";s:3:"糧";s:1:"N";s:3:"良";s:1:"N";s:3:"諒";s:1:"N";s:3:"量";s:1:"N";s:3:"勵";s:1:"N";s:3:"呂";s:1:"N";s:3:"女";s:1:"N";s:3:"廬";s:1:"N";s:3:"旅";s:1:"N";s:3:"濾";s:1:"N";s:3:"礪";s:1:"N";s:3:"閭";s:1:"N";s:3:"驪";s:1:"N";s:3:"麗";s:1:"N";s:3:"黎";s:1:"N";s:3:"力";s:1:"N";s:3:"曆";s:1:"N";s:3:"歷";s:1:"N";s:3:"轢";s:1:"N";s:3:"年";s:1:"N";s:3:"憐";s:1:"N";s:3:"戀";s:1:"N";s:3:"撚";s:1:"N";s:3:"漣";s:1:"N";s:3:"煉";s:1:"N";s:3:"璉";s:1:"N";s:3:"秊";s:1:"N";s:3:"練";s:1:"N";s:3:"聯";s:1:"N";s:3:"輦";s:1:"N";s:3:"蓮";s:1:"N";s:3:"連";s:1:"N";s:3:"鍊";s:1:"N";s:3:"列";s:1:"N";s:3:"劣";s:1:"N";s:3:"咽";s:1:"N";s:3:"烈";s:1:"N";s:3:"裂";s:1:"N";s:3:"說";s:1:"N";s:3:"廉";s:1:"N";s:3:"念";s:1:"N";s:3:"捻";s:1:"N";s:3:"殮";s:1:"N";s:3:"簾";s:1:"N";s:3:"獵";s:1:"N";s:3:"令";s:1:"N";s:3:"囹";s:1:"N";s:3:"寧";s:1:"N";s:3:"嶺";s:1:"N";s:3:"怜";s:1:"N";s:3:"玲";s:1:"N";s:3:"瑩";s:1:"N";s:3:"羚";s:1:"N";s:3:"聆";s:1:"N";s:3:"鈴";s:1:"N";s:3:"零";s:1:"N";s:3:"靈";s:1:"N";s:3:"領";s:1:"N";s:3:"例";s:1:"N";s:3:"禮";s:1:"N";s:3:"醴";s:1:"N";s:3:"隸";s:1:"N";s:3:"惡";s:1:"N";s:3:"了";s:1:"N";s:3:"僚";s:1:"N";s:3:"寮";s:1:"N";s:3:"尿";s:1:"N";s:3:"料";s:1:"N";s:3:"樂";s:1:"N";s:3:"燎";s:1:"N";s:3:"療";s:1:"N";s:3:"蓼";s:1:"N";s:3:"遼";s:1:"N";s:3:"龍";s:1:"N";s:3:"暈";s:1:"N";s:3:"阮";s:1:"N";s:3:"劉";s:1:"N";s:3:"杻";s:1:"N";s:3:"柳";s:1:"N";s:3:"流";s:1:"N";s:3:"溜";s:1:"N";s:3:"琉";s:1:"N";s:3:"留";s:1:"N";s:3:"硫";s:1:"N";s:3:"紐";s:1:"N";s:3:"類";s:1:"N";s:3:"六";s:1:"N";s:3:"戮";s:1:"N";s:3:"陸";s:1:"N";s:3:"倫";s:1:"N";s:3:"崙";s:1:"N";s:3:"淪";s:1:"N";s:3:"輪";s:1:"N";s:3:"律";s:1:"N";s:3:"慄";s:1:"N";s:3:"栗";s:1:"N";s:3:"率";s:1:"N";s:3:"隆";s:1:"N";s:3:"利";s:1:"N";s:3:"吏";s:1:"N";s:3:"履";s:1:"N";s:3:"易";s:1:"N";s:3:"李";s:1:"N";s:3:"梨";s:1:"N";s:3:"泥";s:1:"N";s:3:"理";s:1:"N";s:3:"痢";s:1:"N";s:3:"罹";s:1:"N";s:3:"裏";s:1:"N";s:3:"裡";s:1:"N";s:3:"里";s:1:"N";s:3:"離";s:1:"N";s:3:"匿";s:1:"N";s:3:"溺";s:1:"N";s:3:"吝";s:1:"N";s:3:"燐";s:1:"N";s:3:"璘";s:1:"N";s:3:"藺";s:1:"N";s:3:"隣";s:1:"N";s:3:"鱗";s:1:"N";s:3:"麟";s:1:"N";s:3:"林";s:1:"N";s:3:"淋";s:1:"N";s:3:"臨";s:1:"N";s:3:"立";s:1:"N";s:3:"笠";s:1:"N";s:3:"粒";s:1:"N";s:3:"狀";s:1:"N";s:3:"炙";s:1:"N";s:3:"識";s:1:"N";s:3:"什";s:1:"N";s:3:"茶";s:1:"N";s:3:"刺";s:1:"N";s:3:"切";s:1:"N";s:3:"度";s:1:"N";s:3:"拓";s:1:"N";s:3:"糖";s:1:"N";s:3:"宅";s:1:"N";s:3:"洞";s:1:"N";s:3:"暴";s:1:"N";s:3:"輻";s:1:"N";s:3:"行";s:1:"N";s:3:"降";s:1:"N";s:3:"見";s:1:"N";s:3:"廓";s:1:"N";s:3:"兀";s:1:"N";s:3:"嗀";s:1:"N";s:3:"塚";s:1:"N";s:3:"晴";s:1:"N";s:3:"凞";s:1:"N";s:3:"猪";s:1:"N";s:3:"益";s:1:"N";s:3:"礼";s:1:"N";s:3:"神";s:1:"N";s:3:"祥";s:1:"N";s:3:"福";s:1:"N";s:3:"靖";s:1:"N";s:3:"精";s:1:"N";s:3:"羽";s:1:"N";s:3:"蘒";s:1:"N";s:3:"諸";s:1:"N";s:3:"逸";s:1:"N";s:3:"都";s:1:"N";s:3:"飯";s:1:"N";s:3:"飼";s:1:"N";s:3:"館";s:1:"N";s:3:"鶴";s:1:"N";s:3:"侮";s:1:"N";s:3:"僧";s:1:"N";s:3:"免";s:1:"N";s:3:"勉";s:1:"N";s:3:"勤";s:1:"N";s:3:"卑";s:1:"N";s:3:"喝";s:1:"N";s:3:"嘆";s:1:"N";s:3:"器";s:1:"N";s:3:"塀";s:1:"N";s:3:"墨";s:1:"N";s:3:"層";s:1:"N";s:3:"屮";s:1:"N";s:3:"悔";s:1:"N";s:3:"慨";s:1:"N";s:3:"憎";s:1:"N";s:3:"懲";s:1:"N";s:3:"敏";s:1:"N";s:3:"既";s:1:"N";s:3:"暑";s:1:"N";s:3:"梅";s:1:"N";s:3:"海";s:1:"N";s:3:"渚";s:1:"N";s:3:"漢";s:1:"N";s:3:"煮";s:1:"N";s:3:"爫";s:1:"N";s:3:"琢";s:1:"N";s:3:"碑";s:1:"N";s:3:"社";s:1:"N";s:3:"祉";s:1:"N";s:3:"祈";s:1:"N";s:3:"祐";s:1:"N";s:3:"祖";s:1:"N";s:3:"祝";s:1:"N";s:3:"禍";s:1:"N";s:3:"禎";s:1:"N";s:3:"穀";s:1:"N";s:3:"突";s:1:"N";s:3:"節";s:1:"N";s:3:"練";s:1:"N";s:3:"縉";s:1:"N";s:3:"繁";s:1:"N";s:3:"署";s:1:"N";s:3:"者";s:1:"N";s:3:"臭";s:1:"N";s:3:"艹";s:1:"N";s:3:"艹";s:1:"N";s:3:"著";s:1:"N";s:3:"褐";s:1:"N";s:3:"視";s:1:"N";s:3:"謁";s:1:"N";s:3:"謹";s:1:"N";s:3:"賓";s:1:"N";s:3:"贈";s:1:"N";s:3:"辶";s:1:"N";s:3:"逸";s:1:"N";s:3:"難";s:1:"N";s:3:"響";s:1:"N";s:3:"頻";s:1:"N";s:3:"並";s:1:"N";s:3:"况";s:1:"N";s:3:"全";s:1:"N";s:3:"侀";s:1:"N";s:3:"充";s:1:"N";s:3:"冀";s:1:"N";s:3:"勇";s:1:"N";s:3:"勺";s:1:"N";s:3:"喝";s:1:"N";s:3:"啕";s:1:"N";s:3:"喙";s:1:"N";s:3:"嗢";s:1:"N";s:3:"塚";s:1:"N";s:3:"墳";s:1:"N";s:3:"奄";s:1:"N";s:3:"奔";s:1:"N";s:3:"婢";s:1:"N";s:3:"嬨";s:1:"N";s:3:"廒";s:1:"N";s:3:"廙";s:1:"N";s:3:"彩";s:1:"N";s:3:"徭";s:1:"N";s:3:"惘";s:1:"N";s:3:"慎";s:1:"N";s:3:"愈";s:1:"N";s:3:"憎";s:1:"N";s:3:"慠";s:1:"N";s:3:"懲";s:1:"N";s:3:"戴";s:1:"N";s:3:"揄";s:1:"N";s:3:"搜";s:1:"N";s:3:"摒";s:1:"N";s:3:"敖";s:1:"N";s:3:"晴";s:1:"N";s:3:"朗";s:1:"N";s:3:"望";s:1:"N";s:3:"杖";s:1:"N";s:3:"歹";s:1:"N";s:3:"殺";s:1:"N";s:3:"流";s:1:"N";s:3:"滛";s:1:"N";s:3:"滋";s:1:"N";s:3:"漢";s:1:"N";s:3:"瀞";s:1:"N";s:3:"煮";s:1:"N";s:3:"瞧";s:1:"N";s:3:"爵";s:1:"N";s:3:"犯";s:1:"N";s:3:"猪";s:1:"N";s:3:"瑱";s:1:"N";s:3:"甆";s:1:"N";s:3:"画";s:1:"N";s:3:"瘝";s:1:"N";s:3:"瘟";s:1:"N";s:3:"益";s:1:"N";s:3:"盛";s:1:"N";s:3:"直";s:1:"N";s:3:"睊";s:1:"N";s:3:"着";s:1:"N";s:3:"磌";s:1:"N";s:3:"窱";s:1:"N";s:3:"節";s:1:"N";s:3:"类";s:1:"N";s:3:"絛";s:1:"N";s:3:"練";s:1:"N";s:3:"缾";s:1:"N";s:3:"者";s:1:"N";s:3:"荒";s:1:"N";s:3:"華";s:1:"N";s:3:"蝹";s:1:"N";s:3:"襁";s:1:"N";s:3:"覆";s:1:"N";s:3:"視";s:1:"N";s:3:"調";s:1:"N";s:3:"諸";s:1:"N";s:3:"請";s:1:"N";s:3:"謁";s:1:"N";s:3:"諾";s:1:"N";s:3:"諭";s:1:"N";s:3:"謹";s:1:"N";s:3:"變";s:1:"N";s:3:"贈";s:1:"N";s:3:"輸";s:1:"N";s:3:"遲";s:1:"N";s:3:"醙";s:1:"N";s:3:"鉶";s:1:"N";s:3:"陼";s:1:"N";s:3:"難";s:1:"N";s:3:"靖";s:1:"N";s:3:"韛";s:1:"N";s:3:"響";s:1:"N";s:3:"頋";s:1:"N";s:3:"頻";s:1:"N";s:3:"鬒";s:1:"N";s:3:"龜";s:1:"N";s:3:"𢡊";s:1:"N";s:3:"𢡄";s:1:"N";s:3:"𣏕";s:1:"N";s:3:"㮝";s:1:"N";s:3:"䀘";s:1:"N";s:3:"䀹";s:1:"N";s:3:"𥉉";s:1:"N";s:3:"𥳐";s:1:"N";s:3:"𧻓";s:1:"N";s:3:"齃";s:1:"N";s:3:"龎";s:1:"N";s:3:"יִ";s:1:"N";s:3:"ײַ";s:1:"N";s:3:"שׁ";s:1:"N";s:3:"שׂ";s:1:"N";s:3:"שּׁ";s:1:"N";s:3:"שּׂ";s:1:"N";s:3:"אַ";s:1:"N";s:3:"אָ";s:1:"N";s:3:"אּ";s:1:"N";s:3:"בּ";s:1:"N";s:3:"גּ";s:1:"N";s:3:"דּ";s:1:"N";s:3:"הּ";s:1:"N";s:3:"וּ";s:1:"N";s:3:"זּ";s:1:"N";s:3:"טּ";s:1:"N";s:3:"יּ";s:1:"N";s:3:"ךּ";s:1:"N";s:3:"כּ";s:1:"N";s:3:"לּ";s:1:"N";s:3:"מּ";s:1:"N";s:3:"נּ";s:1:"N";s:3:"סּ";s:1:"N";s:3:"ףּ";s:1:"N";s:3:"פּ";s:1:"N";s:3:"צּ";s:1:"N";s:3:"קּ";s:1:"N";s:3:"רּ";s:1:"N";s:3:"שּ";s:1:"N";s:3:"תּ";s:1:"N";s:3:"וֹ";s:1:"N";s:3:"בֿ";s:1:"N";s:3:"כֿ";s:1:"N";s:3:"פֿ";s:1:"N";s:4:"𝅗𝅥";s:1:"N";s:4:"𝅘𝅥";s:1:"N";s:4:"𝅘𝅥𝅮";s:1:"N";s:4:"𝅘𝅥𝅯";s:1:"N";s:4:"𝅘𝅥𝅰";s:1:"N";s:4:"𝅘𝅥𝅱";s:1:"N";s:4:"𝅘𝅥𝅲";s:1:"N";s:4:"𝆹𝅥";s:1:"N";s:4:"𝆺𝅥";s:1:"N";s:4:"𝆹𝅥𝅮";s:1:"N";s:4:"𝆺𝅥𝅮";s:1:"N";s:4:"𝆹𝅥𝅯";s:1:"N";s:4:"𝆺𝅥𝅯";s:1:"N";s:4:"丽";s:1:"N";s:4:"丸";s:1:"N";s:4:"乁";s:1:"N";s:4:"𠄢";s:1:"N";s:4:"你";s:1:"N";s:4:"侮";s:1:"N";s:4:"侻";s:1:"N";s:4:"倂";s:1:"N";s:4:"偺";s:1:"N";s:4:"備";s:1:"N";s:4:"僧";s:1:"N";s:4:"像";s:1:"N";s:4:"㒞";s:1:"N";s:4:"𠘺";s:1:"N";s:4:"免";s:1:"N";s:4:"兔";s:1:"N";s:4:"兤";s:1:"N";s:4:"具";s:1:"N";s:4:"𠔜";s:1:"N";s:4:"㒹";s:1:"N";s:4:"內";s:1:"N";s:4:"再";s:1:"N";s:4:"𠕋";s:1:"N";s:4:"冗";s:1:"N";s:4:"冤";s:1:"N";s:4:"仌";s:1:"N";s:4:"冬";s:1:"N";s:4:"况";s:1:"N";s:4:"𩇟";s:1:"N";s:4:"凵";s:1:"N";s:4:"刃";s:1:"N";s:4:"㓟";s:1:"N";s:4:"刻";s:1:"N";s:4:"剆";s:1:"N";s:4:"割";s:1:"N";s:4:"剷";s:1:"N";s:4:"㔕";s:1:"N";s:4:"勇";s:1:"N";s:4:"勉";s:1:"N";s:4:"勤";s:1:"N";s:4:"勺";s:1:"N";s:4:"包";s:1:"N";s:4:"匆";s:1:"N";s:4:"北";s:1:"N";s:4:"卉";s:1:"N";s:4:"卑";s:1:"N";s:4:"博";s:1:"N";s:4:"即";s:1:"N";s:4:"卽";s:1:"N";s:4:"卿";s:1:"N";s:4:"卿";s:1:"N";s:4:"卿";s:1:"N";s:4:"𠨬";s:1:"N";s:4:"灰";s:1:"N";s:4:"及";s:1:"N";s:4:"叟";s:1:"N";s:4:"𠭣";s:1:"N";s:4:"叫";s:1:"N";s:4:"叱";s:1:"N";s:4:"吆";s:1:"N";s:4:"咞";s:1:"N";s:4:"吸";s:1:"N";s:4:"呈";s:1:"N";s:4:"周";s:1:"N";s:4:"咢";s:1:"N";s:4:"哶";s:1:"N";s:4:"唐";s:1:"N";s:4:"啓";s:1:"N";s:4:"啣";s:1:"N";s:4:"善";s:1:"N";s:4:"善";s:1:"N";s:4:"喙";s:1:"N";s:4:"喫";s:1:"N";s:4:"喳";s:1:"N";s:4:"嗂";s:1:"N";s:4:"圖";s:1:"N";s:4:"嘆";s:1:"N";s:4:"圗";s:1:"N";s:4:"噑";s:1:"N";s:4:"噴";s:1:"N";s:4:"切";s:1:"N";s:4:"壮";s:1:"N";s:4:"城";s:1:"N";s:4:"埴";s:1:"N";s:4:"堍";s:1:"N";s:4:"型";s:1:"N";s:4:"堲";s:1:"N";s:4:"報";s:1:"N";s:4:"墬";s:1:"N";s:4:"𡓤";s:1:"N";s:4:"売";s:1:"N";s:4:"壷";s:1:"N";s:4:"夆";s:1:"N";s:4:"多";s:1:"N";s:4:"夢";s:1:"N";s:4:"奢";s:1:"N";s:4:"𡚨";s:1:"N";s:4:"𡛪";s:1:"N";s:4:"姬";s:1:"N";s:4:"娛";s:1:"N";s:4:"娧";s:1:"N";s:4:"姘";s:1:"N";s:4:"婦";s:1:"N";s:4:"㛮";s:1:"N";s:4:"㛼";s:1:"N";s:4:"嬈";s:1:"N";s:4:"嬾";s:1:"N";s:4:"嬾";s:1:"N";s:4:"𡧈";s:1:"N";s:4:"寃";s:1:"N";s:4:"寘";s:1:"N";s:4:"寧";s:1:"N";s:4:"寳";s:1:"N";s:4:"𡬘";s:1:"N";s:4:"寿";s:1:"N";s:4:"将";s:1:"N";s:4:"当";s:1:"N";s:4:"尢";s:1:"N";s:4:"㞁";s:1:"N";s:4:"屠";s:1:"N";s:4:"屮";s:1:"N";s:4:"峀";s:1:"N";s:4:"岍";s:1:"N";s:4:"𡷤";s:1:"N";s:4:"嵃";s:1:"N";s:4:"𡷦";s:1:"N";s:4:"嵮";s:1:"N";s:4:"嵫";s:1:"N";s:4:"嵼";s:1:"N";s:4:"巡";s:1:"N";s:4:"巢";s:1:"N";s:4:"㠯";s:1:"N";s:4:"巽";s:1:"N";s:4:"帨";s:1:"N";s:4:"帽";s:1:"N";s:4:"幩";s:1:"N";s:4:"㡢";s:1:"N";s:4:"𢆃";s:1:"N";s:4:"㡼";s:1:"N";s:4:"庰";s:1:"N";s:4:"庳";s:1:"N";s:4:"庶";s:1:"N";s:4:"廊";s:1:"N";s:4:"𪎒";s:1:"N";s:4:"廾";s:1:"N";s:4:"𢌱";s:1:"N";s:4:"𢌱";s:1:"N";s:4:"舁";s:1:"N";s:4:"弢";s:1:"N";s:4:"弢";s:1:"N";s:4:"㣇";s:1:"N";s:4:"𣊸";s:1:"N";s:4:"𦇚";s:1:"N";s:4:"形";s:1:"N";s:4:"彫";s:1:"N";s:4:"㣣";s:1:"N";s:4:"徚";s:1:"N";s:4:"忍";s:1:"N";s:4:"志";s:1:"N";s:4:"忹";s:1:"N";s:4:"悁";s:1:"N";s:4:"㤺";s:1:"N";s:4:"㤜";s:1:"N";s:4:"悔";s:1:"N";s:4:"𢛔";s:1:"N";s:4:"惇";s:1:"N";s:4:"慈";s:1:"N";s:4:"慌";s:1:"N";s:4:"慎";s:1:"N";s:4:"慌";s:1:"N";s:4:"慺";s:1:"N";s:4:"憎";s:1:"N";s:4:"憲";s:1:"N";s:4:"憤";s:1:"N";s:4:"憯";s:1:"N";s:4:"懞";s:1:"N";s:4:"懲";s:1:"N";s:4:"懶";s:1:"N";s:4:"成";s:1:"N";s:4:"戛";s:1:"N";s:4:"扝";s:1:"N";s:4:"抱";s:1:"N";s:4:"拔";s:1:"N";s:4:"捐";s:1:"N";s:4:"𢬌";s:1:"N";s:4:"挽";s:1:"N";s:4:"拼";s:1:"N";s:4:"捨";s:1:"N";s:4:"掃";s:1:"N";s:4:"揤";s:1:"N";s:4:"𢯱";s:1:"N";s:4:"搢";s:1:"N";s:4:"揅";s:1:"N";s:4:"掩";s:1:"N";s:4:"㨮";s:1:"N";s:4:"摩";s:1:"N";s:4:"摾";s:1:"N";s:4:"撝";s:1:"N";s:4:"摷";s:1:"N";s:4:"㩬";s:1:"N";s:4:"敏";s:1:"N";s:4:"敬";s:1:"N";s:4:"𣀊";s:1:"N";s:4:"旣";s:1:"N";s:4:"書";s:1:"N";s:4:"晉";s:1:"N";s:4:"㬙";s:1:"N";s:4:"暑";s:1:"N";s:4:"㬈";s:1:"N";s:4:"㫤";s:1:"N";s:4:"冒";s:1:"N";s:4:"冕";s:1:"N";s:4:"最";s:1:"N";s:4:"暜";s:1:"N";s:4:"肭";s:1:"N";s:4:"䏙";s:1:"N";s:4:"朗";s:1:"N";s:4:"望";s:1:"N";s:4:"朡";s:1:"N";s:4:"杞";s:1:"N";s:4:"杓";s:1:"N";s:4:"𣏃";s:1:"N";s:4:"㭉";s:1:"N";s:4:"柺";s:1:"N";s:4:"枅";s:1:"N";s:4:"桒";s:1:"N";s:4:"梅";s:1:"N";s:4:"𣑭";s:1:"N";s:4:"梎";s:1:"N";s:4:"栟";s:1:"N";s:4:"椔";s:1:"N";s:4:"㮝";s:1:"N";s:4:"楂";s:1:"N";s:4:"榣";s:1:"N";s:4:"槪";s:1:"N";s:4:"檨";s:1:"N";s:4:"𣚣";s:1:"N";s:4:"櫛";s:1:"N";s:4:"㰘";s:1:"N";s:4:"次";s:1:"N";s:4:"𣢧";s:1:"N";s:4:"歔";s:1:"N";s:4:"㱎";s:1:"N";s:4:"歲";s:1:"N";s:4:"殟";s:1:"N";s:4:"殺";s:1:"N";s:4:"殻";s:1:"N";s:4:"𣪍";s:1:"N";s:4:"𡴋";s:1:"N";s:4:"𣫺";s:1:"N";s:4:"汎";s:1:"N";s:4:"𣲼";s:1:"N";s:4:"沿";s:1:"N";s:4:"泍";s:1:"N";s:4:"汧";s:1:"N";s:4:"洖";s:1:"N";s:4:"派";s:1:"N";s:4:"海";s:1:"N";s:4:"流";s:1:"N";s:4:"浩";s:1:"N";s:4:"浸";s:1:"N";s:4:"涅";s:1:"N";s:4:"𣴞";s:1:"N";s:4:"洴";s:1:"N";s:4:"港";s:1:"N";s:4:"湮";s:1:"N";s:4:"㴳";s:1:"N";s:4:"滋";s:1:"N";s:4:"滇";s:1:"N";s:4:"𣻑";s:1:"N";s:4:"淹";s:1:"N";s:4:"潮";s:1:"N";s:4:"𣽞";s:1:"N";s:4:"𣾎";s:1:"N";s:4:"濆";s:1:"N";s:4:"瀹";s:1:"N";s:4:"瀞";s:1:"N";s:4:"瀛";s:1:"N";s:4:"㶖";s:1:"N";s:4:"灊";s:1:"N";s:4:"災";s:1:"N";s:4:"灷";s:1:"N";s:4:"炭";s:1:"N";s:4:"𠔥";s:1:"N";s:4:"煅";s:1:"N";s:4:"𤉣";s:1:"N";s:4:"熜";s:1:"N";s:4:"𤎫";s:1:"N";s:4:"爨";s:1:"N";s:4:"爵";s:1:"N";s:4:"牐";s:1:"N";s:4:"𤘈";s:1:"N";s:4:"犀";s:1:"N";s:4:"犕";s:1:"N";s:4:"𤜵";s:1:"N";s:4:"𤠔";s:1:"N";s:4:"獺";s:1:"N";s:4:"王";s:1:"N";s:4:"㺬";s:1:"N";s:4:"玥";s:1:"N";s:4:"㺸";s:1:"N";s:4:"㺸";s:1:"N";s:4:"瑇";s:1:"N";s:4:"瑜";s:1:"N";s:4:"瑱";s:1:"N";s:4:"璅";s:1:"N";s:4:"瓊";s:1:"N";s:4:"㼛";s:1:"N";s:4:"甤";s:1:"N";s:4:"𤰶";s:1:"N";s:4:"甾";s:1:"N";s:4:"𤲒";s:1:"N";s:4:"異";s:1:"N";s:4:"𢆟";s:1:"N";s:4:"瘐";s:1:"N";s:4:"𤾡";s:1:"N";s:4:"𤾸";s:1:"N";s:4:"𥁄";s:1:"N";s:4:"㿼";s:1:"N";s:4:"䀈";s:1:"N";s:4:"直";s:1:"N";s:4:"𥃳";s:1:"N";s:4:"𥃲";s:1:"N";s:4:"𥄙";s:1:"N";s:4:"𥄳";s:1:"N";s:4:"眞";s:1:"N";s:4:"真";s:1:"N";s:4:"真";s:1:"N";s:4:"睊";s:1:"N";s:4:"䀹";s:1:"N";s:4:"瞋";s:1:"N";s:4:"䁆";s:1:"N";s:4:"䂖";s:1:"N";s:4:"𥐝";s:1:"N";s:4:"硎";s:1:"N";s:4:"碌";s:1:"N";s:4:"磌";s:1:"N";s:4:"䃣";s:1:"N";s:4:"𥘦";s:1:"N";s:4:"祖";s:1:"N";s:4:"𥚚";s:1:"N";s:4:"𥛅";s:1:"N";s:4:"福";s:1:"N";s:4:"秫";s:1:"N";s:4:"䄯";s:1:"N";s:4:"穀";s:1:"N";s:4:"穊";s:1:"N";s:4:"穏";s:1:"N";s:4:"𥥼";s:1:"N";s:4:"𥪧";s:1:"N";s:4:"𥪧";s:1:"N";s:4:"竮";s:1:"N";s:4:"䈂";s:1:"N";s:4:"𥮫";s:1:"N";s:4:"篆";s:1:"N";s:4:"築";s:1:"N";s:4:"䈧";s:1:"N";s:4:"𥲀";s:1:"N";s:4:"糒";s:1:"N";s:4:"䊠";s:1:"N";s:4:"糨";s:1:"N";s:4:"糣";s:1:"N";s:4:"紀";s:1:"N";s:4:"𥾆";s:1:"N";s:4:"絣";s:1:"N";s:4:"䌁";s:1:"N";s:4:"緇";s:1:"N";s:4:"縂";s:1:"N";s:4:"繅";s:1:"N";s:4:"䌴";s:1:"N";s:4:"𦈨";s:1:"N";s:4:"𦉇";s:1:"N";s:4:"䍙";s:1:"N";s:4:"𦋙";s:1:"N";s:4:"罺";s:1:"N";s:4:"𦌾";s:1:"N";s:4:"羕";s:1:"N";s:4:"翺";s:1:"N";s:4:"者";s:1:"N";s:4:"𦓚";s:1:"N";s:4:"𦔣";s:1:"N";s:4:"聠";s:1:"N";s:4:"𦖨";s:1:"N";s:4:"聰";s:1:"N";s:4:"𣍟";s:1:"N";s:4:"䏕";s:1:"N";s:4:"育";s:1:"N";s:4:"脃";s:1:"N";s:4:"䐋";s:1:"N";s:4:"脾";s:1:"N";s:4:"媵";s:1:"N";s:4:"𦞧";s:1:"N";s:4:"𦞵";s:1:"N";s:4:"𣎓";s:1:"N";s:4:"𣎜";s:1:"N";s:4:"舁";s:1:"N";s:4:"舄";s:1:"N";s:4:"辞";s:1:"N";s:4:"䑫";s:1:"N";s:4:"芑";s:1:"N";s:4:"芋";s:1:"N";s:4:"芝";s:1:"N";s:4:"劳";s:1:"N";s:4:"花";s:1:"N";s:4:"芳";s:1:"N";s:4:"芽";s:1:"N";s:4:"苦";s:1:"N";s:4:"𦬼";s:1:"N";s:4:"若";s:1:"N";s:4:"茝";s:1:"N";s:4:"荣";s:1:"N";s:4:"莭";s:1:"N";s:4:"茣";s:1:"N";s:4:"莽";s:1:"N";s:4:"菧";s:1:"N";s:4:"著";s:1:"N";s:4:"荓";s:1:"N";s:4:"菊";s:1:"N";s:4:"菌";s:1:"N";s:4:"菜";s:1:"N";s:4:"𦰶";s:1:"N";s:4:"𦵫";s:1:"N";s:4:"𦳕";s:1:"N";s:4:"䔫";s:1:"N";s:4:"蓱";s:1:"N";s:4:"蓳";s:1:"N";s:4:"蔖";s:1:"N";s:4:"𧏊";s:1:"N";s:4:"蕤";s:1:"N";s:4:"𦼬";s:1:"N";s:4:"䕝";s:1:"N";s:4:"䕡";s:1:"N";s:4:"𦾱";s:1:"N";s:4:"𧃒";s:1:"N";s:4:"䕫";s:1:"N";s:4:"虐";s:1:"N";s:4:"虜";s:1:"N";s:4:"虧";s:1:"N";s:4:"虩";s:1:"N";s:4:"蚩";s:1:"N";s:4:"蚈";s:1:"N";s:4:"蜎";s:1:"N";s:4:"蛢";s:1:"N";s:4:"蝹";s:1:"N";s:4:"蜨";s:1:"N";s:4:"蝫";s:1:"N";s:4:"螆";s:1:"N";s:4:"䗗";s:1:"N";s:4:"蟡";s:1:"N";s:4:"蠁";s:1:"N";s:4:"䗹";s:1:"N";s:4:"衠";s:1:"N";s:4:"衣";s:1:"N";s:4:"𧙧";s:1:"N";s:4:"裗";s:1:"N";s:4:"裞";s:1:"N";s:4:"䘵";s:1:"N";s:4:"裺";s:1:"N";s:4:"㒻";s:1:"N";s:4:"𧢮";s:1:"N";s:4:"𧥦";s:1:"N";s:4:"䚾";s:1:"N";s:4:"䛇";s:1:"N";s:4:"誠";s:1:"N";s:4:"諭";s:1:"N";s:4:"變";s:1:"N";s:4:"豕";s:1:"N";s:4:"𧲨";s:1:"N";s:4:"貫";s:1:"N";s:4:"賁";s:1:"N";s:4:"贛";s:1:"N";s:4:"起";s:1:"N";s:4:"𧼯";s:1:"N";s:4:"𠠄";s:1:"N";s:4:"跋";s:1:"N";s:4:"趼";s:1:"N";s:4:"跰";s:1:"N";s:4:"𠣞";s:1:"N";s:4:"軔";s:1:"N";s:4:"輸";s:1:"N";s:4:"𨗒";s:1:"N";s:4:"𨗭";s:1:"N";s:4:"邔";s:1:"N";s:4:"郱";s:1:"N";s:4:"鄑";s:1:"N";s:4:"𨜮";s:1:"N";s:4:"鄛";s:1:"N";s:4:"鈸";s:1:"N";s:4:"鋗";s:1:"N";s:4:"鋘";s:1:"N";s:4:"鉼";s:1:"N";s:4:"鏹";s:1:"N";s:4:"鐕";s:1:"N";s:4:"𨯺";s:1:"N";s:4:"開";s:1:"N";s:4:"䦕";s:1:"N";s:4:"閷";s:1:"N";s:4:"𨵷";s:1:"N";s:4:"䧦";s:1:"N";s:4:"雃";s:1:"N";s:4:"嶲";s:1:"N";s:4:"霣";s:1:"N";s:4:"𩅅";s:1:"N";s:4:"𩈚";s:1:"N";s:4:"䩮";s:1:"N";s:4:"䩶";s:1:"N";s:4:"韠";s:1:"N";s:4:"𩐊";s:1:"N";s:4:"䪲";s:1:"N";s:4:"𩒖";s:1:"N";s:4:"頋";s:1:"N";s:4:"頋";s:1:"N";s:4:"頩";s:1:"N";s:4:"𩖶";s:1:"N";s:4:"飢";s:1:"N";s:4:"䬳";s:1:"N";s:4:"餩";s:1:"N";s:4:"馧";s:1:"N";s:4:"駂";s:1:"N";s:4:"駾";s:1:"N";s:4:"䯎";s:1:"N";s:4:"𩬰";s:1:"N";s:4:"鬒";s:1:"N";s:4:"鱀";s:1:"N";s:4:"鳽";s:1:"N";s:4:"䳎";s:1:"N";s:4:"䳭";s:1:"N";s:4:"鵧";s:1:"N";s:4:"𪃎";s:1:"N";s:4:"䳸";s:1:"N";s:4:"𪄅";s:1:"N";s:4:"𪈎";s:1:"N";s:4:"𪊑";s:1:"N";s:4:"麻";s:1:"N";s:4:"䵖";s:1:"N";s:4:"黹";s:1:"N";s:4:"黾";s:1:"N";s:4:"鼅";s:1:"N";s:4:"鼏";s:1:"N";s:4:"鼖";s:1:"N";s:4:"鼻";s:1:"N";s:4:"𪘀";s:1:"N";s:2:"̀";s:1:"M";s:2:"́";s:1:"M";s:2:"̂";s:1:"M";s:2:"̃";s:1:"M";s:2:"̄";s:1:"M";s:2:"̆";s:1:"M";s:2:"̇";s:1:"M";s:2:"̈";s:1:"M";s:2:"̉";s:1:"M";s:2:"̊";s:1:"M";s:2:"̋";s:1:"M";s:2:"̌";s:1:"M";s:2:"̏";s:1:"M";s:2:"̑";s:1:"M";s:2:"̓";s:1:"M";s:2:"̔";s:1:"M";s:2:"̛";s:1:"M";s:2:"̣";s:1:"M";s:2:"̤";s:1:"M";s:2:"̥";s:1:"M";s:2:"̦";s:1:"M";s:2:"̧";s:1:"M";s:2:"̨";s:1:"M";s:2:"̭";s:1:"M";s:2:"̮";s:1:"M";s:2:"̰";s:1:"M";s:2:"̱";s:1:"M";s:2:"̸";s:1:"M";s:2:"͂";s:1:"M";s:2:"ͅ";s:1:"M";s:2:"ٓ";s:1:"M";s:2:"ٔ";s:1:"M";s:2:"ٕ";s:1:"M";s:3:"़";s:1:"M";s:3:"া";s:1:"M";s:3:"ৗ";s:1:"M";s:3:"ା";s:1:"M";s:3:"ୖ";s:1:"M";s:3:"ୗ";s:1:"M";s:3:"ா";s:1:"M";s:3:"ௗ";s:1:"M";s:3:"ౖ";s:1:"M";s:3:"ೂ";s:1:"M";s:3:"ೕ";s:1:"M";s:3:"ೖ";s:1:"M";s:3:"ാ";s:1:"M";s:3:"ൗ";s:1:"M";s:3:"්";s:1:"M";s:3:"ා";s:1:"M";s:3:"ෟ";s:1:"M";s:3:"ီ";s:1:"M";s:3:"ᅡ";s:1:"M";s:3:"ᅢ";s:1:"M";s:3:"ᅣ";s:1:"M";s:3:"ᅤ";s:1:"M";s:3:"ᅥ";s:1:"M";s:3:"ᅦ";s:1:"M";s:3:"ᅧ";s:1:"M";s:3:"ᅨ";s:1:"M";s:3:"ᅩ";s:1:"M";s:3:"ᅪ";s:1:"M";s:3:"ᅫ";s:1:"M";s:3:"ᅬ";s:1:"M";s:3:"ᅭ";s:1:"M";s:3:"ᅮ";s:1:"M";s:3:"ᅯ";s:1:"M";s:3:"ᅰ";s:1:"M";s:3:"ᅱ";s:1:"M";s:3:"ᅲ";s:1:"M";s:3:"ᅳ";s:1:"M";s:3:"ᅴ";s:1:"M";s:3:"ᅵ";s:1:"M";s:3:"ᆨ";s:1:"M";s:3:"ᆩ";s:1:"M";s:3:"ᆪ";s:1:"M";s:3:"ᆫ";s:1:"M";s:3:"ᆬ";s:1:"M";s:3:"ᆭ";s:1:"M";s:3:"ᆮ";s:1:"M";s:3:"ᆯ";s:1:"M";s:3:"ᆰ";s:1:"M";s:3:"ᆱ";s:1:"M";s:3:"ᆲ";s:1:"M";s:3:"ᆳ";s:1:"M";s:3:"ᆴ";s:1:"M";s:3:"ᆵ";s:1:"M";s:3:"ᆶ";s:1:"M";s:3:"ᆷ";s:1:"M";s:3:"ᆸ";s:1:"M";s:3:"ᆹ";s:1:"M";s:3:"ᆺ";s:1:"M";s:3:"ᆻ";s:1:"M";s:3:"ᆼ";s:1:"M";s:3:"ᆽ";s:1:"M";s:3:"ᆾ";s:1:"M";s:3:"ᆿ";s:1:"M";s:3:"ᇀ";s:1:"M";s:3:"ᇁ";s:1:"M";s:3:"ᇂ";s:1:"M";s:3:"ᬵ";s:1:"M";s:3:"゙";s:1:"M";s:3:"゚";s:1:"M";}' );
?>
diff --git a/includes/normal/UtfNormalDataK.inc b/includes/normal/UtfNormalDataK.inc
index 0f4cd7a5..5f112e02 100644
--- a/includes/normal/UtfNormalDataK.inc
+++ b/includes/normal/UtfNormalDataK.inc
@@ -2,9 +2,8 @@
/**
* This file was automatically generated -- do not edit!
* Run UtfNormalGenerate.php to create this file again (make clean && make)
- * @package MediaWiki
*/
/** */
global $utfCompatibilityDecomp;
-$utfCompatibilityDecomp = unserialize( 'a:5389:{s:2:" ";s:1:" ";s:2:"¨";s:3:" ̈";s:2:"ª";s:1:"a";s:2:"¯";s:3:" ̄";s:2:"²";s:1:"2";s:2:"³";s:1:"3";s:2:"´";s:3:" ́";s:2:"µ";s:2:"μ";s:2:"¸";s:3:" ̧";s:2:"¹";s:1:"1";s:2:"º";s:1:"o";s:2:"¼";s:5:"1⁄4";s:2:"½";s:5:"1⁄2";s:2:"¾";s:5:"3⁄4";s:2:"À";s:3:"À";s:2:"Á";s:3:"Á";s:2:"Â";s:3:"Â";s:2:"Ã";s:3:"Ã";s:2:"Ä";s:3:"Ä";s:2:"Å";s:3:"Å";s:2:"Ç";s:3:"Ç";s:2:"È";s:3:"È";s:2:"É";s:3:"É";s:2:"Ê";s:3:"Ê";s:2:"Ë";s:3:"Ë";s:2:"Ì";s:3:"Ì";s:2:"Í";s:3:"Í";s:2:"Î";s:3:"Î";s:2:"Ï";s:3:"Ï";s:2:"Ñ";s:3:"Ñ";s:2:"Ò";s:3:"Ò";s:2:"Ó";s:3:"Ó";s:2:"Ô";s:3:"Ô";s:2:"Õ";s:3:"Õ";s:2:"Ö";s:3:"Ö";s:2:"Ù";s:3:"Ù";s:2:"Ú";s:3:"Ú";s:2:"Û";s:3:"Û";s:2:"Ü";s:3:"Ü";s:2:"Ý";s:3:"Ý";s:2:"à";s:3:"à";s:2:"á";s:3:"á";s:2:"â";s:3:"â";s:2:"ã";s:3:"ã";s:2:"ä";s:3:"ä";s:2:"å";s:3:"å";s:2:"ç";s:3:"ç";s:2:"è";s:3:"è";s:2:"é";s:3:"é";s:2:"ê";s:3:"ê";s:2:"ë";s:3:"ë";s:2:"ì";s:3:"ì";s:2:"í";s:3:"í";s:2:"î";s:3:"î";s:2:"ï";s:3:"ï";s:2:"ñ";s:3:"ñ";s:2:"ò";s:3:"ò";s:2:"ó";s:3:"ó";s:2:"ô";s:3:"ô";s:2:"õ";s:3:"õ";s:2:"ö";s:3:"ö";s:2:"ù";s:3:"ù";s:2:"ú";s:3:"ú";s:2:"û";s:3:"û";s:2:"ü";s:3:"ü";s:2:"ý";s:3:"ý";s:2:"ÿ";s:3:"ÿ";s:2:"Ā";s:3:"Ā";s:2:"ā";s:3:"ā";s:2:"Ă";s:3:"Ă";s:2:"ă";s:3:"ă";s:2:"Ą";s:3:"Ą";s:2:"ą";s:3:"ą";s:2:"Ć";s:3:"Ć";s:2:"ć";s:3:"ć";s:2:"Ĉ";s:3:"Ĉ";s:2:"ĉ";s:3:"ĉ";s:2:"Ċ";s:3:"Ċ";s:2:"ċ";s:3:"ċ";s:2:"Č";s:3:"Č";s:2:"č";s:3:"č";s:2:"Ď";s:3:"Ď";s:2:"ď";s:3:"ď";s:2:"Ē";s:3:"Ē";s:2:"ē";s:3:"ē";s:2:"Ĕ";s:3:"Ĕ";s:2:"ĕ";s:3:"ĕ";s:2:"Ė";s:3:"Ė";s:2:"ė";s:3:"ė";s:2:"Ę";s:3:"Ę";s:2:"ę";s:3:"ę";s:2:"Ě";s:3:"Ě";s:2:"ě";s:3:"ě";s:2:"Ĝ";s:3:"Ĝ";s:2:"ĝ";s:3:"ĝ";s:2:"Ğ";s:3:"Ğ";s:2:"ğ";s:3:"ğ";s:2:"Ġ";s:3:"Ġ";s:2:"ġ";s:3:"ġ";s:2:"Ģ";s:3:"Ģ";s:2:"ģ";s:3:"ģ";s:2:"Ĥ";s:3:"Ĥ";s:2:"ĥ";s:3:"ĥ";s:2:"Ĩ";s:3:"Ĩ";s:2:"ĩ";s:3:"ĩ";s:2:"Ī";s:3:"Ī";s:2:"ī";s:3:"ī";s:2:"Ĭ";s:3:"Ĭ";s:2:"ĭ";s:3:"ĭ";s:2:"Į";s:3:"Į";s:2:"į";s:3:"į";s:2:"İ";s:3:"İ";s:2:"IJ";s:2:"IJ";s:2:"ij";s:2:"ij";s:2:"Ĵ";s:3:"Ĵ";s:2:"ĵ";s:3:"ĵ";s:2:"Ķ";s:3:"Ķ";s:2:"ķ";s:3:"ķ";s:2:"Ĺ";s:3:"Ĺ";s:2:"ĺ";s:3:"ĺ";s:2:"Ļ";s:3:"Ļ";s:2:"ļ";s:3:"ļ";s:2:"Ľ";s:3:"Ľ";s:2:"ľ";s:3:"ľ";s:2:"Ŀ";s:3:"L·";s:2:"ŀ";s:3:"l·";s:2:"Ń";s:3:"Ń";s:2:"ń";s:3:"ń";s:2:"Ņ";s:3:"Ņ";s:2:"ņ";s:3:"ņ";s:2:"Ň";s:3:"Ň";s:2:"ň";s:3:"ň";s:2:"ʼn";s:3:"ʼn";s:2:"Ō";s:3:"Ō";s:2:"ō";s:3:"ō";s:2:"Ŏ";s:3:"Ŏ";s:2:"ŏ";s:3:"ŏ";s:2:"Ő";s:3:"Ő";s:2:"ő";s:3:"ő";s:2:"Ŕ";s:3:"Ŕ";s:2:"ŕ";s:3:"ŕ";s:2:"Ŗ";s:3:"Ŗ";s:2:"ŗ";s:3:"ŗ";s:2:"Ř";s:3:"Ř";s:2:"ř";s:3:"ř";s:2:"Ś";s:3:"Ś";s:2:"ś";s:3:"ś";s:2:"Ŝ";s:3:"Ŝ";s:2:"ŝ";s:3:"ŝ";s:2:"Ş";s:3:"Ş";s:2:"ş";s:3:"ş";s:2:"Š";s:3:"Š";s:2:"š";s:3:"š";s:2:"Ţ";s:3:"Ţ";s:2:"ţ";s:3:"ţ";s:2:"Ť";s:3:"Ť";s:2:"ť";s:3:"ť";s:2:"Ũ";s:3:"Ũ";s:2:"ũ";s:3:"ũ";s:2:"Ū";s:3:"Ū";s:2:"ū";s:3:"ū";s:2:"Ŭ";s:3:"Ŭ";s:2:"ŭ";s:3:"ŭ";s:2:"Ů";s:3:"Ů";s:2:"ů";s:3:"ů";s:2:"Ű";s:3:"Ű";s:2:"ű";s:3:"ű";s:2:"Ų";s:3:"Ų";s:2:"ų";s:3:"ų";s:2:"Ŵ";s:3:"Ŵ";s:2:"ŵ";s:3:"ŵ";s:2:"Ŷ";s:3:"Ŷ";s:2:"ŷ";s:3:"ŷ";s:2:"Ÿ";s:3:"Ÿ";s:2:"Ź";s:3:"Ź";s:2:"ź";s:3:"ź";s:2:"Ż";s:3:"Ż";s:2:"ż";s:3:"ż";s:2:"Ž";s:3:"Ž";s:2:"ž";s:3:"ž";s:2:"ſ";s:1:"s";s:2:"Ơ";s:3:"Ơ";s:2:"ơ";s:3:"ơ";s:2:"Ư";s:3:"Ư";s:2:"ư";s:3:"ư";s:2:"DŽ";s:4:"DŽ";s:2:"Dž";s:4:"Dž";s:2:"dž";s:4:"dž";s:2:"LJ";s:2:"LJ";s:2:"Lj";s:2:"Lj";s:2:"lj";s:2:"lj";s:2:"NJ";s:2:"NJ";s:2:"Nj";s:2:"Nj";s:2:"nj";s:2:"nj";s:2:"Ǎ";s:3:"Ǎ";s:2:"ǎ";s:3:"ǎ";s:2:"Ǐ";s:3:"Ǐ";s:2:"ǐ";s:3:"ǐ";s:2:"Ǒ";s:3:"Ǒ";s:2:"ǒ";s:3:"ǒ";s:2:"Ǔ";s:3:"Ǔ";s:2:"ǔ";s:3:"ǔ";s:2:"Ǖ";s:5:"Ǖ";s:2:"ǖ";s:5:"ǖ";s:2:"Ǘ";s:5:"Ǘ";s:2:"ǘ";s:5:"ǘ";s:2:"Ǚ";s:5:"Ǚ";s:2:"ǚ";s:5:"ǚ";s:2:"Ǜ";s:5:"Ǜ";s:2:"ǜ";s:5:"ǜ";s:2:"Ǟ";s:5:"Ǟ";s:2:"ǟ";s:5:"ǟ";s:2:"Ǡ";s:5:"Ǡ";s:2:"ǡ";s:5:"ǡ";s:2:"Ǣ";s:4:"Ǣ";s:2:"ǣ";s:4:"ǣ";s:2:"Ǧ";s:3:"Ǧ";s:2:"ǧ";s:3:"ǧ";s:2:"Ǩ";s:3:"Ǩ";s:2:"ǩ";s:3:"ǩ";s:2:"Ǫ";s:3:"Ǫ";s:2:"ǫ";s:3:"ǫ";s:2:"Ǭ";s:5:"Ǭ";s:2:"ǭ";s:5:"ǭ";s:2:"Ǯ";s:4:"Ǯ";s:2:"ǯ";s:4:"ǯ";s:2:"ǰ";s:3:"ǰ";s:2:"DZ";s:2:"DZ";s:2:"Dz";s:2:"Dz";s:2:"dz";s:2:"dz";s:2:"Ǵ";s:3:"Ǵ";s:2:"ǵ";s:3:"ǵ";s:2:"Ǹ";s:3:"Ǹ";s:2:"ǹ";s:3:"ǹ";s:2:"Ǻ";s:5:"Ǻ";s:2:"ǻ";s:5:"ǻ";s:2:"Ǽ";s:4:"Ǽ";s:2:"ǽ";s:4:"ǽ";s:2:"Ǿ";s:4:"Ǿ";s:2:"ǿ";s:4:"ǿ";s:2:"Ȁ";s:3:"Ȁ";s:2:"ȁ";s:3:"ȁ";s:2:"Ȃ";s:3:"Ȃ";s:2:"ȃ";s:3:"ȃ";s:2:"Ȅ";s:3:"Ȅ";s:2:"ȅ";s:3:"ȅ";s:2:"Ȇ";s:3:"Ȇ";s:2:"ȇ";s:3:"ȇ";s:2:"Ȉ";s:3:"Ȉ";s:2:"ȉ";s:3:"ȉ";s:2:"Ȋ";s:3:"Ȋ";s:2:"ȋ";s:3:"ȋ";s:2:"Ȍ";s:3:"Ȍ";s:2:"ȍ";s:3:"ȍ";s:2:"Ȏ";s:3:"Ȏ";s:2:"ȏ";s:3:"ȏ";s:2:"Ȑ";s:3:"Ȑ";s:2:"ȑ";s:3:"ȑ";s:2:"Ȓ";s:3:"Ȓ";s:2:"ȓ";s:3:"ȓ";s:2:"Ȕ";s:3:"Ȕ";s:2:"ȕ";s:3:"ȕ";s:2:"Ȗ";s:3:"Ȗ";s:2:"ȗ";s:3:"ȗ";s:2:"Ș";s:3:"Ș";s:2:"ș";s:3:"ș";s:2:"Ț";s:3:"Ț";s:2:"ț";s:3:"ț";s:2:"Ȟ";s:3:"Ȟ";s:2:"ȟ";s:3:"ȟ";s:2:"Ȧ";s:3:"Ȧ";s:2:"ȧ";s:3:"ȧ";s:2:"Ȩ";s:3:"Ȩ";s:2:"ȩ";s:3:"ȩ";s:2:"Ȫ";s:5:"Ȫ";s:2:"ȫ";s:5:"ȫ";s:2:"Ȭ";s:5:"Ȭ";s:2:"ȭ";s:5:"ȭ";s:2:"Ȯ";s:3:"Ȯ";s:2:"ȯ";s:3:"ȯ";s:2:"Ȱ";s:5:"Ȱ";s:2:"ȱ";s:5:"ȱ";s:2:"Ȳ";s:3:"Ȳ";s:2:"ȳ";s:3:"ȳ";s:2:"ʰ";s:1:"h";s:2:"ʱ";s:2:"ɦ";s:2:"ʲ";s:1:"j";s:2:"ʳ";s:1:"r";s:2:"ʴ";s:2:"ɹ";s:2:"ʵ";s:2:"ɻ";s:2:"ʶ";s:2:"ʁ";s:2:"ʷ";s:1:"w";s:2:"ʸ";s:1:"y";s:2:"˘";s:3:" ̆";s:2:"˙";s:3:" ̇";s:2:"˚";s:3:" ̊";s:2:"˛";s:3:" ̨";s:2:"˜";s:3:" ̃";s:2:"˝";s:3:" ̋";s:2:"ˠ";s:2:"ɣ";s:2:"ˡ";s:1:"l";s:2:"ˢ";s:1:"s";s:2:"ˣ";s:1:"x";s:2:"ˤ";s:2:"ʕ";s:2:"̀";s:2:"̀";s:2:"́";s:2:"́";s:2:"̓";s:2:"̓";s:2:"̈́";s:4:"̈́";s:2:"ʹ";s:2:"ʹ";s:2:"ͺ";s:3:" ͅ";s:2:";";s:1:";";s:2:"΄";s:3:" ́";s:2:"΅";s:5:" ̈́";s:2:"Ά";s:4:"Ά";s:2:"·";s:2:"·";s:2:"Έ";s:4:"Έ";s:2:"Ή";s:4:"Ή";s:2:"Ί";s:4:"Ί";s:2:"Ό";s:4:"Ό";s:2:"Ύ";s:4:"Ύ";s:2:"Ώ";s:4:"Ώ";s:2:"ΐ";s:6:"ΐ";s:2:"Ϊ";s:4:"Ϊ";s:2:"Ϋ";s:4:"Ϋ";s:2:"ά";s:4:"ά";s:2:"έ";s:4:"έ";s:2:"ή";s:4:"ή";s:2:"ί";s:4:"ί";s:2:"ΰ";s:6:"ΰ";s:2:"ϊ";s:4:"ϊ";s:2:"ϋ";s:4:"ϋ";s:2:"ό";s:4:"ό";s:2:"ύ";s:4:"ύ";s:2:"ώ";s:4:"ώ";s:2:"ϐ";s:2:"β";s:2:"ϑ";s:2:"θ";s:2:"ϒ";s:2:"Υ";s:2:"ϓ";s:4:"Ύ";s:2:"ϔ";s:4:"Ϋ";s:2:"ϕ";s:2:"φ";s:2:"ϖ";s:2:"π";s:2:"ϰ";s:2:"κ";s:2:"ϱ";s:2:"ρ";s:2:"ϲ";s:2:"ς";s:2:"ϴ";s:2:"Θ";s:2:"ϵ";s:2:"ε";s:2:"Ϲ";s:2:"Σ";s:2:"Ѐ";s:4:"Ѐ";s:2:"Ё";s:4:"Ё";s:2:"Ѓ";s:4:"Ѓ";s:2:"Ї";s:4:"Ї";s:2:"Ќ";s:4:"Ќ";s:2:"Ѝ";s:4:"Ѝ";s:2:"Ў";s:4:"Ў";s:2:"Й";s:4:"Й";s:2:"й";s:4:"й";s:2:"ѐ";s:4:"ѐ";s:2:"ё";s:4:"ё";s:2:"ѓ";s:4:"ѓ";s:2:"ї";s:4:"ї";s:2:"ќ";s:4:"ќ";s:2:"ѝ";s:4:"ѝ";s:2:"ў";s:4:"ў";s:2:"Ѷ";s:4:"Ѷ";s:2:"ѷ";s:4:"ѷ";s:2:"Ӂ";s:4:"Ӂ";s:2:"ӂ";s:4:"ӂ";s:2:"Ӑ";s:4:"Ӑ";s:2:"ӑ";s:4:"ӑ";s:2:"Ӓ";s:4:"Ӓ";s:2:"ӓ";s:4:"ӓ";s:2:"Ӗ";s:4:"Ӗ";s:2:"ӗ";s:4:"ӗ";s:2:"Ӛ";s:4:"Ӛ";s:2:"ӛ";s:4:"ӛ";s:2:"Ӝ";s:4:"Ӝ";s:2:"ӝ";s:4:"ӝ";s:2:"Ӟ";s:4:"Ӟ";s:2:"ӟ";s:4:"ӟ";s:2:"Ӣ";s:4:"Ӣ";s:2:"ӣ";s:4:"ӣ";s:2:"Ӥ";s:4:"Ӥ";s:2:"ӥ";s:4:"ӥ";s:2:"Ӧ";s:4:"Ӧ";s:2:"ӧ";s:4:"ӧ";s:2:"Ӫ";s:4:"Ӫ";s:2:"ӫ";s:4:"ӫ";s:2:"Ӭ";s:4:"Ӭ";s:2:"ӭ";s:4:"ӭ";s:2:"Ӯ";s:4:"Ӯ";s:2:"ӯ";s:4:"ӯ";s:2:"Ӱ";s:4:"Ӱ";s:2:"ӱ";s:4:"ӱ";s:2:"Ӳ";s:4:"Ӳ";s:2:"ӳ";s:4:"ӳ";s:2:"Ӵ";s:4:"Ӵ";s:2:"ӵ";s:4:"ӵ";s:2:"Ӹ";s:4:"Ӹ";s:2:"ӹ";s:4:"ӹ";s:2:"և";s:4:"եւ";s:2:"آ";s:4:"آ";s:2:"أ";s:4:"أ";s:2:"ؤ";s:4:"ؤ";s:2:"إ";s:4:"إ";s:2:"ئ";s:4:"ئ";s:2:"ٵ";s:4:"اٴ";s:2:"ٶ";s:4:"وٴ";s:2:"ٷ";s:4:"ۇٴ";s:2:"ٸ";s:4:"يٴ";s:2:"ۀ";s:4:"ۀ";s:2:"ۂ";s:4:"ۂ";s:2:"ۓ";s:4:"ۓ";s:3:"ऩ";s:6:"ऩ";s:3:"ऱ";s:6:"ऱ";s:3:"ऴ";s:6:"ऴ";s:3:"क़";s:6:"क़";s:3:"ख़";s:6:"ख़";s:3:"ग़";s:6:"ग़";s:3:"ज़";s:6:"ज़";s:3:"ड़";s:6:"ड़";s:3:"ढ़";s:6:"ढ़";s:3:"फ़";s:6:"फ़";s:3:"य़";s:6:"य़";s:3:"ো";s:6:"ো";s:3:"ৌ";s:6:"ৌ";s:3:"ড়";s:6:"ড়";s:3:"ঢ়";s:6:"ঢ়";s:3:"য়";s:6:"য়";s:3:"ਲ਼";s:6:"ਲ਼";s:3:"ਸ਼";s:6:"ਸ਼";s:3:"ਖ਼";s:6:"ਖ਼";s:3:"ਗ਼";s:6:"ਗ਼";s:3:"ਜ਼";s:6:"ਜ਼";s:3:"ਫ਼";s:6:"ਫ਼";s:3:"ୈ";s:6:"ୈ";s:3:"ୋ";s:6:"ୋ";s:3:"ୌ";s:6:"ୌ";s:3:"ଡ଼";s:6:"ଡ଼";s:3:"ଢ଼";s:6:"ଢ଼";s:3:"ஔ";s:6:"ஔ";s:3:"ொ";s:6:"ொ";s:3:"ோ";s:6:"ோ";s:3:"ௌ";s:6:"ௌ";s:3:"ై";s:6:"ై";s:3:"ೀ";s:6:"ೀ";s:3:"ೇ";s:6:"ೇ";s:3:"ೈ";s:6:"ೈ";s:3:"ೊ";s:6:"ೊ";s:3:"ೋ";s:9:"ೋ";s:3:"ൊ";s:6:"ൊ";s:3:"ോ";s:6:"ോ";s:3:"ൌ";s:6:"ൌ";s:3:"ේ";s:6:"ේ";s:3:"ො";s:6:"ො";s:3:"ෝ";s:9:"ෝ";s:3:"ෞ";s:6:"ෞ";s:3:"ำ";s:6:"ํา";s:3:"ຳ";s:6:"ໍາ";s:3:"ໜ";s:6:"ຫນ";s:3:"ໝ";s:6:"ຫມ";s:3:"༌";s:3:"་";s:3:"གྷ";s:6:"གྷ";s:3:"ཌྷ";s:6:"ཌྷ";s:3:"དྷ";s:6:"དྷ";s:3:"བྷ";s:6:"བྷ";s:3:"ཛྷ";s:6:"ཛྷ";s:3:"ཀྵ";s:6:"ཀྵ";s:3:"ཱི";s:6:"ཱི";s:3:"ཱུ";s:6:"ཱུ";s:3:"ྲྀ";s:6:"ྲྀ";s:3:"ཷ";s:9:"ྲཱྀ";s:3:"ླྀ";s:6:"ླྀ";s:3:"ཹ";s:9:"ླཱྀ";s:3:"ཱྀ";s:6:"ཱྀ";s:3:"ྒྷ";s:6:"ྒྷ";s:3:"ྜྷ";s:6:"ྜྷ";s:3:"ྡྷ";s:6:"ྡྷ";s:3:"ྦྷ";s:6:"ྦྷ";s:3:"ྫྷ";s:6:"ྫྷ";s:3:"ྐྵ";s:6:"ྐྵ";s:3:"ဦ";s:6:"ဦ";s:3:"ჼ";s:3:"ნ";s:3:"ᴬ";s:1:"A";s:3:"ᴭ";s:2:"Æ";s:3:"ᴮ";s:1:"B";s:3:"ᴰ";s:1:"D";s:3:"ᴱ";s:1:"E";s:3:"ᴲ";s:2:"Ǝ";s:3:"ᴳ";s:1:"G";s:3:"ᴴ";s:1:"H";s:3:"ᴵ";s:1:"I";s:3:"ᴶ";s:1:"J";s:3:"ᴷ";s:1:"K";s:3:"ᴸ";s:1:"L";s:3:"ᴹ";s:1:"M";s:3:"ᴺ";s:1:"N";s:3:"ᴼ";s:1:"O";s:3:"ᴽ";s:2:"Ȣ";s:3:"ᴾ";s:1:"P";s:3:"ᴿ";s:1:"R";s:3:"ᵀ";s:1:"T";s:3:"ᵁ";s:1:"U";s:3:"ᵂ";s:1:"W";s:3:"ᵃ";s:1:"a";s:3:"ᵄ";s:2:"ɐ";s:3:"ᵅ";s:2:"ɑ";s:3:"ᵆ";s:3:"ᴂ";s:3:"ᵇ";s:1:"b";s:3:"ᵈ";s:1:"d";s:3:"ᵉ";s:1:"e";s:3:"ᵊ";s:2:"ə";s:3:"ᵋ";s:2:"ɛ";s:3:"ᵌ";s:2:"ɜ";s:3:"ᵍ";s:1:"g";s:3:"ᵏ";s:1:"k";s:3:"ᵐ";s:1:"m";s:3:"ᵑ";s:2:"ŋ";s:3:"ᵒ";s:1:"o";s:3:"ᵓ";s:2:"ɔ";s:3:"ᵔ";s:3:"ᴖ";s:3:"ᵕ";s:3:"ᴗ";s:3:"ᵖ";s:1:"p";s:3:"ᵗ";s:1:"t";s:3:"ᵘ";s:1:"u";s:3:"ᵙ";s:3:"ᴝ";s:3:"ᵚ";s:2:"ɯ";s:3:"ᵛ";s:1:"v";s:3:"ᵜ";s:3:"ᴥ";s:3:"ᵝ";s:2:"β";s:3:"ᵞ";s:2:"γ";s:3:"ᵟ";s:2:"δ";s:3:"ᵠ";s:2:"φ";s:3:"ᵡ";s:2:"χ";s:3:"ᵢ";s:1:"i";s:3:"ᵣ";s:1:"r";s:3:"ᵤ";s:1:"u";s:3:"ᵥ";s:1:"v";s:3:"ᵦ";s:2:"β";s:3:"ᵧ";s:2:"γ";s:3:"ᵨ";s:2:"ρ";s:3:"ᵩ";s:2:"φ";s:3:"ᵪ";s:2:"χ";s:3:"ᵸ";s:2:"н";s:3:"ᶛ";s:2:"ɒ";s:3:"ᶜ";s:1:"c";s:3:"ᶝ";s:2:"ɕ";s:3:"ᶞ";s:2:"ð";s:3:"ᶟ";s:2:"ɜ";s:3:"ᶠ";s:1:"f";s:3:"ᶡ";s:2:"ɟ";s:3:"ᶢ";s:2:"ɡ";s:3:"ᶣ";s:2:"ɥ";s:3:"ᶤ";s:2:"ɨ";s:3:"ᶥ";s:2:"ɩ";s:3:"ᶦ";s:2:"ɪ";s:3:"ᶧ";s:3:"ᵻ";s:3:"ᶨ";s:2:"ʝ";s:3:"ᶩ";s:2:"ɭ";s:3:"ᶪ";s:3:"ᶅ";s:3:"ᶫ";s:2:"ʟ";s:3:"ᶬ";s:2:"ɱ";s:3:"ᶭ";s:2:"ɰ";s:3:"ᶮ";s:2:"ɲ";s:3:"ᶯ";s:2:"ɳ";s:3:"ᶰ";s:2:"ɴ";s:3:"ᶱ";s:2:"ɵ";s:3:"ᶲ";s:2:"ɸ";s:3:"ᶳ";s:2:"ʂ";s:3:"ᶴ";s:2:"ʃ";s:3:"ᶵ";s:2:"ƫ";s:3:"ᶶ";s:2:"ʉ";s:3:"ᶷ";s:2:"ʊ";s:3:"ᶸ";s:3:"ᴜ";s:3:"ᶹ";s:2:"ʋ";s:3:"ᶺ";s:2:"ʌ";s:3:"ᶻ";s:1:"z";s:3:"ᶼ";s:2:"ʐ";s:3:"ᶽ";s:2:"ʑ";s:3:"ᶾ";s:2:"ʒ";s:3:"ᶿ";s:2:"θ";s:3:"Ḁ";s:3:"Ḁ";s:3:"ḁ";s:3:"ḁ";s:3:"Ḃ";s:3:"Ḃ";s:3:"ḃ";s:3:"ḃ";s:3:"Ḅ";s:3:"Ḅ";s:3:"ḅ";s:3:"ḅ";s:3:"Ḇ";s:3:"Ḇ";s:3:"ḇ";s:3:"ḇ";s:3:"Ḉ";s:5:"Ḉ";s:3:"ḉ";s:5:"ḉ";s:3:"Ḋ";s:3:"Ḋ";s:3:"ḋ";s:3:"ḋ";s:3:"Ḍ";s:3:"Ḍ";s:3:"ḍ";s:3:"ḍ";s:3:"Ḏ";s:3:"Ḏ";s:3:"ḏ";s:3:"ḏ";s:3:"Ḑ";s:3:"Ḑ";s:3:"ḑ";s:3:"ḑ";s:3:"Ḓ";s:3:"Ḓ";s:3:"ḓ";s:3:"ḓ";s:3:"Ḕ";s:5:"Ḕ";s:3:"ḕ";s:5:"ḕ";s:3:"Ḗ";s:5:"Ḗ";s:3:"ḗ";s:5:"ḗ";s:3:"Ḙ";s:3:"Ḙ";s:3:"ḙ";s:3:"ḙ";s:3:"Ḛ";s:3:"Ḛ";s:3:"ḛ";s:3:"ḛ";s:3:"Ḝ";s:5:"Ḝ";s:3:"ḝ";s:5:"ḝ";s:3:"Ḟ";s:3:"Ḟ";s:3:"ḟ";s:3:"ḟ";s:3:"Ḡ";s:3:"Ḡ";s:3:"ḡ";s:3:"ḡ";s:3:"Ḣ";s:3:"Ḣ";s:3:"ḣ";s:3:"ḣ";s:3:"Ḥ";s:3:"Ḥ";s:3:"ḥ";s:3:"ḥ";s:3:"Ḧ";s:3:"Ḧ";s:3:"ḧ";s:3:"ḧ";s:3:"Ḩ";s:3:"Ḩ";s:3:"ḩ";s:3:"ḩ";s:3:"Ḫ";s:3:"Ḫ";s:3:"ḫ";s:3:"ḫ";s:3:"Ḭ";s:3:"Ḭ";s:3:"ḭ";s:3:"ḭ";s:3:"Ḯ";s:5:"Ḯ";s:3:"ḯ";s:5:"ḯ";s:3:"Ḱ";s:3:"Ḱ";s:3:"ḱ";s:3:"ḱ";s:3:"Ḳ";s:3:"Ḳ";s:3:"ḳ";s:3:"ḳ";s:3:"Ḵ";s:3:"Ḵ";s:3:"ḵ";s:3:"ḵ";s:3:"Ḷ";s:3:"Ḷ";s:3:"ḷ";s:3:"ḷ";s:3:"Ḹ";s:5:"Ḹ";s:3:"ḹ";s:5:"ḹ";s:3:"Ḻ";s:3:"Ḻ";s:3:"ḻ";s:3:"ḻ";s:3:"Ḽ";s:3:"Ḽ";s:3:"ḽ";s:3:"ḽ";s:3:"Ḿ";s:3:"Ḿ";s:3:"ḿ";s:3:"ḿ";s:3:"Ṁ";s:3:"Ṁ";s:3:"ṁ";s:3:"ṁ";s:3:"Ṃ";s:3:"Ṃ";s:3:"ṃ";s:3:"ṃ";s:3:"Ṅ";s:3:"Ṅ";s:3:"ṅ";s:3:"ṅ";s:3:"Ṇ";s:3:"Ṇ";s:3:"ṇ";s:3:"ṇ";s:3:"Ṉ";s:3:"Ṉ";s:3:"ṉ";s:3:"ṉ";s:3:"Ṋ";s:3:"Ṋ";s:3:"ṋ";s:3:"ṋ";s:3:"Ṍ";s:5:"Ṍ";s:3:"ṍ";s:5:"ṍ";s:3:"Ṏ";s:5:"Ṏ";s:3:"ṏ";s:5:"ṏ";s:3:"Ṑ";s:5:"Ṑ";s:3:"ṑ";s:5:"ṑ";s:3:"Ṓ";s:5:"Ṓ";s:3:"ṓ";s:5:"ṓ";s:3:"Ṕ";s:3:"Ṕ";s:3:"ṕ";s:3:"ṕ";s:3:"Ṗ";s:3:"Ṗ";s:3:"ṗ";s:3:"ṗ";s:3:"Ṙ";s:3:"Ṙ";s:3:"ṙ";s:3:"ṙ";s:3:"Ṛ";s:3:"Ṛ";s:3:"ṛ";s:3:"ṛ";s:3:"Ṝ";s:5:"Ṝ";s:3:"ṝ";s:5:"ṝ";s:3:"Ṟ";s:3:"Ṟ";s:3:"ṟ";s:3:"ṟ";s:3:"Ṡ";s:3:"Ṡ";s:3:"ṡ";s:3:"ṡ";s:3:"Ṣ";s:3:"Ṣ";s:3:"ṣ";s:3:"ṣ";s:3:"Ṥ";s:5:"Ṥ";s:3:"ṥ";s:5:"ṥ";s:3:"Ṧ";s:5:"Ṧ";s:3:"ṧ";s:5:"ṧ";s:3:"Ṩ";s:5:"Ṩ";s:3:"ṩ";s:5:"ṩ";s:3:"Ṫ";s:3:"Ṫ";s:3:"ṫ";s:3:"ṫ";s:3:"Ṭ";s:3:"Ṭ";s:3:"ṭ";s:3:"ṭ";s:3:"Ṯ";s:3:"Ṯ";s:3:"ṯ";s:3:"ṯ";s:3:"Ṱ";s:3:"Ṱ";s:3:"ṱ";s:3:"ṱ";s:3:"Ṳ";s:3:"Ṳ";s:3:"ṳ";s:3:"ṳ";s:3:"Ṵ";s:3:"Ṵ";s:3:"ṵ";s:3:"ṵ";s:3:"Ṷ";s:3:"Ṷ";s:3:"ṷ";s:3:"ṷ";s:3:"Ṹ";s:5:"Ṹ";s:3:"ṹ";s:5:"ṹ";s:3:"Ṻ";s:5:"Ṻ";s:3:"ṻ";s:5:"ṻ";s:3:"Ṽ";s:3:"Ṽ";s:3:"ṽ";s:3:"ṽ";s:3:"Ṿ";s:3:"Ṿ";s:3:"ṿ";s:3:"ṿ";s:3:"Ẁ";s:3:"Ẁ";s:3:"ẁ";s:3:"ẁ";s:3:"Ẃ";s:3:"Ẃ";s:3:"ẃ";s:3:"ẃ";s:3:"Ẅ";s:3:"Ẅ";s:3:"ẅ";s:3:"ẅ";s:3:"Ẇ";s:3:"Ẇ";s:3:"ẇ";s:3:"ẇ";s:3:"Ẉ";s:3:"Ẉ";s:3:"ẉ";s:3:"ẉ";s:3:"Ẋ";s:3:"Ẋ";s:3:"ẋ";s:3:"ẋ";s:3:"Ẍ";s:3:"Ẍ";s:3:"ẍ";s:3:"ẍ";s:3:"Ẏ";s:3:"Ẏ";s:3:"ẏ";s:3:"ẏ";s:3:"Ẑ";s:3:"Ẑ";s:3:"ẑ";s:3:"ẑ";s:3:"Ẓ";s:3:"Ẓ";s:3:"ẓ";s:3:"ẓ";s:3:"Ẕ";s:3:"Ẕ";s:3:"ẕ";s:3:"ẕ";s:3:"ẖ";s:3:"ẖ";s:3:"ẗ";s:3:"ẗ";s:3:"ẘ";s:3:"ẘ";s:3:"ẙ";s:3:"ẙ";s:3:"ẚ";s:3:"aʾ";s:3:"ẛ";s:3:"ṡ";s:3:"Ạ";s:3:"Ạ";s:3:"ạ";s:3:"ạ";s:3:"Ả";s:3:"Ả";s:3:"ả";s:3:"ả";s:3:"Ấ";s:5:"Ấ";s:3:"ấ";s:5:"ấ";s:3:"Ầ";s:5:"Ầ";s:3:"ầ";s:5:"ầ";s:3:"Ẩ";s:5:"Ẩ";s:3:"ẩ";s:5:"ẩ";s:3:"Ẫ";s:5:"Ẫ";s:3:"ẫ";s:5:"ẫ";s:3:"Ậ";s:5:"Ậ";s:3:"ậ";s:5:"ậ";s:3:"Ắ";s:5:"Ắ";s:3:"ắ";s:5:"ắ";s:3:"Ằ";s:5:"Ằ";s:3:"ằ";s:5:"ằ";s:3:"Ẳ";s:5:"Ẳ";s:3:"ẳ";s:5:"ẳ";s:3:"Ẵ";s:5:"Ẵ";s:3:"ẵ";s:5:"ẵ";s:3:"Ặ";s:5:"Ặ";s:3:"ặ";s:5:"ặ";s:3:"Ẹ";s:3:"Ẹ";s:3:"ẹ";s:3:"ẹ";s:3:"Ẻ";s:3:"Ẻ";s:3:"ẻ";s:3:"ẻ";s:3:"Ẽ";s:3:"Ẽ";s:3:"ẽ";s:3:"ẽ";s:3:"Ế";s:5:"Ế";s:3:"ế";s:5:"ế";s:3:"Ề";s:5:"Ề";s:3:"ề";s:5:"ề";s:3:"Ể";s:5:"Ể";s:3:"ể";s:5:"ể";s:3:"Ễ";s:5:"Ễ";s:3:"ễ";s:5:"ễ";s:3:"Ệ";s:5:"Ệ";s:3:"ệ";s:5:"ệ";s:3:"Ỉ";s:3:"Ỉ";s:3:"ỉ";s:3:"ỉ";s:3:"Ị";s:3:"Ị";s:3:"ị";s:3:"ị";s:3:"Ọ";s:3:"Ọ";s:3:"ọ";s:3:"ọ";s:3:"Ỏ";s:3:"Ỏ";s:3:"ỏ";s:3:"ỏ";s:3:"Ố";s:5:"Ố";s:3:"ố";s:5:"ố";s:3:"Ồ";s:5:"Ồ";s:3:"ồ";s:5:"ồ";s:3:"Ổ";s:5:"Ổ";s:3:"ổ";s:5:"ổ";s:3:"Ỗ";s:5:"Ỗ";s:3:"ỗ";s:5:"ỗ";s:3:"Ộ";s:5:"Ộ";s:3:"ộ";s:5:"ộ";s:3:"Ớ";s:5:"Ớ";s:3:"ớ";s:5:"ớ";s:3:"Ờ";s:5:"Ờ";s:3:"ờ";s:5:"ờ";s:3:"Ở";s:5:"Ở";s:3:"ở";s:5:"ở";s:3:"Ỡ";s:5:"Ỡ";s:3:"ỡ";s:5:"ỡ";s:3:"Ợ";s:5:"Ợ";s:3:"ợ";s:5:"ợ";s:3:"Ụ";s:3:"Ụ";s:3:"ụ";s:3:"ụ";s:3:"Ủ";s:3:"Ủ";s:3:"ủ";s:3:"ủ";s:3:"Ứ";s:5:"Ứ";s:3:"ứ";s:5:"ứ";s:3:"Ừ";s:5:"Ừ";s:3:"ừ";s:5:"ừ";s:3:"Ử";s:5:"Ử";s:3:"ử";s:5:"ử";s:3:"Ữ";s:5:"Ữ";s:3:"ữ";s:5:"ữ";s:3:"Ự";s:5:"Ự";s:3:"ự";s:5:"ự";s:3:"Ỳ";s:3:"Ỳ";s:3:"ỳ";s:3:"ỳ";s:3:"Ỵ";s:3:"Ỵ";s:3:"ỵ";s:3:"ỵ";s:3:"Ỷ";s:3:"Ỷ";s:3:"ỷ";s:3:"ỷ";s:3:"Ỹ";s:3:"Ỹ";s:3:"ỹ";s:3:"ỹ";s:3:"ἀ";s:4:"ἀ";s:3:"ἁ";s:4:"ἁ";s:3:"ἂ";s:6:"ἂ";s:3:"ἃ";s:6:"ἃ";s:3:"ἄ";s:6:"ἄ";s:3:"ἅ";s:6:"ἅ";s:3:"ἆ";s:6:"ἆ";s:3:"ἇ";s:6:"ἇ";s:3:"Ἀ";s:4:"Ἀ";s:3:"Ἁ";s:4:"Ἁ";s:3:"Ἂ";s:6:"Ἂ";s:3:"Ἃ";s:6:"Ἃ";s:3:"Ἄ";s:6:"Ἄ";s:3:"Ἅ";s:6:"Ἅ";s:3:"Ἆ";s:6:"Ἆ";s:3:"Ἇ";s:6:"Ἇ";s:3:"ἐ";s:4:"ἐ";s:3:"ἑ";s:4:"ἑ";s:3:"ἒ";s:6:"ἒ";s:3:"ἓ";s:6:"ἓ";s:3:"ἔ";s:6:"ἔ";s:3:"ἕ";s:6:"ἕ";s:3:"Ἐ";s:4:"Ἐ";s:3:"Ἑ";s:4:"Ἑ";s:3:"Ἒ";s:6:"Ἒ";s:3:"Ἓ";s:6:"Ἓ";s:3:"Ἔ";s:6:"Ἔ";s:3:"Ἕ";s:6:"Ἕ";s:3:"ἠ";s:4:"ἠ";s:3:"ἡ";s:4:"ἡ";s:3:"ἢ";s:6:"ἢ";s:3:"ἣ";s:6:"ἣ";s:3:"ἤ";s:6:"ἤ";s:3:"ἥ";s:6:"ἥ";s:3:"ἦ";s:6:"ἦ";s:3:"ἧ";s:6:"ἧ";s:3:"Ἠ";s:4:"Ἠ";s:3:"Ἡ";s:4:"Ἡ";s:3:"Ἢ";s:6:"Ἢ";s:3:"Ἣ";s:6:"Ἣ";s:3:"Ἤ";s:6:"Ἤ";s:3:"Ἥ";s:6:"Ἥ";s:3:"Ἦ";s:6:"Ἦ";s:3:"Ἧ";s:6:"Ἧ";s:3:"ἰ";s:4:"ἰ";s:3:"ἱ";s:4:"ἱ";s:3:"ἲ";s:6:"ἲ";s:3:"ἳ";s:6:"ἳ";s:3:"ἴ";s:6:"ἴ";s:3:"ἵ";s:6:"ἵ";s:3:"ἶ";s:6:"ἶ";s:3:"ἷ";s:6:"ἷ";s:3:"Ἰ";s:4:"Ἰ";s:3:"Ἱ";s:4:"Ἱ";s:3:"Ἲ";s:6:"Ἲ";s:3:"Ἳ";s:6:"Ἳ";s:3:"Ἴ";s:6:"Ἴ";s:3:"Ἵ";s:6:"Ἵ";s:3:"Ἶ";s:6:"Ἶ";s:3:"Ἷ";s:6:"Ἷ";s:3:"ὀ";s:4:"ὀ";s:3:"ὁ";s:4:"ὁ";s:3:"ὂ";s:6:"ὂ";s:3:"ὃ";s:6:"ὃ";s:3:"ὄ";s:6:"ὄ";s:3:"ὅ";s:6:"ὅ";s:3:"Ὀ";s:4:"Ὀ";s:3:"Ὁ";s:4:"Ὁ";s:3:"Ὂ";s:6:"Ὂ";s:3:"Ὃ";s:6:"Ὃ";s:3:"Ὄ";s:6:"Ὄ";s:3:"Ὅ";s:6:"Ὅ";s:3:"ὐ";s:4:"ὐ";s:3:"ὑ";s:4:"ὑ";s:3:"ὒ";s:6:"ὒ";s:3:"ὓ";s:6:"ὓ";s:3:"ὔ";s:6:"ὔ";s:3:"ὕ";s:6:"ὕ";s:3:"ὖ";s:6:"ὖ";s:3:"ὗ";s:6:"ὗ";s:3:"Ὑ";s:4:"Ὑ";s:3:"Ὓ";s:6:"Ὓ";s:3:"Ὕ";s:6:"Ὕ";s:3:"Ὗ";s:6:"Ὗ";s:3:"ὠ";s:4:"ὠ";s:3:"ὡ";s:4:"ὡ";s:3:"ὢ";s:6:"ὢ";s:3:"ὣ";s:6:"ὣ";s:3:"ὤ";s:6:"ὤ";s:3:"ὥ";s:6:"ὥ";s:3:"ὦ";s:6:"ὦ";s:3:"ὧ";s:6:"ὧ";s:3:"Ὠ";s:4:"Ὠ";s:3:"Ὡ";s:4:"Ὡ";s:3:"Ὢ";s:6:"Ὢ";s:3:"Ὣ";s:6:"Ὣ";s:3:"Ὤ";s:6:"Ὤ";s:3:"Ὥ";s:6:"Ὥ";s:3:"Ὦ";s:6:"Ὦ";s:3:"Ὧ";s:6:"Ὧ";s:3:"ὰ";s:4:"ὰ";s:3:"ά";s:4:"ά";s:3:"ὲ";s:4:"ὲ";s:3:"έ";s:4:"έ";s:3:"ὴ";s:4:"ὴ";s:3:"ή";s:4:"ή";s:3:"ὶ";s:4:"ὶ";s:3:"ί";s:4:"ί";s:3:"ὸ";s:4:"ὸ";s:3:"ό";s:4:"ό";s:3:"ὺ";s:4:"ὺ";s:3:"ύ";s:4:"ύ";s:3:"ὼ";s:4:"ὼ";s:3:"ώ";s:4:"ώ";s:3:"ᾀ";s:6:"ᾀ";s:3:"ᾁ";s:6:"ᾁ";s:3:"ᾂ";s:8:"ᾂ";s:3:"ᾃ";s:8:"ᾃ";s:3:"ᾄ";s:8:"ᾄ";s:3:"ᾅ";s:8:"ᾅ";s:3:"ᾆ";s:8:"ᾆ";s:3:"ᾇ";s:8:"ᾇ";s:3:"ᾈ";s:6:"ᾈ";s:3:"ᾉ";s:6:"ᾉ";s:3:"ᾊ";s:8:"ᾊ";s:3:"ᾋ";s:8:"ᾋ";s:3:"ᾌ";s:8:"ᾌ";s:3:"ᾍ";s:8:"ᾍ";s:3:"ᾎ";s:8:"ᾎ";s:3:"ᾏ";s:8:"ᾏ";s:3:"ᾐ";s:6:"ᾐ";s:3:"ᾑ";s:6:"ᾑ";s:3:"ᾒ";s:8:"ᾒ";s:3:"ᾓ";s:8:"ᾓ";s:3:"ᾔ";s:8:"ᾔ";s:3:"ᾕ";s:8:"ᾕ";s:3:"ᾖ";s:8:"ᾖ";s:3:"ᾗ";s:8:"ᾗ";s:3:"ᾘ";s:6:"ᾘ";s:3:"ᾙ";s:6:"ᾙ";s:3:"ᾚ";s:8:"ᾚ";s:3:"ᾛ";s:8:"ᾛ";s:3:"ᾜ";s:8:"ᾜ";s:3:"ᾝ";s:8:"ᾝ";s:3:"ᾞ";s:8:"ᾞ";s:3:"ᾟ";s:8:"ᾟ";s:3:"ᾠ";s:6:"ᾠ";s:3:"ᾡ";s:6:"ᾡ";s:3:"ᾢ";s:8:"ᾢ";s:3:"ᾣ";s:8:"ᾣ";s:3:"ᾤ";s:8:"ᾤ";s:3:"ᾥ";s:8:"ᾥ";s:3:"ᾦ";s:8:"ᾦ";s:3:"ᾧ";s:8:"ᾧ";s:3:"ᾨ";s:6:"ᾨ";s:3:"ᾩ";s:6:"ᾩ";s:3:"ᾪ";s:8:"ᾪ";s:3:"ᾫ";s:8:"ᾫ";s:3:"ᾬ";s:8:"ᾬ";s:3:"ᾭ";s:8:"ᾭ";s:3:"ᾮ";s:8:"ᾮ";s:3:"ᾯ";s:8:"ᾯ";s:3:"ᾰ";s:4:"ᾰ";s:3:"ᾱ";s:4:"ᾱ";s:3:"ᾲ";s:6:"ᾲ";s:3:"ᾳ";s:4:"ᾳ";s:3:"ᾴ";s:6:"ᾴ";s:3:"ᾶ";s:4:"ᾶ";s:3:"ᾷ";s:6:"ᾷ";s:3:"Ᾰ";s:4:"Ᾰ";s:3:"Ᾱ";s:4:"Ᾱ";s:3:"Ὰ";s:4:"Ὰ";s:3:"Ά";s:4:"Ά";s:3:"ᾼ";s:4:"ᾼ";s:3:"᾽";s:3:" ̓";s:3:"ι";s:2:"ι";s:3:"᾿";s:3:" ̓";s:3:"῀";s:3:" ͂";s:3:"῁";s:5:" ̈͂";s:3:"ῂ";s:6:"ῂ";s:3:"ῃ";s:4:"ῃ";s:3:"ῄ";s:6:"ῄ";s:3:"ῆ";s:4:"ῆ";s:3:"ῇ";s:6:"ῇ";s:3:"Ὲ";s:4:"Ὲ";s:3:"Έ";s:4:"Έ";s:3:"Ὴ";s:4:"Ὴ";s:3:"Ή";s:4:"Ή";s:3:"ῌ";s:4:"ῌ";s:3:"῍";s:5:" ̓̀";s:3:"῎";s:5:" ̓́";s:3:"῏";s:5:" ̓͂";s:3:"ῐ";s:4:"ῐ";s:3:"ῑ";s:4:"ῑ";s:3:"ῒ";s:6:"ῒ";s:3:"ΐ";s:6:"ΐ";s:3:"ῖ";s:4:"ῖ";s:3:"ῗ";s:6:"ῗ";s:3:"Ῐ";s:4:"Ῐ";s:3:"Ῑ";s:4:"Ῑ";s:3:"Ὶ";s:4:"Ὶ";s:3:"Ί";s:4:"Ί";s:3:"῝";s:5:" ̔̀";s:3:"῞";s:5:" ̔́";s:3:"῟";s:5:" ̔͂";s:3:"ῠ";s:4:"ῠ";s:3:"ῡ";s:4:"ῡ";s:3:"ῢ";s:6:"ῢ";s:3:"ΰ";s:6:"ΰ";s:3:"ῤ";s:4:"ῤ";s:3:"ῥ";s:4:"ῥ";s:3:"ῦ";s:4:"ῦ";s:3:"ῧ";s:6:"ῧ";s:3:"Ῠ";s:4:"Ῠ";s:3:"Ῡ";s:4:"Ῡ";s:3:"Ὺ";s:4:"Ὺ";s:3:"Ύ";s:4:"Ύ";s:3:"Ῥ";s:4:"Ῥ";s:3:"῭";s:5:" ̈̀";s:3:"΅";s:5:" ̈́";s:3:"`";s:1:"`";s:3:"ῲ";s:6:"ῲ";s:3:"ῳ";s:4:"ῳ";s:3:"ῴ";s:6:"ῴ";s:3:"ῶ";s:4:"ῶ";s:3:"ῷ";s:6:"ῷ";s:3:"Ὸ";s:4:"Ὸ";s:3:"Ό";s:4:"Ό";s:3:"Ὼ";s:4:"Ὼ";s:3:"Ώ";s:4:"Ώ";s:3:"ῼ";s:4:"ῼ";s:3:"´";s:3:" ́";s:3:"῾";s:3:" ̔";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:"‑";s:3:"‐";s:3:"‗";s:3:" ̳";s:3:"․";s:1:".";s:3:"‥";s:2:"..";s:3:"…";s:3:"...";s:3:" ";s:1:" ";s:3:"″";s:6:"′′";s:3:"‴";s:9:"′′′";s:3:"‶";s:6:"‵‵";s:3:"‷";s:9:"‵‵‵";s:3:"‼";s:2:"!!";s:3:"‾";s:3:" ̅";s:3:"⁇";s:2:"??";s:3:"⁈";s:2:"?!";s:3:"⁉";s:2:"!?";s:3:"⁗";s:12:"′′′′";s:3:" ";s:1:" ";s:3:"⁰";s:1:"0";s:3:"ⁱ";s:1:"i";s:3:"⁴";s:1:"4";s:3:"⁵";s:1:"5";s:3:"⁶";s:1:"6";s:3:"⁷";s:1:"7";s:3:"⁸";s:1:"8";s:3:"⁹";s:1:"9";s:3:"⁺";s:1:"+";s:3:"⁻";s:3:"−";s:3:"⁼";s:1:"=";s:3:"⁽";s:1:"(";s:3:"⁾";s:1:")";s:3:"ⁿ";s:1:"n";s:3:"₀";s:1:"0";s:3:"₁";s:1:"1";s:3:"₂";s:1:"2";s:3:"₃";s:1:"3";s:3:"₄";s:1:"4";s:3:"₅";s:1:"5";s:3:"₆";s:1:"6";s:3:"₇";s:1:"7";s:3:"₈";s:1:"8";s:3:"₉";s:1:"9";s:3:"₊";s:1:"+";s:3:"₋";s:3:"−";s:3:"₌";s:1:"=";s:3:"₍";s:1:"(";s:3:"₎";s:1:")";s:3:"ₐ";s:1:"a";s:3:"ₑ";s:1:"e";s:3:"ₒ";s:1:"o";s:3:"ₓ";s:1:"x";s:3:"ₔ";s:2:"ə";s:3:"₨";s:2:"Rs";s:3:"℀";s:3:"a/c";s:3:"℁";s:3:"a/s";s:3:"ℂ";s:1:"C";s:3:"℃";s:3:"°C";s:3:"℅";s:3:"c/o";s:3:"℆";s:3:"c/u";s:3:"ℇ";s:2:"Ɛ";s:3:"℉";s:3:"°F";s:3:"ℊ";s:1:"g";s:3:"ℋ";s:1:"H";s:3:"ℌ";s:1:"H";s:3:"ℍ";s:1:"H";s:3:"ℎ";s:1:"h";s:3:"ℏ";s:2:"ħ";s:3:"ℐ";s:1:"I";s:3:"ℑ";s:1:"I";s:3:"ℒ";s:1:"L";s:3:"ℓ";s:1:"l";s:3:"ℕ";s:1:"N";s:3:"№";s:2:"No";s:3:"ℙ";s:1:"P";s:3:"ℚ";s:1:"Q";s:3:"ℛ";s:1:"R";s:3:"ℜ";s:1:"R";s:3:"ℝ";s:1:"R";s:3:"℠";s:2:"SM";s:3:"℡";s:3:"TEL";s:3:"™";s:2:"TM";s:3:"ℤ";s:1:"Z";s:3:"Ω";s:2:"Ω";s:3:"ℨ";s:1:"Z";s:3:"K";s:1:"K";s:3:"Å";s:3:"Å";s:3:"ℬ";s:1:"B";s:3:"ℭ";s:1:"C";s:3:"ℯ";s:1:"e";s:3:"ℰ";s:1:"E";s:3:"ℱ";s:1:"F";s:3:"ℳ";s:1:"M";s:3:"ℴ";s:1:"o";s:3:"ℵ";s:2:"א";s:3:"ℶ";s:2:"ב";s:3:"ℷ";s:2:"ג";s:3:"ℸ";s:2:"ד";s:3:"ℹ";s:1:"i";s:3:"℻";s:3:"FAX";s:3:"ℼ";s:2:"π";s:3:"ℽ";s:2:"γ";s:3:"ℾ";s:2:"Γ";s:3:"ℿ";s:2:"Π";s:3:"⅀";s:3:"∑";s:3:"ⅅ";s:1:"D";s:3:"ⅆ";s:1:"d";s:3:"ⅇ";s:1:"e";s:3:"ⅈ";s:1:"i";s:3:"ⅉ";s:1:"j";s:3:"⅓";s:5:"1⁄3";s:3:"⅔";s:5:"2⁄3";s:3:"⅕";s:5:"1⁄5";s:3:"⅖";s:5:"2⁄5";s:3:"⅗";s:5:"3⁄5";s:3:"⅘";s:5:"4⁄5";s:3:"⅙";s:5:"1⁄6";s:3:"⅚";s:5:"5⁄6";s:3:"⅛";s:5:"1⁄8";s:3:"⅜";s:5:"3⁄8";s:3:"⅝";s:5:"5⁄8";s:3:"⅞";s:5:"7⁄8";s:3:"⅟";s:4:"1⁄";s:3:"Ⅰ";s:1:"I";s:3:"Ⅱ";s:2:"II";s:3:"Ⅲ";s:3:"III";s:3:"Ⅳ";s:2:"IV";s:3:"Ⅴ";s:1:"V";s:3:"Ⅵ";s:2:"VI";s:3:"Ⅶ";s:3:"VII";s:3:"Ⅷ";s:4:"VIII";s:3:"Ⅸ";s:2:"IX";s:3:"Ⅹ";s:1:"X";s:3:"Ⅺ";s:2:"XI";s:3:"Ⅻ";s:3:"XII";s:3:"Ⅼ";s:1:"L";s:3:"Ⅽ";s:1:"C";s:3:"Ⅾ";s:1:"D";s:3:"Ⅿ";s:1:"M";s:3:"ⅰ";s:1:"i";s:3:"ⅱ";s:2:"ii";s:3:"ⅲ";s:3:"iii";s:3:"ⅳ";s:2:"iv";s:3:"ⅴ";s:1:"v";s:3:"ⅵ";s:2:"vi";s:3:"ⅶ";s:3:"vii";s:3:"ⅷ";s:4:"viii";s:3:"ⅸ";s:2:"ix";s:3:"ⅹ";s:1:"x";s:3:"ⅺ";s:2:"xi";s:3:"ⅻ";s:3:"xii";s:3:"ⅼ";s:1:"l";s:3:"ⅽ";s:1:"c";s:3:"ⅾ";s:1:"d";s:3:"ⅿ";s:1:"m";s:3:"↚";s:5:"↚";s:3:"↛";s:5:"↛";s:3:"↮";s:5:"↮";s:3:"⇍";s:5:"⇍";s:3:"⇎";s:5:"⇎";s:3:"⇏";s:5:"⇏";s:3:"∄";s:5:"∄";s:3:"∉";s:5:"∉";s:3:"∌";s:5:"∌";s:3:"∤";s:5:"∤";s:3:"∦";s:5:"∦";s:3:"∬";s:6:"∫∫";s:3:"∭";s:9:"∫∫∫";s:3:"∯";s:6:"∮∮";s:3:"∰";s:9:"∮∮∮";s:3:"≁";s:5:"≁";s:3:"≄";s:5:"≄";s:3:"≇";s:5:"≇";s:3:"≉";s:5:"≉";s:3:"≠";s:3:"≠";s:3:"≢";s:5:"≢";s:3:"≭";s:5:"≭";s:3:"≮";s:3:"≮";s:3:"≯";s:3:"≯";s:3:"≰";s:5:"≰";s:3:"≱";s:5:"≱";s:3:"≴";s:5:"≴";s:3:"≵";s:5:"≵";s:3:"≸";s:5:"≸";s:3:"≹";s:5:"≹";s:3:"⊀";s:5:"⊀";s:3:"⊁";s:5:"⊁";s:3:"⊄";s:5:"⊄";s:3:"⊅";s:5:"⊅";s:3:"⊈";s:5:"⊈";s:3:"⊉";s:5:"⊉";s:3:"⊬";s:5:"⊬";s:3:"⊭";s:5:"⊭";s:3:"⊮";s:5:"⊮";s:3:"⊯";s:5:"⊯";s:3:"⋠";s:5:"⋠";s:3:"⋡";s:5:"⋡";s:3:"⋢";s:5:"⋢";s:3:"⋣";s:5:"⋣";s:3:"⋪";s:5:"⋪";s:3:"⋫";s:5:"⋫";s:3:"⋬";s:5:"⋬";s:3:"⋭";s:5:"⋭";s:3:"〈";s:3:"〈";s:3:"〉";s:3:"〉";s:3:"①";s:1:"1";s:3:"②";s:1:"2";s:3:"③";s:1:"3";s:3:"④";s:1:"4";s:3:"⑤";s:1:"5";s:3:"⑥";s:1:"6";s:3:"⑦";s:1:"7";s:3:"⑧";s:1:"8";s:3:"⑨";s:1:"9";s:3:"⑩";s:2:"10";s:3:"⑪";s:2:"11";s:3:"⑫";s:2:"12";s:3:"⑬";s:2:"13";s:3:"⑭";s:2:"14";s:3:"⑮";s:2:"15";s:3:"⑯";s:2:"16";s:3:"⑰";s:2:"17";s:3:"⑱";s:2:"18";s:3:"⑲";s:2:"19";s:3:"⑳";s:2:"20";s:3:"⑴";s:3:"(1)";s:3:"⑵";s:3:"(2)";s:3:"⑶";s:3:"(3)";s:3:"⑷";s:3:"(4)";s:3:"⑸";s:3:"(5)";s:3:"⑹";s:3:"(6)";s:3:"⑺";s:3:"(7)";s:3:"⑻";s:3:"(8)";s:3:"⑼";s:3:"(9)";s:3:"⑽";s:4:"(10)";s:3:"⑾";s:4:"(11)";s:3:"⑿";s:4:"(12)";s:3:"⒀";s:4:"(13)";s:3:"⒁";s:4:"(14)";s:3:"⒂";s:4:"(15)";s:3:"⒃";s:4:"(16)";s:3:"⒄";s:4:"(17)";s:3:"⒅";s:4:"(18)";s:3:"⒆";s:4:"(19)";s:3:"⒇";s:4:"(20)";s:3:"⒈";s:2:"1.";s:3:"⒉";s:2:"2.";s:3:"⒊";s:2:"3.";s:3:"⒋";s:2:"4.";s:3:"⒌";s:2:"5.";s:3:"⒍";s:2:"6.";s:3:"⒎";s:2:"7.";s:3:"⒏";s:2:"8.";s:3:"⒐";s:2:"9.";s:3:"⒑";s:3:"10.";s:3:"⒒";s:3:"11.";s:3:"⒓";s:3:"12.";s:3:"⒔";s:3:"13.";s:3:"⒕";s:3:"14.";s:3:"⒖";s:3:"15.";s:3:"⒗";s:3:"16.";s:3:"⒘";s:3:"17.";s:3:"⒙";s:3:"18.";s:3:"⒚";s:3:"19.";s:3:"⒛";s:3:"20.";s:3:"⒜";s:3:"(a)";s:3:"⒝";s:3:"(b)";s:3:"⒞";s:3:"(c)";s:3:"⒟";s:3:"(d)";s:3:"⒠";s:3:"(e)";s:3:"⒡";s:3:"(f)";s:3:"⒢";s:3:"(g)";s:3:"⒣";s:3:"(h)";s:3:"⒤";s:3:"(i)";s:3:"⒥";s:3:"(j)";s:3:"⒦";s:3:"(k)";s:3:"⒧";s:3:"(l)";s:3:"⒨";s:3:"(m)";s:3:"⒩";s:3:"(n)";s:3:"⒪";s:3:"(o)";s:3:"⒫";s:3:"(p)";s:3:"⒬";s:3:"(q)";s:3:"⒭";s:3:"(r)";s:3:"⒮";s:3:"(s)";s:3:"⒯";s:3:"(t)";s:3:"⒰";s:3:"(u)";s:3:"⒱";s:3:"(v)";s:3:"⒲";s:3:"(w)";s:3:"⒳";s:3:"(x)";s:3:"⒴";s:3:"(y)";s:3:"⒵";s:3:"(z)";s:3:"Ⓐ";s:1:"A";s:3:"Ⓑ";s:1:"B";s:3:"Ⓒ";s:1:"C";s:3:"Ⓓ";s:1:"D";s:3:"Ⓔ";s:1:"E";s:3:"Ⓕ";s:1:"F";s:3:"Ⓖ";s:1:"G";s:3:"Ⓗ";s:1:"H";s:3:"Ⓘ";s:1:"I";s:3:"Ⓙ";s:1:"J";s:3:"Ⓚ";s:1:"K";s:3:"Ⓛ";s:1:"L";s:3:"Ⓜ";s:1:"M";s:3:"Ⓝ";s:1:"N";s:3:"Ⓞ";s:1:"O";s:3:"Ⓟ";s:1:"P";s:3:"Ⓠ";s:1:"Q";s:3:"Ⓡ";s:1:"R";s:3:"Ⓢ";s:1:"S";s:3:"Ⓣ";s:1:"T";s:3:"Ⓤ";s:1:"U";s:3:"Ⓥ";s:1:"V";s:3:"Ⓦ";s:1:"W";s:3:"Ⓧ";s:1:"X";s:3:"Ⓨ";s:1:"Y";s:3:"Ⓩ";s:1:"Z";s:3:"ⓐ";s:1:"a";s:3:"ⓑ";s:1:"b";s:3:"ⓒ";s:1:"c";s:3:"ⓓ";s:1:"d";s:3:"ⓔ";s:1:"e";s:3:"ⓕ";s:1:"f";s:3:"ⓖ";s:1:"g";s:3:"ⓗ";s:1:"h";s:3:"ⓘ";s:1:"i";s:3:"ⓙ";s:1:"j";s:3:"ⓚ";s:1:"k";s:3:"ⓛ";s:1:"l";s:3:"ⓜ";s:1:"m";s:3:"ⓝ";s:1:"n";s:3:"ⓞ";s:1:"o";s:3:"ⓟ";s:1:"p";s:3:"ⓠ";s:1:"q";s:3:"ⓡ";s:1:"r";s:3:"ⓢ";s:1:"s";s:3:"ⓣ";s:1:"t";s:3:"ⓤ";s:1:"u";s:3:"ⓥ";s:1:"v";s:3:"ⓦ";s:1:"w";s:3:"ⓧ";s:1:"x";s:3:"ⓨ";s:1:"y";s:3:"ⓩ";s:1:"z";s:3:"⓪";s:1:"0";s:3:"⨌";s:12:"∫∫∫∫";s:3:"⩴";s:3:"::=";s:3:"⩵";s:2:"==";s:3:"⩶";s:3:"===";s:3:"⫝̸";s:5:"⫝̸";s:3:"ⵯ";s:3:"ⵡ";s:3:"⺟";s:3:"母";s:3:"⻳";s:3:"龟";s:3:"⼀";s:3:"一";s:3:"⼁";s:3:"丨";s:3:"⼂";s:3:"丶";s:3:"⼃";s:3:"丿";s:3:"⼄";s:3:"乙";s:3:"⼅";s:3:"亅";s:3:"⼆";s:3:"二";s:3:"⼇";s:3:"亠";s:3:"⼈";s:3:"人";s:3:"⼉";s:3:"儿";s:3:"⼊";s:3:"入";s:3:"⼋";s:3:"八";s:3:"⼌";s:3:"冂";s:3:"⼍";s:3:"冖";s:3:"⼎";s:3:"冫";s:3:"⼏";s:3:"几";s:3:"⼐";s:3:"凵";s:3:"⼑";s:3:"刀";s:3:"⼒";s:3:"力";s:3:"⼓";s:3:"勹";s:3:"⼔";s:3:"匕";s:3:"⼕";s:3:"匚";s:3:"⼖";s:3:"匸";s:3:"⼗";s:3:"十";s:3:"⼘";s:3:"卜";s:3:"⼙";s:3:"卩";s:3:"⼚";s:3:"厂";s:3:"⼛";s:3:"厶";s:3:"⼜";s:3:"又";s:3:"⼝";s:3:"口";s:3:"⼞";s:3:"囗";s:3:"⼟";s:3:"土";s:3:"⼠";s:3:"士";s:3:"⼡";s:3:"夂";s:3:"⼢";s:3:"夊";s:3:"⼣";s:3:"夕";s:3:"⼤";s:3:"大";s:3:"⼥";s:3:"女";s:3:"⼦";s:3:"子";s:3:"⼧";s:3:"宀";s:3:"⼨";s:3:"寸";s:3:"⼩";s:3:"小";s:3:"⼪";s:3:"尢";s:3:"⼫";s:3:"尸";s:3:"⼬";s:3:"屮";s:3:"⼭";s:3:"山";s:3:"⼮";s:3:"巛";s:3:"⼯";s:3:"工";s:3:"⼰";s:3:"己";s:3:"⼱";s:3:"巾";s:3:"⼲";s:3:"干";s:3:"⼳";s:3:"幺";s:3:"⼴";s:3:"广";s:3:"⼵";s:3:"廴";s:3:"⼶";s:3:"廾";s:3:"⼷";s:3:"弋";s:3:"⼸";s:3:"弓";s:3:"⼹";s:3:"彐";s:3:"⼺";s:3:"彡";s:3:"⼻";s:3:"彳";s:3:"⼼";s:3:"心";s:3:"⼽";s:3:"戈";s:3:"⼾";s:3:"戶";s:3:"⼿";s:3:"手";s:3:"⽀";s:3:"支";s:3:"⽁";s:3:"攴";s:3:"⽂";s:3:"文";s:3:"⽃";s:3:"斗";s:3:"⽄";s:3:"斤";s:3:"⽅";s:3:"方";s:3:"⽆";s:3:"无";s:3:"⽇";s:3:"日";s:3:"⽈";s:3:"曰";s:3:"⽉";s:3:"月";s:3:"⽊";s:3:"木";s:3:"⽋";s:3:"欠";s:3:"⽌";s:3:"止";s:3:"⽍";s:3:"歹";s:3:"⽎";s:3:"殳";s:3:"⽏";s:3:"毋";s:3:"⽐";s:3:"比";s:3:"⽑";s:3:"毛";s:3:"⽒";s:3:"氏";s:3:"⽓";s:3:"气";s:3:"⽔";s:3:"水";s:3:"⽕";s:3:"火";s:3:"⽖";s:3:"爪";s:3:"⽗";s:3:"父";s:3:"⽘";s:3:"爻";s:3:"⽙";s:3:"爿";s:3:"⽚";s:3:"片";s:3:"⽛";s:3:"牙";s:3:"⽜";s:3:"牛";s:3:"⽝";s:3:"犬";s:3:"⽞";s:3:"玄";s:3:"⽟";s:3:"玉";s:3:"⽠";s:3:"瓜";s:3:"⽡";s:3:"瓦";s:3:"⽢";s:3:"甘";s:3:"⽣";s:3:"生";s:3:"⽤";s:3:"用";s:3:"⽥";s:3:"田";s:3:"⽦";s:3:"疋";s:3:"⽧";s:3:"疒";s:3:"⽨";s:3:"癶";s:3:"⽩";s:3:"白";s:3:"⽪";s:3:"皮";s:3:"⽫";s:3:"皿";s:3:"⽬";s:3:"目";s:3:"⽭";s:3:"矛";s:3:"⽮";s:3:"矢";s:3:"⽯";s:3:"石";s:3:"⽰";s:3:"示";s:3:"⽱";s:3:"禸";s:3:"⽲";s:3:"禾";s:3:"⽳";s:3:"穴";s:3:"⽴";s:3:"立";s:3:"⽵";s:3:"竹";s:3:"⽶";s:3:"米";s:3:"⽷";s:3:"糸";s:3:"⽸";s:3:"缶";s:3:"⽹";s:3:"网";s:3:"⽺";s:3:"羊";s:3:"⽻";s:3:"羽";s:3:"⽼";s:3:"老";s:3:"⽽";s:3:"而";s:3:"⽾";s:3:"耒";s:3:"⽿";s:3:"耳";s:3:"⾀";s:3:"聿";s:3:"⾁";s:3:"肉";s:3:"⾂";s:3:"臣";s:3:"⾃";s:3:"自";s:3:"⾄";s:3:"至";s:3:"⾅";s:3:"臼";s:3:"⾆";s:3:"舌";s:3:"⾇";s:3:"舛";s:3:"⾈";s:3:"舟";s:3:"⾉";s:3:"艮";s:3:"⾊";s:3:"色";s:3:"⾋";s:3:"艸";s:3:"⾌";s:3:"虍";s:3:"⾍";s:3:"虫";s:3:"⾎";s:3:"血";s:3:"⾏";s:3:"行";s:3:"⾐";s:3:"衣";s:3:"⾑";s:3:"襾";s:3:"⾒";s:3:"見";s:3:"⾓";s:3:"角";s:3:"⾔";s:3:"言";s:3:"⾕";s:3:"谷";s:3:"⾖";s:3:"豆";s:3:"⾗";s:3:"豕";s:3:"⾘";s:3:"豸";s:3:"⾙";s:3:"貝";s:3:"⾚";s:3:"赤";s:3:"⾛";s:3:"走";s:3:"⾜";s:3:"足";s:3:"⾝";s:3:"身";s:3:"⾞";s:3:"車";s:3:"⾟";s:3:"辛";s:3:"⾠";s:3:"辰";s:3:"⾡";s:3:"辵";s:3:"⾢";s:3:"邑";s:3:"⾣";s:3:"酉";s:3:"⾤";s:3:"釆";s:3:"⾥";s:3:"里";s:3:"⾦";s:3:"金";s:3:"⾧";s:3:"長";s:3:"⾨";s:3:"門";s:3:"⾩";s:3:"阜";s:3:"⾪";s:3:"隶";s:3:"⾫";s:3:"隹";s:3:"⾬";s:3:"雨";s:3:"⾭";s:3:"靑";s:3:"⾮";s:3:"非";s:3:"⾯";s:3:"面";s:3:"⾰";s:3:"革";s:3:"⾱";s:3:"韋";s:3:"⾲";s:3:"韭";s:3:"⾳";s:3:"音";s:3:"⾴";s:3:"頁";s:3:"⾵";s:3:"風";s:3:"⾶";s:3:"飛";s:3:"⾷";s:3:"食";s:3:"⾸";s:3:"首";s:3:"⾹";s:3:"香";s:3:"⾺";s:3:"馬";s:3:"⾻";s:3:"骨";s:3:"⾼";s:3:"高";s:3:"⾽";s:3:"髟";s:3:"⾾";s:3:"鬥";s:3:"⾿";s:3:"鬯";s:3:"⿀";s:3:"鬲";s:3:"⿁";s:3:"鬼";s:3:"⿂";s:3:"魚";s:3:"⿃";s:3:"鳥";s:3:"⿄";s:3:"鹵";s:3:"⿅";s:3:"鹿";s:3:"⿆";s:3:"麥";s:3:"⿇";s:3:"麻";s:3:"⿈";s:3:"黃";s:3:"⿉";s:3:"黍";s:3:"⿊";s:3:"黑";s:3:"⿋";s:3:"黹";s:3:"⿌";s:3:"黽";s:3:"⿍";s:3:"鼎";s:3:"⿎";s:3:"鼓";s:3:"⿏";s:3:"鼠";s:3:"⿐";s:3:"鼻";s:3:"⿑";s:3:"齊";s:3:"⿒";s:3:"齒";s:3:"⿓";s:3:"龍";s:3:"⿔";s:3:"龜";s:3:"⿕";s:3:"龠";s:3:" ";s:1:" ";s:3:"〶";s:3:"〒";s:3:"〸";s:3:"十";s:3:"〹";s:3:"卄";s:3:"〺";s:3:"卅";s:3:"が";s:6:"が";s:3:"ぎ";s:6:"ぎ";s:3:"ぐ";s:6:"ぐ";s:3:"げ";s:6:"げ";s:3:"ご";s:6:"ご";s:3:"ざ";s:6:"ざ";s:3:"じ";s:6:"じ";s:3:"ず";s:6:"ず";s:3:"ぜ";s:6:"ぜ";s:3:"ぞ";s:6:"ぞ";s:3:"だ";s:6:"だ";s:3:"ぢ";s:6:"ぢ";s:3:"づ";s:6:"づ";s:3:"で";s:6:"で";s:3:"ど";s:6:"ど";s:3:"ば";s:6:"ば";s:3:"ぱ";s:6:"ぱ";s:3:"び";s:6:"び";s:3:"ぴ";s:6:"ぴ";s:3:"ぶ";s:6:"ぶ";s:3:"ぷ";s:6:"ぷ";s:3:"べ";s:6:"べ";s:3:"ぺ";s:6:"ぺ";s:3:"ぼ";s:6:"ぼ";s:3:"ぽ";s:6:"ぽ";s:3:"ゔ";s:6:"ゔ";s:3:"゛";s:4:" ゙";s:3:"゜";s:4:" ゚";s:3:"ゞ";s:6:"ゞ";s:3:"ゟ";s:6:"より";s:3:"ガ";s:6:"ガ";s:3:"ギ";s:6:"ギ";s:3:"グ";s:6:"グ";s:3:"ゲ";s:6:"ゲ";s:3:"ゴ";s:6:"ゴ";s:3:"ザ";s:6:"ザ";s:3:"ジ";s:6:"ジ";s:3:"ズ";s:6:"ズ";s:3:"ゼ";s:6:"ゼ";s:3:"ゾ";s:6:"ゾ";s:3:"ダ";s:6:"ダ";s:3:"ヂ";s:6:"ヂ";s:3:"ヅ";s:6:"ヅ";s:3:"デ";s:6:"デ";s:3:"ド";s:6:"ド";s:3:"バ";s:6:"バ";s:3:"パ";s:6:"パ";s:3:"ビ";s:6:"ビ";s:3:"ピ";s:6:"ピ";s:3:"ブ";s:6:"ブ";s:3:"プ";s:6:"プ";s:3:"ベ";s:6:"ベ";s:3:"ペ";s:6:"ペ";s:3:"ボ";s:6:"ボ";s:3:"ポ";s:6:"ポ";s:3:"ヴ";s:6:"ヴ";s:3:"ヷ";s:6:"ヷ";s:3:"ヸ";s:6:"ヸ";s:3:"ヹ";s:6:"ヹ";s:3:"ヺ";s:6:"ヺ";s:3:"ヾ";s:6:"ヾ";s:3:"ヿ";s:6:"コト";s:3:"ㄱ";s:3:"ᄀ";s:3:"ㄲ";s:3:"ᄁ";s:3:"ㄳ";s:3:"ᆪ";s:3:"ㄴ";s:3:"ᄂ";s:3:"ㄵ";s:3:"ᆬ";s:3:"ㄶ";s:3:"ᆭ";s:3:"ㄷ";s:3:"ᄃ";s:3:"ㄸ";s:3:"ᄄ";s:3:"ㄹ";s:3:"ᄅ";s:3:"ㄺ";s:3:"ᆰ";s:3:"ㄻ";s:3:"ᆱ";s:3:"ㄼ";s:3:"ᆲ";s:3:"ㄽ";s:3:"ᆳ";s:3:"ㄾ";s:3:"ᆴ";s:3:"ㄿ";s:3:"ᆵ";s:3:"ㅀ";s:3:"ᄚ";s:3:"ㅁ";s:3:"ᄆ";s:3:"ㅂ";s:3:"ᄇ";s:3:"ㅃ";s:3:"ᄈ";s:3:"ㅄ";s:3:"ᄡ";s:3:"ㅅ";s:3:"ᄉ";s:3:"ㅆ";s:3:"ᄊ";s:3:"ㅇ";s:3:"ᄋ";s:3:"ㅈ";s:3:"ᄌ";s:3:"ㅉ";s:3:"ᄍ";s:3:"ㅊ";s:3:"ᄎ";s:3:"ㅋ";s:3:"ᄏ";s:3:"ㅌ";s:3:"ᄐ";s:3:"ㅍ";s:3:"ᄑ";s:3:"ㅎ";s:3:"ᄒ";s:3:"ㅏ";s:3:"ᅡ";s:3:"ㅐ";s:3:"ᅢ";s:3:"ㅑ";s:3:"ᅣ";s:3:"ㅒ";s:3:"ᅤ";s:3:"ㅓ";s:3:"ᅥ";s:3:"ㅔ";s:3:"ᅦ";s:3:"ㅕ";s:3:"ᅧ";s:3:"ㅖ";s:3:"ᅨ";s:3:"ㅗ";s:3:"ᅩ";s:3:"ㅘ";s:3:"ᅪ";s:3:"ㅙ";s:3:"ᅫ";s:3:"ㅚ";s:3:"ᅬ";s:3:"ㅛ";s:3:"ᅭ";s:3:"ㅜ";s:3:"ᅮ";s:3:"ㅝ";s:3:"ᅯ";s:3:"ㅞ";s:3:"ᅰ";s:3:"ㅟ";s:3:"ᅱ";s:3:"ㅠ";s:3:"ᅲ";s:3:"ㅡ";s:3:"ᅳ";s:3:"ㅢ";s:3:"ᅴ";s:3:"ㅣ";s:3:"ᅵ";s:3:"ㅤ";s:3:"ᅠ";s:3:"ㅥ";s:3:"ᄔ";s:3:"ㅦ";s:3:"ᄕ";s:3:"ㅧ";s:3:"ᇇ";s:3:"ㅨ";s:3:"ᇈ";s:3:"ㅩ";s:3:"ᇌ";s:3:"ㅪ";s:3:"ᇎ";s:3:"ㅫ";s:3:"ᇓ";s:3:"ㅬ";s:3:"ᇗ";s:3:"ㅭ";s:3:"ᇙ";s:3:"ㅮ";s:3:"ᄜ";s:3:"ㅯ";s:3:"ᇝ";s:3:"ㅰ";s:3:"ᇟ";s:3:"ㅱ";s:3:"ᄝ";s:3:"ㅲ";s:3:"ᄞ";s:3:"ㅳ";s:3:"ᄠ";s:3:"ㅴ";s:3:"ᄢ";s:3:"ㅵ";s:3:"ᄣ";s:3:"ㅶ";s:3:"ᄧ";s:3:"ㅷ";s:3:"ᄩ";s:3:"ㅸ";s:3:"ᄫ";s:3:"ㅹ";s:3:"ᄬ";s:3:"ㅺ";s:3:"ᄭ";s:3:"ㅻ";s:3:"ᄮ";s:3:"ㅼ";s:3:"ᄯ";s:3:"ㅽ";s:3:"ᄲ";s:3:"ㅾ";s:3:"ᄶ";s:3:"ㅿ";s:3:"ᅀ";s:3:"ㆀ";s:3:"ᅇ";s:3:"ㆁ";s:3:"ᅌ";s:3:"ㆂ";s:3:"ᇱ";s:3:"ㆃ";s:3:"ᇲ";s:3:"ㆄ";s:3:"ᅗ";s:3:"ㆅ";s:3:"ᅘ";s:3:"ㆆ";s:3:"ᅙ";s:3:"ㆇ";s:3:"ᆄ";s:3:"ㆈ";s:3:"ᆅ";s:3:"ㆉ";s:3:"ᆈ";s:3:"ㆊ";s:3:"ᆑ";s:3:"ㆋ";s:3:"ᆒ";s:3:"ㆌ";s:3:"ᆔ";s:3:"ㆍ";s:3:"ᆞ";s:3:"ㆎ";s:3:"ᆡ";s:3:"㆒";s:3:"一";s:3:"㆓";s:3:"二";s:3:"㆔";s:3:"三";s:3:"㆕";s:3:"四";s:3:"㆖";s:3:"上";s:3:"㆗";s:3:"中";s:3:"㆘";s:3:"下";s:3:"㆙";s:3:"甲";s:3:"㆚";s:3:"乙";s:3:"㆛";s:3:"丙";s:3:"㆜";s:3:"丁";s:3:"㆝";s:3:"天";s:3:"㆞";s:3:"地";s:3:"㆟";s:3:"人";s:3:"㈀";s:5:"(ᄀ)";s:3:"㈁";s:5:"(ᄂ)";s:3:"㈂";s:5:"(ᄃ)";s:3:"㈃";s:5:"(ᄅ)";s:3:"㈄";s:5:"(ᄆ)";s:3:"㈅";s:5:"(ᄇ)";s:3:"㈆";s:5:"(ᄉ)";s:3:"㈇";s:5:"(ᄋ)";s:3:"㈈";s:5:"(ᄌ)";s:3:"㈉";s:5:"(ᄎ)";s:3:"㈊";s:5:"(ᄏ)";s:3:"㈋";s:5:"(ᄐ)";s:3:"㈌";s:5:"(ᄑ)";s:3:"㈍";s:5:"(ᄒ)";s:3:"㈎";s:8:"(가)";s:3:"㈏";s:8:"(나)";s:3:"㈐";s:8:"(다)";s:3:"㈑";s:8:"(라)";s:3:"㈒";s:8:"(마)";s:3:"㈓";s:8:"(바)";s:3:"㈔";s:8:"(사)";s:3:"㈕";s:8:"(아)";s:3:"㈖";s:8:"(자)";s:3:"㈗";s:8:"(차)";s:3:"㈘";s:8:"(카)";s:3:"㈙";s:8:"(타)";s:3:"㈚";s:8:"(파)";s:3:"㈛";s:8:"(하)";s:3:"㈜";s:8:"(주)";s:3:"㈝";s:17:"(오전)";s:3:"㈞";s:14:"(오후)";s:3:"㈠";s:5:"(一)";s:3:"㈡";s:5:"(二)";s:3:"㈢";s:5:"(三)";s:3:"㈣";s:5:"(四)";s:3:"㈤";s:5:"(五)";s:3:"㈥";s:5:"(六)";s:3:"㈦";s:5:"(七)";s:3:"㈧";s:5:"(八)";s:3:"㈨";s:5:"(九)";s:3:"㈩";s:5:"(十)";s:3:"㈪";s:5:"(月)";s:3:"㈫";s:5:"(火)";s:3:"㈬";s:5:"(水)";s:3:"㈭";s:5:"(木)";s:3:"㈮";s:5:"(金)";s:3:"㈯";s:5:"(土)";s:3:"㈰";s:5:"(日)";s:3:"㈱";s:5:"(株)";s:3:"㈲";s:5:"(有)";s:3:"㈳";s:5:"(社)";s:3:"㈴";s:5:"(名)";s:3:"㈵";s:5:"(特)";s:3:"㈶";s:5:"(財)";s:3:"㈷";s:5:"(祝)";s:3:"㈸";s:5:"(労)";s:3:"㈹";s:5:"(代)";s:3:"㈺";s:5:"(呼)";s:3:"㈻";s:5:"(学)";s:3:"㈼";s:5:"(監)";s:3:"㈽";s:5:"(企)";s:3:"㈾";s:5:"(資)";s:3:"㈿";s:5:"(協)";s:3:"㉀";s:5:"(祭)";s:3:"㉁";s:5:"(休)";s:3:"㉂";s:5:"(自)";s:3:"㉃";s:5:"(至)";s:3:"㉐";s:3:"PTE";s:3:"㉑";s:2:"21";s:3:"㉒";s:2:"22";s:3:"㉓";s:2:"23";s:3:"㉔";s:2:"24";s:3:"㉕";s:2:"25";s:3:"㉖";s:2:"26";s:3:"㉗";s:2:"27";s:3:"㉘";s:2:"28";s:3:"㉙";s:2:"29";s:3:"㉚";s:2:"30";s:3:"㉛";s:2:"31";s:3:"㉜";s:2:"32";s:3:"㉝";s:2:"33";s:3:"㉞";s:2:"34";s:3:"㉟";s:2:"35";s:3:"㉠";s:3:"ᄀ";s:3:"㉡";s:3:"ᄂ";s:3:"㉢";s:3:"ᄃ";s:3:"㉣";s:3:"ᄅ";s:3:"㉤";s:3:"ᄆ";s:3:"㉥";s:3:"ᄇ";s:3:"㉦";s:3:"ᄉ";s:3:"㉧";s:3:"ᄋ";s:3:"㉨";s:3:"ᄌ";s:3:"㉩";s:3:"ᄎ";s:3:"㉪";s:3:"ᄏ";s:3:"㉫";s:3:"ᄐ";s:3:"㉬";s:3:"ᄑ";s:3:"㉭";s:3:"ᄒ";s:3:"㉮";s:6:"가";s:3:"㉯";s:6:"나";s:3:"㉰";s:6:"다";s:3:"㉱";s:6:"라";s:3:"㉲";s:6:"마";s:3:"㉳";s:6:"바";s:3:"㉴";s:6:"사";s:3:"㉵";s:6:"아";s:3:"㉶";s:6:"자";s:3:"㉷";s:6:"차";s:3:"㉸";s:6:"카";s:3:"㉹";s:6:"타";s:3:"㉺";s:6:"파";s:3:"㉻";s:6:"하";s:3:"㉼";s:15:"참고";s:3:"㉽";s:12:"주의";s:3:"㉾";s:6:"우";s:3:"㊀";s:3:"一";s:3:"㊁";s:3:"二";s:3:"㊂";s:3:"三";s:3:"㊃";s:3:"四";s:3:"㊄";s:3:"五";s:3:"㊅";s:3:"六";s:3:"㊆";s:3:"七";s:3:"㊇";s:3:"八";s:3:"㊈";s:3:"九";s:3:"㊉";s:3:"十";s:3:"㊊";s:3:"月";s:3:"㊋";s:3:"火";s:3:"㊌";s:3:"水";s:3:"㊍";s:3:"木";s:3:"㊎";s:3:"金";s:3:"㊏";s:3:"土";s:3:"㊐";s:3:"日";s:3:"㊑";s:3:"株";s:3:"㊒";s:3:"有";s:3:"㊓";s:3:"社";s:3:"㊔";s:3:"名";s:3:"㊕";s:3:"特";s:3:"㊖";s:3:"財";s:3:"㊗";s:3:"祝";s:3:"㊘";s:3:"労";s:3:"㊙";s:3:"秘";s:3:"㊚";s:3:"男";s:3:"㊛";s:3:"女";s:3:"㊜";s:3:"適";s:3:"㊝";s:3:"優";s:3:"㊞";s:3:"印";s:3:"㊟";s:3:"注";s:3:"㊠";s:3:"項";s:3:"㊡";s:3:"休";s:3:"㊢";s:3:"写";s:3:"㊣";s:3:"正";s:3:"㊤";s:3:"上";s:3:"㊥";s:3:"中";s:3:"㊦";s:3:"下";s:3:"㊧";s:3:"左";s:3:"㊨";s:3:"右";s:3:"㊩";s:3:"医";s:3:"㊪";s:3:"宗";s:3:"㊫";s:3:"学";s:3:"㊬";s:3:"監";s:3:"㊭";s:3:"企";s:3:"㊮";s:3:"資";s:3:"㊯";s:3:"協";s:3:"㊰";s:3:"夜";s:3:"㊱";s:2:"36";s:3:"㊲";s:2:"37";s:3:"㊳";s:2:"38";s:3:"㊴";s:2:"39";s:3:"㊵";s:2:"40";s:3:"㊶";s:2:"41";s:3:"㊷";s:2:"42";s:3:"㊸";s:2:"43";s:3:"㊹";s:2:"44";s:3:"㊺";s:2:"45";s:3:"㊻";s:2:"46";s:3:"㊼";s:2:"47";s:3:"㊽";s:2:"48";s:3:"㊾";s:2:"49";s:3:"㊿";s:2:"50";s:3:"㋀";s:4:"1月";s:3:"㋁";s:4:"2月";s:3:"㋂";s:4:"3月";s:3:"㋃";s:4:"4月";s:3:"㋄";s:4:"5月";s:3:"㋅";s:4:"6月";s:3:"㋆";s:4:"7月";s:3:"㋇";s:4:"8月";s:3:"㋈";s:4:"9月";s:3:"㋉";s:5:"10月";s:3:"㋊";s:5:"11月";s:3:"㋋";s:5:"12月";s:3:"㋌";s:2:"Hg";s:3:"㋍";s:3:"erg";s:3:"㋎";s:2:"eV";s:3:"㋏";s:3:"LTD";s:3:"㋐";s:3:"ア";s:3:"㋑";s:3:"イ";s:3:"㋒";s:3:"ウ";s:3:"㋓";s:3:"エ";s:3:"㋔";s:3:"オ";s:3:"㋕";s:3:"カ";s:3:"㋖";s:3:"キ";s:3:"㋗";s:3:"ク";s:3:"㋘";s:3:"ケ";s:3:"㋙";s:3:"コ";s:3:"㋚";s:3:"サ";s:3:"㋛";s:3:"シ";s:3:"㋜";s:3:"ス";s:3:"㋝";s:3:"セ";s:3:"㋞";s:3:"ソ";s:3:"㋟";s:3:"タ";s:3:"㋠";s:3:"チ";s:3:"㋡";s:3:"ツ";s:3:"㋢";s:3:"テ";s:3:"㋣";s:3:"ト";s:3:"㋤";s:3:"ナ";s:3:"㋥";s:3:"ニ";s:3:"㋦";s:3:"ヌ";s:3:"㋧";s:3:"ネ";s:3:"㋨";s:3:"ノ";s:3:"㋩";s:3:"ハ";s:3:"㋪";s:3:"ヒ";s:3:"㋫";s:3:"フ";s:3:"㋬";s:3:"ヘ";s:3:"㋭";s:3:"ホ";s:3:"㋮";s:3:"マ";s:3:"㋯";s:3:"ミ";s:3:"㋰";s:3:"ム";s:3:"㋱";s:3:"メ";s:3:"㋲";s:3:"モ";s:3:"㋳";s:3:"ヤ";s:3:"㋴";s:3:"ユ";s:3:"㋵";s:3:"ヨ";s:3:"㋶";s:3:"ラ";s:3:"㋷";s:3:"リ";s:3:"㋸";s:3:"ル";s:3:"㋹";s:3:"レ";s:3:"㋺";s:3:"ロ";s:3:"㋻";s:3:"ワ";s:3:"㋼";s:3:"ヰ";s:3:"㋽";s:3:"ヱ";s:3:"㋾";s:3:"ヲ";s:3:"㌀";s:15:"アパート";s:3:"㌁";s:12:"アルファ";s:3:"㌂";s:15:"アンペア";s:3:"㌃";s:9:"アール";s:3:"㌄";s:15:"イニング";s:3:"㌅";s:9:"インチ";s:3:"㌆";s:9:"ウォン";s:3:"㌇";s:18:"エスクード";s:3:"㌈";s:12:"エーカー";s:3:"㌉";s:9:"オンス";s:3:"㌊";s:9:"オーム";s:3:"㌋";s:9:"カイリ";s:3:"㌌";s:12:"カラット";s:3:"㌍";s:12:"カロリー";s:3:"㌎";s:12:"ガロン";s:3:"㌏";s:12:"ガンマ";s:3:"㌐";s:12:"ギガ";s:3:"㌑";s:12:"ギニー";s:3:"㌒";s:12:"キュリー";s:3:"㌓";s:18:"ギルダー";s:3:"㌔";s:6:"キロ";s:3:"㌕";s:18:"キログラム";s:3:"㌖";s:18:"キロメートル";s:3:"㌗";s:15:"キロワット";s:3:"㌘";s:12:"グラム";s:3:"㌙";s:18:"グラムトン";s:3:"㌚";s:18:"クルゼイロ";s:3:"㌛";s:12:"クローネ";s:3:"㌜";s:9:"ケース";s:3:"㌝";s:9:"コルナ";s:3:"㌞";s:12:"コーポ";s:3:"㌟";s:12:"サイクル";s:3:"㌠";s:15:"サンチーム";s:3:"㌡";s:15:"シリング";s:3:"㌢";s:9:"センチ";s:3:"㌣";s:9:"セント";s:3:"㌤";s:12:"ダース";s:3:"㌥";s:9:"デシ";s:3:"㌦";s:9:"ドル";s:3:"㌧";s:6:"トン";s:3:"㌨";s:6:"ナノ";s:3:"㌩";s:9:"ノット";s:3:"㌪";s:9:"ハイツ";s:3:"㌫";s:18:"パーセント";s:3:"㌬";s:12:"パーツ";s:3:"㌭";s:15:"バーレル";s:3:"㌮";s:18:"ピアストル";s:3:"㌯";s:12:"ピクル";s:3:"㌰";s:9:"ピコ";s:3:"㌱";s:9:"ビル";s:3:"㌲";s:18:"ファラッド";s:3:"㌳";s:12:"フィート";s:3:"㌴";s:18:"ブッシェル";s:3:"㌵";s:9:"フラン";s:3:"㌶";s:15:"ヘクタール";s:3:"㌷";s:9:"ペソ";s:3:"㌸";s:12:"ペニヒ";s:3:"㌹";s:9:"ヘルツ";s:3:"㌺";s:12:"ペンス";s:3:"㌻";s:15:"ページ";s:3:"㌼";s:12:"ベータ";s:3:"㌽";s:15:"ポイント";s:3:"㌾";s:12:"ボルト";s:3:"㌿";s:6:"ホン";s:3:"㍀";s:15:"ポンド";s:3:"㍁";s:9:"ホール";s:3:"㍂";s:9:"ホーン";s:3:"㍃";s:12:"マイクロ";s:3:"㍄";s:9:"マイル";s:3:"㍅";s:9:"マッハ";s:3:"㍆";s:9:"マルク";s:3:"㍇";s:15:"マンション";s:3:"㍈";s:12:"ミクロン";s:3:"㍉";s:6:"ミリ";s:3:"㍊";s:18:"ミリバール";s:3:"㍋";s:9:"メガ";s:3:"㍌";s:15:"メガトン";s:3:"㍍";s:12:"メートル";s:3:"㍎";s:12:"ヤード";s:3:"㍏";s:9:"ヤール";s:3:"㍐";s:9:"ユアン";s:3:"㍑";s:12:"リットル";s:3:"㍒";s:6:"リラ";s:3:"㍓";s:12:"ルピー";s:3:"㍔";s:15:"ルーブル";s:3:"㍕";s:6:"レム";s:3:"㍖";s:18:"レントゲン";s:3:"㍗";s:9:"ワット";s:3:"㍘";s:4:"0点";s:3:"㍙";s:4:"1点";s:3:"㍚";s:4:"2点";s:3:"㍛";s:4:"3点";s:3:"㍜";s:4:"4点";s:3:"㍝";s:4:"5点";s:3:"㍞";s:4:"6点";s:3:"㍟";s:4:"7点";s:3:"㍠";s:4:"8点";s:3:"㍡";s:4:"9点";s:3:"㍢";s:5:"10点";s:3:"㍣";s:5:"11点";s:3:"㍤";s:5:"12点";s:3:"㍥";s:5:"13点";s:3:"㍦";s:5:"14点";s:3:"㍧";s:5:"15点";s:3:"㍨";s:5:"16点";s:3:"㍩";s:5:"17点";s:3:"㍪";s:5:"18点";s:3:"㍫";s:5:"19点";s:3:"㍬";s:5:"20点";s:3:"㍭";s:5:"21点";s:3:"㍮";s:5:"22点";s:3:"㍯";s:5:"23点";s:3:"㍰";s:5:"24点";s:3:"㍱";s:3:"hPa";s:3:"㍲";s:2:"da";s:3:"㍳";s:2:"AU";s:3:"㍴";s:3:"bar";s:3:"㍵";s:2:"oV";s:3:"㍶";s:2:"pc";s:3:"㍷";s:2:"dm";s:3:"㍸";s:3:"dm2";s:3:"㍹";s:3:"dm3";s:3:"㍺";s:2:"IU";s:3:"㍻";s:6:"平成";s:3:"㍼";s:6:"昭和";s:3:"㍽";s:6:"大正";s:3:"㍾";s:6:"明治";s:3:"㍿";s:12:"株式会社";s:3:"㎀";s:2:"pA";s:3:"㎁";s:2:"nA";s:3:"㎂";s:3:"μA";s:3:"㎃";s:2:"mA";s:3:"㎄";s:2:"kA";s:3:"㎅";s:2:"KB";s:3:"㎆";s:2:"MB";s:3:"㎇";s:2:"GB";s:3:"㎈";s:3:"cal";s:3:"㎉";s:4:"kcal";s:3:"㎊";s:2:"pF";s:3:"㎋";s:2:"nF";s:3:"㎌";s:3:"μF";s:3:"㎍";s:3:"μg";s:3:"㎎";s:2:"mg";s:3:"㎏";s:2:"kg";s:3:"㎐";s:2:"Hz";s:3:"㎑";s:3:"kHz";s:3:"㎒";s:3:"MHz";s:3:"㎓";s:3:"GHz";s:3:"㎔";s:3:"THz";s:3:"㎕";s:3:"μl";s:3:"㎖";s:2:"ml";s:3:"㎗";s:2:"dl";s:3:"㎘";s:2:"kl";s:3:"㎙";s:2:"fm";s:3:"㎚";s:2:"nm";s:3:"㎛";s:3:"μm";s:3:"㎜";s:2:"mm";s:3:"㎝";s:2:"cm";s:3:"㎞";s:2:"km";s:3:"㎟";s:3:"mm2";s:3:"㎠";s:3:"cm2";s:3:"㎡";s:2:"m2";s:3:"㎢";s:3:"km2";s:3:"㎣";s:3:"mm3";s:3:"㎤";s:3:"cm3";s:3:"㎥";s:2:"m3";s:3:"㎦";s:3:"km3";s:3:"㎧";s:5:"m∕s";s:3:"㎨";s:6:"m∕s2";s:3:"㎩";s:2:"Pa";s:3:"㎪";s:3:"kPa";s:3:"㎫";s:3:"MPa";s:3:"㎬";s:3:"GPa";s:3:"㎭";s:3:"rad";s:3:"㎮";s:7:"rad∕s";s:3:"㎯";s:8:"rad∕s2";s:3:"㎰";s:2:"ps";s:3:"㎱";s:2:"ns";s:3:"㎲";s:3:"μs";s:3:"㎳";s:2:"ms";s:3:"㎴";s:2:"pV";s:3:"㎵";s:2:"nV";s:3:"㎶";s:3:"μV";s:3:"㎷";s:2:"mV";s:3:"㎸";s:2:"kV";s:3:"㎹";s:2:"MV";s:3:"㎺";s:2:"pW";s:3:"㎻";s:2:"nW";s:3:"㎼";s:3:"μW";s:3:"㎽";s:2:"mW";s:3:"㎾";s:2:"kW";s:3:"㎿";s:2:"MW";s:3:"㏀";s:3:"kΩ";s:3:"㏁";s:3:"MΩ";s:3:"㏂";s:4:"a.m.";s:3:"㏃";s:2:"Bq";s:3:"㏄";s:2:"cc";s:3:"㏅";s:2:"cd";s:3:"㏆";s:6:"C∕kg";s:3:"㏇";s:3:"Co.";s:3:"㏈";s:2:"dB";s:3:"㏉";s:2:"Gy";s:3:"㏊";s:2:"ha";s:3:"㏋";s:2:"HP";s:3:"㏌";s:2:"in";s:3:"㏍";s:2:"KK";s:3:"㏎";s:2:"KM";s:3:"㏏";s:2:"kt";s:3:"㏐";s:2:"lm";s:3:"㏑";s:2:"ln";s:3:"㏒";s:3:"log";s:3:"㏓";s:2:"lx";s:3:"㏔";s:2:"mb";s:3:"㏕";s:3:"mil";s:3:"㏖";s:3:"mol";s:3:"㏗";s:2:"PH";s:3:"㏘";s:4:"p.m.";s:3:"㏙";s:3:"PPM";s:3:"㏚";s:2:"PR";s:3:"㏛";s:2:"sr";s:3:"㏜";s:2:"Sv";s:3:"㏝";s:2:"Wb";s:3:"㏞";s:5:"V∕m";s:3:"㏟";s:5:"A∕m";s:3:"㏠";s:4:"1日";s:3:"㏡";s:4:"2日";s:3:"㏢";s:4:"3日";s:3:"㏣";s:4:"4日";s:3:"㏤";s:4:"5日";s:3:"㏥";s:4:"6日";s:3:"㏦";s:4:"7日";s:3:"㏧";s:4:"8日";s:3:"㏨";s:4:"9日";s:3:"㏩";s:5:"10日";s:3:"㏪";s:5:"11日";s:3:"㏫";s:5:"12日";s:3:"㏬";s:5:"13日";s:3:"㏭";s:5:"14日";s:3:"㏮";s:5:"15日";s:3:"㏯";s:5:"16日";s:3:"㏰";s:5:"17日";s:3:"㏱";s:5:"18日";s:3:"㏲";s:5:"19日";s:3:"㏳";s:5:"20日";s:3:"㏴";s:5:"21日";s:3:"㏵";s:5:"22日";s:3:"㏶";s:5:"23日";s:3:"㏷";s:5:"24日";s:3:"㏸";s:5:"25日";s:3:"㏹";s:5:"26日";s:3:"㏺";s:5:"27日";s:3:"㏻";s:5:"28日";s:3:"㏼";s:5:"29日";s:3:"㏽";s:5:"30日";s:3:"㏾";s:5:"31日";s:3:"㏿";s:3:"gal";s:3:"豈";s:3:"豈";s:3:"更";s:3:"更";s:3:"車";s:3:"車";s:3:"賈";s:3:"賈";s:3:"滑";s:3:"滑";s:3:"串";s:3:"串";s:3:"句";s:3:"句";s:3:"龜";s:3:"龜";s:3:"龜";s:3:"龜";s:3:"契";s:3:"契";s:3:"金";s:3:"金";s:3:"喇";s:3:"喇";s:3:"奈";s:3:"奈";s:3:"懶";s:3:"懶";s:3:"癩";s:3:"癩";s:3:"羅";s:3:"羅";s:3:"蘿";s:3:"蘿";s:3:"螺";s:3:"螺";s:3:"裸";s:3:"裸";s:3:"邏";s:3:"邏";s:3:"樂";s:3:"樂";s:3:"洛";s:3:"洛";s:3:"烙";s:3:"烙";s:3:"珞";s:3:"珞";s:3:"落";s:3:"落";s:3:"酪";s:3:"酪";s:3:"駱";s:3:"駱";s:3:"亂";s:3:"亂";s:3:"卵";s:3:"卵";s:3:"欄";s:3:"欄";s:3:"爛";s:3:"爛";s:3:"蘭";s:3:"蘭";s:3:"鸞";s:3:"鸞";s:3:"嵐";s:3:"嵐";s:3:"濫";s:3:"濫";s:3:"藍";s:3:"藍";s:3:"襤";s:3:"襤";s:3:"拉";s:3:"拉";s:3:"臘";s:3:"臘";s:3:"蠟";s:3:"蠟";s:3:"廊";s:3:"廊";s:3:"朗";s:3:"朗";s:3:"浪";s:3:"浪";s:3:"狼";s:3:"狼";s:3:"郎";s:3:"郎";s:3:"來";s:3:"來";s:3:"冷";s:3:"冷";s:3:"勞";s:3:"勞";s:3:"擄";s:3:"擄";s:3:"櫓";s:3:"櫓";s:3:"爐";s:3:"爐";s:3:"盧";s:3:"盧";s:3:"老";s:3:"老";s:3:"蘆";s:3:"蘆";s:3:"虜";s:3:"虜";s:3:"路";s:3:"路";s:3:"露";s:3:"露";s:3:"魯";s:3:"魯";s:3:"鷺";s:3:"鷺";s:3:"碌";s:3:"碌";s:3:"祿";s:3:"祿";s:3:"綠";s:3:"綠";s:3:"菉";s:3:"菉";s:3:"錄";s:3:"錄";s:3:"鹿";s:3:"鹿";s:3:"論";s:3:"論";s:3:"壟";s:3:"壟";s:3:"弄";s:3:"弄";s:3:"籠";s:3:"籠";s:3:"聾";s:3:"聾";s:3:"牢";s:3:"牢";s:3:"磊";s:3:"磊";s:3:"賂";s:3:"賂";s:3:"雷";s:3:"雷";s:3:"壘";s:3:"壘";s:3:"屢";s:3:"屢";s:3:"樓";s:3:"樓";s:3:"淚";s:3:"淚";s:3:"漏";s:3:"漏";s:3:"累";s:3:"累";s:3:"縷";s:3:"縷";s:3:"陋";s:3:"陋";s:3:"勒";s:3:"勒";s:3:"肋";s:3:"肋";s:3:"凜";s:3:"凜";s:3:"凌";s:3:"凌";s:3:"稜";s:3:"稜";s:3:"綾";s:3:"綾";s:3:"菱";s:3:"菱";s:3:"陵";s:3:"陵";s:3:"讀";s:3:"讀";s:3:"拏";s:3:"拏";s:3:"樂";s:3:"樂";s:3:"諾";s:3:"諾";s:3:"丹";s:3:"丹";s:3:"寧";s:3:"寧";s:3:"怒";s:3:"怒";s:3:"率";s:3:"率";s:3:"異";s:3:"異";s:3:"北";s:3:"北";s:3:"磻";s:3:"磻";s:3:"便";s:3:"便";s:3:"復";s:3:"復";s:3:"不";s:3:"不";s:3:"泌";s:3:"泌";s:3:"數";s:3:"數";s:3:"索";s:3:"索";s:3:"參";s:3:"參";s:3:"塞";s:3:"塞";s:3:"省";s:3:"省";s:3:"葉";s:3:"葉";s:3:"說";s:3:"說";s:3:"殺";s:3:"殺";s:3:"辰";s:3:"辰";s:3:"沈";s:3:"沈";s:3:"拾";s:3:"拾";s:3:"若";s:3:"若";s:3:"掠";s:3:"掠";s:3:"略";s:3:"略";s:3:"亮";s:3:"亮";s:3:"兩";s:3:"兩";s:3:"凉";s:3:"凉";s:3:"梁";s:3:"梁";s:3:"糧";s:3:"糧";s:3:"良";s:3:"良";s:3:"諒";s:3:"諒";s:3:"量";s:3:"量";s:3:"勵";s:3:"勵";s:3:"呂";s:3:"呂";s:3:"女";s:3:"女";s:3:"廬";s:3:"廬";s:3:"旅";s:3:"旅";s:3:"濾";s:3:"濾";s:3:"礪";s:3:"礪";s:3:"閭";s:3:"閭";s:3:"驪";s:3:"驪";s:3:"麗";s:3:"麗";s:3:"黎";s:3:"黎";s:3:"力";s:3:"力";s:3:"曆";s:3:"曆";s:3:"歷";s:3:"歷";s:3:"轢";s:3:"轢";s:3:"年";s:3:"年";s:3:"憐";s:3:"憐";s:3:"戀";s:3:"戀";s:3:"撚";s:3:"撚";s:3:"漣";s:3:"漣";s:3:"煉";s:3:"煉";s:3:"璉";s:3:"璉";s:3:"秊";s:3:"秊";s:3:"練";s:3:"練";s:3:"聯";s:3:"聯";s:3:"輦";s:3:"輦";s:3:"蓮";s:3:"蓮";s:3:"連";s:3:"連";s:3:"鍊";s:3:"鍊";s:3:"列";s:3:"列";s:3:"劣";s:3:"劣";s:3:"咽";s:3:"咽";s:3:"烈";s:3:"烈";s:3:"裂";s:3:"裂";s:3:"說";s:3:"說";s:3:"廉";s:3:"廉";s:3:"念";s:3:"念";s:3:"捻";s:3:"捻";s:3:"殮";s:3:"殮";s:3:"簾";s:3:"簾";s:3:"獵";s:3:"獵";s:3:"令";s:3:"令";s:3:"囹";s:3:"囹";s:3:"寧";s:3:"寧";s:3:"嶺";s:3:"嶺";s:3:"怜";s:3:"怜";s:3:"玲";s:3:"玲";s:3:"瑩";s:3:"瑩";s:3:"羚";s:3:"羚";s:3:"聆";s:3:"聆";s:3:"鈴";s:3:"鈴";s:3:"零";s:3:"零";s:3:"靈";s:3:"靈";s:3:"領";s:3:"領";s:3:"例";s:3:"例";s:3:"禮";s:3:"禮";s:3:"醴";s:3:"醴";s:3:"隸";s:3:"隸";s:3:"惡";s:3:"惡";s:3:"了";s:3:"了";s:3:"僚";s:3:"僚";s:3:"寮";s:3:"寮";s:3:"尿";s:3:"尿";s:3:"料";s:3:"料";s:3:"樂";s:3:"樂";s:3:"燎";s:3:"燎";s:3:"療";s:3:"療";s:3:"蓼";s:3:"蓼";s:3:"遼";s:3:"遼";s:3:"龍";s:3:"龍";s:3:"暈";s:3:"暈";s:3:"阮";s:3:"阮";s:3:"劉";s:3:"劉";s:3:"杻";s:3:"杻";s:3:"柳";s:3:"柳";s:3:"流";s:3:"流";s:3:"溜";s:3:"溜";s:3:"琉";s:3:"琉";s:3:"留";s:3:"留";s:3:"硫";s:3:"硫";s:3:"紐";s:3:"紐";s:3:"類";s:3:"類";s:3:"六";s:3:"六";s:3:"戮";s:3:"戮";s:3:"陸";s:3:"陸";s:3:"倫";s:3:"倫";s:3:"崙";s:3:"崙";s:3:"淪";s:3:"淪";s:3:"輪";s:3:"輪";s:3:"律";s:3:"律";s:3:"慄";s:3:"慄";s:3:"栗";s:3:"栗";s:3:"率";s:3:"率";s:3:"隆";s:3:"隆";s:3:"利";s:3:"利";s:3:"吏";s:3:"吏";s:3:"履";s:3:"履";s:3:"易";s:3:"易";s:3:"李";s:3:"李";s:3:"梨";s:3:"梨";s:3:"泥";s:3:"泥";s:3:"理";s:3:"理";s:3:"痢";s:3:"痢";s:3:"罹";s:3:"罹";s:3:"裏";s:3:"裏";s:3:"裡";s:3:"裡";s:3:"里";s:3:"里";s:3:"離";s:3:"離";s:3:"匿";s:3:"匿";s:3:"溺";s:3:"溺";s:3:"吝";s:3:"吝";s:3:"燐";s:3:"燐";s:3:"璘";s:3:"璘";s:3:"藺";s:3:"藺";s:3:"隣";s:3:"隣";s:3:"鱗";s:3:"鱗";s:3:"麟";s:3:"麟";s:3:"林";s:3:"林";s:3:"淋";s:3:"淋";s:3:"臨";s:3:"臨";s:3:"立";s:3:"立";s:3:"笠";s:3:"笠";s:3:"粒";s:3:"粒";s:3:"狀";s:3:"狀";s:3:"炙";s:3:"炙";s:3:"識";s:3:"識";s:3:"什";s:3:"什";s:3:"茶";s:3:"茶";s:3:"刺";s:3:"刺";s:3:"切";s:3:"切";s:3:"度";s:3:"度";s:3:"拓";s:3:"拓";s:3:"糖";s:3:"糖";s:3:"宅";s:3:"宅";s:3:"洞";s:3:"洞";s:3:"暴";s:3:"暴";s:3:"輻";s:3:"輻";s:3:"行";s:3:"行";s:3:"降";s:3:"降";s:3:"見";s:3:"見";s:3:"廓";s:3:"廓";s:3:"兀";s:3:"兀";s:3:"嗀";s:3:"嗀";s:3:"塚";s:3:"塚";s:3:"晴";s:3:"晴";s:3:"凞";s:3:"凞";s:3:"猪";s:3:"猪";s:3:"益";s:3:"益";s:3:"礼";s:3:"礼";s:3:"神";s:3:"神";s:3:"祥";s:3:"祥";s:3:"福";s:3:"福";s:3:"靖";s:3:"靖";s:3:"精";s:3:"精";s:3:"羽";s:3:"羽";s:3:"蘒";s:3:"蘒";s:3:"諸";s:3:"諸";s:3:"逸";s:3:"逸";s:3:"都";s:3:"都";s:3:"飯";s:3:"飯";s:3:"飼";s:3:"飼";s:3:"館";s:3:"館";s:3:"鶴";s:3:"鶴";s:3:"侮";s:3:"侮";s:3:"僧";s:3:"僧";s:3:"免";s:3:"免";s:3:"勉";s:3:"勉";s:3:"勤";s:3:"勤";s:3:"卑";s:3:"卑";s:3:"喝";s:3:"喝";s:3:"嘆";s:3:"嘆";s:3:"器";s:3:"器";s:3:"塀";s:3:"塀";s:3:"墨";s:3:"墨";s:3:"層";s:3:"層";s:3:"屮";s:3:"屮";s:3:"悔";s:3:"悔";s:3:"慨";s:3:"慨";s:3:"憎";s:3:"憎";s:3:"懲";s:3:"懲";s:3:"敏";s:3:"敏";s:3:"既";s:3:"既";s:3:"暑";s:3:"暑";s:3:"梅";s:3:"梅";s:3:"海";s:3:"海";s:3:"渚";s:3:"渚";s:3:"漢";s:3:"漢";s:3:"煮";s:3:"煮";s:3:"爫";s:3:"爫";s:3:"琢";s:3:"琢";s:3:"碑";s:3:"碑";s:3:"社";s:3:"社";s:3:"祉";s:3:"祉";s:3:"祈";s:3:"祈";s:3:"祐";s:3:"祐";s:3:"祖";s:3:"祖";s:3:"祝";s:3:"祝";s:3:"禍";s:3:"禍";s:3:"禎";s:3:"禎";s:3:"穀";s:3:"穀";s:3:"突";s:3:"突";s:3:"節";s:3:"節";s:3:"練";s:3:"練";s:3:"縉";s:3:"縉";s:3:"繁";s:3:"繁";s:3:"署";s:3:"署";s:3:"者";s:3:"者";s:3:"臭";s:3:"臭";s:3:"艹";s:3:"艹";s:3:"艹";s:3:"艹";s:3:"著";s:3:"著";s:3:"褐";s:3:"褐";s:3:"視";s:3:"視";s:3:"謁";s:3:"謁";s:3:"謹";s:3:"謹";s:3:"賓";s:3:"賓";s:3:"贈";s:3:"贈";s:3:"辶";s:3:"辶";s:3:"逸";s:3:"逸";s:3:"難";s:3:"難";s:3:"響";s:3:"響";s:3:"頻";s:3:"頻";s:3:"並";s:3:"並";s:3:"况";s:3:"况";s:3:"全";s:3:"全";s:3:"侀";s:3:"侀";s:3:"充";s:3:"充";s:3:"冀";s:3:"冀";s:3:"勇";s:3:"勇";s:3:"勺";s:3:"勺";s:3:"喝";s:3:"喝";s:3:"啕";s:3:"啕";s:3:"喙";s:3:"喙";s:3:"嗢";s:3:"嗢";s:3:"塚";s:3:"塚";s:3:"墳";s:3:"墳";s:3:"奄";s:3:"奄";s:3:"奔";s:3:"奔";s:3:"婢";s:3:"婢";s:3:"嬨";s:3:"嬨";s:3:"廒";s:3:"廒";s:3:"廙";s:3:"廙";s:3:"彩";s:3:"彩";s:3:"徭";s:3:"徭";s:3:"惘";s:3:"惘";s:3:"慎";s:3:"慎";s:3:"愈";s:3:"愈";s:3:"憎";s:3:"憎";s:3:"慠";s:3:"慠";s:3:"懲";s:3:"懲";s:3:"戴";s:3:"戴";s:3:"揄";s:3:"揄";s:3:"搜";s:3:"搜";s:3:"摒";s:3:"摒";s:3:"敖";s:3:"敖";s:3:"晴";s:3:"晴";s:3:"朗";s:3:"朗";s:3:"望";s:3:"望";s:3:"杖";s:3:"杖";s:3:"歹";s:3:"歹";s:3:"殺";s:3:"殺";s:3:"流";s:3:"流";s:3:"滛";s:3:"滛";s:3:"滋";s:3:"滋";s:3:"漢";s:3:"漢";s:3:"瀞";s:3:"瀞";s:3:"煮";s:3:"煮";s:3:"瞧";s:3:"瞧";s:3:"爵";s:3:"爵";s:3:"犯";s:3:"犯";s:3:"猪";s:3:"猪";s:3:"瑱";s:3:"瑱";s:3:"甆";s:3:"甆";s:3:"画";s:3:"画";s:3:"瘝";s:3:"瘝";s:3:"瘟";s:3:"瘟";s:3:"益";s:3:"益";s:3:"盛";s:3:"盛";s:3:"直";s:3:"直";s:3:"睊";s:3:"睊";s:3:"着";s:3:"着";s:3:"磌";s:3:"磌";s:3:"窱";s:3:"窱";s:3:"節";s:3:"節";s:3:"类";s:3:"类";s:3:"絛";s:3:"絛";s:3:"練";s:3:"練";s:3:"缾";s:3:"缾";s:3:"者";s:3:"者";s:3:"荒";s:3:"荒";s:3:"華";s:3:"華";s:3:"蝹";s:3:"蝹";s:3:"襁";s:3:"襁";s:3:"覆";s:3:"覆";s:3:"視";s:3:"視";s:3:"調";s:3:"調";s:3:"諸";s:3:"諸";s:3:"請";s:3:"請";s:3:"謁";s:3:"謁";s:3:"諾";s:3:"諾";s:3:"諭";s:3:"諭";s:3:"謹";s:3:"謹";s:3:"變";s:3:"變";s:3:"贈";s:3:"贈";s:3:"輸";s:3:"輸";s:3:"遲";s:3:"遲";s:3:"醙";s:3:"醙";s:3:"鉶";s:3:"鉶";s:3:"陼";s:3:"陼";s:3:"難";s:3:"難";s:3:"靖";s:3:"靖";s:3:"韛";s:3:"韛";s:3:"響";s:3:"響";s:3:"頋";s:3:"頋";s:3:"頻";s:3:"頻";s:3:"鬒";s:3:"鬒";s:3:"龜";s:3:"龜";s:3:"𢡊";s:4:"𢡊";s:3:"𢡄";s:4:"𢡄";s:3:"𣏕";s:4:"𣏕";s:3:"㮝";s:3:"㮝";s:3:"䀘";s:3:"䀘";s:3:"䀹";s:3:"䀹";s:3:"𥉉";s:4:"𥉉";s:3:"𥳐";s:4:"𥳐";s:3:"𧻓";s:4:"𧻓";s:3:"齃";s:3:"齃";s:3:"龎";s:3:"龎";s:3:"ff";s:2:"ff";s:3:"fi";s:2:"fi";s:3:"fl";s:2:"fl";s:3:"ffi";s:3:"ffi";s:3:"ffl";s:3:"ffl";s:3:"ſt";s:2:"st";s:3:"st";s:2:"st";s:3:"ﬓ";s:4:"մն";s:3:"ﬔ";s:4:"մե";s:3:"ﬕ";s:4:"մի";s:3:"ﬖ";s:4:"վն";s:3:"ﬗ";s:4:"մխ";s:3:"יִ";s:4:"יִ";s:3:"ײַ";s:4:"ײַ";s:3:"ﬠ";s:2:"ע";s:3:"ﬡ";s:2:"א";s:3:"ﬢ";s:2:"ד";s:3:"ﬣ";s:2:"ה";s:3:"ﬤ";s:2:"כ";s:3:"ﬥ";s:2:"ל";s:3:"ﬦ";s:2:"ם";s:3:"ﬧ";s:2:"ר";s:3:"ﬨ";s:2:"ת";s:3:"﬩";s:1:"+";s:3:"שׁ";s:4:"שׁ";s:3:"שׂ";s:4:"שׂ";s:3:"שּׁ";s:6:"שּׁ";s:3:"שּׂ";s:6:"שּׂ";s:3:"אַ";s:4:"אַ";s:3:"אָ";s:4:"אָ";s:3:"אּ";s:4:"אּ";s:3:"בּ";s:4:"בּ";s:3:"גּ";s:4:"גּ";s:3:"דּ";s:4:"דּ";s:3:"הּ";s:4:"הּ";s:3:"וּ";s:4:"וּ";s:3:"זּ";s:4:"זּ";s:3:"טּ";s:4:"טּ";s:3:"יּ";s:4:"יּ";s:3:"ךּ";s:4:"ךּ";s:3:"כּ";s:4:"כּ";s:3:"לּ";s:4:"לּ";s:3:"מּ";s:4:"מּ";s:3:"נּ";s:4:"נּ";s:3:"סּ";s:4:"סּ";s:3:"ףּ";s:4:"ףּ";s:3:"פּ";s:4:"פּ";s:3:"צּ";s:4:"צּ";s:3:"קּ";s:4:"קּ";s:3:"רּ";s:4:"רּ";s:3:"שּ";s:4:"שּ";s:3:"תּ";s:4:"תּ";s:3:"וֹ";s:4:"וֹ";s:3:"בֿ";s:4:"בֿ";s:3:"כֿ";s:4:"כֿ";s:3:"פֿ";s:4:"פֿ";s:3:"ﭏ";s:4:"אל";s:3:"ﭐ";s:2:"ٱ";s:3:"ﭑ";s:2:"ٱ";s:3:"ﭒ";s:2:"ٻ";s:3:"ﭓ";s:2:"ٻ";s:3:"ﭔ";s:2:"ٻ";s:3:"ﭕ";s:2:"ٻ";s:3:"ﭖ";s:2:"پ";s:3:"ﭗ";s:2:"پ";s:3:"ﭘ";s:2:"پ";s:3:"ﭙ";s:2:"پ";s:3:"ﭚ";s:2:"ڀ";s:3:"ﭛ";s:2:"ڀ";s:3:"ﭜ";s:2:"ڀ";s:3:"ﭝ";s:2:"ڀ";s:3:"ﭞ";s:2:"ٺ";s:3:"ﭟ";s:2:"ٺ";s:3:"ﭠ";s:2:"ٺ";s:3:"ﭡ";s:2:"ٺ";s:3:"ﭢ";s:2:"ٿ";s:3:"ﭣ";s:2:"ٿ";s:3:"ﭤ";s:2:"ٿ";s:3:"ﭥ";s:2:"ٿ";s:3:"ﭦ";s:2:"ٹ";s:3:"ﭧ";s:2:"ٹ";s:3:"ﭨ";s:2:"ٹ";s:3:"ﭩ";s:2:"ٹ";s:3:"ﭪ";s:2:"ڤ";s:3:"ﭫ";s:2:"ڤ";s:3:"ﭬ";s:2:"ڤ";s:3:"ﭭ";s:2:"ڤ";s:3:"ﭮ";s:2:"ڦ";s:3:"ﭯ";s:2:"ڦ";s:3:"ﭰ";s:2:"ڦ";s:3:"ﭱ";s:2:"ڦ";s:3:"ﭲ";s:2:"ڄ";s:3:"ﭳ";s:2:"ڄ";s:3:"ﭴ";s:2:"ڄ";s:3:"ﭵ";s:2:"ڄ";s:3:"ﭶ";s:2:"ڃ";s:3:"ﭷ";s:2:"ڃ";s:3:"ﭸ";s:2:"ڃ";s:3:"ﭹ";s:2:"ڃ";s:3:"ﭺ";s:2:"چ";s:3:"ﭻ";s:2:"چ";s:3:"ﭼ";s:2:"چ";s:3:"ﭽ";s:2:"چ";s:3:"ﭾ";s:2:"ڇ";s:3:"ﭿ";s:2:"ڇ";s:3:"ﮀ";s:2:"ڇ";s:3:"ﮁ";s:2:"ڇ";s:3:"ﮂ";s:2:"ڍ";s:3:"ﮃ";s:2:"ڍ";s:3:"ﮄ";s:2:"ڌ";s:3:"ﮅ";s:2:"ڌ";s:3:"ﮆ";s:2:"ڎ";s:3:"ﮇ";s:2:"ڎ";s:3:"ﮈ";s:2:"ڈ";s:3:"ﮉ";s:2:"ڈ";s:3:"ﮊ";s:2:"ژ";s:3:"ﮋ";s:2:"ژ";s:3:"ﮌ";s:2:"ڑ";s:3:"ﮍ";s:2:"ڑ";s:3:"ﮎ";s:2:"ک";s:3:"ﮏ";s:2:"ک";s:3:"ﮐ";s:2:"ک";s:3:"ﮑ";s:2:"ک";s:3:"ﮒ";s:2:"گ";s:3:"ﮓ";s:2:"گ";s:3:"ﮔ";s:2:"گ";s:3:"ﮕ";s:2:"گ";s:3:"ﮖ";s:2:"ڳ";s:3:"ﮗ";s:2:"ڳ";s:3:"ﮘ";s:2:"ڳ";s:3:"ﮙ";s:2:"ڳ";s:3:"ﮚ";s:2:"ڱ";s:3:"ﮛ";s:2:"ڱ";s:3:"ﮜ";s:2:"ڱ";s:3:"ﮝ";s:2:"ڱ";s:3:"ﮞ";s:2:"ں";s:3:"ﮟ";s:2:"ں";s:3:"ﮠ";s:2:"ڻ";s:3:"ﮡ";s:2:"ڻ";s:3:"ﮢ";s:2:"ڻ";s:3:"ﮣ";s:2:"ڻ";s:3:"ﮤ";s:4:"ۀ";s:3:"ﮥ";s:4:"ۀ";s:3:"ﮦ";s:2:"ہ";s:3:"ﮧ";s:2:"ہ";s:3:"ﮨ";s:2:"ہ";s:3:"ﮩ";s:2:"ہ";s:3:"ﮪ";s:2:"ھ";s:3:"ﮫ";s:2:"ھ";s:3:"ﮬ";s:2:"ھ";s:3:"ﮭ";s:2:"ھ";s:3:"ﮮ";s:2:"ے";s:3:"ﮯ";s:2:"ے";s:3:"ﮰ";s:4:"ۓ";s:3:"ﮱ";s:4:"ۓ";s:3:"ﯓ";s:2:"ڭ";s:3:"ﯔ";s:2:"ڭ";s:3:"ﯕ";s:2:"ڭ";s:3:"ﯖ";s:2:"ڭ";s:3:"ﯗ";s:2:"ۇ";s:3:"ﯘ";s:2:"ۇ";s:3:"ﯙ";s:2:"ۆ";s:3:"ﯚ";s:2:"ۆ";s:3:"ﯛ";s:2:"ۈ";s:3:"ﯜ";s:2:"ۈ";s:3:"ﯝ";s:4:"ۇٴ";s:3:"ﯞ";s:2:"ۋ";s:3:"ﯟ";s:2:"ۋ";s:3:"ﯠ";s:2:"ۅ";s:3:"ﯡ";s:2:"ۅ";s:3:"ﯢ";s:2:"ۉ";s:3:"ﯣ";s:2:"ۉ";s:3:"ﯤ";s:2:"ې";s:3:"ﯥ";s:2:"ې";s:3:"ﯦ";s:2:"ې";s:3:"ﯧ";s:2:"ې";s:3:"ﯨ";s:2:"ى";s:3:"ﯩ";s:2:"ى";s:3:"ﯪ";s:6:"ئا";s:3:"ﯫ";s:6:"ئا";s:3:"ﯬ";s:6:"ئە";s:3:"ﯭ";s:6:"ئە";s:3:"ﯮ";s:6:"ئو";s:3:"ﯯ";s:6:"ئو";s:3:"ﯰ";s:6:"ئۇ";s:3:"ﯱ";s:6:"ئۇ";s:3:"ﯲ";s:6:"ئۆ";s:3:"ﯳ";s:6:"ئۆ";s:3:"ﯴ";s:6:"ئۈ";s:3:"ﯵ";s:6:"ئۈ";s:3:"ﯶ";s:6:"ئې";s:3:"ﯷ";s:6:"ئې";s:3:"ﯸ";s:6:"ئې";s:3:"ﯹ";s:6:"ئى";s:3:"ﯺ";s:6:"ئى";s:3:"ﯻ";s:6:"ئى";s:3:"ﯼ";s:2:"ی";s:3:"ﯽ";s:2:"ی";s:3:"ﯾ";s:2:"ی";s:3:"ﯿ";s:2:"ی";s:3:"ﰀ";s:6:"ئج";s:3:"ﰁ";s:6:"ئح";s:3:"ﰂ";s:6:"ئم";s:3:"ﰃ";s:6:"ئى";s:3:"ﰄ";s:6:"ئي";s:3:"ﰅ";s:4:"بج";s:3:"ﰆ";s:4:"بح";s:3:"ﰇ";s:4:"بخ";s:3:"ﰈ";s:4:"بم";s:3:"ﰉ";s:4:"بى";s:3:"ﰊ";s:4:"بي";s:3:"ﰋ";s:4:"تج";s:3:"ﰌ";s:4:"تح";s:3:"ﰍ";s:4:"تخ";s:3:"ﰎ";s:4:"تم";s:3:"ﰏ";s:4:"تى";s:3:"ﰐ";s:4:"تي";s:3:"ﰑ";s:4:"ثج";s:3:"ﰒ";s:4:"ثم";s:3:"ﰓ";s:4:"ثى";s:3:"ﰔ";s:4:"ثي";s:3:"ﰕ";s:4:"جح";s:3:"ﰖ";s:4:"جم";s:3:"ﰗ";s:4:"حج";s:3:"ﰘ";s:4:"حم";s:3:"ﰙ";s:4:"خج";s:3:"ﰚ";s:4:"خح";s:3:"ﰛ";s:4:"خم";s:3:"ﰜ";s:4:"سج";s:3:"ﰝ";s:4:"سح";s:3:"ﰞ";s:4:"سخ";s:3:"ﰟ";s:4:"سم";s:3:"ﰠ";s:4:"صح";s:3:"ﰡ";s:4:"صم";s:3:"ﰢ";s:4:"ضج";s:3:"ﰣ";s:4:"ضح";s:3:"ﰤ";s:4:"ضخ";s:3:"ﰥ";s:4:"ضم";s:3:"ﰦ";s:4:"طح";s:3:"ﰧ";s:4:"طم";s:3:"ﰨ";s:4:"ظم";s:3:"ﰩ";s:4:"عج";s:3:"ﰪ";s:4:"عم";s:3:"ﰫ";s:4:"غج";s:3:"ﰬ";s:4:"غم";s:3:"ﰭ";s:4:"فج";s:3:"ﰮ";s:4:"فح";s:3:"ﰯ";s:4:"فخ";s:3:"ﰰ";s:4:"فم";s:3:"ﰱ";s:4:"فى";s:3:"ﰲ";s:4:"في";s:3:"ﰳ";s:4:"قح";s:3:"ﰴ";s:4:"قم";s:3:"ﰵ";s:4:"قى";s:3:"ﰶ";s:4:"قي";s:3:"ﰷ";s:4:"كا";s:3:"ﰸ";s:4:"كج";s:3:"ﰹ";s:4:"كح";s:3:"ﰺ";s:4:"كخ";s:3:"ﰻ";s:4:"كل";s:3:"ﰼ";s:4:"كم";s:3:"ﰽ";s:4:"كى";s:3:"ﰾ";s:4:"كي";s:3:"ﰿ";s:4:"لج";s:3:"ﱀ";s:4:"لح";s:3:"ﱁ";s:4:"لخ";s:3:"ﱂ";s:4:"لم";s:3:"ﱃ";s:4:"لى";s:3:"ﱄ";s:4:"لي";s:3:"ﱅ";s:4:"مج";s:3:"ﱆ";s:4:"مح";s:3:"ﱇ";s:4:"مخ";s:3:"ﱈ";s:4:"مم";s:3:"ﱉ";s:4:"مى";s:3:"ﱊ";s:4:"مي";s:3:"ﱋ";s:4:"نج";s:3:"ﱌ";s:4:"نح";s:3:"ﱍ";s:4:"نخ";s:3:"ﱎ";s:4:"نم";s:3:"ﱏ";s:4:"نى";s:3:"ﱐ";s:4:"ني";s:3:"ﱑ";s:4:"هج";s:3:"ﱒ";s:4:"هم";s:3:"ﱓ";s:4:"هى";s:3:"ﱔ";s:4:"هي";s:3:"ﱕ";s:4:"يج";s:3:"ﱖ";s:4:"يح";s:3:"ﱗ";s:4:"يخ";s:3:"ﱘ";s:4:"يم";s:3:"ﱙ";s:4:"يى";s:3:"ﱚ";s:4:"يي";s:3:"ﱛ";s:4:"ذٰ";s:3:"ﱜ";s:4:"رٰ";s:3:"ﱝ";s:4:"ىٰ";s:3:"ﱞ";s:5:" ٌّ";s:3:"ﱟ";s:5:" ٍّ";s:3:"ﱠ";s:5:" َّ";s:3:"ﱡ";s:5:" ُّ";s:3:"ﱢ";s:5:" ِّ";s:3:"ﱣ";s:5:" ّٰ";s:3:"ﱤ";s:6:"ئر";s:3:"ﱥ";s:6:"ئز";s:3:"ﱦ";s:6:"ئم";s:3:"ﱧ";s:6:"ئن";s:3:"ﱨ";s:6:"ئى";s:3:"ﱩ";s:6:"ئي";s:3:"ﱪ";s:4:"بر";s:3:"ﱫ";s:4:"بز";s:3:"ﱬ";s:4:"بم";s:3:"ﱭ";s:4:"بن";s:3:"ﱮ";s:4:"بى";s:3:"ﱯ";s:4:"بي";s:3:"ﱰ";s:4:"تر";s:3:"ﱱ";s:4:"تز";s:3:"ﱲ";s:4:"تم";s:3:"ﱳ";s:4:"تن";s:3:"ﱴ";s:4:"تى";s:3:"ﱵ";s:4:"تي";s:3:"ﱶ";s:4:"ثر";s:3:"ﱷ";s:4:"ثز";s:3:"ﱸ";s:4:"ثم";s:3:"ﱹ";s:4:"ثن";s:3:"ﱺ";s:4:"ثى";s:3:"ﱻ";s:4:"ثي";s:3:"ﱼ";s:4:"فى";s:3:"ﱽ";s:4:"في";s:3:"ﱾ";s:4:"قى";s:3:"ﱿ";s:4:"قي";s:3:"ﲀ";s:4:"كا";s:3:"ﲁ";s:4:"كل";s:3:"ﲂ";s:4:"كم";s:3:"ﲃ";s:4:"كى";s:3:"ﲄ";s:4:"كي";s:3:"ﲅ";s:4:"لم";s:3:"ﲆ";s:4:"لى";s:3:"ﲇ";s:4:"لي";s:3:"ﲈ";s:4:"ما";s:3:"ﲉ";s:4:"مم";s:3:"ﲊ";s:4:"نر";s:3:"ﲋ";s:4:"نز";s:3:"ﲌ";s:4:"نم";s:3:"ﲍ";s:4:"نن";s:3:"ﲎ";s:4:"نى";s:3:"ﲏ";s:4:"ني";s:3:"ﲐ";s:4:"ىٰ";s:3:"ﲑ";s:4:"ير";s:3:"ﲒ";s:4:"يز";s:3:"ﲓ";s:4:"يم";s:3:"ﲔ";s:4:"ين";s:3:"ﲕ";s:4:"يى";s:3:"ﲖ";s:4:"يي";s:3:"ﲗ";s:6:"ئج";s:3:"ﲘ";s:6:"ئح";s:3:"ﲙ";s:6:"ئخ";s:3:"ﲚ";s:6:"ئم";s:3:"ﲛ";s:6:"ئه";s:3:"ﲜ";s:4:"بج";s:3:"ﲝ";s:4:"بح";s:3:"ﲞ";s:4:"بخ";s:3:"ﲟ";s:4:"بم";s:3:"ﲠ";s:4:"به";s:3:"ﲡ";s:4:"تج";s:3:"ﲢ";s:4:"تح";s:3:"ﲣ";s:4:"تخ";s:3:"ﲤ";s:4:"تم";s:3:"ﲥ";s:4:"ته";s:3:"ﲦ";s:4:"ثم";s:3:"ﲧ";s:4:"جح";s:3:"ﲨ";s:4:"جم";s:3:"ﲩ";s:4:"حج";s:3:"ﲪ";s:4:"حم";s:3:"ﲫ";s:4:"خج";s:3:"ﲬ";s:4:"خم";s:3:"ﲭ";s:4:"سج";s:3:"ﲮ";s:4:"سح";s:3:"ﲯ";s:4:"سخ";s:3:"ﲰ";s:4:"سم";s:3:"ﲱ";s:4:"صح";s:3:"ﲲ";s:4:"صخ";s:3:"ﲳ";s:4:"صم";s:3:"ﲴ";s:4:"ضج";s:3:"ﲵ";s:4:"ضح";s:3:"ﲶ";s:4:"ضخ";s:3:"ﲷ";s:4:"ضم";s:3:"ﲸ";s:4:"طح";s:3:"ﲹ";s:4:"ظم";s:3:"ﲺ";s:4:"عج";s:3:"ﲻ";s:4:"عم";s:3:"ﲼ";s:4:"غج";s:3:"ﲽ";s:4:"غم";s:3:"ﲾ";s:4:"فج";s:3:"ﲿ";s:4:"فح";s:3:"ﳀ";s:4:"فخ";s:3:"ﳁ";s:4:"فم";s:3:"ﳂ";s:4:"قح";s:3:"ﳃ";s:4:"قم";s:3:"ﳄ";s:4:"كج";s:3:"ﳅ";s:4:"كح";s:3:"ﳆ";s:4:"كخ";s:3:"ﳇ";s:4:"كل";s:3:"ﳈ";s:4:"كم";s:3:"ﳉ";s:4:"لج";s:3:"ﳊ";s:4:"لح";s:3:"ﳋ";s:4:"لخ";s:3:"ﳌ";s:4:"لم";s:3:"ﳍ";s:4:"له";s:3:"ﳎ";s:4:"مج";s:3:"ﳏ";s:4:"مح";s:3:"ﳐ";s:4:"مخ";s:3:"ﳑ";s:4:"مم";s:3:"ﳒ";s:4:"نج";s:3:"ﳓ";s:4:"نح";s:3:"ﳔ";s:4:"نخ";s:3:"ﳕ";s:4:"نم";s:3:"ﳖ";s:4:"نه";s:3:"ﳗ";s:4:"هج";s:3:"ﳘ";s:4:"هم";s:3:"ﳙ";s:4:"هٰ";s:3:"ﳚ";s:4:"يج";s:3:"ﳛ";s:4:"يح";s:3:"ﳜ";s:4:"يخ";s:3:"ﳝ";s:4:"يم";s:3:"ﳞ";s:4:"يه";s:3:"ﳟ";s:6:"ئم";s:3:"ﳠ";s:6:"ئه";s:3:"ﳡ";s:4:"بم";s:3:"ﳢ";s:4:"به";s:3:"ﳣ";s:4:"تم";s:3:"ﳤ";s:4:"ته";s:3:"ﳥ";s:4:"ثم";s:3:"ﳦ";s:4:"ثه";s:3:"ﳧ";s:4:"سم";s:3:"ﳨ";s:4:"سه";s:3:"ﳩ";s:4:"شم";s:3:"ﳪ";s:4:"شه";s:3:"ﳫ";s:4:"كل";s:3:"ﳬ";s:4:"كم";s:3:"ﳭ";s:4:"لم";s:3:"ﳮ";s:4:"نم";s:3:"ﳯ";s:4:"نه";s:3:"ﳰ";s:4:"يم";s:3:"ﳱ";s:4:"يه";s:3:"ﳲ";s:6:"ـَّ";s:3:"ﳳ";s:6:"ـُّ";s:3:"ﳴ";s:6:"ـِّ";s:3:"ﳵ";s:4:"طى";s:3:"ﳶ";s:4:"طي";s:3:"ﳷ";s:4:"عى";s:3:"ﳸ";s:4:"عي";s:3:"ﳹ";s:4:"غى";s:3:"ﳺ";s:4:"غي";s:3:"ﳻ";s:4:"سى";s:3:"ﳼ";s:4:"سي";s:3:"ﳽ";s:4:"شى";s:3:"ﳾ";s:4:"شي";s:3:"ﳿ";s:4:"حى";s:3:"ﴀ";s:4:"حي";s:3:"ﴁ";s:4:"جى";s:3:"ﴂ";s:4:"جي";s:3:"ﴃ";s:4:"خى";s:3:"ﴄ";s:4:"خي";s:3:"ﴅ";s:4:"صى";s:3:"ﴆ";s:4:"صي";s:3:"ﴇ";s:4:"ضى";s:3:"ﴈ";s:4:"ضي";s:3:"ﴉ";s:4:"شج";s:3:"ﴊ";s:4:"شح";s:3:"ﴋ";s:4:"شخ";s:3:"ﴌ";s:4:"شم";s:3:"ﴍ";s:4:"شر";s:3:"ﴎ";s:4:"سر";s:3:"ﴏ";s:4:"صر";s:3:"ﴐ";s:4:"ضر";s:3:"ﴑ";s:4:"طى";s:3:"ﴒ";s:4:"طي";s:3:"ﴓ";s:4:"عى";s:3:"ﴔ";s:4:"عي";s:3:"ﴕ";s:4:"غى";s:3:"ﴖ";s:4:"غي";s:3:"ﴗ";s:4:"سى";s:3:"ﴘ";s:4:"سي";s:3:"ﴙ";s:4:"شى";s:3:"ﴚ";s:4:"شي";s:3:"ﴛ";s:4:"حى";s:3:"ﴜ";s:4:"حي";s:3:"ﴝ";s:4:"جى";s:3:"ﴞ";s:4:"جي";s:3:"ﴟ";s:4:"خى";s:3:"ﴠ";s:4:"خي";s:3:"ﴡ";s:4:"صى";s:3:"ﴢ";s:4:"صي";s:3:"ﴣ";s:4:"ضى";s:3:"ﴤ";s:4:"ضي";s:3:"ﴥ";s:4:"شج";s:3:"ﴦ";s:4:"شح";s:3:"ﴧ";s:4:"شخ";s:3:"ﴨ";s:4:"شم";s:3:"ﴩ";s:4:"شر";s:3:"ﴪ";s:4:"سر";s:3:"ﴫ";s:4:"صر";s:3:"ﴬ";s:4:"ضر";s:3:"ﴭ";s:4:"شج";s:3:"ﴮ";s:4:"شح";s:3:"ﴯ";s:4:"شخ";s:3:"ﴰ";s:4:"شم";s:3:"ﴱ";s:4:"سه";s:3:"ﴲ";s:4:"شه";s:3:"ﴳ";s:4:"طم";s:3:"ﴴ";s:4:"سج";s:3:"ﴵ";s:4:"سح";s:3:"ﴶ";s:4:"سخ";s:3:"ﴷ";s:4:"شج";s:3:"ﴸ";s:4:"شح";s:3:"ﴹ";s:4:"شخ";s:3:"ﴺ";s:4:"طم";s:3:"ﴻ";s:4:"ظم";s:3:"ﴼ";s:4:"اً";s:3:"ﴽ";s:4:"اً";s:3:"ﵐ";s:6:"تجم";s:3:"ﵑ";s:6:"تحج";s:3:"ﵒ";s:6:"تحج";s:3:"ﵓ";s:6:"تحم";s:3:"ﵔ";s:6:"تخم";s:3:"ﵕ";s:6:"تمج";s:3:"ﵖ";s:6:"تمح";s:3:"ﵗ";s:6:"تمخ";s:3:"ﵘ";s:6:"جمح";s:3:"ﵙ";s:6:"جمح";s:3:"ﵚ";s:6:"حمي";s:3:"ﵛ";s:6:"حمى";s:3:"ﵜ";s:6:"سحج";s:3:"ﵝ";s:6:"سجح";s:3:"ﵞ";s:6:"سجى";s:3:"ﵟ";s:6:"سمح";s:3:"ﵠ";s:6:"سمح";s:3:"ﵡ";s:6:"سمج";s:3:"ﵢ";s:6:"سمم";s:3:"ﵣ";s:6:"سمم";s:3:"ﵤ";s:6:"صحح";s:3:"ﵥ";s:6:"صحح";s:3:"ﵦ";s:6:"صمم";s:3:"ﵧ";s:6:"شحم";s:3:"ﵨ";s:6:"شحم";s:3:"ﵩ";s:6:"شجي";s:3:"ﵪ";s:6:"شمخ";s:3:"ﵫ";s:6:"شمخ";s:3:"ﵬ";s:6:"شمم";s:3:"ﵭ";s:6:"شمم";s:3:"ﵮ";s:6:"ضحى";s:3:"ﵯ";s:6:"ضخم";s:3:"ﵰ";s:6:"ضخم";s:3:"ﵱ";s:6:"طمح";s:3:"ﵲ";s:6:"طمح";s:3:"ﵳ";s:6:"طمم";s:3:"ﵴ";s:6:"طمي";s:3:"ﵵ";s:6:"عجم";s:3:"ﵶ";s:6:"عمم";s:3:"ﵷ";s:6:"عمم";s:3:"ﵸ";s:6:"عمى";s:3:"ﵹ";s:6:"غمم";s:3:"ﵺ";s:6:"غمي";s:3:"ﵻ";s:6:"غمى";s:3:"ﵼ";s:6:"فخم";s:3:"ﵽ";s:6:"فخم";s:3:"ﵾ";s:6:"قمح";s:3:"ﵿ";s:6:"قمم";s:3:"ﶀ";s:6:"لحم";s:3:"ﶁ";s:6:"لحي";s:3:"ﶂ";s:6:"لحى";s:3:"ﶃ";s:6:"لجج";s:3:"ﶄ";s:6:"لجج";s:3:"ﶅ";s:6:"لخم";s:3:"ﶆ";s:6:"لخم";s:3:"ﶇ";s:6:"لمح";s:3:"ﶈ";s:6:"لمح";s:3:"ﶉ";s:6:"محج";s:3:"ﶊ";s:6:"محم";s:3:"ﶋ";s:6:"محي";s:3:"ﶌ";s:6:"مجح";s:3:"ﶍ";s:6:"مجم";s:3:"ﶎ";s:6:"مخج";s:3:"ﶏ";s:6:"مخم";s:3:"ﶒ";s:6:"مجخ";s:3:"ﶓ";s:6:"همج";s:3:"ﶔ";s:6:"همم";s:3:"ﶕ";s:6:"نحم";s:3:"ﶖ";s:6:"نحى";s:3:"ﶗ";s:6:"نجم";s:3:"ﶘ";s:6:"نجم";s:3:"ﶙ";s:6:"نجى";s:3:"ﶚ";s:6:"نمي";s:3:"ﶛ";s:6:"نمى";s:3:"ﶜ";s:6:"يمم";s:3:"ﶝ";s:6:"يمم";s:3:"ﶞ";s:6:"بخي";s:3:"ﶟ";s:6:"تجي";s:3:"ﶠ";s:6:"تجى";s:3:"ﶡ";s:6:"تخي";s:3:"ﶢ";s:6:"تخى";s:3:"ﶣ";s:6:"تمي";s:3:"ﶤ";s:6:"تمى";s:3:"ﶥ";s:6:"جمي";s:3:"ﶦ";s:6:"جحى";s:3:"ﶧ";s:6:"جمى";s:3:"ﶨ";s:6:"سخى";s:3:"ﶩ";s:6:"صحي";s:3:"ﶪ";s:6:"شحي";s:3:"ﶫ";s:6:"ضحي";s:3:"ﶬ";s:6:"لجي";s:3:"ﶭ";s:6:"لمي";s:3:"ﶮ";s:6:"يحي";s:3:"ﶯ";s:6:"يجي";s:3:"ﶰ";s:6:"يمي";s:3:"ﶱ";s:6:"ممي";s:3:"ﶲ";s:6:"قمي";s:3:"ﶳ";s:6:"نحي";s:3:"ﶴ";s:6:"قمح";s:3:"ﶵ";s:6:"لحم";s:3:"ﶶ";s:6:"عمي";s:3:"ﶷ";s:6:"كمي";s:3:"ﶸ";s:6:"نجح";s:3:"ﶹ";s:6:"مخي";s:3:"ﶺ";s:6:"لجم";s:3:"ﶻ";s:6:"كمم";s:3:"ﶼ";s:6:"لجم";s:3:"ﶽ";s:6:"نجح";s:3:"ﶾ";s:6:"جحي";s:3:"ﶿ";s:6:"حجي";s:3:"ﷀ";s:6:"مجي";s:3:"ﷁ";s:6:"فمي";s:3:"ﷂ";s:6:"بحي";s:3:"ﷃ";s:6:"كمم";s:3:"ﷄ";s:6:"عجم";s:3:"ﷅ";s:6:"صمم";s:3:"ﷆ";s:6:"سخي";s:3:"ﷇ";s:6:"نجي";s:3:"ﷰ";s:6:"صلے";s:3:"ﷱ";s:6:"قلے";s:3:"ﷲ";s:8:"الله";s:3:"ﷳ";s:8:"اكبر";s:3:"ﷴ";s:8:"محمد";s:3:"ﷵ";s:8:"صلعم";s:3:"ﷶ";s:8:"رسول";s:3:"ﷷ";s:8:"عليه";s:3:"ﷸ";s:8:"وسلم";s:3:"ﷹ";s:6:"صلى";s:3:"ﷺ";s:33:"صلى الله عليه وسلم";s:3:"ﷻ";s:15:"جل جلاله";s:3:"﷼";s:8:"ریال";s:3:"︐";s:1:",";s:3:"︑";s:3:"、";s:3:"︒";s:3:"。";s:3:"︓";s:1:":";s:3:"︔";s:1:";";s:3:"︕";s:1:"!";s:3:"︖";s:1:"?";s:3:"︗";s:3:"〖";s:3:"︘";s:3:"〗";s:3:"︙";s:3:"...";s:3:"︰";s:2:"..";s:3:"︱";s:3:"—";s:3:"︲";s:3:"–";s:3:"︳";s:1:"_";s:3:"︴";s:1:"_";s:3:"︵";s:1:"(";s:3:"︶";s:1:")";s:3:"︷";s:1:"{";s:3:"︸";s:1:"}";s:3:"︹";s:3:"〔";s:3:"︺";s:3:"〕";s:3:"︻";s:3:"【";s:3:"︼";s:3:"】";s:3:"︽";s:3:"《";s:3:"︾";s:3:"》";s:3:"︿";s:3:"〈";s:3:"﹀";s:3:"〉";s:3:"﹁";s:3:"「";s:3:"﹂";s:3:"」";s:3:"﹃";s:3:"『";s:3:"﹄";s:3:"』";s:3:"﹇";s:1:"[";s:3:"﹈";s:1:"]";s:3:"﹉";s:3:" ̅";s:3:"﹊";s:3:" ̅";s:3:"﹋";s:3:" ̅";s:3:"﹌";s:3:" ̅";s:3:"﹍";s:1:"_";s:3:"﹎";s:1:"_";s:3:"﹏";s:1:"_";s:3:"﹐";s:1:",";s:3:"﹑";s:3:"、";s:3:"﹒";s:1:".";s:3:"﹔";s:1:";";s:3:"﹕";s:1:":";s:3:"﹖";s:1:"?";s:3:"﹗";s:1:"!";s:3:"﹘";s:3:"—";s:3:"﹙";s:1:"(";s:3:"﹚";s:1:")";s:3:"﹛";s:1:"{";s:3:"﹜";s:1:"}";s:3:"﹝";s:3:"〔";s:3:"﹞";s:3:"〕";s:3:"﹟";s:1:"#";s:3:"﹠";s:1:"&";s:3:"﹡";s:1:"*";s:3:"﹢";s:1:"+";s:3:"﹣";s:1:"-";s:3:"﹤";s:1:"<";s:3:"﹥";s:1:">";s:3:"﹦";s:1:"=";s:3:"﹨";s:1:"\\";s:3:"﹩";s:1:"$";s:3:"﹪";s:1:"%";s:3:"﹫";s:1:"@";s:3:"ﹰ";s:3:" ً";s:3:"ﹱ";s:4:"ـً";s:3:"ﹲ";s:3:" ٌ";s:3:"ﹴ";s:3:" ٍ";s:3:"ﹶ";s:3:" َ";s:3:"ﹷ";s:4:"ـَ";s:3:"ﹸ";s:3:" ُ";s:3:"ﹹ";s:4:"ـُ";s:3:"ﹺ";s:3:" ِ";s:3:"ﹻ";s:4:"ـِ";s:3:"ﹼ";s:3:" ّ";s:3:"ﹽ";s:4:"ـّ";s:3:"ﹾ";s:3:" ْ";s:3:"ﹿ";s:4:"ـْ";s:3:"ﺀ";s:2:"ء";s:3:"ﺁ";s:4:"آ";s:3:"ﺂ";s:4:"آ";s:3:"ﺃ";s:4:"أ";s:3:"ﺄ";s:4:"أ";s:3:"ﺅ";s:4:"ؤ";s:3:"ﺆ";s:4:"ؤ";s:3:"ﺇ";s:4:"إ";s:3:"ﺈ";s:4:"إ";s:3:"ﺉ";s:4:"ئ";s:3:"ﺊ";s:4:"ئ";s:3:"ﺋ";s:4:"ئ";s:3:"ﺌ";s:4:"ئ";s:3:"ﺍ";s:2:"ا";s:3:"ﺎ";s:2:"ا";s:3:"ﺏ";s:2:"ب";s:3:"ﺐ";s:2:"ب";s:3:"ﺑ";s:2:"ب";s:3:"ﺒ";s:2:"ب";s:3:"ﺓ";s:2:"ة";s:3:"ﺔ";s:2:"ة";s:3:"ﺕ";s:2:"ت";s:3:"ﺖ";s:2:"ت";s:3:"ﺗ";s:2:"ت";s:3:"ﺘ";s:2:"ت";s:3:"ﺙ";s:2:"ث";s:3:"ﺚ";s:2:"ث";s:3:"ﺛ";s:2:"ث";s:3:"ﺜ";s:2:"ث";s:3:"ﺝ";s:2:"ج";s:3:"ﺞ";s:2:"ج";s:3:"ﺟ";s:2:"ج";s:3:"ﺠ";s:2:"ج";s:3:"ﺡ";s:2:"ح";s:3:"ﺢ";s:2:"ح";s:3:"ﺣ";s:2:"ح";s:3:"ﺤ";s:2:"ح";s:3:"ﺥ";s:2:"خ";s:3:"ﺦ";s:2:"خ";s:3:"ﺧ";s:2:"خ";s:3:"ﺨ";s:2:"خ";s:3:"ﺩ";s:2:"د";s:3:"ﺪ";s:2:"د";s:3:"ﺫ";s:2:"ذ";s:3:"ﺬ";s:2:"ذ";s:3:"ﺭ";s:2:"ر";s:3:"ﺮ";s:2:"ر";s:3:"ﺯ";s:2:"ز";s:3:"ﺰ";s:2:"ز";s:3:"ﺱ";s:2:"س";s:3:"ﺲ";s:2:"س";s:3:"ﺳ";s:2:"س";s:3:"ﺴ";s:2:"س";s:3:"ﺵ";s:2:"ش";s:3:"ﺶ";s:2:"ش";s:3:"ﺷ";s:2:"ش";s:3:"ﺸ";s:2:"ش";s:3:"ﺹ";s:2:"ص";s:3:"ﺺ";s:2:"ص";s:3:"ﺻ";s:2:"ص";s:3:"ﺼ";s:2:"ص";s:3:"ﺽ";s:2:"ض";s:3:"ﺾ";s:2:"ض";s:3:"ﺿ";s:2:"ض";s:3:"ﻀ";s:2:"ض";s:3:"ﻁ";s:2:"ط";s:3:"ﻂ";s:2:"ط";s:3:"ﻃ";s:2:"ط";s:3:"ﻄ";s:2:"ط";s:3:"ﻅ";s:2:"ظ";s:3:"ﻆ";s:2:"ظ";s:3:"ﻇ";s:2:"ظ";s:3:"ﻈ";s:2:"ظ";s:3:"ﻉ";s:2:"ع";s:3:"ﻊ";s:2:"ع";s:3:"ﻋ";s:2:"ع";s:3:"ﻌ";s:2:"ع";s:3:"ﻍ";s:2:"غ";s:3:"ﻎ";s:2:"غ";s:3:"ﻏ";s:2:"غ";s:3:"ﻐ";s:2:"غ";s:3:"ﻑ";s:2:"ف";s:3:"ﻒ";s:2:"ف";s:3:"ﻓ";s:2:"ف";s:3:"ﻔ";s:2:"ف";s:3:"ﻕ";s:2:"ق";s:3:"ﻖ";s:2:"ق";s:3:"ﻗ";s:2:"ق";s:3:"ﻘ";s:2:"ق";s:3:"ﻙ";s:2:"ك";s:3:"ﻚ";s:2:"ك";s:3:"ﻛ";s:2:"ك";s:3:"ﻜ";s:2:"ك";s:3:"ﻝ";s:2:"ل";s:3:"ﻞ";s:2:"ل";s:3:"ﻟ";s:2:"ل";s:3:"ﻠ";s:2:"ل";s:3:"ﻡ";s:2:"م";s:3:"ﻢ";s:2:"م";s:3:"ﻣ";s:2:"م";s:3:"ﻤ";s:2:"م";s:3:"ﻥ";s:2:"ن";s:3:"ﻦ";s:2:"ن";s:3:"ﻧ";s:2:"ن";s:3:"ﻨ";s:2:"ن";s:3:"ﻩ";s:2:"ه";s:3:"ﻪ";s:2:"ه";s:3:"ﻫ";s:2:"ه";s:3:"ﻬ";s:2:"ه";s:3:"ﻭ";s:2:"و";s:3:"ﻮ";s:2:"و";s:3:"ﻯ";s:2:"ى";s:3:"ﻰ";s:2:"ى";s:3:"ﻱ";s:2:"ي";s:3:"ﻲ";s:2:"ي";s:3:"ﻳ";s:2:"ي";s:3:"ﻴ";s:2:"ي";s:3:"ﻵ";s:6:"لآ";s:3:"ﻶ";s:6:"لآ";s:3:"ﻷ";s:6:"لأ";s:3:"ﻸ";s:6:"لأ";s:3:"ﻹ";s:6:"لإ";s:3:"ﻺ";s:6:"لإ";s:3:"ﻻ";s:4:"لا";s:3:"ﻼ";s:4:"لا";s:3:"!";s:1:"!";s:3:""";s:1:""";s:3:"#";s:1:"#";s:3:"$";s:1:"$";s:3:"%";s:1:"%";s:3:"&";s:1:"&";s:3:"'";s:1:"\'";s:3:"(";s:1:"(";s:3:")";s:1:")";s:3:"*";s:1:"*";s:3:"+";s:1:"+";s:3:",";s:1:",";s:3:"-";s:1:"-";s:3:".";s:1:".";s:3:"/";s:1:"/";s:3:"0";s:1:"0";s:3:"1";s:1:"1";s:3:"2";s:1:"2";s:3:"3";s:1:"3";s:3:"4";s:1:"4";s:3:"5";s:1:"5";s:3:"6";s:1:"6";s:3:"7";s:1:"7";s:3:"8";s:1:"8";s:3:"9";s:1:"9";s:3:":";s:1:":";s:3:";";s:1:";";s:3:"<";s:1:"<";s:3:"=";s:1:"=";s:3:">";s:1:">";s:3:"?";s:1:"?";s:3:"@";s:1:"@";s:3:"A";s:1:"A";s:3:"B";s:1:"B";s:3:"C";s:1:"C";s:3:"D";s:1:"D";s:3:"E";s:1:"E";s:3:"F";s:1:"F";s:3:"G";s:1:"G";s:3:"H";s:1:"H";s:3:"I";s:1:"I";s:3:"J";s:1:"J";s:3:"K";s:1:"K";s:3:"L";s:1:"L";s:3:"M";s:1:"M";s:3:"N";s:1:"N";s:3:"O";s:1:"O";s:3:"P";s:1:"P";s:3:"Q";s:1:"Q";s:3:"R";s:1:"R";s:3:"S";s:1:"S";s:3:"T";s:1:"T";s:3:"U";s:1:"U";s:3:"V";s:1:"V";s:3:"W";s:1:"W";s:3:"X";s:1:"X";s:3:"Y";s:1:"Y";s:3:"Z";s:1:"Z";s:3:"[";s:1:"[";s:3:"\";s:1:"\\";s:3:"]";s:1:"]";s:3:"^";s:1:"^";s:3:"_";s:1:"_";s:3:"`";s:1:"`";s:3:"a";s:1:"a";s:3:"b";s:1:"b";s:3:"c";s:1:"c";s:3:"d";s:1:"d";s:3:"e";s:1:"e";s:3:"f";s:1:"f";s:3:"g";s:1:"g";s:3:"h";s:1:"h";s:3:"i";s:1:"i";s:3:"j";s:1:"j";s:3:"k";s:1:"k";s:3:"l";s:1:"l";s:3:"m";s:1:"m";s:3:"n";s:1:"n";s:3:"o";s:1:"o";s:3:"p";s:1:"p";s:3:"q";s:1:"q";s:3:"r";s:1:"r";s:3:"s";s:1:"s";s:3:"t";s:1:"t";s:3:"u";s:1:"u";s:3:"v";s:1:"v";s:3:"w";s:1:"w";s:3:"x";s:1:"x";s:3:"y";s:1:"y";s:3:"z";s:1:"z";s:3:"{";s:1:"{";s:3:"|";s:1:"|";s:3:"}";s:1:"}";s:3:"~";s:1:"~";s:3:"⦅";s:3:"⦅";s:3:"⦆";s:3:"⦆";s:3:"。";s:3:"。";s:3:"「";s:3:"「";s:3:"」";s:3:"」";s:3:"、";s:3:"、";s:3:"・";s:3:"・";s:3:"ヲ";s:3:"ヲ";s:3:"ァ";s:3:"ァ";s:3:"ィ";s:3:"ィ";s:3:"ゥ";s:3:"ゥ";s:3:"ェ";s:3:"ェ";s:3:"ォ";s:3:"ォ";s:3:"ャ";s:3:"ャ";s:3:"ュ";s:3:"ュ";s:3:"ョ";s:3:"ョ";s:3:"ッ";s:3:"ッ";s:3:"ー";s:3:"ー";s:3:"ア";s:3:"ア";s:3:"イ";s:3:"イ";s:3:"ウ";s:3:"ウ";s:3:"エ";s:3:"エ";s:3:"オ";s:3:"オ";s:3:"カ";s:3:"カ";s:3:"キ";s:3:"キ";s:3:"ク";s:3:"ク";s:3:"ケ";s:3:"ケ";s:3:"コ";s:3:"コ";s:3:"サ";s:3:"サ";s:3:"シ";s:3:"シ";s:3:"ス";s:3:"ス";s:3:"セ";s:3:"セ";s:3:"ソ";s:3:"ソ";s:3:"タ";s:3:"タ";s:3:"チ";s:3:"チ";s:3:"ツ";s:3:"ツ";s:3:"テ";s:3:"テ";s:3:"ト";s:3:"ト";s:3:"ナ";s:3:"ナ";s:3:"ニ";s:3:"ニ";s:3:"ヌ";s:3:"ヌ";s:3:"ネ";s:3:"ネ";s:3:"ノ";s:3:"ノ";s:3:"ハ";s:3:"ハ";s:3:"ヒ";s:3:"ヒ";s:3:"フ";s:3:"フ";s:3:"ヘ";s:3:"ヘ";s:3:"ホ";s:3:"ホ";s:3:"マ";s:3:"マ";s:3:"ミ";s:3:"ミ";s:3:"ム";s:3:"ム";s:3:"メ";s:3:"メ";s:3:"モ";s:3:"モ";s:3:"ヤ";s:3:"ヤ";s:3:"ユ";s:3:"ユ";s:3:"ヨ";s:3:"ヨ";s:3:"ラ";s:3:"ラ";s:3:"リ";s:3:"リ";s:3:"ル";s:3:"ル";s:3:"レ";s:3:"レ";s:3:"ロ";s:3:"ロ";s:3:"ワ";s:3:"ワ";s:3:"ン";s:3:"ン";s:3:"゙";s:3:"゙";s:3:"゚";s:3:"゚";s:3:"ᅠ";s:3:"ᅠ";s:3:"ᄀ";s:3:"ᄀ";s:3:"ᄁ";s:3:"ᄁ";s:3:"ᆪ";s:3:"ᆪ";s:3:"ᄂ";s:3:"ᄂ";s:3:"ᆬ";s:3:"ᆬ";s:3:"ᆭ";s:3:"ᆭ";s:3:"ᄃ";s:3:"ᄃ";s:3:"ᄄ";s:3:"ᄄ";s:3:"ᄅ";s:3:"ᄅ";s:3:"ᆰ";s:3:"ᆰ";s:3:"ᆱ";s:3:"ᆱ";s:3:"ᆲ";s:3:"ᆲ";s:3:"ᆳ";s:3:"ᆳ";s:3:"ᆴ";s:3:"ᆴ";s:3:"ᆵ";s:3:"ᆵ";s:3:"ᄚ";s:3:"ᄚ";s:3:"ᄆ";s:3:"ᄆ";s:3:"ᄇ";s:3:"ᄇ";s:3:"ᄈ";s:3:"ᄈ";s:3:"ᄡ";s:3:"ᄡ";s:3:"ᄉ";s:3:"ᄉ";s:3:"ᄊ";s:3:"ᄊ";s:3:"ᄋ";s:3:"ᄋ";s:3:"ᄌ";s:3:"ᄌ";s:3:"ᄍ";s:3:"ᄍ";s:3:"ᄎ";s:3:"ᄎ";s:3:"ᄏ";s:3:"ᄏ";s:3:"ᄐ";s:3:"ᄐ";s:3:"ᄑ";s:3:"ᄑ";s:3:"ᄒ";s:3:"ᄒ";s:3:"ᅡ";s:3:"ᅡ";s:3:"ᅢ";s:3:"ᅢ";s:3:"ᅣ";s:3:"ᅣ";s:3:"ᅤ";s:3:"ᅤ";s:3:"ᅥ";s:3:"ᅥ";s:3:"ᅦ";s:3:"ᅦ";s:3:"ᅧ";s:3:"ᅧ";s:3:"ᅨ";s:3:"ᅨ";s:3:"ᅩ";s:3:"ᅩ";s:3:"ᅪ";s:3:"ᅪ";s:3:"ᅫ";s:3:"ᅫ";s:3:"ᅬ";s:3:"ᅬ";s:3:"ᅭ";s:3:"ᅭ";s:3:"ᅮ";s:3:"ᅮ";s:3:"ᅯ";s:3:"ᅯ";s:3:"ᅰ";s:3:"ᅰ";s:3:"ᅱ";s:3:"ᅱ";s:3:"ᅲ";s:3:"ᅲ";s:3:"ᅳ";s:3:"ᅳ";s:3:"ᅴ";s:3:"ᅴ";s:3:"ᅵ";s:3:"ᅵ";s:3:"¢";s:2:"¢";s:3:"£";s:2:"£";s:3:"¬";s:2:"¬";s:3:" ̄";s:3:" ̄";s:3:"¦";s:2:"¦";s:3:"¥";s:2:"¥";s:3:"₩";s:3:"₩";s:3:"│";s:3:"│";s:3:"←";s:3:"←";s:3:"↑";s:3:"↑";s:3:"→";s:3:"→";s:3:"↓";s:3:"↓";s:3:"■";s:3:"■";s:3:"○";s:3:"○";s:4:"𝅗𝅥";s:8:"𝅗𝅥";s:4:"𝅘𝅥";s:8:"𝅘𝅥";s:4:"𝅘𝅥𝅮";s:12:"𝅘𝅥𝅮";s:4:"𝅘𝅥𝅯";s:12:"𝅘𝅥𝅯";s:4:"𝅘𝅥𝅰";s:12:"𝅘𝅥𝅰";s:4:"𝅘𝅥𝅱";s:12:"𝅘𝅥𝅱";s:4:"𝅘𝅥𝅲";s:12:"𝅘𝅥𝅲";s:4:"𝆹𝅥";s:8:"𝆹𝅥";s:4:"𝆺𝅥";s:8:"𝆺𝅥";s:4:"𝆹𝅥𝅮";s:12:"𝆹𝅥𝅮";s:4:"𝆺𝅥𝅮";s:12:"𝆺𝅥𝅮";s:4:"𝆹𝅥𝅯";s:12:"𝆹𝅥𝅯";s:4:"𝆺𝅥𝅯";s:12:"𝆺𝅥𝅯";s:4:"𝐀";s:1:"A";s:4:"𝐁";s:1:"B";s:4:"𝐂";s:1:"C";s:4:"𝐃";s:1:"D";s:4:"𝐄";s:1:"E";s:4:"𝐅";s:1:"F";s:4:"𝐆";s:1:"G";s:4:"𝐇";s:1:"H";s:4:"𝐈";s:1:"I";s:4:"𝐉";s:1:"J";s:4:"𝐊";s:1:"K";s:4:"𝐋";s:1:"L";s:4:"𝐌";s:1:"M";s:4:"𝐍";s:1:"N";s:4:"𝐎";s:1:"O";s:4:"𝐏";s:1:"P";s:4:"𝐐";s:1:"Q";s:4:"𝐑";s:1:"R";s:4:"𝐒";s:1:"S";s:4:"𝐓";s:1:"T";s:4:"𝐔";s:1:"U";s:4:"𝐕";s:1:"V";s:4:"𝐖";s:1:"W";s:4:"𝐗";s:1:"X";s:4:"𝐘";s:1:"Y";s:4:"𝐙";s:1:"Z";s:4:"𝐚";s:1:"a";s:4:"𝐛";s:1:"b";s:4:"𝐜";s:1:"c";s:4:"𝐝";s:1:"d";s:4:"𝐞";s:1:"e";s:4:"𝐟";s:1:"f";s:4:"𝐠";s:1:"g";s:4:"𝐡";s:1:"h";s:4:"𝐢";s:1:"i";s:4:"𝐣";s:1:"j";s:4:"𝐤";s:1:"k";s:4:"𝐥";s:1:"l";s:4:"𝐦";s:1:"m";s:4:"𝐧";s:1:"n";s:4:"𝐨";s:1:"o";s:4:"𝐩";s:1:"p";s:4:"𝐪";s:1:"q";s:4:"𝐫";s:1:"r";s:4:"𝐬";s:1:"s";s:4:"𝐭";s:1:"t";s:4:"𝐮";s:1:"u";s:4:"𝐯";s:1:"v";s:4:"𝐰";s:1:"w";s:4:"𝐱";s:1:"x";s:4:"𝐲";s:1:"y";s:4:"𝐳";s:1:"z";s:4:"𝐴";s:1:"A";s:4:"𝐵";s:1:"B";s:4:"𝐶";s:1:"C";s:4:"𝐷";s:1:"D";s:4:"𝐸";s:1:"E";s:4:"𝐹";s:1:"F";s:4:"𝐺";s:1:"G";s:4:"𝐻";s:1:"H";s:4:"𝐼";s:1:"I";s:4:"𝐽";s:1:"J";s:4:"𝐾";s:1:"K";s:4:"𝐿";s:1:"L";s:4:"𝑀";s:1:"M";s:4:"𝑁";s:1:"N";s:4:"𝑂";s:1:"O";s:4:"𝑃";s:1:"P";s:4:"𝑄";s:1:"Q";s:4:"𝑅";s:1:"R";s:4:"𝑆";s:1:"S";s:4:"𝑇";s:1:"T";s:4:"𝑈";s:1:"U";s:4:"𝑉";s:1:"V";s:4:"𝑊";s:1:"W";s:4:"𝑋";s:1:"X";s:4:"𝑌";s:1:"Y";s:4:"𝑍";s:1:"Z";s:4:"𝑎";s:1:"a";s:4:"𝑏";s:1:"b";s:4:"𝑐";s:1:"c";s:4:"𝑑";s:1:"d";s:4:"𝑒";s:1:"e";s:4:"𝑓";s:1:"f";s:4:"𝑔";s:1:"g";s:4:"𝑖";s:1:"i";s:4:"𝑗";s:1:"j";s:4:"𝑘";s:1:"k";s:4:"𝑙";s:1:"l";s:4:"𝑚";s:1:"m";s:4:"𝑛";s:1:"n";s:4:"𝑜";s:1:"o";s:4:"𝑝";s:1:"p";s:4:"𝑞";s:1:"q";s:4:"𝑟";s:1:"r";s:4:"𝑠";s:1:"s";s:4:"𝑡";s:1:"t";s:4:"𝑢";s:1:"u";s:4:"𝑣";s:1:"v";s:4:"𝑤";s:1:"w";s:4:"𝑥";s:1:"x";s:4:"𝑦";s:1:"y";s:4:"𝑧";s:1:"z";s:4:"𝑨";s:1:"A";s:4:"𝑩";s:1:"B";s:4:"𝑪";s:1:"C";s:4:"𝑫";s:1:"D";s:4:"𝑬";s:1:"E";s:4:"𝑭";s:1:"F";s:4:"𝑮";s:1:"G";s:4:"𝑯";s:1:"H";s:4:"𝑰";s:1:"I";s:4:"𝑱";s:1:"J";s:4:"𝑲";s:1:"K";s:4:"𝑳";s:1:"L";s:4:"𝑴";s:1:"M";s:4:"𝑵";s:1:"N";s:4:"𝑶";s:1:"O";s:4:"𝑷";s:1:"P";s:4:"𝑸";s:1:"Q";s:4:"𝑹";s:1:"R";s:4:"𝑺";s:1:"S";s:4:"𝑻";s:1:"T";s:4:"𝑼";s:1:"U";s:4:"𝑽";s:1:"V";s:4:"𝑾";s:1:"W";s:4:"𝑿";s:1:"X";s:4:"𝒀";s:1:"Y";s:4:"𝒁";s:1:"Z";s:4:"𝒂";s:1:"a";s:4:"𝒃";s:1:"b";s:4:"𝒄";s:1:"c";s:4:"𝒅";s:1:"d";s:4:"𝒆";s:1:"e";s:4:"𝒇";s:1:"f";s:4:"𝒈";s:1:"g";s:4:"𝒉";s:1:"h";s:4:"𝒊";s:1:"i";s:4:"𝒋";s:1:"j";s:4:"𝒌";s:1:"k";s:4:"𝒍";s:1:"l";s:4:"𝒎";s:1:"m";s:4:"𝒏";s:1:"n";s:4:"𝒐";s:1:"o";s:4:"𝒑";s:1:"p";s:4:"𝒒";s:1:"q";s:4:"𝒓";s:1:"r";s:4:"𝒔";s:1:"s";s:4:"𝒕";s:1:"t";s:4:"𝒖";s:1:"u";s:4:"𝒗";s:1:"v";s:4:"𝒘";s:1:"w";s:4:"𝒙";s:1:"x";s:4:"𝒚";s:1:"y";s:4:"𝒛";s:1:"z";s:4:"𝒜";s:1:"A";s:4:"𝒞";s:1:"C";s:4:"𝒟";s:1:"D";s:4:"𝒢";s:1:"G";s:4:"𝒥";s:1:"J";s:4:"𝒦";s:1:"K";s:4:"𝒩";s:1:"N";s:4:"𝒪";s:1:"O";s:4:"𝒫";s:1:"P";s:4:"𝒬";s:1:"Q";s:4:"𝒮";s:1:"S";s:4:"𝒯";s:1:"T";s:4:"𝒰";s:1:"U";s:4:"𝒱";s:1:"V";s:4:"𝒲";s:1:"W";s:4:"𝒳";s:1:"X";s:4:"𝒴";s:1:"Y";s:4:"𝒵";s:1:"Z";s:4:"𝒶";s:1:"a";s:4:"𝒷";s:1:"b";s:4:"𝒸";s:1:"c";s:4:"𝒹";s:1:"d";s:4:"𝒻";s:1:"f";s:4:"𝒽";s:1:"h";s:4:"𝒾";s:1:"i";s:4:"𝒿";s:1:"j";s:4:"𝓀";s:1:"k";s:4:"𝓁";s:1:"l";s:4:"𝓂";s:1:"m";s:4:"𝓃";s:1:"n";s:4:"𝓅";s:1:"p";s:4:"𝓆";s:1:"q";s:4:"𝓇";s:1:"r";s:4:"𝓈";s:1:"s";s:4:"𝓉";s:1:"t";s:4:"𝓊";s:1:"u";s:4:"𝓋";s:1:"v";s:4:"𝓌";s:1:"w";s:4:"𝓍";s:1:"x";s:4:"𝓎";s:1:"y";s:4:"𝓏";s:1:"z";s:4:"𝓐";s:1:"A";s:4:"𝓑";s:1:"B";s:4:"𝓒";s:1:"C";s:4:"𝓓";s:1:"D";s:4:"𝓔";s:1:"E";s:4:"𝓕";s:1:"F";s:4:"𝓖";s:1:"G";s:4:"𝓗";s:1:"H";s:4:"𝓘";s:1:"I";s:4:"𝓙";s:1:"J";s:4:"𝓚";s:1:"K";s:4:"𝓛";s:1:"L";s:4:"𝓜";s:1:"M";s:4:"𝓝";s:1:"N";s:4:"𝓞";s:1:"O";s:4:"𝓟";s:1:"P";s:4:"𝓠";s:1:"Q";s:4:"𝓡";s:1:"R";s:4:"𝓢";s:1:"S";s:4:"𝓣";s:1:"T";s:4:"𝓤";s:1:"U";s:4:"𝓥";s:1:"V";s:4:"𝓦";s:1:"W";s:4:"𝓧";s:1:"X";s:4:"𝓨";s:1:"Y";s:4:"𝓩";s:1:"Z";s:4:"𝓪";s:1:"a";s:4:"𝓫";s:1:"b";s:4:"𝓬";s:1:"c";s:4:"𝓭";s:1:"d";s:4:"𝓮";s:1:"e";s:4:"𝓯";s:1:"f";s:4:"𝓰";s:1:"g";s:4:"𝓱";s:1:"h";s:4:"𝓲";s:1:"i";s:4:"𝓳";s:1:"j";s:4:"𝓴";s:1:"k";s:4:"𝓵";s:1:"l";s:4:"𝓶";s:1:"m";s:4:"𝓷";s:1:"n";s:4:"𝓸";s:1:"o";s:4:"𝓹";s:1:"p";s:4:"𝓺";s:1:"q";s:4:"𝓻";s:1:"r";s:4:"𝓼";s:1:"s";s:4:"𝓽";s:1:"t";s:4:"𝓾";s:1:"u";s:4:"𝓿";s:1:"v";s:4:"𝔀";s:1:"w";s:4:"𝔁";s:1:"x";s:4:"𝔂";s:1:"y";s:4:"𝔃";s:1:"z";s:4:"𝔄";s:1:"A";s:4:"𝔅";s:1:"B";s:4:"𝔇";s:1:"D";s:4:"𝔈";s:1:"E";s:4:"𝔉";s:1:"F";s:4:"𝔊";s:1:"G";s:4:"𝔍";s:1:"J";s:4:"𝔎";s:1:"K";s:4:"𝔏";s:1:"L";s:4:"𝔐";s:1:"M";s:4:"𝔑";s:1:"N";s:4:"𝔒";s:1:"O";s:4:"𝔓";s:1:"P";s:4:"𝔔";s:1:"Q";s:4:"𝔖";s:1:"S";s:4:"𝔗";s:1:"T";s:4:"𝔘";s:1:"U";s:4:"𝔙";s:1:"V";s:4:"𝔚";s:1:"W";s:4:"𝔛";s:1:"X";s:4:"𝔜";s:1:"Y";s:4:"𝔞";s:1:"a";s:4:"𝔟";s:1:"b";s:4:"𝔠";s:1:"c";s:4:"𝔡";s:1:"d";s:4:"𝔢";s:1:"e";s:4:"𝔣";s:1:"f";s:4:"𝔤";s:1:"g";s:4:"𝔥";s:1:"h";s:4:"𝔦";s:1:"i";s:4:"𝔧";s:1:"j";s:4:"𝔨";s:1:"k";s:4:"𝔩";s:1:"l";s:4:"𝔪";s:1:"m";s:4:"𝔫";s:1:"n";s:4:"𝔬";s:1:"o";s:4:"𝔭";s:1:"p";s:4:"𝔮";s:1:"q";s:4:"𝔯";s:1:"r";s:4:"𝔰";s:1:"s";s:4:"𝔱";s:1:"t";s:4:"𝔲";s:1:"u";s:4:"𝔳";s:1:"v";s:4:"𝔴";s:1:"w";s:4:"𝔵";s:1:"x";s:4:"𝔶";s:1:"y";s:4:"𝔷";s:1:"z";s:4:"𝔸";s:1:"A";s:4:"𝔹";s:1:"B";s:4:"𝔻";s:1:"D";s:4:"𝔼";s:1:"E";s:4:"𝔽";s:1:"F";s:4:"𝔾";s:1:"G";s:4:"𝕀";s:1:"I";s:4:"𝕁";s:1:"J";s:4:"𝕂";s:1:"K";s:4:"𝕃";s:1:"L";s:4:"𝕄";s:1:"M";s:4:"𝕆";s:1:"O";s:4:"𝕊";s:1:"S";s:4:"𝕋";s:1:"T";s:4:"𝕌";s:1:"U";s:4:"𝕍";s:1:"V";s:4:"𝕎";s:1:"W";s:4:"𝕏";s:1:"X";s:4:"𝕐";s:1:"Y";s:4:"𝕒";s:1:"a";s:4:"𝕓";s:1:"b";s:4:"𝕔";s:1:"c";s:4:"𝕕";s:1:"d";s:4:"𝕖";s:1:"e";s:4:"𝕗";s:1:"f";s:4:"𝕘";s:1:"g";s:4:"𝕙";s:1:"h";s:4:"𝕚";s:1:"i";s:4:"𝕛";s:1:"j";s:4:"𝕜";s:1:"k";s:4:"𝕝";s:1:"l";s:4:"𝕞";s:1:"m";s:4:"𝕟";s:1:"n";s:4:"𝕠";s:1:"o";s:4:"𝕡";s:1:"p";s:4:"𝕢";s:1:"q";s:4:"𝕣";s:1:"r";s:4:"𝕤";s:1:"s";s:4:"𝕥";s:1:"t";s:4:"𝕦";s:1:"u";s:4:"𝕧";s:1:"v";s:4:"𝕨";s:1:"w";s:4:"𝕩";s:1:"x";s:4:"𝕪";s:1:"y";s:4:"𝕫";s:1:"z";s:4:"𝕬";s:1:"A";s:4:"𝕭";s:1:"B";s:4:"𝕮";s:1:"C";s:4:"𝕯";s:1:"D";s:4:"𝕰";s:1:"E";s:4:"𝕱";s:1:"F";s:4:"𝕲";s:1:"G";s:4:"𝕳";s:1:"H";s:4:"𝕴";s:1:"I";s:4:"𝕵";s:1:"J";s:4:"𝕶";s:1:"K";s:4:"𝕷";s:1:"L";s:4:"𝕸";s:1:"M";s:4:"𝕹";s:1:"N";s:4:"𝕺";s:1:"O";s:4:"𝕻";s:1:"P";s:4:"𝕼";s:1:"Q";s:4:"𝕽";s:1:"R";s:4:"𝕾";s:1:"S";s:4:"𝕿";s:1:"T";s:4:"𝖀";s:1:"U";s:4:"𝖁";s:1:"V";s:4:"𝖂";s:1:"W";s:4:"𝖃";s:1:"X";s:4:"𝖄";s:1:"Y";s:4:"𝖅";s:1:"Z";s:4:"𝖆";s:1:"a";s:4:"𝖇";s:1:"b";s:4:"𝖈";s:1:"c";s:4:"𝖉";s:1:"d";s:4:"𝖊";s:1:"e";s:4:"𝖋";s:1:"f";s:4:"𝖌";s:1:"g";s:4:"𝖍";s:1:"h";s:4:"𝖎";s:1:"i";s:4:"𝖏";s:1:"j";s:4:"𝖐";s:1:"k";s:4:"𝖑";s:1:"l";s:4:"𝖒";s:1:"m";s:4:"𝖓";s:1:"n";s:4:"𝖔";s:1:"o";s:4:"𝖕";s:1:"p";s:4:"𝖖";s:1:"q";s:4:"𝖗";s:1:"r";s:4:"𝖘";s:1:"s";s:4:"𝖙";s:1:"t";s:4:"𝖚";s:1:"u";s:4:"𝖛";s:1:"v";s:4:"𝖜";s:1:"w";s:4:"𝖝";s:1:"x";s:4:"𝖞";s:1:"y";s:4:"𝖟";s:1:"z";s:4:"𝖠";s:1:"A";s:4:"𝖡";s:1:"B";s:4:"𝖢";s:1:"C";s:4:"𝖣";s:1:"D";s:4:"𝖤";s:1:"E";s:4:"𝖥";s:1:"F";s:4:"𝖦";s:1:"G";s:4:"𝖧";s:1:"H";s:4:"𝖨";s:1:"I";s:4:"𝖩";s:1:"J";s:4:"𝖪";s:1:"K";s:4:"𝖫";s:1:"L";s:4:"𝖬";s:1:"M";s:4:"𝖭";s:1:"N";s:4:"𝖮";s:1:"O";s:4:"𝖯";s:1:"P";s:4:"𝖰";s:1:"Q";s:4:"𝖱";s:1:"R";s:4:"𝖲";s:1:"S";s:4:"𝖳";s:1:"T";s:4:"𝖴";s:1:"U";s:4:"𝖵";s:1:"V";s:4:"𝖶";s:1:"W";s:4:"𝖷";s:1:"X";s:4:"𝖸";s:1:"Y";s:4:"𝖹";s:1:"Z";s:4:"𝖺";s:1:"a";s:4:"𝖻";s:1:"b";s:4:"𝖼";s:1:"c";s:4:"𝖽";s:1:"d";s:4:"𝖾";s:1:"e";s:4:"𝖿";s:1:"f";s:4:"𝗀";s:1:"g";s:4:"𝗁";s:1:"h";s:4:"𝗂";s:1:"i";s:4:"𝗃";s:1:"j";s:4:"𝗄";s:1:"k";s:4:"𝗅";s:1:"l";s:4:"𝗆";s:1:"m";s:4:"𝗇";s:1:"n";s:4:"𝗈";s:1:"o";s:4:"𝗉";s:1:"p";s:4:"𝗊";s:1:"q";s:4:"𝗋";s:1:"r";s:4:"𝗌";s:1:"s";s:4:"𝗍";s:1:"t";s:4:"𝗎";s:1:"u";s:4:"𝗏";s:1:"v";s:4:"𝗐";s:1:"w";s:4:"𝗑";s:1:"x";s:4:"𝗒";s:1:"y";s:4:"𝗓";s:1:"z";s:4:"𝗔";s:1:"A";s:4:"𝗕";s:1:"B";s:4:"𝗖";s:1:"C";s:4:"𝗗";s:1:"D";s:4:"𝗘";s:1:"E";s:4:"𝗙";s:1:"F";s:4:"𝗚";s:1:"G";s:4:"𝗛";s:1:"H";s:4:"𝗜";s:1:"I";s:4:"𝗝";s:1:"J";s:4:"𝗞";s:1:"K";s:4:"𝗟";s:1:"L";s:4:"𝗠";s:1:"M";s:4:"𝗡";s:1:"N";s:4:"𝗢";s:1:"O";s:4:"𝗣";s:1:"P";s:4:"𝗤";s:1:"Q";s:4:"𝗥";s:1:"R";s:4:"𝗦";s:1:"S";s:4:"𝗧";s:1:"T";s:4:"𝗨";s:1:"U";s:4:"𝗩";s:1:"V";s:4:"𝗪";s:1:"W";s:4:"𝗫";s:1:"X";s:4:"𝗬";s:1:"Y";s:4:"𝗭";s:1:"Z";s:4:"𝗮";s:1:"a";s:4:"𝗯";s:1:"b";s:4:"𝗰";s:1:"c";s:4:"𝗱";s:1:"d";s:4:"𝗲";s:1:"e";s:4:"𝗳";s:1:"f";s:4:"𝗴";s:1:"g";s:4:"𝗵";s:1:"h";s:4:"𝗶";s:1:"i";s:4:"𝗷";s:1:"j";s:4:"𝗸";s:1:"k";s:4:"𝗹";s:1:"l";s:4:"𝗺";s:1:"m";s:4:"𝗻";s:1:"n";s:4:"𝗼";s:1:"o";s:4:"𝗽";s:1:"p";s:4:"𝗾";s:1:"q";s:4:"𝗿";s:1:"r";s:4:"𝘀";s:1:"s";s:4:"𝘁";s:1:"t";s:4:"𝘂";s:1:"u";s:4:"𝘃";s:1:"v";s:4:"𝘄";s:1:"w";s:4:"𝘅";s:1:"x";s:4:"𝘆";s:1:"y";s:4:"𝘇";s:1:"z";s:4:"𝘈";s:1:"A";s:4:"𝘉";s:1:"B";s:4:"𝘊";s:1:"C";s:4:"𝘋";s:1:"D";s:4:"𝘌";s:1:"E";s:4:"𝘍";s:1:"F";s:4:"𝘎";s:1:"G";s:4:"𝘏";s:1:"H";s:4:"𝘐";s:1:"I";s:4:"𝘑";s:1:"J";s:4:"𝘒";s:1:"K";s:4:"𝘓";s:1:"L";s:4:"𝘔";s:1:"M";s:4:"𝘕";s:1:"N";s:4:"𝘖";s:1:"O";s:4:"𝘗";s:1:"P";s:4:"𝘘";s:1:"Q";s:4:"𝘙";s:1:"R";s:4:"𝘚";s:1:"S";s:4:"𝘛";s:1:"T";s:4:"𝘜";s:1:"U";s:4:"𝘝";s:1:"V";s:4:"𝘞";s:1:"W";s:4:"𝘟";s:1:"X";s:4:"𝘠";s:1:"Y";s:4:"𝘡";s:1:"Z";s:4:"𝘢";s:1:"a";s:4:"𝘣";s:1:"b";s:4:"𝘤";s:1:"c";s:4:"𝘥";s:1:"d";s:4:"𝘦";s:1:"e";s:4:"𝘧";s:1:"f";s:4:"𝘨";s:1:"g";s:4:"𝘩";s:1:"h";s:4:"𝘪";s:1:"i";s:4:"𝘫";s:1:"j";s:4:"𝘬";s:1:"k";s:4:"𝘭";s:1:"l";s:4:"𝘮";s:1:"m";s:4:"𝘯";s:1:"n";s:4:"𝘰";s:1:"o";s:4:"𝘱";s:1:"p";s:4:"𝘲";s:1:"q";s:4:"𝘳";s:1:"r";s:4:"𝘴";s:1:"s";s:4:"𝘵";s:1:"t";s:4:"𝘶";s:1:"u";s:4:"𝘷";s:1:"v";s:4:"𝘸";s:1:"w";s:4:"𝘹";s:1:"x";s:4:"𝘺";s:1:"y";s:4:"𝘻";s:1:"z";s:4:"𝘼";s:1:"A";s:4:"𝘽";s:1:"B";s:4:"𝘾";s:1:"C";s:4:"𝘿";s:1:"D";s:4:"𝙀";s:1:"E";s:4:"𝙁";s:1:"F";s:4:"𝙂";s:1:"G";s:4:"𝙃";s:1:"H";s:4:"𝙄";s:1:"I";s:4:"𝙅";s:1:"J";s:4:"𝙆";s:1:"K";s:4:"𝙇";s:1:"L";s:4:"𝙈";s:1:"M";s:4:"𝙉";s:1:"N";s:4:"𝙊";s:1:"O";s:4:"𝙋";s:1:"P";s:4:"𝙌";s:1:"Q";s:4:"𝙍";s:1:"R";s:4:"𝙎";s:1:"S";s:4:"𝙏";s:1:"T";s:4:"𝙐";s:1:"U";s:4:"𝙑";s:1:"V";s:4:"𝙒";s:1:"W";s:4:"𝙓";s:1:"X";s:4:"𝙔";s:1:"Y";s:4:"𝙕";s:1:"Z";s:4:"𝙖";s:1:"a";s:4:"𝙗";s:1:"b";s:4:"𝙘";s:1:"c";s:4:"𝙙";s:1:"d";s:4:"𝙚";s:1:"e";s:4:"𝙛";s:1:"f";s:4:"𝙜";s:1:"g";s:4:"𝙝";s:1:"h";s:4:"𝙞";s:1:"i";s:4:"𝙟";s:1:"j";s:4:"𝙠";s:1:"k";s:4:"𝙡";s:1:"l";s:4:"𝙢";s:1:"m";s:4:"𝙣";s:1:"n";s:4:"𝙤";s:1:"o";s:4:"𝙥";s:1:"p";s:4:"𝙦";s:1:"q";s:4:"𝙧";s:1:"r";s:4:"𝙨";s:1:"s";s:4:"𝙩";s:1:"t";s:4:"𝙪";s:1:"u";s:4:"𝙫";s:1:"v";s:4:"𝙬";s:1:"w";s:4:"𝙭";s:1:"x";s:4:"𝙮";s:1:"y";s:4:"𝙯";s:1:"z";s:4:"𝙰";s:1:"A";s:4:"𝙱";s:1:"B";s:4:"𝙲";s:1:"C";s:4:"𝙳";s:1:"D";s:4:"𝙴";s:1:"E";s:4:"𝙵";s:1:"F";s:4:"𝙶";s:1:"G";s:4:"𝙷";s:1:"H";s:4:"𝙸";s:1:"I";s:4:"𝙹";s:1:"J";s:4:"𝙺";s:1:"K";s:4:"𝙻";s:1:"L";s:4:"𝙼";s:1:"M";s:4:"𝙽";s:1:"N";s:4:"𝙾";s:1:"O";s:4:"𝙿";s:1:"P";s:4:"𝚀";s:1:"Q";s:4:"𝚁";s:1:"R";s:4:"𝚂";s:1:"S";s:4:"𝚃";s:1:"T";s:4:"𝚄";s:1:"U";s:4:"𝚅";s:1:"V";s:4:"𝚆";s:1:"W";s:4:"𝚇";s:1:"X";s:4:"𝚈";s:1:"Y";s:4:"𝚉";s:1:"Z";s:4:"𝚊";s:1:"a";s:4:"𝚋";s:1:"b";s:4:"𝚌";s:1:"c";s:4:"𝚍";s:1:"d";s:4:"𝚎";s:1:"e";s:4:"𝚏";s:1:"f";s:4:"𝚐";s:1:"g";s:4:"𝚑";s:1:"h";s:4:"𝚒";s:1:"i";s:4:"𝚓";s:1:"j";s:4:"𝚔";s:1:"k";s:4:"𝚕";s:1:"l";s:4:"𝚖";s:1:"m";s:4:"𝚗";s:1:"n";s:4:"𝚘";s:1:"o";s:4:"𝚙";s:1:"p";s:4:"𝚚";s:1:"q";s:4:"𝚛";s:1:"r";s:4:"𝚜";s:1:"s";s:4:"𝚝";s:1:"t";s:4:"𝚞";s:1:"u";s:4:"𝚟";s:1:"v";s:4:"𝚠";s:1:"w";s:4:"𝚡";s:1:"x";s:4:"𝚢";s:1:"y";s:4:"𝚣";s:1:"z";s:4:"𝚤";s:2:"ı";s:4:"𝚥";s:2:"ȷ";s:4:"𝚨";s:2:"Α";s:4:"𝚩";s:2:"Β";s:4:"𝚪";s:2:"Γ";s:4:"𝚫";s:2:"Δ";s:4:"𝚬";s:2:"Ε";s:4:"𝚭";s:2:"Ζ";s:4:"𝚮";s:2:"Η";s:4:"𝚯";s:2:"Θ";s:4:"𝚰";s:2:"Ι";s:4:"𝚱";s:2:"Κ";s:4:"𝚲";s:2:"Λ";s:4:"𝚳";s:2:"Μ";s:4:"𝚴";s:2:"Ν";s:4:"𝚵";s:2:"Ξ";s:4:"𝚶";s:2:"Ο";s:4:"𝚷";s:2:"Π";s:4:"𝚸";s:2:"Ρ";s:4:"𝚹";s:2:"Θ";s:4:"𝚺";s:2:"Σ";s:4:"𝚻";s:2:"Τ";s:4:"𝚼";s:2:"Υ";s:4:"𝚽";s:2:"Φ";s:4:"𝚾";s:2:"Χ";s:4:"𝚿";s:2:"Ψ";s:4:"𝛀";s:2:"Ω";s:4:"𝛁";s:3:"∇";s:4:"𝛂";s:2:"α";s:4:"𝛃";s:2:"β";s:4:"𝛄";s:2:"γ";s:4:"𝛅";s:2:"δ";s:4:"𝛆";s:2:"ε";s:4:"𝛇";s:2:"ζ";s:4:"𝛈";s:2:"η";s:4:"𝛉";s:2:"θ";s:4:"𝛊";s:2:"ι";s:4:"𝛋";s:2:"κ";s:4:"𝛌";s:2:"λ";s:4:"𝛍";s:2:"μ";s:4:"𝛎";s:2:"ν";s:4:"𝛏";s:2:"ξ";s:4:"𝛐";s:2:"ο";s:4:"𝛑";s:2:"π";s:4:"𝛒";s:2:"ρ";s:4:"𝛓";s:2:"ς";s:4:"𝛔";s:2:"σ";s:4:"𝛕";s:2:"τ";s:4:"𝛖";s:2:"υ";s:4:"𝛗";s:2:"φ";s:4:"𝛘";s:2:"χ";s:4:"𝛙";s:2:"ψ";s:4:"𝛚";s:2:"ω";s:4:"𝛛";s:3:"∂";s:4:"𝛜";s:2:"ε";s:4:"𝛝";s:2:"θ";s:4:"𝛞";s:2:"κ";s:4:"𝛟";s:2:"φ";s:4:"𝛠";s:2:"ρ";s:4:"𝛡";s:2:"π";s:4:"𝛢";s:2:"Α";s:4:"𝛣";s:2:"Β";s:4:"𝛤";s:2:"Γ";s:4:"𝛥";s:2:"Δ";s:4:"𝛦";s:2:"Ε";s:4:"𝛧";s:2:"Ζ";s:4:"𝛨";s:2:"Η";s:4:"𝛩";s:2:"Θ";s:4:"𝛪";s:2:"Ι";s:4:"𝛫";s:2:"Κ";s:4:"𝛬";s:2:"Λ";s:4:"𝛭";s:2:"Μ";s:4:"𝛮";s:2:"Ν";s:4:"𝛯";s:2:"Ξ";s:4:"𝛰";s:2:"Ο";s:4:"𝛱";s:2:"Π";s:4:"𝛲";s:2:"Ρ";s:4:"𝛳";s:2:"Θ";s:4:"𝛴";s:2:"Σ";s:4:"𝛵";s:2:"Τ";s:4:"𝛶";s:2:"Υ";s:4:"𝛷";s:2:"Φ";s:4:"𝛸";s:2:"Χ";s:4:"𝛹";s:2:"Ψ";s:4:"𝛺";s:2:"Ω";s:4:"𝛻";s:3:"∇";s:4:"𝛼";s:2:"α";s:4:"𝛽";s:2:"β";s:4:"𝛾";s:2:"γ";s:4:"𝛿";s:2:"δ";s:4:"𝜀";s:2:"ε";s:4:"𝜁";s:2:"ζ";s:4:"𝜂";s:2:"η";s:4:"𝜃";s:2:"θ";s:4:"𝜄";s:2:"ι";s:4:"𝜅";s:2:"κ";s:4:"𝜆";s:2:"λ";s:4:"𝜇";s:2:"μ";s:4:"𝜈";s:2:"ν";s:4:"𝜉";s:2:"ξ";s:4:"𝜊";s:2:"ο";s:4:"𝜋";s:2:"π";s:4:"𝜌";s:2:"ρ";s:4:"𝜍";s:2:"ς";s:4:"𝜎";s:2:"σ";s:4:"𝜏";s:2:"τ";s:4:"𝜐";s:2:"υ";s:4:"𝜑";s:2:"φ";s:4:"𝜒";s:2:"χ";s:4:"𝜓";s:2:"ψ";s:4:"𝜔";s:2:"ω";s:4:"𝜕";s:3:"∂";s:4:"𝜖";s:2:"ε";s:4:"𝜗";s:2:"θ";s:4:"𝜘";s:2:"κ";s:4:"𝜙";s:2:"φ";s:4:"𝜚";s:2:"ρ";s:4:"𝜛";s:2:"π";s:4:"𝜜";s:2:"Α";s:4:"𝜝";s:2:"Β";s:4:"𝜞";s:2:"Γ";s:4:"𝜟";s:2:"Δ";s:4:"𝜠";s:2:"Ε";s:4:"𝜡";s:2:"Ζ";s:4:"𝜢";s:2:"Η";s:4:"𝜣";s:2:"Θ";s:4:"𝜤";s:2:"Ι";s:4:"𝜥";s:2:"Κ";s:4:"𝜦";s:2:"Λ";s:4:"𝜧";s:2:"Μ";s:4:"𝜨";s:2:"Ν";s:4:"𝜩";s:2:"Ξ";s:4:"𝜪";s:2:"Ο";s:4:"𝜫";s:2:"Π";s:4:"𝜬";s:2:"Ρ";s:4:"𝜭";s:2:"Θ";s:4:"𝜮";s:2:"Σ";s:4:"𝜯";s:2:"Τ";s:4:"𝜰";s:2:"Υ";s:4:"𝜱";s:2:"Φ";s:4:"𝜲";s:2:"Χ";s:4:"𝜳";s:2:"Ψ";s:4:"𝜴";s:2:"Ω";s:4:"𝜵";s:3:"∇";s:4:"𝜶";s:2:"α";s:4:"𝜷";s:2:"β";s:4:"𝜸";s:2:"γ";s:4:"𝜹";s:2:"δ";s:4:"𝜺";s:2:"ε";s:4:"𝜻";s:2:"ζ";s:4:"𝜼";s:2:"η";s:4:"𝜽";s:2:"θ";s:4:"𝜾";s:2:"ι";s:4:"𝜿";s:2:"κ";s:4:"𝝀";s:2:"λ";s:4:"𝝁";s:2:"μ";s:4:"𝝂";s:2:"ν";s:4:"𝝃";s:2:"ξ";s:4:"𝝄";s:2:"ο";s:4:"𝝅";s:2:"π";s:4:"𝝆";s:2:"ρ";s:4:"𝝇";s:2:"ς";s:4:"𝝈";s:2:"σ";s:4:"𝝉";s:2:"τ";s:4:"𝝊";s:2:"υ";s:4:"𝝋";s:2:"φ";s:4:"𝝌";s:2:"χ";s:4:"𝝍";s:2:"ψ";s:4:"𝝎";s:2:"ω";s:4:"𝝏";s:3:"∂";s:4:"𝝐";s:2:"ε";s:4:"𝝑";s:2:"θ";s:4:"𝝒";s:2:"κ";s:4:"𝝓";s:2:"φ";s:4:"𝝔";s:2:"ρ";s:4:"𝝕";s:2:"π";s:4:"𝝖";s:2:"Α";s:4:"𝝗";s:2:"Β";s:4:"𝝘";s:2:"Γ";s:4:"𝝙";s:2:"Δ";s:4:"𝝚";s:2:"Ε";s:4:"𝝛";s:2:"Ζ";s:4:"𝝜";s:2:"Η";s:4:"𝝝";s:2:"Θ";s:4:"𝝞";s:2:"Ι";s:4:"𝝟";s:2:"Κ";s:4:"𝝠";s:2:"Λ";s:4:"𝝡";s:2:"Μ";s:4:"𝝢";s:2:"Ν";s:4:"𝝣";s:2:"Ξ";s:4:"𝝤";s:2:"Ο";s:4:"𝝥";s:2:"Π";s:4:"𝝦";s:2:"Ρ";s:4:"𝝧";s:2:"Θ";s:4:"𝝨";s:2:"Σ";s:4:"𝝩";s:2:"Τ";s:4:"𝝪";s:2:"Υ";s:4:"𝝫";s:2:"Φ";s:4:"𝝬";s:2:"Χ";s:4:"𝝭";s:2:"Ψ";s:4:"𝝮";s:2:"Ω";s:4:"𝝯";s:3:"∇";s:4:"𝝰";s:2:"α";s:4:"𝝱";s:2:"β";s:4:"𝝲";s:2:"γ";s:4:"𝝳";s:2:"δ";s:4:"𝝴";s:2:"ε";s:4:"𝝵";s:2:"ζ";s:4:"𝝶";s:2:"η";s:4:"𝝷";s:2:"θ";s:4:"𝝸";s:2:"ι";s:4:"𝝹";s:2:"κ";s:4:"𝝺";s:2:"λ";s:4:"𝝻";s:2:"μ";s:4:"𝝼";s:2:"ν";s:4:"𝝽";s:2:"ξ";s:4:"𝝾";s:2:"ο";s:4:"𝝿";s:2:"π";s:4:"𝞀";s:2:"ρ";s:4:"𝞁";s:2:"ς";s:4:"𝞂";s:2:"σ";s:4:"𝞃";s:2:"τ";s:4:"𝞄";s:2:"υ";s:4:"𝞅";s:2:"φ";s:4:"𝞆";s:2:"χ";s:4:"𝞇";s:2:"ψ";s:4:"𝞈";s:2:"ω";s:4:"𝞉";s:3:"∂";s:4:"𝞊";s:2:"ε";s:4:"𝞋";s:2:"θ";s:4:"𝞌";s:2:"κ";s:4:"𝞍";s:2:"φ";s:4:"𝞎";s:2:"ρ";s:4:"𝞏";s:2:"π";s:4:"𝞐";s:2:"Α";s:4:"𝞑";s:2:"Β";s:4:"𝞒";s:2:"Γ";s:4:"𝞓";s:2:"Δ";s:4:"𝞔";s:2:"Ε";s:4:"𝞕";s:2:"Ζ";s:4:"𝞖";s:2:"Η";s:4:"𝞗";s:2:"Θ";s:4:"𝞘";s:2:"Ι";s:4:"𝞙";s:2:"Κ";s:4:"𝞚";s:2:"Λ";s:4:"𝞛";s:2:"Μ";s:4:"𝞜";s:2:"Ν";s:4:"𝞝";s:2:"Ξ";s:4:"𝞞";s:2:"Ο";s:4:"𝞟";s:2:"Π";s:4:"𝞠";s:2:"Ρ";s:4:"𝞡";s:2:"Θ";s:4:"𝞢";s:2:"Σ";s:4:"𝞣";s:2:"Τ";s:4:"𝞤";s:2:"Υ";s:4:"𝞥";s:2:"Φ";s:4:"𝞦";s:2:"Χ";s:4:"𝞧";s:2:"Ψ";s:4:"𝞨";s:2:"Ω";s:4:"𝞩";s:3:"∇";s:4:"𝞪";s:2:"α";s:4:"𝞫";s:2:"β";s:4:"𝞬";s:2:"γ";s:4:"𝞭";s:2:"δ";s:4:"𝞮";s:2:"ε";s:4:"𝞯";s:2:"ζ";s:4:"𝞰";s:2:"η";s:4:"𝞱";s:2:"θ";s:4:"𝞲";s:2:"ι";s:4:"𝞳";s:2:"κ";s:4:"𝞴";s:2:"λ";s:4:"𝞵";s:2:"μ";s:4:"𝞶";s:2:"ν";s:4:"𝞷";s:2:"ξ";s:4:"𝞸";s:2:"ο";s:4:"𝞹";s:2:"π";s:4:"𝞺";s:2:"ρ";s:4:"𝞻";s:2:"ς";s:4:"𝞼";s:2:"σ";s:4:"𝞽";s:2:"τ";s:4:"𝞾";s:2:"υ";s:4:"𝞿";s:2:"φ";s:4:"𝟀";s:2:"χ";s:4:"𝟁";s:2:"ψ";s:4:"𝟂";s:2:"ω";s:4:"𝟃";s:3:"∂";s:4:"𝟄";s:2:"ε";s:4:"𝟅";s:2:"θ";s:4:"𝟆";s:2:"κ";s:4:"𝟇";s:2:"φ";s:4:"𝟈";s:2:"ρ";s:4:"𝟉";s:2:"π";s:4:"𝟎";s:1:"0";s:4:"𝟏";s:1:"1";s:4:"𝟐";s:1:"2";s:4:"𝟑";s:1:"3";s:4:"𝟒";s:1:"4";s:4:"𝟓";s:1:"5";s:4:"𝟔";s:1:"6";s:4:"𝟕";s:1:"7";s:4:"𝟖";s:1:"8";s:4:"𝟗";s:1:"9";s:4:"𝟘";s:1:"0";s:4:"𝟙";s:1:"1";s:4:"𝟚";s:1:"2";s:4:"𝟛";s:1:"3";s:4:"𝟜";s:1:"4";s:4:"𝟝";s:1:"5";s:4:"𝟞";s:1:"6";s:4:"𝟟";s:1:"7";s:4:"𝟠";s:1:"8";s:4:"𝟡";s:1:"9";s:4:"𝟢";s:1:"0";s:4:"𝟣";s:1:"1";s:4:"𝟤";s:1:"2";s:4:"𝟥";s:1:"3";s:4:"𝟦";s:1:"4";s:4:"𝟧";s:1:"5";s:4:"𝟨";s:1:"6";s:4:"𝟩";s:1:"7";s:4:"𝟪";s:1:"8";s:4:"𝟫";s:1:"9";s:4:"𝟬";s:1:"0";s:4:"𝟭";s:1:"1";s:4:"𝟮";s:1:"2";s:4:"𝟯";s:1:"3";s:4:"𝟰";s:1:"4";s:4:"𝟱";s:1:"5";s:4:"𝟲";s:1:"6";s:4:"𝟳";s:1:"7";s:4:"𝟴";s:1:"8";s:4:"𝟵";s:1:"9";s:4:"𝟶";s:1:"0";s:4:"𝟷";s:1:"1";s:4:"𝟸";s:1:"2";s:4:"𝟹";s:1:"3";s:4:"𝟺";s:1:"4";s:4:"𝟻";s:1:"5";s:4:"𝟼";s:1:"6";s:4:"𝟽";s:1:"7";s:4:"𝟾";s:1:"8";s:4:"𝟿";s:1:"9";s:4:"丽";s:3:"丽";s:4:"丸";s:3:"丸";s:4:"乁";s:3:"乁";s:4:"𠄢";s:4:"𠄢";s:4:"你";s:3:"你";s:4:"侮";s:3:"侮";s:4:"侻";s:3:"侻";s:4:"倂";s:3:"倂";s:4:"偺";s:3:"偺";s:4:"備";s:3:"備";s:4:"僧";s:3:"僧";s:4:"像";s:3:"像";s:4:"㒞";s:3:"㒞";s:4:"𠘺";s:4:"𠘺";s:4:"免";s:3:"免";s:4:"兔";s:3:"兔";s:4:"兤";s:3:"兤";s:4:"具";s:3:"具";s:4:"𠔜";s:4:"𠔜";s:4:"㒹";s:3:"㒹";s:4:"內";s:3:"內";s:4:"再";s:3:"再";s:4:"𠕋";s:4:"𠕋";s:4:"冗";s:3:"冗";s:4:"冤";s:3:"冤";s:4:"仌";s:3:"仌";s:4:"冬";s:3:"冬";s:4:"况";s:3:"况";s:4:"𩇟";s:4:"𩇟";s:4:"凵";s:3:"凵";s:4:"刃";s:3:"刃";s:4:"㓟";s:3:"㓟";s:4:"刻";s:3:"刻";s:4:"剆";s:3:"剆";s:4:"割";s:3:"割";s:4:"剷";s:3:"剷";s:4:"㔕";s:3:"㔕";s:4:"勇";s:3:"勇";s:4:"勉";s:3:"勉";s:4:"勤";s:3:"勤";s:4:"勺";s:3:"勺";s:4:"包";s:3:"包";s:4:"匆";s:3:"匆";s:4:"北";s:3:"北";s:4:"卉";s:3:"卉";s:4:"卑";s:3:"卑";s:4:"博";s:3:"博";s:4:"即";s:3:"即";s:4:"卽";s:3:"卽";s:4:"卿";s:3:"卿";s:4:"卿";s:3:"卿";s:4:"卿";s:3:"卿";s:4:"𠨬";s:4:"𠨬";s:4:"灰";s:3:"灰";s:4:"及";s:3:"及";s:4:"叟";s:3:"叟";s:4:"𠭣";s:4:"𠭣";s:4:"叫";s:3:"叫";s:4:"叱";s:3:"叱";s:4:"吆";s:3:"吆";s:4:"咞";s:3:"咞";s:4:"吸";s:3:"吸";s:4:"呈";s:3:"呈";s:4:"周";s:3:"周";s:4:"咢";s:3:"咢";s:4:"哶";s:3:"哶";s:4:"唐";s:3:"唐";s:4:"啓";s:3:"啓";s:4:"啣";s:3:"啣";s:4:"善";s:3:"善";s:4:"善";s:3:"善";s:4:"喙";s:3:"喙";s:4:"喫";s:3:"喫";s:4:"喳";s:3:"喳";s:4:"嗂";s:3:"嗂";s:4:"圖";s:3:"圖";s:4:"嘆";s:3:"嘆";s:4:"圗";s:3:"圗";s:4:"噑";s:3:"噑";s:4:"噴";s:3:"噴";s:4:"切";s:3:"切";s:4:"壮";s:3:"壮";s:4:"城";s:3:"城";s:4:"埴";s:3:"埴";s:4:"堍";s:3:"堍";s:4:"型";s:3:"型";s:4:"堲";s:3:"堲";s:4:"報";s:3:"報";s:4:"墬";s:3:"墬";s:4:"𡓤";s:4:"𡓤";s:4:"売";s:3:"売";s:4:"壷";s:3:"壷";s:4:"夆";s:3:"夆";s:4:"多";s:3:"多";s:4:"夢";s:3:"夢";s:4:"奢";s:3:"奢";s:4:"𡚨";s:4:"𡚨";s:4:"𡛪";s:4:"𡛪";s:4:"姬";s:3:"姬";s:4:"娛";s:3:"娛";s:4:"娧";s:3:"娧";s:4:"姘";s:3:"姘";s:4:"婦";s:3:"婦";s:4:"㛮";s:3:"㛮";s:4:"㛼";s:3:"㛼";s:4:"嬈";s:3:"嬈";s:4:"嬾";s:3:"嬾";s:4:"嬾";s:3:"嬾";s:4:"𡧈";s:4:"𡧈";s:4:"寃";s:3:"寃";s:4:"寘";s:3:"寘";s:4:"寧";s:3:"寧";s:4:"寳";s:3:"寳";s:4:"𡬘";s:4:"𡬘";s:4:"寿";s:3:"寿";s:4:"将";s:3:"将";s:4:"当";s:3:"当";s:4:"尢";s:3:"尢";s:4:"㞁";s:3:"㞁";s:4:"屠";s:3:"屠";s:4:"屮";s:3:"屮";s:4:"峀";s:3:"峀";s:4:"岍";s:3:"岍";s:4:"𡷤";s:4:"𡷤";s:4:"嵃";s:3:"嵃";s:4:"𡷦";s:4:"𡷦";s:4:"嵮";s:3:"嵮";s:4:"嵫";s:3:"嵫";s:4:"嵼";s:3:"嵼";s:4:"巡";s:3:"巡";s:4:"巢";s:3:"巢";s:4:"㠯";s:3:"㠯";s:4:"巽";s:3:"巽";s:4:"帨";s:3:"帨";s:4:"帽";s:3:"帽";s:4:"幩";s:3:"幩";s:4:"㡢";s:3:"㡢";s:4:"𢆃";s:4:"𢆃";s:4:"㡼";s:3:"㡼";s:4:"庰";s:3:"庰";s:4:"庳";s:3:"庳";s:4:"庶";s:3:"庶";s:4:"廊";s:3:"廊";s:4:"𪎒";s:4:"𪎒";s:4:"廾";s:3:"廾";s:4:"𢌱";s:4:"𢌱";s:4:"𢌱";s:4:"𢌱";s:4:"舁";s:3:"舁";s:4:"弢";s:3:"弢";s:4:"弢";s:3:"弢";s:4:"㣇";s:3:"㣇";s:4:"𣊸";s:4:"𣊸";s:4:"𦇚";s:4:"𦇚";s:4:"形";s:3:"形";s:4:"彫";s:3:"彫";s:4:"㣣";s:3:"㣣";s:4:"徚";s:3:"徚";s:4:"忍";s:3:"忍";s:4:"志";s:3:"志";s:4:"忹";s:3:"忹";s:4:"悁";s:3:"悁";s:4:"㤺";s:3:"㤺";s:4:"㤜";s:3:"㤜";s:4:"悔";s:3:"悔";s:4:"𢛔";s:4:"𢛔";s:4:"惇";s:3:"惇";s:4:"慈";s:3:"慈";s:4:"慌";s:3:"慌";s:4:"慎";s:3:"慎";s:4:"慌";s:3:"慌";s:4:"慺";s:3:"慺";s:4:"憎";s:3:"憎";s:4:"憲";s:3:"憲";s:4:"憤";s:3:"憤";s:4:"憯";s:3:"憯";s:4:"懞";s:3:"懞";s:4:"懲";s:3:"懲";s:4:"懶";s:3:"懶";s:4:"成";s:3:"成";s:4:"戛";s:3:"戛";s:4:"扝";s:3:"扝";s:4:"抱";s:3:"抱";s:4:"拔";s:3:"拔";s:4:"捐";s:3:"捐";s:4:"𢬌";s:4:"𢬌";s:4:"挽";s:3:"挽";s:4:"拼";s:3:"拼";s:4:"捨";s:3:"捨";s:4:"掃";s:3:"掃";s:4:"揤";s:3:"揤";s:4:"𢯱";s:4:"𢯱";s:4:"搢";s:3:"搢";s:4:"揅";s:3:"揅";s:4:"掩";s:3:"掩";s:4:"㨮";s:3:"㨮";s:4:"摩";s:3:"摩";s:4:"摾";s:3:"摾";s:4:"撝";s:3:"撝";s:4:"摷";s:3:"摷";s:4:"㩬";s:3:"㩬";s:4:"敏";s:3:"敏";s:4:"敬";s:3:"敬";s:4:"𣀊";s:4:"𣀊";s:4:"旣";s:3:"旣";s:4:"書";s:3:"書";s:4:"晉";s:3:"晉";s:4:"㬙";s:3:"㬙";s:4:"暑";s:3:"暑";s:4:"㬈";s:3:"㬈";s:4:"㫤";s:3:"㫤";s:4:"冒";s:3:"冒";s:4:"冕";s:3:"冕";s:4:"最";s:3:"最";s:4:"暜";s:3:"暜";s:4:"肭";s:3:"肭";s:4:"䏙";s:3:"䏙";s:4:"朗";s:3:"朗";s:4:"望";s:3:"望";s:4:"朡";s:3:"朡";s:4:"杞";s:3:"杞";s:4:"杓";s:3:"杓";s:4:"𣏃";s:4:"𣏃";s:4:"㭉";s:3:"㭉";s:4:"柺";s:3:"柺";s:4:"枅";s:3:"枅";s:4:"桒";s:3:"桒";s:4:"梅";s:3:"梅";s:4:"𣑭";s:4:"𣑭";s:4:"梎";s:3:"梎";s:4:"栟";s:3:"栟";s:4:"椔";s:3:"椔";s:4:"㮝";s:3:"㮝";s:4:"楂";s:3:"楂";s:4:"榣";s:3:"榣";s:4:"槪";s:3:"槪";s:4:"檨";s:3:"檨";s:4:"𣚣";s:4:"𣚣";s:4:"櫛";s:3:"櫛";s:4:"㰘";s:3:"㰘";s:4:"次";s:3:"次";s:4:"𣢧";s:4:"𣢧";s:4:"歔";s:3:"歔";s:4:"㱎";s:3:"㱎";s:4:"歲";s:3:"歲";s:4:"殟";s:3:"殟";s:4:"殺";s:3:"殺";s:4:"殻";s:3:"殻";s:4:"𣪍";s:4:"𣪍";s:4:"𡴋";s:4:"𡴋";s:4:"𣫺";s:4:"𣫺";s:4:"汎";s:3:"汎";s:4:"𣲼";s:4:"𣲼";s:4:"沿";s:3:"沿";s:4:"泍";s:3:"泍";s:4:"汧";s:3:"汧";s:4:"洖";s:3:"洖";s:4:"派";s:3:"派";s:4:"海";s:3:"海";s:4:"流";s:3:"流";s:4:"浩";s:3:"浩";s:4:"浸";s:3:"浸";s:4:"涅";s:3:"涅";s:4:"𣴞";s:4:"𣴞";s:4:"洴";s:3:"洴";s:4:"港";s:3:"港";s:4:"湮";s:3:"湮";s:4:"㴳";s:3:"㴳";s:4:"滋";s:3:"滋";s:4:"滇";s:3:"滇";s:4:"𣻑";s:4:"𣻑";s:4:"淹";s:3:"淹";s:4:"潮";s:3:"潮";s:4:"𣽞";s:4:"𣽞";s:4:"𣾎";s:4:"𣾎";s:4:"濆";s:3:"濆";s:4:"瀹";s:3:"瀹";s:4:"瀞";s:3:"瀞";s:4:"瀛";s:3:"瀛";s:4:"㶖";s:3:"㶖";s:4:"灊";s:3:"灊";s:4:"災";s:3:"災";s:4:"灷";s:3:"灷";s:4:"炭";s:3:"炭";s:4:"𠔥";s:4:"𠔥";s:4:"煅";s:3:"煅";s:4:"𤉣";s:4:"𤉣";s:4:"熜";s:3:"熜";s:4:"𤎫";s:4:"𤎫";s:4:"爨";s:3:"爨";s:4:"爵";s:3:"爵";s:4:"牐";s:3:"牐";s:4:"𤘈";s:4:"𤘈";s:4:"犀";s:3:"犀";s:4:"犕";s:3:"犕";s:4:"𤜵";s:4:"𤜵";s:4:"𤠔";s:4:"𤠔";s:4:"獺";s:3:"獺";s:4:"王";s:3:"王";s:4:"㺬";s:3:"㺬";s:4:"玥";s:3:"玥";s:4:"㺸";s:3:"㺸";s:4:"㺸";s:3:"㺸";s:4:"瑇";s:3:"瑇";s:4:"瑜";s:3:"瑜";s:4:"瑱";s:3:"瑱";s:4:"璅";s:3:"璅";s:4:"瓊";s:3:"瓊";s:4:"㼛";s:3:"㼛";s:4:"甤";s:3:"甤";s:4:"𤰶";s:4:"𤰶";s:4:"甾";s:3:"甾";s:4:"𤲒";s:4:"𤲒";s:4:"異";s:3:"異";s:4:"𢆟";s:4:"𢆟";s:4:"瘐";s:3:"瘐";s:4:"𤾡";s:4:"𤾡";s:4:"𤾸";s:4:"𤾸";s:4:"𥁄";s:4:"𥁄";s:4:"㿼";s:3:"㿼";s:4:"䀈";s:3:"䀈";s:4:"直";s:3:"直";s:4:"𥃳";s:4:"𥃳";s:4:"𥃲";s:4:"𥃲";s:4:"𥄙";s:4:"𥄙";s:4:"𥄳";s:4:"𥄳";s:4:"眞";s:3:"眞";s:4:"真";s:3:"真";s:4:"真";s:3:"真";s:4:"睊";s:3:"睊";s:4:"䀹";s:3:"䀹";s:4:"瞋";s:3:"瞋";s:4:"䁆";s:3:"䁆";s:4:"䂖";s:3:"䂖";s:4:"𥐝";s:4:"𥐝";s:4:"硎";s:3:"硎";s:4:"碌";s:3:"碌";s:4:"磌";s:3:"磌";s:4:"䃣";s:3:"䃣";s:4:"𥘦";s:4:"𥘦";s:4:"祖";s:3:"祖";s:4:"𥚚";s:4:"𥚚";s:4:"𥛅";s:4:"𥛅";s:4:"福";s:3:"福";s:4:"秫";s:3:"秫";s:4:"䄯";s:3:"䄯";s:4:"穀";s:3:"穀";s:4:"穊";s:3:"穊";s:4:"穏";s:3:"穏";s:4:"𥥼";s:4:"𥥼";s:4:"𥪧";s:4:"𥪧";s:4:"𥪧";s:4:"𥪧";s:4:"竮";s:3:"竮";s:4:"䈂";s:3:"䈂";s:4:"𥮫";s:4:"𥮫";s:4:"篆";s:3:"篆";s:4:"築";s:3:"築";s:4:"䈧";s:3:"䈧";s:4:"𥲀";s:4:"𥲀";s:4:"糒";s:3:"糒";s:4:"䊠";s:3:"䊠";s:4:"糨";s:3:"糨";s:4:"糣";s:3:"糣";s:4:"紀";s:3:"紀";s:4:"𥾆";s:4:"𥾆";s:4:"絣";s:3:"絣";s:4:"䌁";s:3:"䌁";s:4:"緇";s:3:"緇";s:4:"縂";s:3:"縂";s:4:"繅";s:3:"繅";s:4:"䌴";s:3:"䌴";s:4:"𦈨";s:4:"𦈨";s:4:"𦉇";s:4:"𦉇";s:4:"䍙";s:3:"䍙";s:4:"𦋙";s:4:"𦋙";s:4:"罺";s:3:"罺";s:4:"𦌾";s:4:"𦌾";s:4:"羕";s:3:"羕";s:4:"翺";s:3:"翺";s:4:"者";s:3:"者";s:4:"𦓚";s:4:"𦓚";s:4:"𦔣";s:4:"𦔣";s:4:"聠";s:3:"聠";s:4:"𦖨";s:4:"𦖨";s:4:"聰";s:3:"聰";s:4:"𣍟";s:4:"𣍟";s:4:"䏕";s:3:"䏕";s:4:"育";s:3:"育";s:4:"脃";s:3:"脃";s:4:"䐋";s:3:"䐋";s:4:"脾";s:3:"脾";s:4:"媵";s:3:"媵";s:4:"𦞧";s:4:"𦞧";s:4:"𦞵";s:4:"𦞵";s:4:"𣎓";s:4:"𣎓";s:4:"𣎜";s:4:"𣎜";s:4:"舁";s:3:"舁";s:4:"舄";s:3:"舄";s:4:"辞";s:3:"辞";s:4:"䑫";s:3:"䑫";s:4:"芑";s:3:"芑";s:4:"芋";s:3:"芋";s:4:"芝";s:3:"芝";s:4:"劳";s:3:"劳";s:4:"花";s:3:"花";s:4:"芳";s:3:"芳";s:4:"芽";s:3:"芽";s:4:"苦";s:3:"苦";s:4:"𦬼";s:4:"𦬼";s:4:"若";s:3:"若";s:4:"茝";s:3:"茝";s:4:"荣";s:3:"荣";s:4:"莭";s:3:"莭";s:4:"茣";s:3:"茣";s:4:"莽";s:3:"莽";s:4:"菧";s:3:"菧";s:4:"著";s:3:"著";s:4:"荓";s:3:"荓";s:4:"菊";s:3:"菊";s:4:"菌";s:3:"菌";s:4:"菜";s:3:"菜";s:4:"𦰶";s:4:"𦰶";s:4:"𦵫";s:4:"𦵫";s:4:"𦳕";s:4:"𦳕";s:4:"䔫";s:3:"䔫";s:4:"蓱";s:3:"蓱";s:4:"蓳";s:3:"蓳";s:4:"蔖";s:3:"蔖";s:4:"𧏊";s:4:"𧏊";s:4:"蕤";s:3:"蕤";s:4:"𦼬";s:4:"𦼬";s:4:"䕝";s:3:"䕝";s:4:"䕡";s:3:"䕡";s:4:"𦾱";s:4:"𦾱";s:4:"𧃒";s:4:"𧃒";s:4:"䕫";s:3:"䕫";s:4:"虐";s:3:"虐";s:4:"虜";s:3:"虜";s:4:"虧";s:3:"虧";s:4:"虩";s:3:"虩";s:4:"蚩";s:3:"蚩";s:4:"蚈";s:3:"蚈";s:4:"蜎";s:3:"蜎";s:4:"蛢";s:3:"蛢";s:4:"蝹";s:3:"蝹";s:4:"蜨";s:3:"蜨";s:4:"蝫";s:3:"蝫";s:4:"螆";s:3:"螆";s:4:"䗗";s:3:"䗗";s:4:"蟡";s:3:"蟡";s:4:"蠁";s:3:"蠁";s:4:"䗹";s:3:"䗹";s:4:"衠";s:3:"衠";s:4:"衣";s:3:"衣";s:4:"𧙧";s:4:"𧙧";s:4:"裗";s:3:"裗";s:4:"裞";s:3:"裞";s:4:"䘵";s:3:"䘵";s:4:"裺";s:3:"裺";s:4:"㒻";s:3:"㒻";s:4:"𧢮";s:4:"𧢮";s:4:"𧥦";s:4:"𧥦";s:4:"䚾";s:3:"䚾";s:4:"䛇";s:3:"䛇";s:4:"誠";s:3:"誠";s:4:"諭";s:3:"諭";s:4:"變";s:3:"變";s:4:"豕";s:3:"豕";s:4:"𧲨";s:4:"𧲨";s:4:"貫";s:3:"貫";s:4:"賁";s:3:"賁";s:4:"贛";s:3:"贛";s:4:"起";s:3:"起";s:4:"𧼯";s:4:"𧼯";s:4:"𠠄";s:4:"𠠄";s:4:"跋";s:3:"跋";s:4:"趼";s:3:"趼";s:4:"跰";s:3:"跰";s:4:"𠣞";s:4:"𠣞";s:4:"軔";s:3:"軔";s:4:"輸";s:3:"輸";s:4:"𨗒";s:4:"𨗒";s:4:"𨗭";s:4:"𨗭";s:4:"邔";s:3:"邔";s:4:"郱";s:3:"郱";s:4:"鄑";s:3:"鄑";s:4:"𨜮";s:4:"𨜮";s:4:"鄛";s:3:"鄛";s:4:"鈸";s:3:"鈸";s:4:"鋗";s:3:"鋗";s:4:"鋘";s:3:"鋘";s:4:"鉼";s:3:"鉼";s:4:"鏹";s:3:"鏹";s:4:"鐕";s:3:"鐕";s:4:"𨯺";s:4:"𨯺";s:4:"開";s:3:"開";s:4:"䦕";s:3:"䦕";s:4:"閷";s:3:"閷";s:4:"𨵷";s:4:"𨵷";s:4:"䧦";s:3:"䧦";s:4:"雃";s:3:"雃";s:4:"嶲";s:3:"嶲";s:4:"霣";s:3:"霣";s:4:"𩅅";s:4:"𩅅";s:4:"𩈚";s:4:"𩈚";s:4:"䩮";s:3:"䩮";s:4:"䩶";s:3:"䩶";s:4:"韠";s:3:"韠";s:4:"𩐊";s:4:"𩐊";s:4:"䪲";s:3:"䪲";s:4:"𩒖";s:4:"𩒖";s:4:"頋";s:3:"頋";s:4:"頋";s:3:"頋";s:4:"頩";s:3:"頩";s:4:"𩖶";s:4:"𩖶";s:4:"飢";s:3:"飢";s:4:"䬳";s:3:"䬳";s:4:"餩";s:3:"餩";s:4:"馧";s:3:"馧";s:4:"駂";s:3:"駂";s:4:"駾";s:3:"駾";s:4:"䯎";s:3:"䯎";s:4:"𩬰";s:4:"𩬰";s:4:"鬒";s:3:"鬒";s:4:"鱀";s:3:"鱀";s:4:"鳽";s:3:"鳽";s:4:"䳎";s:3:"䳎";s:4:"䳭";s:3:"䳭";s:4:"鵧";s:3:"鵧";s:4:"𪃎";s:4:"𪃎";s:4:"䳸";s:3:"䳸";s:4:"𪄅";s:4:"𪄅";s:4:"𪈎";s:4:"𪈎";s:4:"𪊑";s:4:"𪊑";s:4:"麻";s:3:"麻";s:4:"䵖";s:3:"䵖";s:4:"黹";s:3:"黹";s:4:"黾";s:3:"黾";s:4:"鼅";s:3:"鼅";s:4:"鼏";s:3:"鼏";s:4:"鼖";s:3:"鼖";s:4:"鼻";s:3:"鼻";s:4:"𪘀";s:4:"𪘀";}' );
+$utfCompatibilityDecomp = unserialize( 'a:5402:{s:2:" ";s:1:" ";s:2:"¨";s:3:" ̈";s:2:"ª";s:1:"a";s:2:"¯";s:3:" ̄";s:2:"²";s:1:"2";s:2:"³";s:1:"3";s:2:"´";s:3:" ́";s:2:"µ";s:2:"μ";s:2:"¸";s:3:" ̧";s:2:"¹";s:1:"1";s:2:"º";s:1:"o";s:2:"¼";s:5:"1⁄4";s:2:"½";s:5:"1⁄2";s:2:"¾";s:5:"3⁄4";s:2:"À";s:3:"À";s:2:"Á";s:3:"Á";s:2:"Â";s:3:"Â";s:2:"Ã";s:3:"Ã";s:2:"Ä";s:3:"Ä";s:2:"Å";s:3:"Å";s:2:"Ç";s:3:"Ç";s:2:"È";s:3:"È";s:2:"É";s:3:"É";s:2:"Ê";s:3:"Ê";s:2:"Ë";s:3:"Ë";s:2:"Ì";s:3:"Ì";s:2:"Í";s:3:"Í";s:2:"Î";s:3:"Î";s:2:"Ï";s:3:"Ï";s:2:"Ñ";s:3:"Ñ";s:2:"Ò";s:3:"Ò";s:2:"Ó";s:3:"Ó";s:2:"Ô";s:3:"Ô";s:2:"Õ";s:3:"Õ";s:2:"Ö";s:3:"Ö";s:2:"Ù";s:3:"Ù";s:2:"Ú";s:3:"Ú";s:2:"Û";s:3:"Û";s:2:"Ü";s:3:"Ü";s:2:"Ý";s:3:"Ý";s:2:"à";s:3:"à";s:2:"á";s:3:"á";s:2:"â";s:3:"â";s:2:"ã";s:3:"ã";s:2:"ä";s:3:"ä";s:2:"å";s:3:"å";s:2:"ç";s:3:"ç";s:2:"è";s:3:"è";s:2:"é";s:3:"é";s:2:"ê";s:3:"ê";s:2:"ë";s:3:"ë";s:2:"ì";s:3:"ì";s:2:"í";s:3:"í";s:2:"î";s:3:"î";s:2:"ï";s:3:"ï";s:2:"ñ";s:3:"ñ";s:2:"ò";s:3:"ò";s:2:"ó";s:3:"ó";s:2:"ô";s:3:"ô";s:2:"õ";s:3:"õ";s:2:"ö";s:3:"ö";s:2:"ù";s:3:"ù";s:2:"ú";s:3:"ú";s:2:"û";s:3:"û";s:2:"ü";s:3:"ü";s:2:"ý";s:3:"ý";s:2:"ÿ";s:3:"ÿ";s:2:"Ā";s:3:"Ā";s:2:"ā";s:3:"ā";s:2:"Ă";s:3:"Ă";s:2:"ă";s:3:"ă";s:2:"Ą";s:3:"Ą";s:2:"ą";s:3:"ą";s:2:"Ć";s:3:"Ć";s:2:"ć";s:3:"ć";s:2:"Ĉ";s:3:"Ĉ";s:2:"ĉ";s:3:"ĉ";s:2:"Ċ";s:3:"Ċ";s:2:"ċ";s:3:"ċ";s:2:"Č";s:3:"Č";s:2:"č";s:3:"č";s:2:"Ď";s:3:"Ď";s:2:"ď";s:3:"ď";s:2:"Ē";s:3:"Ē";s:2:"ē";s:3:"ē";s:2:"Ĕ";s:3:"Ĕ";s:2:"ĕ";s:3:"ĕ";s:2:"Ė";s:3:"Ė";s:2:"ė";s:3:"ė";s:2:"Ę";s:3:"Ę";s:2:"ę";s:3:"ę";s:2:"Ě";s:3:"Ě";s:2:"ě";s:3:"ě";s:2:"Ĝ";s:3:"Ĝ";s:2:"ĝ";s:3:"ĝ";s:2:"Ğ";s:3:"Ğ";s:2:"ğ";s:3:"ğ";s:2:"Ġ";s:3:"Ġ";s:2:"ġ";s:3:"ġ";s:2:"Ģ";s:3:"Ģ";s:2:"ģ";s:3:"ģ";s:2:"Ĥ";s:3:"Ĥ";s:2:"ĥ";s:3:"ĥ";s:2:"Ĩ";s:3:"Ĩ";s:2:"ĩ";s:3:"ĩ";s:2:"Ī";s:3:"Ī";s:2:"ī";s:3:"ī";s:2:"Ĭ";s:3:"Ĭ";s:2:"ĭ";s:3:"ĭ";s:2:"Į";s:3:"Į";s:2:"į";s:3:"į";s:2:"İ";s:3:"İ";s:2:"IJ";s:2:"IJ";s:2:"ij";s:2:"ij";s:2:"Ĵ";s:3:"Ĵ";s:2:"ĵ";s:3:"ĵ";s:2:"Ķ";s:3:"Ķ";s:2:"ķ";s:3:"ķ";s:2:"Ĺ";s:3:"Ĺ";s:2:"ĺ";s:3:"ĺ";s:2:"Ļ";s:3:"Ļ";s:2:"ļ";s:3:"ļ";s:2:"Ľ";s:3:"Ľ";s:2:"ľ";s:3:"ľ";s:2:"Ŀ";s:3:"L·";s:2:"ŀ";s:3:"l·";s:2:"Ń";s:3:"Ń";s:2:"ń";s:3:"ń";s:2:"Ņ";s:3:"Ņ";s:2:"ņ";s:3:"ņ";s:2:"Ň";s:3:"Ň";s:2:"ň";s:3:"ň";s:2:"ʼn";s:3:"ʼn";s:2:"Ō";s:3:"Ō";s:2:"ō";s:3:"ō";s:2:"Ŏ";s:3:"Ŏ";s:2:"ŏ";s:3:"ŏ";s:2:"Ő";s:3:"Ő";s:2:"ő";s:3:"ő";s:2:"Ŕ";s:3:"Ŕ";s:2:"ŕ";s:3:"ŕ";s:2:"Ŗ";s:3:"Ŗ";s:2:"ŗ";s:3:"ŗ";s:2:"Ř";s:3:"Ř";s:2:"ř";s:3:"ř";s:2:"Ś";s:3:"Ś";s:2:"ś";s:3:"ś";s:2:"Ŝ";s:3:"Ŝ";s:2:"ŝ";s:3:"ŝ";s:2:"Ş";s:3:"Ş";s:2:"ş";s:3:"ş";s:2:"Š";s:3:"Š";s:2:"š";s:3:"š";s:2:"Ţ";s:3:"Ţ";s:2:"ţ";s:3:"ţ";s:2:"Ť";s:3:"Ť";s:2:"ť";s:3:"ť";s:2:"Ũ";s:3:"Ũ";s:2:"ũ";s:3:"ũ";s:2:"Ū";s:3:"Ū";s:2:"ū";s:3:"ū";s:2:"Ŭ";s:3:"Ŭ";s:2:"ŭ";s:3:"ŭ";s:2:"Ů";s:3:"Ů";s:2:"ů";s:3:"ů";s:2:"Ű";s:3:"Ű";s:2:"ű";s:3:"ű";s:2:"Ų";s:3:"Ų";s:2:"ų";s:3:"ų";s:2:"Ŵ";s:3:"Ŵ";s:2:"ŵ";s:3:"ŵ";s:2:"Ŷ";s:3:"Ŷ";s:2:"ŷ";s:3:"ŷ";s:2:"Ÿ";s:3:"Ÿ";s:2:"Ź";s:3:"Ź";s:2:"ź";s:3:"ź";s:2:"Ż";s:3:"Ż";s:2:"ż";s:3:"ż";s:2:"Ž";s:3:"Ž";s:2:"ž";s:3:"ž";s:2:"ſ";s:1:"s";s:2:"Ơ";s:3:"Ơ";s:2:"ơ";s:3:"ơ";s:2:"Ư";s:3:"Ư";s:2:"ư";s:3:"ư";s:2:"DŽ";s:4:"DŽ";s:2:"Dž";s:4:"Dž";s:2:"dž";s:4:"dž";s:2:"LJ";s:2:"LJ";s:2:"Lj";s:2:"Lj";s:2:"lj";s:2:"lj";s:2:"NJ";s:2:"NJ";s:2:"Nj";s:2:"Nj";s:2:"nj";s:2:"nj";s:2:"Ǎ";s:3:"Ǎ";s:2:"ǎ";s:3:"ǎ";s:2:"Ǐ";s:3:"Ǐ";s:2:"ǐ";s:3:"ǐ";s:2:"Ǒ";s:3:"Ǒ";s:2:"ǒ";s:3:"ǒ";s:2:"Ǔ";s:3:"Ǔ";s:2:"ǔ";s:3:"ǔ";s:2:"Ǖ";s:5:"Ǖ";s:2:"ǖ";s:5:"ǖ";s:2:"Ǘ";s:5:"Ǘ";s:2:"ǘ";s:5:"ǘ";s:2:"Ǚ";s:5:"Ǚ";s:2:"ǚ";s:5:"ǚ";s:2:"Ǜ";s:5:"Ǜ";s:2:"ǜ";s:5:"ǜ";s:2:"Ǟ";s:5:"Ǟ";s:2:"ǟ";s:5:"ǟ";s:2:"Ǡ";s:5:"Ǡ";s:2:"ǡ";s:5:"ǡ";s:2:"Ǣ";s:4:"Ǣ";s:2:"ǣ";s:4:"ǣ";s:2:"Ǧ";s:3:"Ǧ";s:2:"ǧ";s:3:"ǧ";s:2:"Ǩ";s:3:"Ǩ";s:2:"ǩ";s:3:"ǩ";s:2:"Ǫ";s:3:"Ǫ";s:2:"ǫ";s:3:"ǫ";s:2:"Ǭ";s:5:"Ǭ";s:2:"ǭ";s:5:"ǭ";s:2:"Ǯ";s:4:"Ǯ";s:2:"ǯ";s:4:"ǯ";s:2:"ǰ";s:3:"ǰ";s:2:"DZ";s:2:"DZ";s:2:"Dz";s:2:"Dz";s:2:"dz";s:2:"dz";s:2:"Ǵ";s:3:"Ǵ";s:2:"ǵ";s:3:"ǵ";s:2:"Ǹ";s:3:"Ǹ";s:2:"ǹ";s:3:"ǹ";s:2:"Ǻ";s:5:"Ǻ";s:2:"ǻ";s:5:"ǻ";s:2:"Ǽ";s:4:"Ǽ";s:2:"ǽ";s:4:"ǽ";s:2:"Ǿ";s:4:"Ǿ";s:2:"ǿ";s:4:"ǿ";s:2:"Ȁ";s:3:"Ȁ";s:2:"ȁ";s:3:"ȁ";s:2:"Ȃ";s:3:"Ȃ";s:2:"ȃ";s:3:"ȃ";s:2:"Ȅ";s:3:"Ȅ";s:2:"ȅ";s:3:"ȅ";s:2:"Ȇ";s:3:"Ȇ";s:2:"ȇ";s:3:"ȇ";s:2:"Ȉ";s:3:"Ȉ";s:2:"ȉ";s:3:"ȉ";s:2:"Ȋ";s:3:"Ȋ";s:2:"ȋ";s:3:"ȋ";s:2:"Ȍ";s:3:"Ȍ";s:2:"ȍ";s:3:"ȍ";s:2:"Ȏ";s:3:"Ȏ";s:2:"ȏ";s:3:"ȏ";s:2:"Ȑ";s:3:"Ȑ";s:2:"ȑ";s:3:"ȑ";s:2:"Ȓ";s:3:"Ȓ";s:2:"ȓ";s:3:"ȓ";s:2:"Ȕ";s:3:"Ȕ";s:2:"ȕ";s:3:"ȕ";s:2:"Ȗ";s:3:"Ȗ";s:2:"ȗ";s:3:"ȗ";s:2:"Ș";s:3:"Ș";s:2:"ș";s:3:"ș";s:2:"Ț";s:3:"Ț";s:2:"ț";s:3:"ț";s:2:"Ȟ";s:3:"Ȟ";s:2:"ȟ";s:3:"ȟ";s:2:"Ȧ";s:3:"Ȧ";s:2:"ȧ";s:3:"ȧ";s:2:"Ȩ";s:3:"Ȩ";s:2:"ȩ";s:3:"ȩ";s:2:"Ȫ";s:5:"Ȫ";s:2:"ȫ";s:5:"ȫ";s:2:"Ȭ";s:5:"Ȭ";s:2:"ȭ";s:5:"ȭ";s:2:"Ȯ";s:3:"Ȯ";s:2:"ȯ";s:3:"ȯ";s:2:"Ȱ";s:5:"Ȱ";s:2:"ȱ";s:5:"ȱ";s:2:"Ȳ";s:3:"Ȳ";s:2:"ȳ";s:3:"ȳ";s:2:"ʰ";s:1:"h";s:2:"ʱ";s:2:"ɦ";s:2:"ʲ";s:1:"j";s:2:"ʳ";s:1:"r";s:2:"ʴ";s:2:"ɹ";s:2:"ʵ";s:2:"ɻ";s:2:"ʶ";s:2:"ʁ";s:2:"ʷ";s:1:"w";s:2:"ʸ";s:1:"y";s:2:"˘";s:3:" ̆";s:2:"˙";s:3:" ̇";s:2:"˚";s:3:" ̊";s:2:"˛";s:3:" ̨";s:2:"˜";s:3:" ̃";s:2:"˝";s:3:" ̋";s:2:"ˠ";s:2:"ɣ";s:2:"ˡ";s:1:"l";s:2:"ˢ";s:1:"s";s:2:"ˣ";s:1:"x";s:2:"ˤ";s:2:"ʕ";s:2:"̀";s:2:"̀";s:2:"́";s:2:"́";s:2:"̓";s:2:"̓";s:2:"̈́";s:4:"̈́";s:2:"ʹ";s:2:"ʹ";s:2:"ͺ";s:3:" ͅ";s:2:";";s:1:";";s:2:"΄";s:3:" ́";s:2:"΅";s:5:" ̈́";s:2:"Ά";s:4:"Ά";s:2:"·";s:2:"·";s:2:"Έ";s:4:"Έ";s:2:"Ή";s:4:"Ή";s:2:"Ί";s:4:"Ί";s:2:"Ό";s:4:"Ό";s:2:"Ύ";s:4:"Ύ";s:2:"Ώ";s:4:"Ώ";s:2:"ΐ";s:6:"ΐ";s:2:"Ϊ";s:4:"Ϊ";s:2:"Ϋ";s:4:"Ϋ";s:2:"ά";s:4:"ά";s:2:"έ";s:4:"έ";s:2:"ή";s:4:"ή";s:2:"ί";s:4:"ί";s:2:"ΰ";s:6:"ΰ";s:2:"ϊ";s:4:"ϊ";s:2:"ϋ";s:4:"ϋ";s:2:"ό";s:4:"ό";s:2:"ύ";s:4:"ύ";s:2:"ώ";s:4:"ώ";s:2:"ϐ";s:2:"β";s:2:"ϑ";s:2:"θ";s:2:"ϒ";s:2:"Υ";s:2:"ϓ";s:4:"Ύ";s:2:"ϔ";s:4:"Ϋ";s:2:"ϕ";s:2:"φ";s:2:"ϖ";s:2:"π";s:2:"ϰ";s:2:"κ";s:2:"ϱ";s:2:"ρ";s:2:"ϲ";s:2:"ς";s:2:"ϴ";s:2:"Θ";s:2:"ϵ";s:2:"ε";s:2:"Ϲ";s:2:"Σ";s:2:"Ѐ";s:4:"Ѐ";s:2:"Ё";s:4:"Ё";s:2:"Ѓ";s:4:"Ѓ";s:2:"Ї";s:4:"Ї";s:2:"Ќ";s:4:"Ќ";s:2:"Ѝ";s:4:"Ѝ";s:2:"Ў";s:4:"Ў";s:2:"Й";s:4:"Й";s:2:"й";s:4:"й";s:2:"ѐ";s:4:"ѐ";s:2:"ё";s:4:"ё";s:2:"ѓ";s:4:"ѓ";s:2:"ї";s:4:"ї";s:2:"ќ";s:4:"ќ";s:2:"ѝ";s:4:"ѝ";s:2:"ў";s:4:"ў";s:2:"Ѷ";s:4:"Ѷ";s:2:"ѷ";s:4:"ѷ";s:2:"Ӂ";s:4:"Ӂ";s:2:"ӂ";s:4:"ӂ";s:2:"Ӑ";s:4:"Ӑ";s:2:"ӑ";s:4:"ӑ";s:2:"Ӓ";s:4:"Ӓ";s:2:"ӓ";s:4:"ӓ";s:2:"Ӗ";s:4:"Ӗ";s:2:"ӗ";s:4:"ӗ";s:2:"Ӛ";s:4:"Ӛ";s:2:"ӛ";s:4:"ӛ";s:2:"Ӝ";s:4:"Ӝ";s:2:"ӝ";s:4:"ӝ";s:2:"Ӟ";s:4:"Ӟ";s:2:"ӟ";s:4:"ӟ";s:2:"Ӣ";s:4:"Ӣ";s:2:"ӣ";s:4:"ӣ";s:2:"Ӥ";s:4:"Ӥ";s:2:"ӥ";s:4:"ӥ";s:2:"Ӧ";s:4:"Ӧ";s:2:"ӧ";s:4:"ӧ";s:2:"Ӫ";s:4:"Ӫ";s:2:"ӫ";s:4:"ӫ";s:2:"Ӭ";s:4:"Ӭ";s:2:"ӭ";s:4:"ӭ";s:2:"Ӯ";s:4:"Ӯ";s:2:"ӯ";s:4:"ӯ";s:2:"Ӱ";s:4:"Ӱ";s:2:"ӱ";s:4:"ӱ";s:2:"Ӳ";s:4:"Ӳ";s:2:"ӳ";s:4:"ӳ";s:2:"Ӵ";s:4:"Ӵ";s:2:"ӵ";s:4:"ӵ";s:2:"Ӹ";s:4:"Ӹ";s:2:"ӹ";s:4:"ӹ";s:2:"և";s:4:"եւ";s:2:"آ";s:4:"آ";s:2:"أ";s:4:"أ";s:2:"ؤ";s:4:"ؤ";s:2:"إ";s:4:"إ";s:2:"ئ";s:4:"ئ";s:2:"ٵ";s:4:"اٴ";s:2:"ٶ";s:4:"وٴ";s:2:"ٷ";s:4:"ۇٴ";s:2:"ٸ";s:4:"يٴ";s:2:"ۀ";s:4:"ۀ";s:2:"ۂ";s:4:"ۂ";s:2:"ۓ";s:4:"ۓ";s:3:"ऩ";s:6:"ऩ";s:3:"ऱ";s:6:"ऱ";s:3:"ऴ";s:6:"ऴ";s:3:"क़";s:6:"क़";s:3:"ख़";s:6:"ख़";s:3:"ग़";s:6:"ग़";s:3:"ज़";s:6:"ज़";s:3:"ड़";s:6:"ड़";s:3:"ढ़";s:6:"ढ़";s:3:"फ़";s:6:"फ़";s:3:"य़";s:6:"य़";s:3:"ো";s:6:"ো";s:3:"ৌ";s:6:"ৌ";s:3:"ড়";s:6:"ড়";s:3:"ঢ়";s:6:"ঢ়";s:3:"য়";s:6:"য়";s:3:"ਲ਼";s:6:"ਲ਼";s:3:"ਸ਼";s:6:"ਸ਼";s:3:"ਖ਼";s:6:"ਖ਼";s:3:"ਗ਼";s:6:"ਗ਼";s:3:"ਜ਼";s:6:"ਜ਼";s:3:"ਫ਼";s:6:"ਫ਼";s:3:"ୈ";s:6:"ୈ";s:3:"ୋ";s:6:"ୋ";s:3:"ୌ";s:6:"ୌ";s:3:"ଡ଼";s:6:"ଡ଼";s:3:"ଢ଼";s:6:"ଢ଼";s:3:"ஔ";s:6:"ஔ";s:3:"ொ";s:6:"ொ";s:3:"ோ";s:6:"ோ";s:3:"ௌ";s:6:"ௌ";s:3:"ై";s:6:"ై";s:3:"ೀ";s:6:"ೀ";s:3:"ೇ";s:6:"ೇ";s:3:"ೈ";s:6:"ೈ";s:3:"ೊ";s:6:"ೊ";s:3:"ೋ";s:9:"ೋ";s:3:"ൊ";s:6:"ൊ";s:3:"ോ";s:6:"ോ";s:3:"ൌ";s:6:"ൌ";s:3:"ේ";s:6:"ේ";s:3:"ො";s:6:"ො";s:3:"ෝ";s:9:"ෝ";s:3:"ෞ";s:6:"ෞ";s:3:"ำ";s:6:"ํา";s:3:"ຳ";s:6:"ໍາ";s:3:"ໜ";s:6:"ຫນ";s:3:"ໝ";s:6:"ຫມ";s:3:"༌";s:3:"་";s:3:"གྷ";s:6:"གྷ";s:3:"ཌྷ";s:6:"ཌྷ";s:3:"དྷ";s:6:"དྷ";s:3:"བྷ";s:6:"བྷ";s:3:"ཛྷ";s:6:"ཛྷ";s:3:"ཀྵ";s:6:"ཀྵ";s:3:"ཱི";s:6:"ཱི";s:3:"ཱུ";s:6:"ཱུ";s:3:"ྲྀ";s:6:"ྲྀ";s:3:"ཷ";s:9:"ྲཱྀ";s:3:"ླྀ";s:6:"ླྀ";s:3:"ཹ";s:9:"ླཱྀ";s:3:"ཱྀ";s:6:"ཱྀ";s:3:"ྒྷ";s:6:"ྒྷ";s:3:"ྜྷ";s:6:"ྜྷ";s:3:"ྡྷ";s:6:"ྡྷ";s:3:"ྦྷ";s:6:"ྦྷ";s:3:"ྫྷ";s:6:"ྫྷ";s:3:"ྐྵ";s:6:"ྐྵ";s:3:"ဦ";s:6:"ဦ";s:3:"ჼ";s:3:"ნ";s:3:"ᬆ";s:6:"ᬆ";s:3:"ᬈ";s:6:"ᬈ";s:3:"ᬊ";s:6:"ᬊ";s:3:"ᬌ";s:6:"ᬌ";s:3:"ᬎ";s:6:"ᬎ";s:3:"ᬒ";s:6:"ᬒ";s:3:"ᬻ";s:6:"ᬻ";s:3:"ᬽ";s:6:"ᬽ";s:3:"ᭀ";s:6:"ᭀ";s:3:"ᭁ";s:6:"ᭁ";s:3:"ᭃ";s:6:"ᭃ";s:3:"ᴬ";s:1:"A";s:3:"ᴭ";s:2:"Æ";s:3:"ᴮ";s:1:"B";s:3:"ᴰ";s:1:"D";s:3:"ᴱ";s:1:"E";s:3:"ᴲ";s:2:"Ǝ";s:3:"ᴳ";s:1:"G";s:3:"ᴴ";s:1:"H";s:3:"ᴵ";s:1:"I";s:3:"ᴶ";s:1:"J";s:3:"ᴷ";s:1:"K";s:3:"ᴸ";s:1:"L";s:3:"ᴹ";s:1:"M";s:3:"ᴺ";s:1:"N";s:3:"ᴼ";s:1:"O";s:3:"ᴽ";s:2:"Ȣ";s:3:"ᴾ";s:1:"P";s:3:"ᴿ";s:1:"R";s:3:"ᵀ";s:1:"T";s:3:"ᵁ";s:1:"U";s:3:"ᵂ";s:1:"W";s:3:"ᵃ";s:1:"a";s:3:"ᵄ";s:2:"ɐ";s:3:"ᵅ";s:2:"ɑ";s:3:"ᵆ";s:3:"ᴂ";s:3:"ᵇ";s:1:"b";s:3:"ᵈ";s:1:"d";s:3:"ᵉ";s:1:"e";s:3:"ᵊ";s:2:"ə";s:3:"ᵋ";s:2:"ɛ";s:3:"ᵌ";s:2:"ɜ";s:3:"ᵍ";s:1:"g";s:3:"ᵏ";s:1:"k";s:3:"ᵐ";s:1:"m";s:3:"ᵑ";s:2:"ŋ";s:3:"ᵒ";s:1:"o";s:3:"ᵓ";s:2:"ɔ";s:3:"ᵔ";s:3:"ᴖ";s:3:"ᵕ";s:3:"ᴗ";s:3:"ᵖ";s:1:"p";s:3:"ᵗ";s:1:"t";s:3:"ᵘ";s:1:"u";s:3:"ᵙ";s:3:"ᴝ";s:3:"ᵚ";s:2:"ɯ";s:3:"ᵛ";s:1:"v";s:3:"ᵜ";s:3:"ᴥ";s:3:"ᵝ";s:2:"β";s:3:"ᵞ";s:2:"γ";s:3:"ᵟ";s:2:"δ";s:3:"ᵠ";s:2:"φ";s:3:"ᵡ";s:2:"χ";s:3:"ᵢ";s:1:"i";s:3:"ᵣ";s:1:"r";s:3:"ᵤ";s:1:"u";s:3:"ᵥ";s:1:"v";s:3:"ᵦ";s:2:"β";s:3:"ᵧ";s:2:"γ";s:3:"ᵨ";s:2:"ρ";s:3:"ᵩ";s:2:"φ";s:3:"ᵪ";s:2:"χ";s:3:"ᵸ";s:2:"н";s:3:"ᶛ";s:2:"ɒ";s:3:"ᶜ";s:1:"c";s:3:"ᶝ";s:2:"ɕ";s:3:"ᶞ";s:2:"ð";s:3:"ᶟ";s:2:"ɜ";s:3:"ᶠ";s:1:"f";s:3:"ᶡ";s:2:"ɟ";s:3:"ᶢ";s:2:"ɡ";s:3:"ᶣ";s:2:"ɥ";s:3:"ᶤ";s:2:"ɨ";s:3:"ᶥ";s:2:"ɩ";s:3:"ᶦ";s:2:"ɪ";s:3:"ᶧ";s:3:"ᵻ";s:3:"ᶨ";s:2:"ʝ";s:3:"ᶩ";s:2:"ɭ";s:3:"ᶪ";s:3:"ᶅ";s:3:"ᶫ";s:2:"ʟ";s:3:"ᶬ";s:2:"ɱ";s:3:"ᶭ";s:2:"ɰ";s:3:"ᶮ";s:2:"ɲ";s:3:"ᶯ";s:2:"ɳ";s:3:"ᶰ";s:2:"ɴ";s:3:"ᶱ";s:2:"ɵ";s:3:"ᶲ";s:2:"ɸ";s:3:"ᶳ";s:2:"ʂ";s:3:"ᶴ";s:2:"ʃ";s:3:"ᶵ";s:2:"ƫ";s:3:"ᶶ";s:2:"ʉ";s:3:"ᶷ";s:2:"ʊ";s:3:"ᶸ";s:3:"ᴜ";s:3:"ᶹ";s:2:"ʋ";s:3:"ᶺ";s:2:"ʌ";s:3:"ᶻ";s:1:"z";s:3:"ᶼ";s:2:"ʐ";s:3:"ᶽ";s:2:"ʑ";s:3:"ᶾ";s:2:"ʒ";s:3:"ᶿ";s:2:"θ";s:3:"Ḁ";s:3:"Ḁ";s:3:"ḁ";s:3:"ḁ";s:3:"Ḃ";s:3:"Ḃ";s:3:"ḃ";s:3:"ḃ";s:3:"Ḅ";s:3:"Ḅ";s:3:"ḅ";s:3:"ḅ";s:3:"Ḇ";s:3:"Ḇ";s:3:"ḇ";s:3:"ḇ";s:3:"Ḉ";s:5:"Ḉ";s:3:"ḉ";s:5:"ḉ";s:3:"Ḋ";s:3:"Ḋ";s:3:"ḋ";s:3:"ḋ";s:3:"Ḍ";s:3:"Ḍ";s:3:"ḍ";s:3:"ḍ";s:3:"Ḏ";s:3:"Ḏ";s:3:"ḏ";s:3:"ḏ";s:3:"Ḑ";s:3:"Ḑ";s:3:"ḑ";s:3:"ḑ";s:3:"Ḓ";s:3:"Ḓ";s:3:"ḓ";s:3:"ḓ";s:3:"Ḕ";s:5:"Ḕ";s:3:"ḕ";s:5:"ḕ";s:3:"Ḗ";s:5:"Ḗ";s:3:"ḗ";s:5:"ḗ";s:3:"Ḙ";s:3:"Ḙ";s:3:"ḙ";s:3:"ḙ";s:3:"Ḛ";s:3:"Ḛ";s:3:"ḛ";s:3:"ḛ";s:3:"Ḝ";s:5:"Ḝ";s:3:"ḝ";s:5:"ḝ";s:3:"Ḟ";s:3:"Ḟ";s:3:"ḟ";s:3:"ḟ";s:3:"Ḡ";s:3:"Ḡ";s:3:"ḡ";s:3:"ḡ";s:3:"Ḣ";s:3:"Ḣ";s:3:"ḣ";s:3:"ḣ";s:3:"Ḥ";s:3:"Ḥ";s:3:"ḥ";s:3:"ḥ";s:3:"Ḧ";s:3:"Ḧ";s:3:"ḧ";s:3:"ḧ";s:3:"Ḩ";s:3:"Ḩ";s:3:"ḩ";s:3:"ḩ";s:3:"Ḫ";s:3:"Ḫ";s:3:"ḫ";s:3:"ḫ";s:3:"Ḭ";s:3:"Ḭ";s:3:"ḭ";s:3:"ḭ";s:3:"Ḯ";s:5:"Ḯ";s:3:"ḯ";s:5:"ḯ";s:3:"Ḱ";s:3:"Ḱ";s:3:"ḱ";s:3:"ḱ";s:3:"Ḳ";s:3:"Ḳ";s:3:"ḳ";s:3:"ḳ";s:3:"Ḵ";s:3:"Ḵ";s:3:"ḵ";s:3:"ḵ";s:3:"Ḷ";s:3:"Ḷ";s:3:"ḷ";s:3:"ḷ";s:3:"Ḹ";s:5:"Ḹ";s:3:"ḹ";s:5:"ḹ";s:3:"Ḻ";s:3:"Ḻ";s:3:"ḻ";s:3:"ḻ";s:3:"Ḽ";s:3:"Ḽ";s:3:"ḽ";s:3:"ḽ";s:3:"Ḿ";s:3:"Ḿ";s:3:"ḿ";s:3:"ḿ";s:3:"Ṁ";s:3:"Ṁ";s:3:"ṁ";s:3:"ṁ";s:3:"Ṃ";s:3:"Ṃ";s:3:"ṃ";s:3:"ṃ";s:3:"Ṅ";s:3:"Ṅ";s:3:"ṅ";s:3:"ṅ";s:3:"Ṇ";s:3:"Ṇ";s:3:"ṇ";s:3:"ṇ";s:3:"Ṉ";s:3:"Ṉ";s:3:"ṉ";s:3:"ṉ";s:3:"Ṋ";s:3:"Ṋ";s:3:"ṋ";s:3:"ṋ";s:3:"Ṍ";s:5:"Ṍ";s:3:"ṍ";s:5:"ṍ";s:3:"Ṏ";s:5:"Ṏ";s:3:"ṏ";s:5:"ṏ";s:3:"Ṑ";s:5:"Ṑ";s:3:"ṑ";s:5:"ṑ";s:3:"Ṓ";s:5:"Ṓ";s:3:"ṓ";s:5:"ṓ";s:3:"Ṕ";s:3:"Ṕ";s:3:"ṕ";s:3:"ṕ";s:3:"Ṗ";s:3:"Ṗ";s:3:"ṗ";s:3:"ṗ";s:3:"Ṙ";s:3:"Ṙ";s:3:"ṙ";s:3:"ṙ";s:3:"Ṛ";s:3:"Ṛ";s:3:"ṛ";s:3:"ṛ";s:3:"Ṝ";s:5:"Ṝ";s:3:"ṝ";s:5:"ṝ";s:3:"Ṟ";s:3:"Ṟ";s:3:"ṟ";s:3:"ṟ";s:3:"Ṡ";s:3:"Ṡ";s:3:"ṡ";s:3:"ṡ";s:3:"Ṣ";s:3:"Ṣ";s:3:"ṣ";s:3:"ṣ";s:3:"Ṥ";s:5:"Ṥ";s:3:"ṥ";s:5:"ṥ";s:3:"Ṧ";s:5:"Ṧ";s:3:"ṧ";s:5:"ṧ";s:3:"Ṩ";s:5:"Ṩ";s:3:"ṩ";s:5:"ṩ";s:3:"Ṫ";s:3:"Ṫ";s:3:"ṫ";s:3:"ṫ";s:3:"Ṭ";s:3:"Ṭ";s:3:"ṭ";s:3:"ṭ";s:3:"Ṯ";s:3:"Ṯ";s:3:"ṯ";s:3:"ṯ";s:3:"Ṱ";s:3:"Ṱ";s:3:"ṱ";s:3:"ṱ";s:3:"Ṳ";s:3:"Ṳ";s:3:"ṳ";s:3:"ṳ";s:3:"Ṵ";s:3:"Ṵ";s:3:"ṵ";s:3:"ṵ";s:3:"Ṷ";s:3:"Ṷ";s:3:"ṷ";s:3:"ṷ";s:3:"Ṹ";s:5:"Ṹ";s:3:"ṹ";s:5:"ṹ";s:3:"Ṻ";s:5:"Ṻ";s:3:"ṻ";s:5:"ṻ";s:3:"Ṽ";s:3:"Ṽ";s:3:"ṽ";s:3:"ṽ";s:3:"Ṿ";s:3:"Ṿ";s:3:"ṿ";s:3:"ṿ";s:3:"Ẁ";s:3:"Ẁ";s:3:"ẁ";s:3:"ẁ";s:3:"Ẃ";s:3:"Ẃ";s:3:"ẃ";s:3:"ẃ";s:3:"Ẅ";s:3:"Ẅ";s:3:"ẅ";s:3:"ẅ";s:3:"Ẇ";s:3:"Ẇ";s:3:"ẇ";s:3:"ẇ";s:3:"Ẉ";s:3:"Ẉ";s:3:"ẉ";s:3:"ẉ";s:3:"Ẋ";s:3:"Ẋ";s:3:"ẋ";s:3:"ẋ";s:3:"Ẍ";s:3:"Ẍ";s:3:"ẍ";s:3:"ẍ";s:3:"Ẏ";s:3:"Ẏ";s:3:"ẏ";s:3:"ẏ";s:3:"Ẑ";s:3:"Ẑ";s:3:"ẑ";s:3:"ẑ";s:3:"Ẓ";s:3:"Ẓ";s:3:"ẓ";s:3:"ẓ";s:3:"Ẕ";s:3:"Ẕ";s:3:"ẕ";s:3:"ẕ";s:3:"ẖ";s:3:"ẖ";s:3:"ẗ";s:3:"ẗ";s:3:"ẘ";s:3:"ẘ";s:3:"ẙ";s:3:"ẙ";s:3:"ẚ";s:3:"aʾ";s:3:"ẛ";s:3:"ṡ";s:3:"Ạ";s:3:"Ạ";s:3:"ạ";s:3:"ạ";s:3:"Ả";s:3:"Ả";s:3:"ả";s:3:"ả";s:3:"Ấ";s:5:"Ấ";s:3:"ấ";s:5:"ấ";s:3:"Ầ";s:5:"Ầ";s:3:"ầ";s:5:"ầ";s:3:"Ẩ";s:5:"Ẩ";s:3:"ẩ";s:5:"ẩ";s:3:"Ẫ";s:5:"Ẫ";s:3:"ẫ";s:5:"ẫ";s:3:"Ậ";s:5:"Ậ";s:3:"ậ";s:5:"ậ";s:3:"Ắ";s:5:"Ắ";s:3:"ắ";s:5:"ắ";s:3:"Ằ";s:5:"Ằ";s:3:"ằ";s:5:"ằ";s:3:"Ẳ";s:5:"Ẳ";s:3:"ẳ";s:5:"ẳ";s:3:"Ẵ";s:5:"Ẵ";s:3:"ẵ";s:5:"ẵ";s:3:"Ặ";s:5:"Ặ";s:3:"ặ";s:5:"ặ";s:3:"Ẹ";s:3:"Ẹ";s:3:"ẹ";s:3:"ẹ";s:3:"Ẻ";s:3:"Ẻ";s:3:"ẻ";s:3:"ẻ";s:3:"Ẽ";s:3:"Ẽ";s:3:"ẽ";s:3:"ẽ";s:3:"Ế";s:5:"Ế";s:3:"ế";s:5:"ế";s:3:"Ề";s:5:"Ề";s:3:"ề";s:5:"ề";s:3:"Ể";s:5:"Ể";s:3:"ể";s:5:"ể";s:3:"Ễ";s:5:"Ễ";s:3:"ễ";s:5:"ễ";s:3:"Ệ";s:5:"Ệ";s:3:"ệ";s:5:"ệ";s:3:"Ỉ";s:3:"Ỉ";s:3:"ỉ";s:3:"ỉ";s:3:"Ị";s:3:"Ị";s:3:"ị";s:3:"ị";s:3:"Ọ";s:3:"Ọ";s:3:"ọ";s:3:"ọ";s:3:"Ỏ";s:3:"Ỏ";s:3:"ỏ";s:3:"ỏ";s:3:"Ố";s:5:"Ố";s:3:"ố";s:5:"ố";s:3:"Ồ";s:5:"Ồ";s:3:"ồ";s:5:"ồ";s:3:"Ổ";s:5:"Ổ";s:3:"ổ";s:5:"ổ";s:3:"Ỗ";s:5:"Ỗ";s:3:"ỗ";s:5:"ỗ";s:3:"Ộ";s:5:"Ộ";s:3:"ộ";s:5:"ộ";s:3:"Ớ";s:5:"Ớ";s:3:"ớ";s:5:"ớ";s:3:"Ờ";s:5:"Ờ";s:3:"ờ";s:5:"ờ";s:3:"Ở";s:5:"Ở";s:3:"ở";s:5:"ở";s:3:"Ỡ";s:5:"Ỡ";s:3:"ỡ";s:5:"ỡ";s:3:"Ợ";s:5:"Ợ";s:3:"ợ";s:5:"ợ";s:3:"Ụ";s:3:"Ụ";s:3:"ụ";s:3:"ụ";s:3:"Ủ";s:3:"Ủ";s:3:"ủ";s:3:"ủ";s:3:"Ứ";s:5:"Ứ";s:3:"ứ";s:5:"ứ";s:3:"Ừ";s:5:"Ừ";s:3:"ừ";s:5:"ừ";s:3:"Ử";s:5:"Ử";s:3:"ử";s:5:"ử";s:3:"Ữ";s:5:"Ữ";s:3:"ữ";s:5:"ữ";s:3:"Ự";s:5:"Ự";s:3:"ự";s:5:"ự";s:3:"Ỳ";s:3:"Ỳ";s:3:"ỳ";s:3:"ỳ";s:3:"Ỵ";s:3:"Ỵ";s:3:"ỵ";s:3:"ỵ";s:3:"Ỷ";s:3:"Ỷ";s:3:"ỷ";s:3:"ỷ";s:3:"Ỹ";s:3:"Ỹ";s:3:"ỹ";s:3:"ỹ";s:3:"ἀ";s:4:"ἀ";s:3:"ἁ";s:4:"ἁ";s:3:"ἂ";s:6:"ἂ";s:3:"ἃ";s:6:"ἃ";s:3:"ἄ";s:6:"ἄ";s:3:"ἅ";s:6:"ἅ";s:3:"ἆ";s:6:"ἆ";s:3:"ἇ";s:6:"ἇ";s:3:"Ἀ";s:4:"Ἀ";s:3:"Ἁ";s:4:"Ἁ";s:3:"Ἂ";s:6:"Ἂ";s:3:"Ἃ";s:6:"Ἃ";s:3:"Ἄ";s:6:"Ἄ";s:3:"Ἅ";s:6:"Ἅ";s:3:"Ἆ";s:6:"Ἆ";s:3:"Ἇ";s:6:"Ἇ";s:3:"ἐ";s:4:"ἐ";s:3:"ἑ";s:4:"ἑ";s:3:"ἒ";s:6:"ἒ";s:3:"ἓ";s:6:"ἓ";s:3:"ἔ";s:6:"ἔ";s:3:"ἕ";s:6:"ἕ";s:3:"Ἐ";s:4:"Ἐ";s:3:"Ἑ";s:4:"Ἑ";s:3:"Ἒ";s:6:"Ἒ";s:3:"Ἓ";s:6:"Ἓ";s:3:"Ἔ";s:6:"Ἔ";s:3:"Ἕ";s:6:"Ἕ";s:3:"ἠ";s:4:"ἠ";s:3:"ἡ";s:4:"ἡ";s:3:"ἢ";s:6:"ἢ";s:3:"ἣ";s:6:"ἣ";s:3:"ἤ";s:6:"ἤ";s:3:"ἥ";s:6:"ἥ";s:3:"ἦ";s:6:"ἦ";s:3:"ἧ";s:6:"ἧ";s:3:"Ἠ";s:4:"Ἠ";s:3:"Ἡ";s:4:"Ἡ";s:3:"Ἢ";s:6:"Ἢ";s:3:"Ἣ";s:6:"Ἣ";s:3:"Ἤ";s:6:"Ἤ";s:3:"Ἥ";s:6:"Ἥ";s:3:"Ἦ";s:6:"Ἦ";s:3:"Ἧ";s:6:"Ἧ";s:3:"ἰ";s:4:"ἰ";s:3:"ἱ";s:4:"ἱ";s:3:"ἲ";s:6:"ἲ";s:3:"ἳ";s:6:"ἳ";s:3:"ἴ";s:6:"ἴ";s:3:"ἵ";s:6:"ἵ";s:3:"ἶ";s:6:"ἶ";s:3:"ἷ";s:6:"ἷ";s:3:"Ἰ";s:4:"Ἰ";s:3:"Ἱ";s:4:"Ἱ";s:3:"Ἲ";s:6:"Ἲ";s:3:"Ἳ";s:6:"Ἳ";s:3:"Ἴ";s:6:"Ἴ";s:3:"Ἵ";s:6:"Ἵ";s:3:"Ἶ";s:6:"Ἶ";s:3:"Ἷ";s:6:"Ἷ";s:3:"ὀ";s:4:"ὀ";s:3:"ὁ";s:4:"ὁ";s:3:"ὂ";s:6:"ὂ";s:3:"ὃ";s:6:"ὃ";s:3:"ὄ";s:6:"ὄ";s:3:"ὅ";s:6:"ὅ";s:3:"Ὀ";s:4:"Ὀ";s:3:"Ὁ";s:4:"Ὁ";s:3:"Ὂ";s:6:"Ὂ";s:3:"Ὃ";s:6:"Ὃ";s:3:"Ὄ";s:6:"Ὄ";s:3:"Ὅ";s:6:"Ὅ";s:3:"ὐ";s:4:"ὐ";s:3:"ὑ";s:4:"ὑ";s:3:"ὒ";s:6:"ὒ";s:3:"ὓ";s:6:"ὓ";s:3:"ὔ";s:6:"ὔ";s:3:"ὕ";s:6:"ὕ";s:3:"ὖ";s:6:"ὖ";s:3:"ὗ";s:6:"ὗ";s:3:"Ὑ";s:4:"Ὑ";s:3:"Ὓ";s:6:"Ὓ";s:3:"Ὕ";s:6:"Ὕ";s:3:"Ὗ";s:6:"Ὗ";s:3:"ὠ";s:4:"ὠ";s:3:"ὡ";s:4:"ὡ";s:3:"ὢ";s:6:"ὢ";s:3:"ὣ";s:6:"ὣ";s:3:"ὤ";s:6:"ὤ";s:3:"ὥ";s:6:"ὥ";s:3:"ὦ";s:6:"ὦ";s:3:"ὧ";s:6:"ὧ";s:3:"Ὠ";s:4:"Ὠ";s:3:"Ὡ";s:4:"Ὡ";s:3:"Ὢ";s:6:"Ὢ";s:3:"Ὣ";s:6:"Ὣ";s:3:"Ὤ";s:6:"Ὤ";s:3:"Ὥ";s:6:"Ὥ";s:3:"Ὦ";s:6:"Ὦ";s:3:"Ὧ";s:6:"Ὧ";s:3:"ὰ";s:4:"ὰ";s:3:"ά";s:4:"ά";s:3:"ὲ";s:4:"ὲ";s:3:"έ";s:4:"έ";s:3:"ὴ";s:4:"ὴ";s:3:"ή";s:4:"ή";s:3:"ὶ";s:4:"ὶ";s:3:"ί";s:4:"ί";s:3:"ὸ";s:4:"ὸ";s:3:"ό";s:4:"ό";s:3:"ὺ";s:4:"ὺ";s:3:"ύ";s:4:"ύ";s:3:"ὼ";s:4:"ὼ";s:3:"ώ";s:4:"ώ";s:3:"ᾀ";s:6:"ᾀ";s:3:"ᾁ";s:6:"ᾁ";s:3:"ᾂ";s:8:"ᾂ";s:3:"ᾃ";s:8:"ᾃ";s:3:"ᾄ";s:8:"ᾄ";s:3:"ᾅ";s:8:"ᾅ";s:3:"ᾆ";s:8:"ᾆ";s:3:"ᾇ";s:8:"ᾇ";s:3:"ᾈ";s:6:"ᾈ";s:3:"ᾉ";s:6:"ᾉ";s:3:"ᾊ";s:8:"ᾊ";s:3:"ᾋ";s:8:"ᾋ";s:3:"ᾌ";s:8:"ᾌ";s:3:"ᾍ";s:8:"ᾍ";s:3:"ᾎ";s:8:"ᾎ";s:3:"ᾏ";s:8:"ᾏ";s:3:"ᾐ";s:6:"ᾐ";s:3:"ᾑ";s:6:"ᾑ";s:3:"ᾒ";s:8:"ᾒ";s:3:"ᾓ";s:8:"ᾓ";s:3:"ᾔ";s:8:"ᾔ";s:3:"ᾕ";s:8:"ᾕ";s:3:"ᾖ";s:8:"ᾖ";s:3:"ᾗ";s:8:"ᾗ";s:3:"ᾘ";s:6:"ᾘ";s:3:"ᾙ";s:6:"ᾙ";s:3:"ᾚ";s:8:"ᾚ";s:3:"ᾛ";s:8:"ᾛ";s:3:"ᾜ";s:8:"ᾜ";s:3:"ᾝ";s:8:"ᾝ";s:3:"ᾞ";s:8:"ᾞ";s:3:"ᾟ";s:8:"ᾟ";s:3:"ᾠ";s:6:"ᾠ";s:3:"ᾡ";s:6:"ᾡ";s:3:"ᾢ";s:8:"ᾢ";s:3:"ᾣ";s:8:"ᾣ";s:3:"ᾤ";s:8:"ᾤ";s:3:"ᾥ";s:8:"ᾥ";s:3:"ᾦ";s:8:"ᾦ";s:3:"ᾧ";s:8:"ᾧ";s:3:"ᾨ";s:6:"ᾨ";s:3:"ᾩ";s:6:"ᾩ";s:3:"ᾪ";s:8:"ᾪ";s:3:"ᾫ";s:8:"ᾫ";s:3:"ᾬ";s:8:"ᾬ";s:3:"ᾭ";s:8:"ᾭ";s:3:"ᾮ";s:8:"ᾮ";s:3:"ᾯ";s:8:"ᾯ";s:3:"ᾰ";s:4:"ᾰ";s:3:"ᾱ";s:4:"ᾱ";s:3:"ᾲ";s:6:"ᾲ";s:3:"ᾳ";s:4:"ᾳ";s:3:"ᾴ";s:6:"ᾴ";s:3:"ᾶ";s:4:"ᾶ";s:3:"ᾷ";s:6:"ᾷ";s:3:"Ᾰ";s:4:"Ᾰ";s:3:"Ᾱ";s:4:"Ᾱ";s:3:"Ὰ";s:4:"Ὰ";s:3:"Ά";s:4:"Ά";s:3:"ᾼ";s:4:"ᾼ";s:3:"᾽";s:3:" ̓";s:3:"ι";s:2:"ι";s:3:"᾿";s:3:" ̓";s:3:"῀";s:3:" ͂";s:3:"῁";s:5:" ̈͂";s:3:"ῂ";s:6:"ῂ";s:3:"ῃ";s:4:"ῃ";s:3:"ῄ";s:6:"ῄ";s:3:"ῆ";s:4:"ῆ";s:3:"ῇ";s:6:"ῇ";s:3:"Ὲ";s:4:"Ὲ";s:3:"Έ";s:4:"Έ";s:3:"Ὴ";s:4:"Ὴ";s:3:"Ή";s:4:"Ή";s:3:"ῌ";s:4:"ῌ";s:3:"῍";s:5:" ̓̀";s:3:"῎";s:5:" ̓́";s:3:"῏";s:5:" ̓͂";s:3:"ῐ";s:4:"ῐ";s:3:"ῑ";s:4:"ῑ";s:3:"ῒ";s:6:"ῒ";s:3:"ΐ";s:6:"ΐ";s:3:"ῖ";s:4:"ῖ";s:3:"ῗ";s:6:"ῗ";s:3:"Ῐ";s:4:"Ῐ";s:3:"Ῑ";s:4:"Ῑ";s:3:"Ὶ";s:4:"Ὶ";s:3:"Ί";s:4:"Ί";s:3:"῝";s:5:" ̔̀";s:3:"῞";s:5:" ̔́";s:3:"῟";s:5:" ̔͂";s:3:"ῠ";s:4:"ῠ";s:3:"ῡ";s:4:"ῡ";s:3:"ῢ";s:6:"ῢ";s:3:"ΰ";s:6:"ΰ";s:3:"ῤ";s:4:"ῤ";s:3:"ῥ";s:4:"ῥ";s:3:"ῦ";s:4:"ῦ";s:3:"ῧ";s:6:"ῧ";s:3:"Ῠ";s:4:"Ῠ";s:3:"Ῡ";s:4:"Ῡ";s:3:"Ὺ";s:4:"Ὺ";s:3:"Ύ";s:4:"Ύ";s:3:"Ῥ";s:4:"Ῥ";s:3:"῭";s:5:" ̈̀";s:3:"΅";s:5:" ̈́";s:3:"`";s:1:"`";s:3:"ῲ";s:6:"ῲ";s:3:"ῳ";s:4:"ῳ";s:3:"ῴ";s:6:"ῴ";s:3:"ῶ";s:4:"ῶ";s:3:"ῷ";s:6:"ῷ";s:3:"Ὸ";s:4:"Ὸ";s:3:"Ό";s:4:"Ό";s:3:"Ὼ";s:4:"Ὼ";s:3:"Ώ";s:4:"Ώ";s:3:"ῼ";s:4:"ῼ";s:3:"´";s:3:" ́";s:3:"῾";s:3:" ̔";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:" ";s:1:" ";s:3:"‑";s:3:"‐";s:3:"‗";s:3:" ̳";s:3:"․";s:1:".";s:3:"‥";s:2:"..";s:3:"…";s:3:"...";s:3:" ";s:1:" ";s:3:"″";s:6:"′′";s:3:"‴";s:9:"′′′";s:3:"‶";s:6:"‵‵";s:3:"‷";s:9:"‵‵‵";s:3:"‼";s:2:"!!";s:3:"‾";s:3:" ̅";s:3:"⁇";s:2:"??";s:3:"⁈";s:2:"?!";s:3:"⁉";s:2:"!?";s:3:"⁗";s:12:"′′′′";s:3:" ";s:1:" ";s:3:"⁰";s:1:"0";s:3:"ⁱ";s:1:"i";s:3:"⁴";s:1:"4";s:3:"⁵";s:1:"5";s:3:"⁶";s:1:"6";s:3:"⁷";s:1:"7";s:3:"⁸";s:1:"8";s:3:"⁹";s:1:"9";s:3:"⁺";s:1:"+";s:3:"⁻";s:3:"−";s:3:"⁼";s:1:"=";s:3:"⁽";s:1:"(";s:3:"⁾";s:1:")";s:3:"ⁿ";s:1:"n";s:3:"₀";s:1:"0";s:3:"₁";s:1:"1";s:3:"₂";s:1:"2";s:3:"₃";s:1:"3";s:3:"₄";s:1:"4";s:3:"₅";s:1:"5";s:3:"₆";s:1:"6";s:3:"₇";s:1:"7";s:3:"₈";s:1:"8";s:3:"₉";s:1:"9";s:3:"₊";s:1:"+";s:3:"₋";s:3:"−";s:3:"₌";s:1:"=";s:3:"₍";s:1:"(";s:3:"₎";s:1:")";s:3:"ₐ";s:1:"a";s:3:"ₑ";s:1:"e";s:3:"ₒ";s:1:"o";s:3:"ₓ";s:1:"x";s:3:"ₔ";s:2:"ə";s:3:"₨";s:2:"Rs";s:3:"℀";s:3:"a/c";s:3:"℁";s:3:"a/s";s:3:"ℂ";s:1:"C";s:3:"℃";s:3:"°C";s:3:"℅";s:3:"c/o";s:3:"℆";s:3:"c/u";s:3:"ℇ";s:2:"Ɛ";s:3:"℉";s:3:"°F";s:3:"ℊ";s:1:"g";s:3:"ℋ";s:1:"H";s:3:"ℌ";s:1:"H";s:3:"ℍ";s:1:"H";s:3:"ℎ";s:1:"h";s:3:"ℏ";s:2:"ħ";s:3:"ℐ";s:1:"I";s:3:"ℑ";s:1:"I";s:3:"ℒ";s:1:"L";s:3:"ℓ";s:1:"l";s:3:"ℕ";s:1:"N";s:3:"№";s:2:"No";s:3:"ℙ";s:1:"P";s:3:"ℚ";s:1:"Q";s:3:"ℛ";s:1:"R";s:3:"ℜ";s:1:"R";s:3:"ℝ";s:1:"R";s:3:"℠";s:2:"SM";s:3:"℡";s:3:"TEL";s:3:"™";s:2:"TM";s:3:"ℤ";s:1:"Z";s:3:"Ω";s:2:"Ω";s:3:"ℨ";s:1:"Z";s:3:"K";s:1:"K";s:3:"Å";s:3:"Å";s:3:"ℬ";s:1:"B";s:3:"ℭ";s:1:"C";s:3:"ℯ";s:1:"e";s:3:"ℰ";s:1:"E";s:3:"ℱ";s:1:"F";s:3:"ℳ";s:1:"M";s:3:"ℴ";s:1:"o";s:3:"ℵ";s:2:"א";s:3:"ℶ";s:2:"ב";s:3:"ℷ";s:2:"ג";s:3:"ℸ";s:2:"ד";s:3:"ℹ";s:1:"i";s:3:"℻";s:3:"FAX";s:3:"ℼ";s:2:"π";s:3:"ℽ";s:2:"γ";s:3:"ℾ";s:2:"Γ";s:3:"ℿ";s:2:"Π";s:3:"⅀";s:3:"∑";s:3:"ⅅ";s:1:"D";s:3:"ⅆ";s:1:"d";s:3:"ⅇ";s:1:"e";s:3:"ⅈ";s:1:"i";s:3:"ⅉ";s:1:"j";s:3:"⅓";s:5:"1⁄3";s:3:"⅔";s:5:"2⁄3";s:3:"⅕";s:5:"1⁄5";s:3:"⅖";s:5:"2⁄5";s:3:"⅗";s:5:"3⁄5";s:3:"⅘";s:5:"4⁄5";s:3:"⅙";s:5:"1⁄6";s:3:"⅚";s:5:"5⁄6";s:3:"⅛";s:5:"1⁄8";s:3:"⅜";s:5:"3⁄8";s:3:"⅝";s:5:"5⁄8";s:3:"⅞";s:5:"7⁄8";s:3:"⅟";s:4:"1⁄";s:3:"Ⅰ";s:1:"I";s:3:"Ⅱ";s:2:"II";s:3:"Ⅲ";s:3:"III";s:3:"Ⅳ";s:2:"IV";s:3:"Ⅴ";s:1:"V";s:3:"Ⅵ";s:2:"VI";s:3:"Ⅶ";s:3:"VII";s:3:"Ⅷ";s:4:"VIII";s:3:"Ⅸ";s:2:"IX";s:3:"Ⅹ";s:1:"X";s:3:"Ⅺ";s:2:"XI";s:3:"Ⅻ";s:3:"XII";s:3:"Ⅼ";s:1:"L";s:3:"Ⅽ";s:1:"C";s:3:"Ⅾ";s:1:"D";s:3:"Ⅿ";s:1:"M";s:3:"ⅰ";s:1:"i";s:3:"ⅱ";s:2:"ii";s:3:"ⅲ";s:3:"iii";s:3:"ⅳ";s:2:"iv";s:3:"ⅴ";s:1:"v";s:3:"ⅵ";s:2:"vi";s:3:"ⅶ";s:3:"vii";s:3:"ⅷ";s:4:"viii";s:3:"ⅸ";s:2:"ix";s:3:"ⅹ";s:1:"x";s:3:"ⅺ";s:2:"xi";s:3:"ⅻ";s:3:"xii";s:3:"ⅼ";s:1:"l";s:3:"ⅽ";s:1:"c";s:3:"ⅾ";s:1:"d";s:3:"ⅿ";s:1:"m";s:3:"↚";s:5:"↚";s:3:"↛";s:5:"↛";s:3:"↮";s:5:"↮";s:3:"⇍";s:5:"⇍";s:3:"⇎";s:5:"⇎";s:3:"⇏";s:5:"⇏";s:3:"∄";s:5:"∄";s:3:"∉";s:5:"∉";s:3:"∌";s:5:"∌";s:3:"∤";s:5:"∤";s:3:"∦";s:5:"∦";s:3:"∬";s:6:"∫∫";s:3:"∭";s:9:"∫∫∫";s:3:"∯";s:6:"∮∮";s:3:"∰";s:9:"∮∮∮";s:3:"≁";s:5:"≁";s:3:"≄";s:5:"≄";s:3:"≇";s:5:"≇";s:3:"≉";s:5:"≉";s:3:"≠";s:3:"≠";s:3:"≢";s:5:"≢";s:3:"≭";s:5:"≭";s:3:"≮";s:3:"≮";s:3:"≯";s:3:"≯";s:3:"≰";s:5:"≰";s:3:"≱";s:5:"≱";s:3:"≴";s:5:"≴";s:3:"≵";s:5:"≵";s:3:"≸";s:5:"≸";s:3:"≹";s:5:"≹";s:3:"⊀";s:5:"⊀";s:3:"⊁";s:5:"⊁";s:3:"⊄";s:5:"⊄";s:3:"⊅";s:5:"⊅";s:3:"⊈";s:5:"⊈";s:3:"⊉";s:5:"⊉";s:3:"⊬";s:5:"⊬";s:3:"⊭";s:5:"⊭";s:3:"⊮";s:5:"⊮";s:3:"⊯";s:5:"⊯";s:3:"⋠";s:5:"⋠";s:3:"⋡";s:5:"⋡";s:3:"⋢";s:5:"⋢";s:3:"⋣";s:5:"⋣";s:3:"⋪";s:5:"⋪";s:3:"⋫";s:5:"⋫";s:3:"⋬";s:5:"⋬";s:3:"⋭";s:5:"⋭";s:3:"〈";s:3:"〈";s:3:"〉";s:3:"〉";s:3:"①";s:1:"1";s:3:"②";s:1:"2";s:3:"③";s:1:"3";s:3:"④";s:1:"4";s:3:"⑤";s:1:"5";s:3:"⑥";s:1:"6";s:3:"⑦";s:1:"7";s:3:"⑧";s:1:"8";s:3:"⑨";s:1:"9";s:3:"⑩";s:2:"10";s:3:"⑪";s:2:"11";s:3:"⑫";s:2:"12";s:3:"⑬";s:2:"13";s:3:"⑭";s:2:"14";s:3:"⑮";s:2:"15";s:3:"⑯";s:2:"16";s:3:"⑰";s:2:"17";s:3:"⑱";s:2:"18";s:3:"⑲";s:2:"19";s:3:"⑳";s:2:"20";s:3:"⑴";s:3:"(1)";s:3:"⑵";s:3:"(2)";s:3:"⑶";s:3:"(3)";s:3:"⑷";s:3:"(4)";s:3:"⑸";s:3:"(5)";s:3:"⑹";s:3:"(6)";s:3:"⑺";s:3:"(7)";s:3:"⑻";s:3:"(8)";s:3:"⑼";s:3:"(9)";s:3:"⑽";s:4:"(10)";s:3:"⑾";s:4:"(11)";s:3:"⑿";s:4:"(12)";s:3:"⒀";s:4:"(13)";s:3:"⒁";s:4:"(14)";s:3:"⒂";s:4:"(15)";s:3:"⒃";s:4:"(16)";s:3:"⒄";s:4:"(17)";s:3:"⒅";s:4:"(18)";s:3:"⒆";s:4:"(19)";s:3:"⒇";s:4:"(20)";s:3:"⒈";s:2:"1.";s:3:"⒉";s:2:"2.";s:3:"⒊";s:2:"3.";s:3:"⒋";s:2:"4.";s:3:"⒌";s:2:"5.";s:3:"⒍";s:2:"6.";s:3:"⒎";s:2:"7.";s:3:"⒏";s:2:"8.";s:3:"⒐";s:2:"9.";s:3:"⒑";s:3:"10.";s:3:"⒒";s:3:"11.";s:3:"⒓";s:3:"12.";s:3:"⒔";s:3:"13.";s:3:"⒕";s:3:"14.";s:3:"⒖";s:3:"15.";s:3:"⒗";s:3:"16.";s:3:"⒘";s:3:"17.";s:3:"⒙";s:3:"18.";s:3:"⒚";s:3:"19.";s:3:"⒛";s:3:"20.";s:3:"⒜";s:3:"(a)";s:3:"⒝";s:3:"(b)";s:3:"⒞";s:3:"(c)";s:3:"⒟";s:3:"(d)";s:3:"⒠";s:3:"(e)";s:3:"⒡";s:3:"(f)";s:3:"⒢";s:3:"(g)";s:3:"⒣";s:3:"(h)";s:3:"⒤";s:3:"(i)";s:3:"⒥";s:3:"(j)";s:3:"⒦";s:3:"(k)";s:3:"⒧";s:3:"(l)";s:3:"⒨";s:3:"(m)";s:3:"⒩";s:3:"(n)";s:3:"⒪";s:3:"(o)";s:3:"⒫";s:3:"(p)";s:3:"⒬";s:3:"(q)";s:3:"⒭";s:3:"(r)";s:3:"⒮";s:3:"(s)";s:3:"⒯";s:3:"(t)";s:3:"⒰";s:3:"(u)";s:3:"⒱";s:3:"(v)";s:3:"⒲";s:3:"(w)";s:3:"⒳";s:3:"(x)";s:3:"⒴";s:3:"(y)";s:3:"⒵";s:3:"(z)";s:3:"Ⓐ";s:1:"A";s:3:"Ⓑ";s:1:"B";s:3:"Ⓒ";s:1:"C";s:3:"Ⓓ";s:1:"D";s:3:"Ⓔ";s:1:"E";s:3:"Ⓕ";s:1:"F";s:3:"Ⓖ";s:1:"G";s:3:"Ⓗ";s:1:"H";s:3:"Ⓘ";s:1:"I";s:3:"Ⓙ";s:1:"J";s:3:"Ⓚ";s:1:"K";s:3:"Ⓛ";s:1:"L";s:3:"Ⓜ";s:1:"M";s:3:"Ⓝ";s:1:"N";s:3:"Ⓞ";s:1:"O";s:3:"Ⓟ";s:1:"P";s:3:"Ⓠ";s:1:"Q";s:3:"Ⓡ";s:1:"R";s:3:"Ⓢ";s:1:"S";s:3:"Ⓣ";s:1:"T";s:3:"Ⓤ";s:1:"U";s:3:"Ⓥ";s:1:"V";s:3:"Ⓦ";s:1:"W";s:3:"Ⓧ";s:1:"X";s:3:"Ⓨ";s:1:"Y";s:3:"Ⓩ";s:1:"Z";s:3:"ⓐ";s:1:"a";s:3:"ⓑ";s:1:"b";s:3:"ⓒ";s:1:"c";s:3:"ⓓ";s:1:"d";s:3:"ⓔ";s:1:"e";s:3:"ⓕ";s:1:"f";s:3:"ⓖ";s:1:"g";s:3:"ⓗ";s:1:"h";s:3:"ⓘ";s:1:"i";s:3:"ⓙ";s:1:"j";s:3:"ⓚ";s:1:"k";s:3:"ⓛ";s:1:"l";s:3:"ⓜ";s:1:"m";s:3:"ⓝ";s:1:"n";s:3:"ⓞ";s:1:"o";s:3:"ⓟ";s:1:"p";s:3:"ⓠ";s:1:"q";s:3:"ⓡ";s:1:"r";s:3:"ⓢ";s:1:"s";s:3:"ⓣ";s:1:"t";s:3:"ⓤ";s:1:"u";s:3:"ⓥ";s:1:"v";s:3:"ⓦ";s:1:"w";s:3:"ⓧ";s:1:"x";s:3:"ⓨ";s:1:"y";s:3:"ⓩ";s:1:"z";s:3:"⓪";s:1:"0";s:3:"⨌";s:12:"∫∫∫∫";s:3:"⩴";s:3:"::=";s:3:"⩵";s:2:"==";s:3:"⩶";s:3:"===";s:3:"⫝̸";s:5:"⫝̸";s:3:"ⵯ";s:3:"ⵡ";s:3:"⺟";s:3:"母";s:3:"⻳";s:3:"龟";s:3:"⼀";s:3:"一";s:3:"⼁";s:3:"丨";s:3:"⼂";s:3:"丶";s:3:"⼃";s:3:"丿";s:3:"⼄";s:3:"乙";s:3:"⼅";s:3:"亅";s:3:"⼆";s:3:"二";s:3:"⼇";s:3:"亠";s:3:"⼈";s:3:"人";s:3:"⼉";s:3:"儿";s:3:"⼊";s:3:"入";s:3:"⼋";s:3:"八";s:3:"⼌";s:3:"冂";s:3:"⼍";s:3:"冖";s:3:"⼎";s:3:"冫";s:3:"⼏";s:3:"几";s:3:"⼐";s:3:"凵";s:3:"⼑";s:3:"刀";s:3:"⼒";s:3:"力";s:3:"⼓";s:3:"勹";s:3:"⼔";s:3:"匕";s:3:"⼕";s:3:"匚";s:3:"⼖";s:3:"匸";s:3:"⼗";s:3:"十";s:3:"⼘";s:3:"卜";s:3:"⼙";s:3:"卩";s:3:"⼚";s:3:"厂";s:3:"⼛";s:3:"厶";s:3:"⼜";s:3:"又";s:3:"⼝";s:3:"口";s:3:"⼞";s:3:"囗";s:3:"⼟";s:3:"土";s:3:"⼠";s:3:"士";s:3:"⼡";s:3:"夂";s:3:"⼢";s:3:"夊";s:3:"⼣";s:3:"夕";s:3:"⼤";s:3:"大";s:3:"⼥";s:3:"女";s:3:"⼦";s:3:"子";s:3:"⼧";s:3:"宀";s:3:"⼨";s:3:"寸";s:3:"⼩";s:3:"小";s:3:"⼪";s:3:"尢";s:3:"⼫";s:3:"尸";s:3:"⼬";s:3:"屮";s:3:"⼭";s:3:"山";s:3:"⼮";s:3:"巛";s:3:"⼯";s:3:"工";s:3:"⼰";s:3:"己";s:3:"⼱";s:3:"巾";s:3:"⼲";s:3:"干";s:3:"⼳";s:3:"幺";s:3:"⼴";s:3:"广";s:3:"⼵";s:3:"廴";s:3:"⼶";s:3:"廾";s:3:"⼷";s:3:"弋";s:3:"⼸";s:3:"弓";s:3:"⼹";s:3:"彐";s:3:"⼺";s:3:"彡";s:3:"⼻";s:3:"彳";s:3:"⼼";s:3:"心";s:3:"⼽";s:3:"戈";s:3:"⼾";s:3:"戶";s:3:"⼿";s:3:"手";s:3:"⽀";s:3:"支";s:3:"⽁";s:3:"攴";s:3:"⽂";s:3:"文";s:3:"⽃";s:3:"斗";s:3:"⽄";s:3:"斤";s:3:"⽅";s:3:"方";s:3:"⽆";s:3:"无";s:3:"⽇";s:3:"日";s:3:"⽈";s:3:"曰";s:3:"⽉";s:3:"月";s:3:"⽊";s:3:"木";s:3:"⽋";s:3:"欠";s:3:"⽌";s:3:"止";s:3:"⽍";s:3:"歹";s:3:"⽎";s:3:"殳";s:3:"⽏";s:3:"毋";s:3:"⽐";s:3:"比";s:3:"⽑";s:3:"毛";s:3:"⽒";s:3:"氏";s:3:"⽓";s:3:"气";s:3:"⽔";s:3:"水";s:3:"⽕";s:3:"火";s:3:"⽖";s:3:"爪";s:3:"⽗";s:3:"父";s:3:"⽘";s:3:"爻";s:3:"⽙";s:3:"爿";s:3:"⽚";s:3:"片";s:3:"⽛";s:3:"牙";s:3:"⽜";s:3:"牛";s:3:"⽝";s:3:"犬";s:3:"⽞";s:3:"玄";s:3:"⽟";s:3:"玉";s:3:"⽠";s:3:"瓜";s:3:"⽡";s:3:"瓦";s:3:"⽢";s:3:"甘";s:3:"⽣";s:3:"生";s:3:"⽤";s:3:"用";s:3:"⽥";s:3:"田";s:3:"⽦";s:3:"疋";s:3:"⽧";s:3:"疒";s:3:"⽨";s:3:"癶";s:3:"⽩";s:3:"白";s:3:"⽪";s:3:"皮";s:3:"⽫";s:3:"皿";s:3:"⽬";s:3:"目";s:3:"⽭";s:3:"矛";s:3:"⽮";s:3:"矢";s:3:"⽯";s:3:"石";s:3:"⽰";s:3:"示";s:3:"⽱";s:3:"禸";s:3:"⽲";s:3:"禾";s:3:"⽳";s:3:"穴";s:3:"⽴";s:3:"立";s:3:"⽵";s:3:"竹";s:3:"⽶";s:3:"米";s:3:"⽷";s:3:"糸";s:3:"⽸";s:3:"缶";s:3:"⽹";s:3:"网";s:3:"⽺";s:3:"羊";s:3:"⽻";s:3:"羽";s:3:"⽼";s:3:"老";s:3:"⽽";s:3:"而";s:3:"⽾";s:3:"耒";s:3:"⽿";s:3:"耳";s:3:"⾀";s:3:"聿";s:3:"⾁";s:3:"肉";s:3:"⾂";s:3:"臣";s:3:"⾃";s:3:"自";s:3:"⾄";s:3:"至";s:3:"⾅";s:3:"臼";s:3:"⾆";s:3:"舌";s:3:"⾇";s:3:"舛";s:3:"⾈";s:3:"舟";s:3:"⾉";s:3:"艮";s:3:"⾊";s:3:"色";s:3:"⾋";s:3:"艸";s:3:"⾌";s:3:"虍";s:3:"⾍";s:3:"虫";s:3:"⾎";s:3:"血";s:3:"⾏";s:3:"行";s:3:"⾐";s:3:"衣";s:3:"⾑";s:3:"襾";s:3:"⾒";s:3:"見";s:3:"⾓";s:3:"角";s:3:"⾔";s:3:"言";s:3:"⾕";s:3:"谷";s:3:"⾖";s:3:"豆";s:3:"⾗";s:3:"豕";s:3:"⾘";s:3:"豸";s:3:"⾙";s:3:"貝";s:3:"⾚";s:3:"赤";s:3:"⾛";s:3:"走";s:3:"⾜";s:3:"足";s:3:"⾝";s:3:"身";s:3:"⾞";s:3:"車";s:3:"⾟";s:3:"辛";s:3:"⾠";s:3:"辰";s:3:"⾡";s:3:"辵";s:3:"⾢";s:3:"邑";s:3:"⾣";s:3:"酉";s:3:"⾤";s:3:"釆";s:3:"⾥";s:3:"里";s:3:"⾦";s:3:"金";s:3:"⾧";s:3:"長";s:3:"⾨";s:3:"門";s:3:"⾩";s:3:"阜";s:3:"⾪";s:3:"隶";s:3:"⾫";s:3:"隹";s:3:"⾬";s:3:"雨";s:3:"⾭";s:3:"靑";s:3:"⾮";s:3:"非";s:3:"⾯";s:3:"面";s:3:"⾰";s:3:"革";s:3:"⾱";s:3:"韋";s:3:"⾲";s:3:"韭";s:3:"⾳";s:3:"音";s:3:"⾴";s:3:"頁";s:3:"⾵";s:3:"風";s:3:"⾶";s:3:"飛";s:3:"⾷";s:3:"食";s:3:"⾸";s:3:"首";s:3:"⾹";s:3:"香";s:3:"⾺";s:3:"馬";s:3:"⾻";s:3:"骨";s:3:"⾼";s:3:"高";s:3:"⾽";s:3:"髟";s:3:"⾾";s:3:"鬥";s:3:"⾿";s:3:"鬯";s:3:"⿀";s:3:"鬲";s:3:"⿁";s:3:"鬼";s:3:"⿂";s:3:"魚";s:3:"⿃";s:3:"鳥";s:3:"⿄";s:3:"鹵";s:3:"⿅";s:3:"鹿";s:3:"⿆";s:3:"麥";s:3:"⿇";s:3:"麻";s:3:"⿈";s:3:"黃";s:3:"⿉";s:3:"黍";s:3:"⿊";s:3:"黑";s:3:"⿋";s:3:"黹";s:3:"⿌";s:3:"黽";s:3:"⿍";s:3:"鼎";s:3:"⿎";s:3:"鼓";s:3:"⿏";s:3:"鼠";s:3:"⿐";s:3:"鼻";s:3:"⿑";s:3:"齊";s:3:"⿒";s:3:"齒";s:3:"⿓";s:3:"龍";s:3:"⿔";s:3:"龜";s:3:"⿕";s:3:"龠";s:3:" ";s:1:" ";s:3:"〶";s:3:"〒";s:3:"〸";s:3:"十";s:3:"〹";s:3:"卄";s:3:"〺";s:3:"卅";s:3:"が";s:6:"が";s:3:"ぎ";s:6:"ぎ";s:3:"ぐ";s:6:"ぐ";s:3:"げ";s:6:"げ";s:3:"ご";s:6:"ご";s:3:"ざ";s:6:"ざ";s:3:"じ";s:6:"じ";s:3:"ず";s:6:"ず";s:3:"ぜ";s:6:"ぜ";s:3:"ぞ";s:6:"ぞ";s:3:"だ";s:6:"だ";s:3:"ぢ";s:6:"ぢ";s:3:"づ";s:6:"づ";s:3:"で";s:6:"で";s:3:"ど";s:6:"ど";s:3:"ば";s:6:"ば";s:3:"ぱ";s:6:"ぱ";s:3:"び";s:6:"び";s:3:"ぴ";s:6:"ぴ";s:3:"ぶ";s:6:"ぶ";s:3:"ぷ";s:6:"ぷ";s:3:"べ";s:6:"べ";s:3:"ぺ";s:6:"ぺ";s:3:"ぼ";s:6:"ぼ";s:3:"ぽ";s:6:"ぽ";s:3:"ゔ";s:6:"ゔ";s:3:"゛";s:4:" ゙";s:3:"゜";s:4:" ゚";s:3:"ゞ";s:6:"ゞ";s:3:"ゟ";s:6:"より";s:3:"ガ";s:6:"ガ";s:3:"ギ";s:6:"ギ";s:3:"グ";s:6:"グ";s:3:"ゲ";s:6:"ゲ";s:3:"ゴ";s:6:"ゴ";s:3:"ザ";s:6:"ザ";s:3:"ジ";s:6:"ジ";s:3:"ズ";s:6:"ズ";s:3:"ゼ";s:6:"ゼ";s:3:"ゾ";s:6:"ゾ";s:3:"ダ";s:6:"ダ";s:3:"ヂ";s:6:"ヂ";s:3:"ヅ";s:6:"ヅ";s:3:"デ";s:6:"デ";s:3:"ド";s:6:"ド";s:3:"バ";s:6:"バ";s:3:"パ";s:6:"パ";s:3:"ビ";s:6:"ビ";s:3:"ピ";s:6:"ピ";s:3:"ブ";s:6:"ブ";s:3:"プ";s:6:"プ";s:3:"ベ";s:6:"ベ";s:3:"ペ";s:6:"ペ";s:3:"ボ";s:6:"ボ";s:3:"ポ";s:6:"ポ";s:3:"ヴ";s:6:"ヴ";s:3:"ヷ";s:6:"ヷ";s:3:"ヸ";s:6:"ヸ";s:3:"ヹ";s:6:"ヹ";s:3:"ヺ";s:6:"ヺ";s:3:"ヾ";s:6:"ヾ";s:3:"ヿ";s:6:"コト";s:3:"ㄱ";s:3:"ᄀ";s:3:"ㄲ";s:3:"ᄁ";s:3:"ㄳ";s:3:"ᆪ";s:3:"ㄴ";s:3:"ᄂ";s:3:"ㄵ";s:3:"ᆬ";s:3:"ㄶ";s:3:"ᆭ";s:3:"ㄷ";s:3:"ᄃ";s:3:"ㄸ";s:3:"ᄄ";s:3:"ㄹ";s:3:"ᄅ";s:3:"ㄺ";s:3:"ᆰ";s:3:"ㄻ";s:3:"ᆱ";s:3:"ㄼ";s:3:"ᆲ";s:3:"ㄽ";s:3:"ᆳ";s:3:"ㄾ";s:3:"ᆴ";s:3:"ㄿ";s:3:"ᆵ";s:3:"ㅀ";s:3:"ᄚ";s:3:"ㅁ";s:3:"ᄆ";s:3:"ㅂ";s:3:"ᄇ";s:3:"ㅃ";s:3:"ᄈ";s:3:"ㅄ";s:3:"ᄡ";s:3:"ㅅ";s:3:"ᄉ";s:3:"ㅆ";s:3:"ᄊ";s:3:"ㅇ";s:3:"ᄋ";s:3:"ㅈ";s:3:"ᄌ";s:3:"ㅉ";s:3:"ᄍ";s:3:"ㅊ";s:3:"ᄎ";s:3:"ㅋ";s:3:"ᄏ";s:3:"ㅌ";s:3:"ᄐ";s:3:"ㅍ";s:3:"ᄑ";s:3:"ㅎ";s:3:"ᄒ";s:3:"ㅏ";s:3:"ᅡ";s:3:"ㅐ";s:3:"ᅢ";s:3:"ㅑ";s:3:"ᅣ";s:3:"ㅒ";s:3:"ᅤ";s:3:"ㅓ";s:3:"ᅥ";s:3:"ㅔ";s:3:"ᅦ";s:3:"ㅕ";s:3:"ᅧ";s:3:"ㅖ";s:3:"ᅨ";s:3:"ㅗ";s:3:"ᅩ";s:3:"ㅘ";s:3:"ᅪ";s:3:"ㅙ";s:3:"ᅫ";s:3:"ㅚ";s:3:"ᅬ";s:3:"ㅛ";s:3:"ᅭ";s:3:"ㅜ";s:3:"ᅮ";s:3:"ㅝ";s:3:"ᅯ";s:3:"ㅞ";s:3:"ᅰ";s:3:"ㅟ";s:3:"ᅱ";s:3:"ㅠ";s:3:"ᅲ";s:3:"ㅡ";s:3:"ᅳ";s:3:"ㅢ";s:3:"ᅴ";s:3:"ㅣ";s:3:"ᅵ";s:3:"ㅤ";s:3:"ᅠ";s:3:"ㅥ";s:3:"ᄔ";s:3:"ㅦ";s:3:"ᄕ";s:3:"ㅧ";s:3:"ᇇ";s:3:"ㅨ";s:3:"ᇈ";s:3:"ㅩ";s:3:"ᇌ";s:3:"ㅪ";s:3:"ᇎ";s:3:"ㅫ";s:3:"ᇓ";s:3:"ㅬ";s:3:"ᇗ";s:3:"ㅭ";s:3:"ᇙ";s:3:"ㅮ";s:3:"ᄜ";s:3:"ㅯ";s:3:"ᇝ";s:3:"ㅰ";s:3:"ᇟ";s:3:"ㅱ";s:3:"ᄝ";s:3:"ㅲ";s:3:"ᄞ";s:3:"ㅳ";s:3:"ᄠ";s:3:"ㅴ";s:3:"ᄢ";s:3:"ㅵ";s:3:"ᄣ";s:3:"ㅶ";s:3:"ᄧ";s:3:"ㅷ";s:3:"ᄩ";s:3:"ㅸ";s:3:"ᄫ";s:3:"ㅹ";s:3:"ᄬ";s:3:"ㅺ";s:3:"ᄭ";s:3:"ㅻ";s:3:"ᄮ";s:3:"ㅼ";s:3:"ᄯ";s:3:"ㅽ";s:3:"ᄲ";s:3:"ㅾ";s:3:"ᄶ";s:3:"ㅿ";s:3:"ᅀ";s:3:"ㆀ";s:3:"ᅇ";s:3:"ㆁ";s:3:"ᅌ";s:3:"ㆂ";s:3:"ᇱ";s:3:"ㆃ";s:3:"ᇲ";s:3:"ㆄ";s:3:"ᅗ";s:3:"ㆅ";s:3:"ᅘ";s:3:"ㆆ";s:3:"ᅙ";s:3:"ㆇ";s:3:"ᆄ";s:3:"ㆈ";s:3:"ᆅ";s:3:"ㆉ";s:3:"ᆈ";s:3:"ㆊ";s:3:"ᆑ";s:3:"ㆋ";s:3:"ᆒ";s:3:"ㆌ";s:3:"ᆔ";s:3:"ㆍ";s:3:"ᆞ";s:3:"ㆎ";s:3:"ᆡ";s:3:"㆒";s:3:"一";s:3:"㆓";s:3:"二";s:3:"㆔";s:3:"三";s:3:"㆕";s:3:"四";s:3:"㆖";s:3:"上";s:3:"㆗";s:3:"中";s:3:"㆘";s:3:"下";s:3:"㆙";s:3:"甲";s:3:"㆚";s:3:"乙";s:3:"㆛";s:3:"丙";s:3:"㆜";s:3:"丁";s:3:"㆝";s:3:"天";s:3:"㆞";s:3:"地";s:3:"㆟";s:3:"人";s:3:"㈀";s:5:"(ᄀ)";s:3:"㈁";s:5:"(ᄂ)";s:3:"㈂";s:5:"(ᄃ)";s:3:"㈃";s:5:"(ᄅ)";s:3:"㈄";s:5:"(ᄆ)";s:3:"㈅";s:5:"(ᄇ)";s:3:"㈆";s:5:"(ᄉ)";s:3:"㈇";s:5:"(ᄋ)";s:3:"㈈";s:5:"(ᄌ)";s:3:"㈉";s:5:"(ᄎ)";s:3:"㈊";s:5:"(ᄏ)";s:3:"㈋";s:5:"(ᄐ)";s:3:"㈌";s:5:"(ᄑ)";s:3:"㈍";s:5:"(ᄒ)";s:3:"㈎";s:8:"(가)";s:3:"㈏";s:8:"(나)";s:3:"㈐";s:8:"(다)";s:3:"㈑";s:8:"(라)";s:3:"㈒";s:8:"(마)";s:3:"㈓";s:8:"(바)";s:3:"㈔";s:8:"(사)";s:3:"㈕";s:8:"(아)";s:3:"㈖";s:8:"(자)";s:3:"㈗";s:8:"(차)";s:3:"㈘";s:8:"(카)";s:3:"㈙";s:8:"(타)";s:3:"㈚";s:8:"(파)";s:3:"㈛";s:8:"(하)";s:3:"㈜";s:8:"(주)";s:3:"㈝";s:17:"(오전)";s:3:"㈞";s:14:"(오후)";s:3:"㈠";s:5:"(一)";s:3:"㈡";s:5:"(二)";s:3:"㈢";s:5:"(三)";s:3:"㈣";s:5:"(四)";s:3:"㈤";s:5:"(五)";s:3:"㈥";s:5:"(六)";s:3:"㈦";s:5:"(七)";s:3:"㈧";s:5:"(八)";s:3:"㈨";s:5:"(九)";s:3:"㈩";s:5:"(十)";s:3:"㈪";s:5:"(月)";s:3:"㈫";s:5:"(火)";s:3:"㈬";s:5:"(水)";s:3:"㈭";s:5:"(木)";s:3:"㈮";s:5:"(金)";s:3:"㈯";s:5:"(土)";s:3:"㈰";s:5:"(日)";s:3:"㈱";s:5:"(株)";s:3:"㈲";s:5:"(有)";s:3:"㈳";s:5:"(社)";s:3:"㈴";s:5:"(名)";s:3:"㈵";s:5:"(特)";s:3:"㈶";s:5:"(財)";s:3:"㈷";s:5:"(祝)";s:3:"㈸";s:5:"(労)";s:3:"㈹";s:5:"(代)";s:3:"㈺";s:5:"(呼)";s:3:"㈻";s:5:"(学)";s:3:"㈼";s:5:"(監)";s:3:"㈽";s:5:"(企)";s:3:"㈾";s:5:"(資)";s:3:"㈿";s:5:"(協)";s:3:"㉀";s:5:"(祭)";s:3:"㉁";s:5:"(休)";s:3:"㉂";s:5:"(自)";s:3:"㉃";s:5:"(至)";s:3:"㉐";s:3:"PTE";s:3:"㉑";s:2:"21";s:3:"㉒";s:2:"22";s:3:"㉓";s:2:"23";s:3:"㉔";s:2:"24";s:3:"㉕";s:2:"25";s:3:"㉖";s:2:"26";s:3:"㉗";s:2:"27";s:3:"㉘";s:2:"28";s:3:"㉙";s:2:"29";s:3:"㉚";s:2:"30";s:3:"㉛";s:2:"31";s:3:"㉜";s:2:"32";s:3:"㉝";s:2:"33";s:3:"㉞";s:2:"34";s:3:"㉟";s:2:"35";s:3:"㉠";s:3:"ᄀ";s:3:"㉡";s:3:"ᄂ";s:3:"㉢";s:3:"ᄃ";s:3:"㉣";s:3:"ᄅ";s:3:"㉤";s:3:"ᄆ";s:3:"㉥";s:3:"ᄇ";s:3:"㉦";s:3:"ᄉ";s:3:"㉧";s:3:"ᄋ";s:3:"㉨";s:3:"ᄌ";s:3:"㉩";s:3:"ᄎ";s:3:"㉪";s:3:"ᄏ";s:3:"㉫";s:3:"ᄐ";s:3:"㉬";s:3:"ᄑ";s:3:"㉭";s:3:"ᄒ";s:3:"㉮";s:6:"가";s:3:"㉯";s:6:"나";s:3:"㉰";s:6:"다";s:3:"㉱";s:6:"라";s:3:"㉲";s:6:"마";s:3:"㉳";s:6:"바";s:3:"㉴";s:6:"사";s:3:"㉵";s:6:"아";s:3:"㉶";s:6:"자";s:3:"㉷";s:6:"차";s:3:"㉸";s:6:"카";s:3:"㉹";s:6:"타";s:3:"㉺";s:6:"파";s:3:"㉻";s:6:"하";s:3:"㉼";s:15:"참고";s:3:"㉽";s:12:"주의";s:3:"㉾";s:6:"우";s:3:"㊀";s:3:"一";s:3:"㊁";s:3:"二";s:3:"㊂";s:3:"三";s:3:"㊃";s:3:"四";s:3:"㊄";s:3:"五";s:3:"㊅";s:3:"六";s:3:"㊆";s:3:"七";s:3:"㊇";s:3:"八";s:3:"㊈";s:3:"九";s:3:"㊉";s:3:"十";s:3:"㊊";s:3:"月";s:3:"㊋";s:3:"火";s:3:"㊌";s:3:"水";s:3:"㊍";s:3:"木";s:3:"㊎";s:3:"金";s:3:"㊏";s:3:"土";s:3:"㊐";s:3:"日";s:3:"㊑";s:3:"株";s:3:"㊒";s:3:"有";s:3:"㊓";s:3:"社";s:3:"㊔";s:3:"名";s:3:"㊕";s:3:"特";s:3:"㊖";s:3:"財";s:3:"㊗";s:3:"祝";s:3:"㊘";s:3:"労";s:3:"㊙";s:3:"秘";s:3:"㊚";s:3:"男";s:3:"㊛";s:3:"女";s:3:"㊜";s:3:"適";s:3:"㊝";s:3:"優";s:3:"㊞";s:3:"印";s:3:"㊟";s:3:"注";s:3:"㊠";s:3:"項";s:3:"㊡";s:3:"休";s:3:"㊢";s:3:"写";s:3:"㊣";s:3:"正";s:3:"㊤";s:3:"上";s:3:"㊥";s:3:"中";s:3:"㊦";s:3:"下";s:3:"㊧";s:3:"左";s:3:"㊨";s:3:"右";s:3:"㊩";s:3:"医";s:3:"㊪";s:3:"宗";s:3:"㊫";s:3:"学";s:3:"㊬";s:3:"監";s:3:"㊭";s:3:"企";s:3:"㊮";s:3:"資";s:3:"㊯";s:3:"協";s:3:"㊰";s:3:"夜";s:3:"㊱";s:2:"36";s:3:"㊲";s:2:"37";s:3:"㊳";s:2:"38";s:3:"㊴";s:2:"39";s:3:"㊵";s:2:"40";s:3:"㊶";s:2:"41";s:3:"㊷";s:2:"42";s:3:"㊸";s:2:"43";s:3:"㊹";s:2:"44";s:3:"㊺";s:2:"45";s:3:"㊻";s:2:"46";s:3:"㊼";s:2:"47";s:3:"㊽";s:2:"48";s:3:"㊾";s:2:"49";s:3:"㊿";s:2:"50";s:3:"㋀";s:4:"1月";s:3:"㋁";s:4:"2月";s:3:"㋂";s:4:"3月";s:3:"㋃";s:4:"4月";s:3:"㋄";s:4:"5月";s:3:"㋅";s:4:"6月";s:3:"㋆";s:4:"7月";s:3:"㋇";s:4:"8月";s:3:"㋈";s:4:"9月";s:3:"㋉";s:5:"10月";s:3:"㋊";s:5:"11月";s:3:"㋋";s:5:"12月";s:3:"㋌";s:2:"Hg";s:3:"㋍";s:3:"erg";s:3:"㋎";s:2:"eV";s:3:"㋏";s:3:"LTD";s:3:"㋐";s:3:"ア";s:3:"㋑";s:3:"イ";s:3:"㋒";s:3:"ウ";s:3:"㋓";s:3:"エ";s:3:"㋔";s:3:"オ";s:3:"㋕";s:3:"カ";s:3:"㋖";s:3:"キ";s:3:"㋗";s:3:"ク";s:3:"㋘";s:3:"ケ";s:3:"㋙";s:3:"コ";s:3:"㋚";s:3:"サ";s:3:"㋛";s:3:"シ";s:3:"㋜";s:3:"ス";s:3:"㋝";s:3:"セ";s:3:"㋞";s:3:"ソ";s:3:"㋟";s:3:"タ";s:3:"㋠";s:3:"チ";s:3:"㋡";s:3:"ツ";s:3:"㋢";s:3:"テ";s:3:"㋣";s:3:"ト";s:3:"㋤";s:3:"ナ";s:3:"㋥";s:3:"ニ";s:3:"㋦";s:3:"ヌ";s:3:"㋧";s:3:"ネ";s:3:"㋨";s:3:"ノ";s:3:"㋩";s:3:"ハ";s:3:"㋪";s:3:"ヒ";s:3:"㋫";s:3:"フ";s:3:"㋬";s:3:"ヘ";s:3:"㋭";s:3:"ホ";s:3:"㋮";s:3:"マ";s:3:"㋯";s:3:"ミ";s:3:"㋰";s:3:"ム";s:3:"㋱";s:3:"メ";s:3:"㋲";s:3:"モ";s:3:"㋳";s:3:"ヤ";s:3:"㋴";s:3:"ユ";s:3:"㋵";s:3:"ヨ";s:3:"㋶";s:3:"ラ";s:3:"㋷";s:3:"リ";s:3:"㋸";s:3:"ル";s:3:"㋹";s:3:"レ";s:3:"㋺";s:3:"ロ";s:3:"㋻";s:3:"ワ";s:3:"㋼";s:3:"ヰ";s:3:"㋽";s:3:"ヱ";s:3:"㋾";s:3:"ヲ";s:3:"㌀";s:15:"アパート";s:3:"㌁";s:12:"アルファ";s:3:"㌂";s:15:"アンペア";s:3:"㌃";s:9:"アール";s:3:"㌄";s:15:"イニング";s:3:"㌅";s:9:"インチ";s:3:"㌆";s:9:"ウォン";s:3:"㌇";s:18:"エスクード";s:3:"㌈";s:12:"エーカー";s:3:"㌉";s:9:"オンス";s:3:"㌊";s:9:"オーム";s:3:"㌋";s:9:"カイリ";s:3:"㌌";s:12:"カラット";s:3:"㌍";s:12:"カロリー";s:3:"㌎";s:12:"ガロン";s:3:"㌏";s:12:"ガンマ";s:3:"㌐";s:12:"ギガ";s:3:"㌑";s:12:"ギニー";s:3:"㌒";s:12:"キュリー";s:3:"㌓";s:18:"ギルダー";s:3:"㌔";s:6:"キロ";s:3:"㌕";s:18:"キログラム";s:3:"㌖";s:18:"キロメートル";s:3:"㌗";s:15:"キロワット";s:3:"㌘";s:12:"グラム";s:3:"㌙";s:18:"グラムトン";s:3:"㌚";s:18:"クルゼイロ";s:3:"㌛";s:12:"クローネ";s:3:"㌜";s:9:"ケース";s:3:"㌝";s:9:"コルナ";s:3:"㌞";s:12:"コーポ";s:3:"㌟";s:12:"サイクル";s:3:"㌠";s:15:"サンチーム";s:3:"㌡";s:15:"シリング";s:3:"㌢";s:9:"センチ";s:3:"㌣";s:9:"セント";s:3:"㌤";s:12:"ダース";s:3:"㌥";s:9:"デシ";s:3:"㌦";s:9:"ドル";s:3:"㌧";s:6:"トン";s:3:"㌨";s:6:"ナノ";s:3:"㌩";s:9:"ノット";s:3:"㌪";s:9:"ハイツ";s:3:"㌫";s:18:"パーセント";s:3:"㌬";s:12:"パーツ";s:3:"㌭";s:15:"バーレル";s:3:"㌮";s:18:"ピアストル";s:3:"㌯";s:12:"ピクル";s:3:"㌰";s:9:"ピコ";s:3:"㌱";s:9:"ビル";s:3:"㌲";s:18:"ファラッド";s:3:"㌳";s:12:"フィート";s:3:"㌴";s:18:"ブッシェル";s:3:"㌵";s:9:"フラン";s:3:"㌶";s:15:"ヘクタール";s:3:"㌷";s:9:"ペソ";s:3:"㌸";s:12:"ペニヒ";s:3:"㌹";s:9:"ヘルツ";s:3:"㌺";s:12:"ペンス";s:3:"㌻";s:15:"ページ";s:3:"㌼";s:12:"ベータ";s:3:"㌽";s:15:"ポイント";s:3:"㌾";s:12:"ボルト";s:3:"㌿";s:6:"ホン";s:3:"㍀";s:15:"ポンド";s:3:"㍁";s:9:"ホール";s:3:"㍂";s:9:"ホーン";s:3:"㍃";s:12:"マイクロ";s:3:"㍄";s:9:"マイル";s:3:"㍅";s:9:"マッハ";s:3:"㍆";s:9:"マルク";s:3:"㍇";s:15:"マンション";s:3:"㍈";s:12:"ミクロン";s:3:"㍉";s:6:"ミリ";s:3:"㍊";s:18:"ミリバール";s:3:"㍋";s:9:"メガ";s:3:"㍌";s:15:"メガトン";s:3:"㍍";s:12:"メートル";s:3:"㍎";s:12:"ヤード";s:3:"㍏";s:9:"ヤール";s:3:"㍐";s:9:"ユアン";s:3:"㍑";s:12:"リットル";s:3:"㍒";s:6:"リラ";s:3:"㍓";s:12:"ルピー";s:3:"㍔";s:15:"ルーブル";s:3:"㍕";s:6:"レム";s:3:"㍖";s:18:"レントゲン";s:3:"㍗";s:9:"ワット";s:3:"㍘";s:4:"0点";s:3:"㍙";s:4:"1点";s:3:"㍚";s:4:"2点";s:3:"㍛";s:4:"3点";s:3:"㍜";s:4:"4点";s:3:"㍝";s:4:"5点";s:3:"㍞";s:4:"6点";s:3:"㍟";s:4:"7点";s:3:"㍠";s:4:"8点";s:3:"㍡";s:4:"9点";s:3:"㍢";s:5:"10点";s:3:"㍣";s:5:"11点";s:3:"㍤";s:5:"12点";s:3:"㍥";s:5:"13点";s:3:"㍦";s:5:"14点";s:3:"㍧";s:5:"15点";s:3:"㍨";s:5:"16点";s:3:"㍩";s:5:"17点";s:3:"㍪";s:5:"18点";s:3:"㍫";s:5:"19点";s:3:"㍬";s:5:"20点";s:3:"㍭";s:5:"21点";s:3:"㍮";s:5:"22点";s:3:"㍯";s:5:"23点";s:3:"㍰";s:5:"24点";s:3:"㍱";s:3:"hPa";s:3:"㍲";s:2:"da";s:3:"㍳";s:2:"AU";s:3:"㍴";s:3:"bar";s:3:"㍵";s:2:"oV";s:3:"㍶";s:2:"pc";s:3:"㍷";s:2:"dm";s:3:"㍸";s:3:"dm2";s:3:"㍹";s:3:"dm3";s:3:"㍺";s:2:"IU";s:3:"㍻";s:6:"平成";s:3:"㍼";s:6:"昭和";s:3:"㍽";s:6:"大正";s:3:"㍾";s:6:"明治";s:3:"㍿";s:12:"株式会社";s:3:"㎀";s:2:"pA";s:3:"㎁";s:2:"nA";s:3:"㎂";s:3:"μA";s:3:"㎃";s:2:"mA";s:3:"㎄";s:2:"kA";s:3:"㎅";s:2:"KB";s:3:"㎆";s:2:"MB";s:3:"㎇";s:2:"GB";s:3:"㎈";s:3:"cal";s:3:"㎉";s:4:"kcal";s:3:"㎊";s:2:"pF";s:3:"㎋";s:2:"nF";s:3:"㎌";s:3:"μF";s:3:"㎍";s:3:"μg";s:3:"㎎";s:2:"mg";s:3:"㎏";s:2:"kg";s:3:"㎐";s:2:"Hz";s:3:"㎑";s:3:"kHz";s:3:"㎒";s:3:"MHz";s:3:"㎓";s:3:"GHz";s:3:"㎔";s:3:"THz";s:3:"㎕";s:3:"μl";s:3:"㎖";s:2:"ml";s:3:"㎗";s:2:"dl";s:3:"㎘";s:2:"kl";s:3:"㎙";s:2:"fm";s:3:"㎚";s:2:"nm";s:3:"㎛";s:3:"μm";s:3:"㎜";s:2:"mm";s:3:"㎝";s:2:"cm";s:3:"㎞";s:2:"km";s:3:"㎟";s:3:"mm2";s:3:"㎠";s:3:"cm2";s:3:"㎡";s:2:"m2";s:3:"㎢";s:3:"km2";s:3:"㎣";s:3:"mm3";s:3:"㎤";s:3:"cm3";s:3:"㎥";s:2:"m3";s:3:"㎦";s:3:"km3";s:3:"㎧";s:5:"m∕s";s:3:"㎨";s:6:"m∕s2";s:3:"㎩";s:2:"Pa";s:3:"㎪";s:3:"kPa";s:3:"㎫";s:3:"MPa";s:3:"㎬";s:3:"GPa";s:3:"㎭";s:3:"rad";s:3:"㎮";s:7:"rad∕s";s:3:"㎯";s:8:"rad∕s2";s:3:"㎰";s:2:"ps";s:3:"㎱";s:2:"ns";s:3:"㎲";s:3:"μs";s:3:"㎳";s:2:"ms";s:3:"㎴";s:2:"pV";s:3:"㎵";s:2:"nV";s:3:"㎶";s:3:"μV";s:3:"㎷";s:2:"mV";s:3:"㎸";s:2:"kV";s:3:"㎹";s:2:"MV";s:3:"㎺";s:2:"pW";s:3:"㎻";s:2:"nW";s:3:"㎼";s:3:"μW";s:3:"㎽";s:2:"mW";s:3:"㎾";s:2:"kW";s:3:"㎿";s:2:"MW";s:3:"㏀";s:3:"kΩ";s:3:"㏁";s:3:"MΩ";s:3:"㏂";s:4:"a.m.";s:3:"㏃";s:2:"Bq";s:3:"㏄";s:2:"cc";s:3:"㏅";s:2:"cd";s:3:"㏆";s:6:"C∕kg";s:3:"㏇";s:3:"Co.";s:3:"㏈";s:2:"dB";s:3:"㏉";s:2:"Gy";s:3:"㏊";s:2:"ha";s:3:"㏋";s:2:"HP";s:3:"㏌";s:2:"in";s:3:"㏍";s:2:"KK";s:3:"㏎";s:2:"KM";s:3:"㏏";s:2:"kt";s:3:"㏐";s:2:"lm";s:3:"㏑";s:2:"ln";s:3:"㏒";s:3:"log";s:3:"㏓";s:2:"lx";s:3:"㏔";s:2:"mb";s:3:"㏕";s:3:"mil";s:3:"㏖";s:3:"mol";s:3:"㏗";s:2:"PH";s:3:"㏘";s:4:"p.m.";s:3:"㏙";s:3:"PPM";s:3:"㏚";s:2:"PR";s:3:"㏛";s:2:"sr";s:3:"㏜";s:2:"Sv";s:3:"㏝";s:2:"Wb";s:3:"㏞";s:5:"V∕m";s:3:"㏟";s:5:"A∕m";s:3:"㏠";s:4:"1日";s:3:"㏡";s:4:"2日";s:3:"㏢";s:4:"3日";s:3:"㏣";s:4:"4日";s:3:"㏤";s:4:"5日";s:3:"㏥";s:4:"6日";s:3:"㏦";s:4:"7日";s:3:"㏧";s:4:"8日";s:3:"㏨";s:4:"9日";s:3:"㏩";s:5:"10日";s:3:"㏪";s:5:"11日";s:3:"㏫";s:5:"12日";s:3:"㏬";s:5:"13日";s:3:"㏭";s:5:"14日";s:3:"㏮";s:5:"15日";s:3:"㏯";s:5:"16日";s:3:"㏰";s:5:"17日";s:3:"㏱";s:5:"18日";s:3:"㏲";s:5:"19日";s:3:"㏳";s:5:"20日";s:3:"㏴";s:5:"21日";s:3:"㏵";s:5:"22日";s:3:"㏶";s:5:"23日";s:3:"㏷";s:5:"24日";s:3:"㏸";s:5:"25日";s:3:"㏹";s:5:"26日";s:3:"㏺";s:5:"27日";s:3:"㏻";s:5:"28日";s:3:"㏼";s:5:"29日";s:3:"㏽";s:5:"30日";s:3:"㏾";s:5:"31日";s:3:"㏿";s:3:"gal";s:3:"豈";s:3:"豈";s:3:"更";s:3:"更";s:3:"車";s:3:"車";s:3:"賈";s:3:"賈";s:3:"滑";s:3:"滑";s:3:"串";s:3:"串";s:3:"句";s:3:"句";s:3:"龜";s:3:"龜";s:3:"龜";s:3:"龜";s:3:"契";s:3:"契";s:3:"金";s:3:"金";s:3:"喇";s:3:"喇";s:3:"奈";s:3:"奈";s:3:"懶";s:3:"懶";s:3:"癩";s:3:"癩";s:3:"羅";s:3:"羅";s:3:"蘿";s:3:"蘿";s:3:"螺";s:3:"螺";s:3:"裸";s:3:"裸";s:3:"邏";s:3:"邏";s:3:"樂";s:3:"樂";s:3:"洛";s:3:"洛";s:3:"烙";s:3:"烙";s:3:"珞";s:3:"珞";s:3:"落";s:3:"落";s:3:"酪";s:3:"酪";s:3:"駱";s:3:"駱";s:3:"亂";s:3:"亂";s:3:"卵";s:3:"卵";s:3:"欄";s:3:"欄";s:3:"爛";s:3:"爛";s:3:"蘭";s:3:"蘭";s:3:"鸞";s:3:"鸞";s:3:"嵐";s:3:"嵐";s:3:"濫";s:3:"濫";s:3:"藍";s:3:"藍";s:3:"襤";s:3:"襤";s:3:"拉";s:3:"拉";s:3:"臘";s:3:"臘";s:3:"蠟";s:3:"蠟";s:3:"廊";s:3:"廊";s:3:"朗";s:3:"朗";s:3:"浪";s:3:"浪";s:3:"狼";s:3:"狼";s:3:"郎";s:3:"郎";s:3:"來";s:3:"來";s:3:"冷";s:3:"冷";s:3:"勞";s:3:"勞";s:3:"擄";s:3:"擄";s:3:"櫓";s:3:"櫓";s:3:"爐";s:3:"爐";s:3:"盧";s:3:"盧";s:3:"老";s:3:"老";s:3:"蘆";s:3:"蘆";s:3:"虜";s:3:"虜";s:3:"路";s:3:"路";s:3:"露";s:3:"露";s:3:"魯";s:3:"魯";s:3:"鷺";s:3:"鷺";s:3:"碌";s:3:"碌";s:3:"祿";s:3:"祿";s:3:"綠";s:3:"綠";s:3:"菉";s:3:"菉";s:3:"錄";s:3:"錄";s:3:"鹿";s:3:"鹿";s:3:"論";s:3:"論";s:3:"壟";s:3:"壟";s:3:"弄";s:3:"弄";s:3:"籠";s:3:"籠";s:3:"聾";s:3:"聾";s:3:"牢";s:3:"牢";s:3:"磊";s:3:"磊";s:3:"賂";s:3:"賂";s:3:"雷";s:3:"雷";s:3:"壘";s:3:"壘";s:3:"屢";s:3:"屢";s:3:"樓";s:3:"樓";s:3:"淚";s:3:"淚";s:3:"漏";s:3:"漏";s:3:"累";s:3:"累";s:3:"縷";s:3:"縷";s:3:"陋";s:3:"陋";s:3:"勒";s:3:"勒";s:3:"肋";s:3:"肋";s:3:"凜";s:3:"凜";s:3:"凌";s:3:"凌";s:3:"稜";s:3:"稜";s:3:"綾";s:3:"綾";s:3:"菱";s:3:"菱";s:3:"陵";s:3:"陵";s:3:"讀";s:3:"讀";s:3:"拏";s:3:"拏";s:3:"樂";s:3:"樂";s:3:"諾";s:3:"諾";s:3:"丹";s:3:"丹";s:3:"寧";s:3:"寧";s:3:"怒";s:3:"怒";s:3:"率";s:3:"率";s:3:"異";s:3:"異";s:3:"北";s:3:"北";s:3:"磻";s:3:"磻";s:3:"便";s:3:"便";s:3:"復";s:3:"復";s:3:"不";s:3:"不";s:3:"泌";s:3:"泌";s:3:"數";s:3:"數";s:3:"索";s:3:"索";s:3:"參";s:3:"參";s:3:"塞";s:3:"塞";s:3:"省";s:3:"省";s:3:"葉";s:3:"葉";s:3:"說";s:3:"說";s:3:"殺";s:3:"殺";s:3:"辰";s:3:"辰";s:3:"沈";s:3:"沈";s:3:"拾";s:3:"拾";s:3:"若";s:3:"若";s:3:"掠";s:3:"掠";s:3:"略";s:3:"略";s:3:"亮";s:3:"亮";s:3:"兩";s:3:"兩";s:3:"凉";s:3:"凉";s:3:"梁";s:3:"梁";s:3:"糧";s:3:"糧";s:3:"良";s:3:"良";s:3:"諒";s:3:"諒";s:3:"量";s:3:"量";s:3:"勵";s:3:"勵";s:3:"呂";s:3:"呂";s:3:"女";s:3:"女";s:3:"廬";s:3:"廬";s:3:"旅";s:3:"旅";s:3:"濾";s:3:"濾";s:3:"礪";s:3:"礪";s:3:"閭";s:3:"閭";s:3:"驪";s:3:"驪";s:3:"麗";s:3:"麗";s:3:"黎";s:3:"黎";s:3:"力";s:3:"力";s:3:"曆";s:3:"曆";s:3:"歷";s:3:"歷";s:3:"轢";s:3:"轢";s:3:"年";s:3:"年";s:3:"憐";s:3:"憐";s:3:"戀";s:3:"戀";s:3:"撚";s:3:"撚";s:3:"漣";s:3:"漣";s:3:"煉";s:3:"煉";s:3:"璉";s:3:"璉";s:3:"秊";s:3:"秊";s:3:"練";s:3:"練";s:3:"聯";s:3:"聯";s:3:"輦";s:3:"輦";s:3:"蓮";s:3:"蓮";s:3:"連";s:3:"連";s:3:"鍊";s:3:"鍊";s:3:"列";s:3:"列";s:3:"劣";s:3:"劣";s:3:"咽";s:3:"咽";s:3:"烈";s:3:"烈";s:3:"裂";s:3:"裂";s:3:"說";s:3:"說";s:3:"廉";s:3:"廉";s:3:"念";s:3:"念";s:3:"捻";s:3:"捻";s:3:"殮";s:3:"殮";s:3:"簾";s:3:"簾";s:3:"獵";s:3:"獵";s:3:"令";s:3:"令";s:3:"囹";s:3:"囹";s:3:"寧";s:3:"寧";s:3:"嶺";s:3:"嶺";s:3:"怜";s:3:"怜";s:3:"玲";s:3:"玲";s:3:"瑩";s:3:"瑩";s:3:"羚";s:3:"羚";s:3:"聆";s:3:"聆";s:3:"鈴";s:3:"鈴";s:3:"零";s:3:"零";s:3:"靈";s:3:"靈";s:3:"領";s:3:"領";s:3:"例";s:3:"例";s:3:"禮";s:3:"禮";s:3:"醴";s:3:"醴";s:3:"隸";s:3:"隸";s:3:"惡";s:3:"惡";s:3:"了";s:3:"了";s:3:"僚";s:3:"僚";s:3:"寮";s:3:"寮";s:3:"尿";s:3:"尿";s:3:"料";s:3:"料";s:3:"樂";s:3:"樂";s:3:"燎";s:3:"燎";s:3:"療";s:3:"療";s:3:"蓼";s:3:"蓼";s:3:"遼";s:3:"遼";s:3:"龍";s:3:"龍";s:3:"暈";s:3:"暈";s:3:"阮";s:3:"阮";s:3:"劉";s:3:"劉";s:3:"杻";s:3:"杻";s:3:"柳";s:3:"柳";s:3:"流";s:3:"流";s:3:"溜";s:3:"溜";s:3:"琉";s:3:"琉";s:3:"留";s:3:"留";s:3:"硫";s:3:"硫";s:3:"紐";s:3:"紐";s:3:"類";s:3:"類";s:3:"六";s:3:"六";s:3:"戮";s:3:"戮";s:3:"陸";s:3:"陸";s:3:"倫";s:3:"倫";s:3:"崙";s:3:"崙";s:3:"淪";s:3:"淪";s:3:"輪";s:3:"輪";s:3:"律";s:3:"律";s:3:"慄";s:3:"慄";s:3:"栗";s:3:"栗";s:3:"率";s:3:"率";s:3:"隆";s:3:"隆";s:3:"利";s:3:"利";s:3:"吏";s:3:"吏";s:3:"履";s:3:"履";s:3:"易";s:3:"易";s:3:"李";s:3:"李";s:3:"梨";s:3:"梨";s:3:"泥";s:3:"泥";s:3:"理";s:3:"理";s:3:"痢";s:3:"痢";s:3:"罹";s:3:"罹";s:3:"裏";s:3:"裏";s:3:"裡";s:3:"裡";s:3:"里";s:3:"里";s:3:"離";s:3:"離";s:3:"匿";s:3:"匿";s:3:"溺";s:3:"溺";s:3:"吝";s:3:"吝";s:3:"燐";s:3:"燐";s:3:"璘";s:3:"璘";s:3:"藺";s:3:"藺";s:3:"隣";s:3:"隣";s:3:"鱗";s:3:"鱗";s:3:"麟";s:3:"麟";s:3:"林";s:3:"林";s:3:"淋";s:3:"淋";s:3:"臨";s:3:"臨";s:3:"立";s:3:"立";s:3:"笠";s:3:"笠";s:3:"粒";s:3:"粒";s:3:"狀";s:3:"狀";s:3:"炙";s:3:"炙";s:3:"識";s:3:"識";s:3:"什";s:3:"什";s:3:"茶";s:3:"茶";s:3:"刺";s:3:"刺";s:3:"切";s:3:"切";s:3:"度";s:3:"度";s:3:"拓";s:3:"拓";s:3:"糖";s:3:"糖";s:3:"宅";s:3:"宅";s:3:"洞";s:3:"洞";s:3:"暴";s:3:"暴";s:3:"輻";s:3:"輻";s:3:"行";s:3:"行";s:3:"降";s:3:"降";s:3:"見";s:3:"見";s:3:"廓";s:3:"廓";s:3:"兀";s:3:"兀";s:3:"嗀";s:3:"嗀";s:3:"塚";s:3:"塚";s:3:"晴";s:3:"晴";s:3:"凞";s:3:"凞";s:3:"猪";s:3:"猪";s:3:"益";s:3:"益";s:3:"礼";s:3:"礼";s:3:"神";s:3:"神";s:3:"祥";s:3:"祥";s:3:"福";s:3:"福";s:3:"靖";s:3:"靖";s:3:"精";s:3:"精";s:3:"羽";s:3:"羽";s:3:"蘒";s:3:"蘒";s:3:"諸";s:3:"諸";s:3:"逸";s:3:"逸";s:3:"都";s:3:"都";s:3:"飯";s:3:"飯";s:3:"飼";s:3:"飼";s:3:"館";s:3:"館";s:3:"鶴";s:3:"鶴";s:3:"侮";s:3:"侮";s:3:"僧";s:3:"僧";s:3:"免";s:3:"免";s:3:"勉";s:3:"勉";s:3:"勤";s:3:"勤";s:3:"卑";s:3:"卑";s:3:"喝";s:3:"喝";s:3:"嘆";s:3:"嘆";s:3:"器";s:3:"器";s:3:"塀";s:3:"塀";s:3:"墨";s:3:"墨";s:3:"層";s:3:"層";s:3:"屮";s:3:"屮";s:3:"悔";s:3:"悔";s:3:"慨";s:3:"慨";s:3:"憎";s:3:"憎";s:3:"懲";s:3:"懲";s:3:"敏";s:3:"敏";s:3:"既";s:3:"既";s:3:"暑";s:3:"暑";s:3:"梅";s:3:"梅";s:3:"海";s:3:"海";s:3:"渚";s:3:"渚";s:3:"漢";s:3:"漢";s:3:"煮";s:3:"煮";s:3:"爫";s:3:"爫";s:3:"琢";s:3:"琢";s:3:"碑";s:3:"碑";s:3:"社";s:3:"社";s:3:"祉";s:3:"祉";s:3:"祈";s:3:"祈";s:3:"祐";s:3:"祐";s:3:"祖";s:3:"祖";s:3:"祝";s:3:"祝";s:3:"禍";s:3:"禍";s:3:"禎";s:3:"禎";s:3:"穀";s:3:"穀";s:3:"突";s:3:"突";s:3:"節";s:3:"節";s:3:"練";s:3:"練";s:3:"縉";s:3:"縉";s:3:"繁";s:3:"繁";s:3:"署";s:3:"署";s:3:"者";s:3:"者";s:3:"臭";s:3:"臭";s:3:"艹";s:3:"艹";s:3:"艹";s:3:"艹";s:3:"著";s:3:"著";s:3:"褐";s:3:"褐";s:3:"視";s:3:"視";s:3:"謁";s:3:"謁";s:3:"謹";s:3:"謹";s:3:"賓";s:3:"賓";s:3:"贈";s:3:"贈";s:3:"辶";s:3:"辶";s:3:"逸";s:3:"逸";s:3:"難";s:3:"難";s:3:"響";s:3:"響";s:3:"頻";s:3:"頻";s:3:"並";s:3:"並";s:3:"况";s:3:"况";s:3:"全";s:3:"全";s:3:"侀";s:3:"侀";s:3:"充";s:3:"充";s:3:"冀";s:3:"冀";s:3:"勇";s:3:"勇";s:3:"勺";s:3:"勺";s:3:"喝";s:3:"喝";s:3:"啕";s:3:"啕";s:3:"喙";s:3:"喙";s:3:"嗢";s:3:"嗢";s:3:"塚";s:3:"塚";s:3:"墳";s:3:"墳";s:3:"奄";s:3:"奄";s:3:"奔";s:3:"奔";s:3:"婢";s:3:"婢";s:3:"嬨";s:3:"嬨";s:3:"廒";s:3:"廒";s:3:"廙";s:3:"廙";s:3:"彩";s:3:"彩";s:3:"徭";s:3:"徭";s:3:"惘";s:3:"惘";s:3:"慎";s:3:"慎";s:3:"愈";s:3:"愈";s:3:"憎";s:3:"憎";s:3:"慠";s:3:"慠";s:3:"懲";s:3:"懲";s:3:"戴";s:3:"戴";s:3:"揄";s:3:"揄";s:3:"搜";s:3:"搜";s:3:"摒";s:3:"摒";s:3:"敖";s:3:"敖";s:3:"晴";s:3:"晴";s:3:"朗";s:3:"朗";s:3:"望";s:3:"望";s:3:"杖";s:3:"杖";s:3:"歹";s:3:"歹";s:3:"殺";s:3:"殺";s:3:"流";s:3:"流";s:3:"滛";s:3:"滛";s:3:"滋";s:3:"滋";s:3:"漢";s:3:"漢";s:3:"瀞";s:3:"瀞";s:3:"煮";s:3:"煮";s:3:"瞧";s:3:"瞧";s:3:"爵";s:3:"爵";s:3:"犯";s:3:"犯";s:3:"猪";s:3:"猪";s:3:"瑱";s:3:"瑱";s:3:"甆";s:3:"甆";s:3:"画";s:3:"画";s:3:"瘝";s:3:"瘝";s:3:"瘟";s:3:"瘟";s:3:"益";s:3:"益";s:3:"盛";s:3:"盛";s:3:"直";s:3:"直";s:3:"睊";s:3:"睊";s:3:"着";s:3:"着";s:3:"磌";s:3:"磌";s:3:"窱";s:3:"窱";s:3:"節";s:3:"節";s:3:"类";s:3:"类";s:3:"絛";s:3:"絛";s:3:"練";s:3:"練";s:3:"缾";s:3:"缾";s:3:"者";s:3:"者";s:3:"荒";s:3:"荒";s:3:"華";s:3:"華";s:3:"蝹";s:3:"蝹";s:3:"襁";s:3:"襁";s:3:"覆";s:3:"覆";s:3:"視";s:3:"視";s:3:"調";s:3:"調";s:3:"諸";s:3:"諸";s:3:"請";s:3:"請";s:3:"謁";s:3:"謁";s:3:"諾";s:3:"諾";s:3:"諭";s:3:"諭";s:3:"謹";s:3:"謹";s:3:"變";s:3:"變";s:3:"贈";s:3:"贈";s:3:"輸";s:3:"輸";s:3:"遲";s:3:"遲";s:3:"醙";s:3:"醙";s:3:"鉶";s:3:"鉶";s:3:"陼";s:3:"陼";s:3:"難";s:3:"難";s:3:"靖";s:3:"靖";s:3:"韛";s:3:"韛";s:3:"響";s:3:"響";s:3:"頋";s:3:"頋";s:3:"頻";s:3:"頻";s:3:"鬒";s:3:"鬒";s:3:"龜";s:3:"龜";s:3:"𢡊";s:4:"𢡊";s:3:"𢡄";s:4:"𢡄";s:3:"𣏕";s:4:"𣏕";s:3:"㮝";s:3:"㮝";s:3:"䀘";s:3:"䀘";s:3:"䀹";s:3:"䀹";s:3:"𥉉";s:4:"𥉉";s:3:"𥳐";s:4:"𥳐";s:3:"𧻓";s:4:"𧻓";s:3:"齃";s:3:"齃";s:3:"龎";s:3:"龎";s:3:"ff";s:2:"ff";s:3:"fi";s:2:"fi";s:3:"fl";s:2:"fl";s:3:"ffi";s:3:"ffi";s:3:"ffl";s:3:"ffl";s:3:"ſt";s:2:"st";s:3:"st";s:2:"st";s:3:"ﬓ";s:4:"մն";s:3:"ﬔ";s:4:"մե";s:3:"ﬕ";s:4:"մի";s:3:"ﬖ";s:4:"վն";s:3:"ﬗ";s:4:"մխ";s:3:"יִ";s:4:"יִ";s:3:"ײַ";s:4:"ײַ";s:3:"ﬠ";s:2:"ע";s:3:"ﬡ";s:2:"א";s:3:"ﬢ";s:2:"ד";s:3:"ﬣ";s:2:"ה";s:3:"ﬤ";s:2:"כ";s:3:"ﬥ";s:2:"ל";s:3:"ﬦ";s:2:"ם";s:3:"ﬧ";s:2:"ר";s:3:"ﬨ";s:2:"ת";s:3:"﬩";s:1:"+";s:3:"שׁ";s:4:"שׁ";s:3:"שׂ";s:4:"שׂ";s:3:"שּׁ";s:6:"שּׁ";s:3:"שּׂ";s:6:"שּׂ";s:3:"אַ";s:4:"אַ";s:3:"אָ";s:4:"אָ";s:3:"אּ";s:4:"אּ";s:3:"בּ";s:4:"בּ";s:3:"גּ";s:4:"גּ";s:3:"דּ";s:4:"דּ";s:3:"הּ";s:4:"הּ";s:3:"וּ";s:4:"וּ";s:3:"זּ";s:4:"זּ";s:3:"טּ";s:4:"טּ";s:3:"יּ";s:4:"יּ";s:3:"ךּ";s:4:"ךּ";s:3:"כּ";s:4:"כּ";s:3:"לּ";s:4:"לּ";s:3:"מּ";s:4:"מּ";s:3:"נּ";s:4:"נּ";s:3:"סּ";s:4:"סּ";s:3:"ףּ";s:4:"ףּ";s:3:"פּ";s:4:"פּ";s:3:"צּ";s:4:"צּ";s:3:"קּ";s:4:"קּ";s:3:"רּ";s:4:"רּ";s:3:"שּ";s:4:"שּ";s:3:"תּ";s:4:"תּ";s:3:"וֹ";s:4:"וֹ";s:3:"בֿ";s:4:"בֿ";s:3:"כֿ";s:4:"כֿ";s:3:"פֿ";s:4:"פֿ";s:3:"ﭏ";s:4:"אל";s:3:"ﭐ";s:2:"ٱ";s:3:"ﭑ";s:2:"ٱ";s:3:"ﭒ";s:2:"ٻ";s:3:"ﭓ";s:2:"ٻ";s:3:"ﭔ";s:2:"ٻ";s:3:"ﭕ";s:2:"ٻ";s:3:"ﭖ";s:2:"پ";s:3:"ﭗ";s:2:"پ";s:3:"ﭘ";s:2:"پ";s:3:"ﭙ";s:2:"پ";s:3:"ﭚ";s:2:"ڀ";s:3:"ﭛ";s:2:"ڀ";s:3:"ﭜ";s:2:"ڀ";s:3:"ﭝ";s:2:"ڀ";s:3:"ﭞ";s:2:"ٺ";s:3:"ﭟ";s:2:"ٺ";s:3:"ﭠ";s:2:"ٺ";s:3:"ﭡ";s:2:"ٺ";s:3:"ﭢ";s:2:"ٿ";s:3:"ﭣ";s:2:"ٿ";s:3:"ﭤ";s:2:"ٿ";s:3:"ﭥ";s:2:"ٿ";s:3:"ﭦ";s:2:"ٹ";s:3:"ﭧ";s:2:"ٹ";s:3:"ﭨ";s:2:"ٹ";s:3:"ﭩ";s:2:"ٹ";s:3:"ﭪ";s:2:"ڤ";s:3:"ﭫ";s:2:"ڤ";s:3:"ﭬ";s:2:"ڤ";s:3:"ﭭ";s:2:"ڤ";s:3:"ﭮ";s:2:"ڦ";s:3:"ﭯ";s:2:"ڦ";s:3:"ﭰ";s:2:"ڦ";s:3:"ﭱ";s:2:"ڦ";s:3:"ﭲ";s:2:"ڄ";s:3:"ﭳ";s:2:"ڄ";s:3:"ﭴ";s:2:"ڄ";s:3:"ﭵ";s:2:"ڄ";s:3:"ﭶ";s:2:"ڃ";s:3:"ﭷ";s:2:"ڃ";s:3:"ﭸ";s:2:"ڃ";s:3:"ﭹ";s:2:"ڃ";s:3:"ﭺ";s:2:"چ";s:3:"ﭻ";s:2:"چ";s:3:"ﭼ";s:2:"چ";s:3:"ﭽ";s:2:"چ";s:3:"ﭾ";s:2:"ڇ";s:3:"ﭿ";s:2:"ڇ";s:3:"ﮀ";s:2:"ڇ";s:3:"ﮁ";s:2:"ڇ";s:3:"ﮂ";s:2:"ڍ";s:3:"ﮃ";s:2:"ڍ";s:3:"ﮄ";s:2:"ڌ";s:3:"ﮅ";s:2:"ڌ";s:3:"ﮆ";s:2:"ڎ";s:3:"ﮇ";s:2:"ڎ";s:3:"ﮈ";s:2:"ڈ";s:3:"ﮉ";s:2:"ڈ";s:3:"ﮊ";s:2:"ژ";s:3:"ﮋ";s:2:"ژ";s:3:"ﮌ";s:2:"ڑ";s:3:"ﮍ";s:2:"ڑ";s:3:"ﮎ";s:2:"ک";s:3:"ﮏ";s:2:"ک";s:3:"ﮐ";s:2:"ک";s:3:"ﮑ";s:2:"ک";s:3:"ﮒ";s:2:"گ";s:3:"ﮓ";s:2:"گ";s:3:"ﮔ";s:2:"گ";s:3:"ﮕ";s:2:"گ";s:3:"ﮖ";s:2:"ڳ";s:3:"ﮗ";s:2:"ڳ";s:3:"ﮘ";s:2:"ڳ";s:3:"ﮙ";s:2:"ڳ";s:3:"ﮚ";s:2:"ڱ";s:3:"ﮛ";s:2:"ڱ";s:3:"ﮜ";s:2:"ڱ";s:3:"ﮝ";s:2:"ڱ";s:3:"ﮞ";s:2:"ں";s:3:"ﮟ";s:2:"ں";s:3:"ﮠ";s:2:"ڻ";s:3:"ﮡ";s:2:"ڻ";s:3:"ﮢ";s:2:"ڻ";s:3:"ﮣ";s:2:"ڻ";s:3:"ﮤ";s:4:"ۀ";s:3:"ﮥ";s:4:"ۀ";s:3:"ﮦ";s:2:"ہ";s:3:"ﮧ";s:2:"ہ";s:3:"ﮨ";s:2:"ہ";s:3:"ﮩ";s:2:"ہ";s:3:"ﮪ";s:2:"ھ";s:3:"ﮫ";s:2:"ھ";s:3:"ﮬ";s:2:"ھ";s:3:"ﮭ";s:2:"ھ";s:3:"ﮮ";s:2:"ے";s:3:"ﮯ";s:2:"ے";s:3:"ﮰ";s:4:"ۓ";s:3:"ﮱ";s:4:"ۓ";s:3:"ﯓ";s:2:"ڭ";s:3:"ﯔ";s:2:"ڭ";s:3:"ﯕ";s:2:"ڭ";s:3:"ﯖ";s:2:"ڭ";s:3:"ﯗ";s:2:"ۇ";s:3:"ﯘ";s:2:"ۇ";s:3:"ﯙ";s:2:"ۆ";s:3:"ﯚ";s:2:"ۆ";s:3:"ﯛ";s:2:"ۈ";s:3:"ﯜ";s:2:"ۈ";s:3:"ﯝ";s:4:"ۇٴ";s:3:"ﯞ";s:2:"ۋ";s:3:"ﯟ";s:2:"ۋ";s:3:"ﯠ";s:2:"ۅ";s:3:"ﯡ";s:2:"ۅ";s:3:"ﯢ";s:2:"ۉ";s:3:"ﯣ";s:2:"ۉ";s:3:"ﯤ";s:2:"ې";s:3:"ﯥ";s:2:"ې";s:3:"ﯦ";s:2:"ې";s:3:"ﯧ";s:2:"ې";s:3:"ﯨ";s:2:"ى";s:3:"ﯩ";s:2:"ى";s:3:"ﯪ";s:6:"ئا";s:3:"ﯫ";s:6:"ئا";s:3:"ﯬ";s:6:"ئە";s:3:"ﯭ";s:6:"ئە";s:3:"ﯮ";s:6:"ئو";s:3:"ﯯ";s:6:"ئو";s:3:"ﯰ";s:6:"ئۇ";s:3:"ﯱ";s:6:"ئۇ";s:3:"ﯲ";s:6:"ئۆ";s:3:"ﯳ";s:6:"ئۆ";s:3:"ﯴ";s:6:"ئۈ";s:3:"ﯵ";s:6:"ئۈ";s:3:"ﯶ";s:6:"ئې";s:3:"ﯷ";s:6:"ئې";s:3:"ﯸ";s:6:"ئې";s:3:"ﯹ";s:6:"ئى";s:3:"ﯺ";s:6:"ئى";s:3:"ﯻ";s:6:"ئى";s:3:"ﯼ";s:2:"ی";s:3:"ﯽ";s:2:"ی";s:3:"ﯾ";s:2:"ی";s:3:"ﯿ";s:2:"ی";s:3:"ﰀ";s:6:"ئج";s:3:"ﰁ";s:6:"ئح";s:3:"ﰂ";s:6:"ئم";s:3:"ﰃ";s:6:"ئى";s:3:"ﰄ";s:6:"ئي";s:3:"ﰅ";s:4:"بج";s:3:"ﰆ";s:4:"بح";s:3:"ﰇ";s:4:"بخ";s:3:"ﰈ";s:4:"بم";s:3:"ﰉ";s:4:"بى";s:3:"ﰊ";s:4:"بي";s:3:"ﰋ";s:4:"تج";s:3:"ﰌ";s:4:"تح";s:3:"ﰍ";s:4:"تخ";s:3:"ﰎ";s:4:"تم";s:3:"ﰏ";s:4:"تى";s:3:"ﰐ";s:4:"تي";s:3:"ﰑ";s:4:"ثج";s:3:"ﰒ";s:4:"ثم";s:3:"ﰓ";s:4:"ثى";s:3:"ﰔ";s:4:"ثي";s:3:"ﰕ";s:4:"جح";s:3:"ﰖ";s:4:"جم";s:3:"ﰗ";s:4:"حج";s:3:"ﰘ";s:4:"حم";s:3:"ﰙ";s:4:"خج";s:3:"ﰚ";s:4:"خح";s:3:"ﰛ";s:4:"خم";s:3:"ﰜ";s:4:"سج";s:3:"ﰝ";s:4:"سح";s:3:"ﰞ";s:4:"سخ";s:3:"ﰟ";s:4:"سم";s:3:"ﰠ";s:4:"صح";s:3:"ﰡ";s:4:"صم";s:3:"ﰢ";s:4:"ضج";s:3:"ﰣ";s:4:"ضح";s:3:"ﰤ";s:4:"ضخ";s:3:"ﰥ";s:4:"ضم";s:3:"ﰦ";s:4:"طح";s:3:"ﰧ";s:4:"طم";s:3:"ﰨ";s:4:"ظم";s:3:"ﰩ";s:4:"عج";s:3:"ﰪ";s:4:"عم";s:3:"ﰫ";s:4:"غج";s:3:"ﰬ";s:4:"غم";s:3:"ﰭ";s:4:"فج";s:3:"ﰮ";s:4:"فح";s:3:"ﰯ";s:4:"فخ";s:3:"ﰰ";s:4:"فم";s:3:"ﰱ";s:4:"فى";s:3:"ﰲ";s:4:"في";s:3:"ﰳ";s:4:"قح";s:3:"ﰴ";s:4:"قم";s:3:"ﰵ";s:4:"قى";s:3:"ﰶ";s:4:"قي";s:3:"ﰷ";s:4:"كا";s:3:"ﰸ";s:4:"كج";s:3:"ﰹ";s:4:"كح";s:3:"ﰺ";s:4:"كخ";s:3:"ﰻ";s:4:"كل";s:3:"ﰼ";s:4:"كم";s:3:"ﰽ";s:4:"كى";s:3:"ﰾ";s:4:"كي";s:3:"ﰿ";s:4:"لج";s:3:"ﱀ";s:4:"لح";s:3:"ﱁ";s:4:"لخ";s:3:"ﱂ";s:4:"لم";s:3:"ﱃ";s:4:"لى";s:3:"ﱄ";s:4:"لي";s:3:"ﱅ";s:4:"مج";s:3:"ﱆ";s:4:"مح";s:3:"ﱇ";s:4:"مخ";s:3:"ﱈ";s:4:"مم";s:3:"ﱉ";s:4:"مى";s:3:"ﱊ";s:4:"مي";s:3:"ﱋ";s:4:"نج";s:3:"ﱌ";s:4:"نح";s:3:"ﱍ";s:4:"نخ";s:3:"ﱎ";s:4:"نم";s:3:"ﱏ";s:4:"نى";s:3:"ﱐ";s:4:"ني";s:3:"ﱑ";s:4:"هج";s:3:"ﱒ";s:4:"هم";s:3:"ﱓ";s:4:"هى";s:3:"ﱔ";s:4:"هي";s:3:"ﱕ";s:4:"يج";s:3:"ﱖ";s:4:"يح";s:3:"ﱗ";s:4:"يخ";s:3:"ﱘ";s:4:"يم";s:3:"ﱙ";s:4:"يى";s:3:"ﱚ";s:4:"يي";s:3:"ﱛ";s:4:"ذٰ";s:3:"ﱜ";s:4:"رٰ";s:3:"ﱝ";s:4:"ىٰ";s:3:"ﱞ";s:5:" ٌّ";s:3:"ﱟ";s:5:" ٍّ";s:3:"ﱠ";s:5:" َّ";s:3:"ﱡ";s:5:" ُّ";s:3:"ﱢ";s:5:" ِّ";s:3:"ﱣ";s:5:" ّٰ";s:3:"ﱤ";s:6:"ئر";s:3:"ﱥ";s:6:"ئز";s:3:"ﱦ";s:6:"ئم";s:3:"ﱧ";s:6:"ئن";s:3:"ﱨ";s:6:"ئى";s:3:"ﱩ";s:6:"ئي";s:3:"ﱪ";s:4:"بر";s:3:"ﱫ";s:4:"بز";s:3:"ﱬ";s:4:"بم";s:3:"ﱭ";s:4:"بن";s:3:"ﱮ";s:4:"بى";s:3:"ﱯ";s:4:"بي";s:3:"ﱰ";s:4:"تر";s:3:"ﱱ";s:4:"تز";s:3:"ﱲ";s:4:"تم";s:3:"ﱳ";s:4:"تن";s:3:"ﱴ";s:4:"تى";s:3:"ﱵ";s:4:"تي";s:3:"ﱶ";s:4:"ثر";s:3:"ﱷ";s:4:"ثز";s:3:"ﱸ";s:4:"ثم";s:3:"ﱹ";s:4:"ثن";s:3:"ﱺ";s:4:"ثى";s:3:"ﱻ";s:4:"ثي";s:3:"ﱼ";s:4:"فى";s:3:"ﱽ";s:4:"في";s:3:"ﱾ";s:4:"قى";s:3:"ﱿ";s:4:"قي";s:3:"ﲀ";s:4:"كا";s:3:"ﲁ";s:4:"كل";s:3:"ﲂ";s:4:"كم";s:3:"ﲃ";s:4:"كى";s:3:"ﲄ";s:4:"كي";s:3:"ﲅ";s:4:"لم";s:3:"ﲆ";s:4:"لى";s:3:"ﲇ";s:4:"لي";s:3:"ﲈ";s:4:"ما";s:3:"ﲉ";s:4:"مم";s:3:"ﲊ";s:4:"نر";s:3:"ﲋ";s:4:"نز";s:3:"ﲌ";s:4:"نم";s:3:"ﲍ";s:4:"نن";s:3:"ﲎ";s:4:"نى";s:3:"ﲏ";s:4:"ني";s:3:"ﲐ";s:4:"ىٰ";s:3:"ﲑ";s:4:"ير";s:3:"ﲒ";s:4:"يز";s:3:"ﲓ";s:4:"يم";s:3:"ﲔ";s:4:"ين";s:3:"ﲕ";s:4:"يى";s:3:"ﲖ";s:4:"يي";s:3:"ﲗ";s:6:"ئج";s:3:"ﲘ";s:6:"ئح";s:3:"ﲙ";s:6:"ئخ";s:3:"ﲚ";s:6:"ئم";s:3:"ﲛ";s:6:"ئه";s:3:"ﲜ";s:4:"بج";s:3:"ﲝ";s:4:"بح";s:3:"ﲞ";s:4:"بخ";s:3:"ﲟ";s:4:"بم";s:3:"ﲠ";s:4:"به";s:3:"ﲡ";s:4:"تج";s:3:"ﲢ";s:4:"تح";s:3:"ﲣ";s:4:"تخ";s:3:"ﲤ";s:4:"تم";s:3:"ﲥ";s:4:"ته";s:3:"ﲦ";s:4:"ثم";s:3:"ﲧ";s:4:"جح";s:3:"ﲨ";s:4:"جم";s:3:"ﲩ";s:4:"حج";s:3:"ﲪ";s:4:"حم";s:3:"ﲫ";s:4:"خج";s:3:"ﲬ";s:4:"خم";s:3:"ﲭ";s:4:"سج";s:3:"ﲮ";s:4:"سح";s:3:"ﲯ";s:4:"سخ";s:3:"ﲰ";s:4:"سم";s:3:"ﲱ";s:4:"صح";s:3:"ﲲ";s:4:"صخ";s:3:"ﲳ";s:4:"صم";s:3:"ﲴ";s:4:"ضج";s:3:"ﲵ";s:4:"ضح";s:3:"ﲶ";s:4:"ضخ";s:3:"ﲷ";s:4:"ضم";s:3:"ﲸ";s:4:"طح";s:3:"ﲹ";s:4:"ظم";s:3:"ﲺ";s:4:"عج";s:3:"ﲻ";s:4:"عم";s:3:"ﲼ";s:4:"غج";s:3:"ﲽ";s:4:"غم";s:3:"ﲾ";s:4:"فج";s:3:"ﲿ";s:4:"فح";s:3:"ﳀ";s:4:"فخ";s:3:"ﳁ";s:4:"فم";s:3:"ﳂ";s:4:"قح";s:3:"ﳃ";s:4:"قم";s:3:"ﳄ";s:4:"كج";s:3:"ﳅ";s:4:"كح";s:3:"ﳆ";s:4:"كخ";s:3:"ﳇ";s:4:"كل";s:3:"ﳈ";s:4:"كم";s:3:"ﳉ";s:4:"لج";s:3:"ﳊ";s:4:"لح";s:3:"ﳋ";s:4:"لخ";s:3:"ﳌ";s:4:"لم";s:3:"ﳍ";s:4:"له";s:3:"ﳎ";s:4:"مج";s:3:"ﳏ";s:4:"مح";s:3:"ﳐ";s:4:"مخ";s:3:"ﳑ";s:4:"مم";s:3:"ﳒ";s:4:"نج";s:3:"ﳓ";s:4:"نح";s:3:"ﳔ";s:4:"نخ";s:3:"ﳕ";s:4:"نم";s:3:"ﳖ";s:4:"نه";s:3:"ﳗ";s:4:"هج";s:3:"ﳘ";s:4:"هم";s:3:"ﳙ";s:4:"هٰ";s:3:"ﳚ";s:4:"يج";s:3:"ﳛ";s:4:"يح";s:3:"ﳜ";s:4:"يخ";s:3:"ﳝ";s:4:"يم";s:3:"ﳞ";s:4:"يه";s:3:"ﳟ";s:6:"ئم";s:3:"ﳠ";s:6:"ئه";s:3:"ﳡ";s:4:"بم";s:3:"ﳢ";s:4:"به";s:3:"ﳣ";s:4:"تم";s:3:"ﳤ";s:4:"ته";s:3:"ﳥ";s:4:"ثم";s:3:"ﳦ";s:4:"ثه";s:3:"ﳧ";s:4:"سم";s:3:"ﳨ";s:4:"سه";s:3:"ﳩ";s:4:"شم";s:3:"ﳪ";s:4:"شه";s:3:"ﳫ";s:4:"كل";s:3:"ﳬ";s:4:"كم";s:3:"ﳭ";s:4:"لم";s:3:"ﳮ";s:4:"نم";s:3:"ﳯ";s:4:"نه";s:3:"ﳰ";s:4:"يم";s:3:"ﳱ";s:4:"يه";s:3:"ﳲ";s:6:"ـَّ";s:3:"ﳳ";s:6:"ـُّ";s:3:"ﳴ";s:6:"ـِّ";s:3:"ﳵ";s:4:"طى";s:3:"ﳶ";s:4:"طي";s:3:"ﳷ";s:4:"عى";s:3:"ﳸ";s:4:"عي";s:3:"ﳹ";s:4:"غى";s:3:"ﳺ";s:4:"غي";s:3:"ﳻ";s:4:"سى";s:3:"ﳼ";s:4:"سي";s:3:"ﳽ";s:4:"شى";s:3:"ﳾ";s:4:"شي";s:3:"ﳿ";s:4:"حى";s:3:"ﴀ";s:4:"حي";s:3:"ﴁ";s:4:"جى";s:3:"ﴂ";s:4:"جي";s:3:"ﴃ";s:4:"خى";s:3:"ﴄ";s:4:"خي";s:3:"ﴅ";s:4:"صى";s:3:"ﴆ";s:4:"صي";s:3:"ﴇ";s:4:"ضى";s:3:"ﴈ";s:4:"ضي";s:3:"ﴉ";s:4:"شج";s:3:"ﴊ";s:4:"شح";s:3:"ﴋ";s:4:"شخ";s:3:"ﴌ";s:4:"شم";s:3:"ﴍ";s:4:"شر";s:3:"ﴎ";s:4:"سر";s:3:"ﴏ";s:4:"صر";s:3:"ﴐ";s:4:"ضر";s:3:"ﴑ";s:4:"طى";s:3:"ﴒ";s:4:"طي";s:3:"ﴓ";s:4:"عى";s:3:"ﴔ";s:4:"عي";s:3:"ﴕ";s:4:"غى";s:3:"ﴖ";s:4:"غي";s:3:"ﴗ";s:4:"سى";s:3:"ﴘ";s:4:"سي";s:3:"ﴙ";s:4:"شى";s:3:"ﴚ";s:4:"شي";s:3:"ﴛ";s:4:"حى";s:3:"ﴜ";s:4:"حي";s:3:"ﴝ";s:4:"جى";s:3:"ﴞ";s:4:"جي";s:3:"ﴟ";s:4:"خى";s:3:"ﴠ";s:4:"خي";s:3:"ﴡ";s:4:"صى";s:3:"ﴢ";s:4:"صي";s:3:"ﴣ";s:4:"ضى";s:3:"ﴤ";s:4:"ضي";s:3:"ﴥ";s:4:"شج";s:3:"ﴦ";s:4:"شح";s:3:"ﴧ";s:4:"شخ";s:3:"ﴨ";s:4:"شم";s:3:"ﴩ";s:4:"شر";s:3:"ﴪ";s:4:"سر";s:3:"ﴫ";s:4:"صر";s:3:"ﴬ";s:4:"ضر";s:3:"ﴭ";s:4:"شج";s:3:"ﴮ";s:4:"شح";s:3:"ﴯ";s:4:"شخ";s:3:"ﴰ";s:4:"شم";s:3:"ﴱ";s:4:"سه";s:3:"ﴲ";s:4:"شه";s:3:"ﴳ";s:4:"طم";s:3:"ﴴ";s:4:"سج";s:3:"ﴵ";s:4:"سح";s:3:"ﴶ";s:4:"سخ";s:3:"ﴷ";s:4:"شج";s:3:"ﴸ";s:4:"شح";s:3:"ﴹ";s:4:"شخ";s:3:"ﴺ";s:4:"طم";s:3:"ﴻ";s:4:"ظم";s:3:"ﴼ";s:4:"اً";s:3:"ﴽ";s:4:"اً";s:3:"ﵐ";s:6:"تجم";s:3:"ﵑ";s:6:"تحج";s:3:"ﵒ";s:6:"تحج";s:3:"ﵓ";s:6:"تحم";s:3:"ﵔ";s:6:"تخم";s:3:"ﵕ";s:6:"تمج";s:3:"ﵖ";s:6:"تمح";s:3:"ﵗ";s:6:"تمخ";s:3:"ﵘ";s:6:"جمح";s:3:"ﵙ";s:6:"جمح";s:3:"ﵚ";s:6:"حمي";s:3:"ﵛ";s:6:"حمى";s:3:"ﵜ";s:6:"سحج";s:3:"ﵝ";s:6:"سجح";s:3:"ﵞ";s:6:"سجى";s:3:"ﵟ";s:6:"سمح";s:3:"ﵠ";s:6:"سمح";s:3:"ﵡ";s:6:"سمج";s:3:"ﵢ";s:6:"سمم";s:3:"ﵣ";s:6:"سمم";s:3:"ﵤ";s:6:"صحح";s:3:"ﵥ";s:6:"صحح";s:3:"ﵦ";s:6:"صمم";s:3:"ﵧ";s:6:"شحم";s:3:"ﵨ";s:6:"شحم";s:3:"ﵩ";s:6:"شجي";s:3:"ﵪ";s:6:"شمخ";s:3:"ﵫ";s:6:"شمخ";s:3:"ﵬ";s:6:"شمم";s:3:"ﵭ";s:6:"شمم";s:3:"ﵮ";s:6:"ضحى";s:3:"ﵯ";s:6:"ضخم";s:3:"ﵰ";s:6:"ضخم";s:3:"ﵱ";s:6:"طمح";s:3:"ﵲ";s:6:"طمح";s:3:"ﵳ";s:6:"طمم";s:3:"ﵴ";s:6:"طمي";s:3:"ﵵ";s:6:"عجم";s:3:"ﵶ";s:6:"عمم";s:3:"ﵷ";s:6:"عمم";s:3:"ﵸ";s:6:"عمى";s:3:"ﵹ";s:6:"غمم";s:3:"ﵺ";s:6:"غمي";s:3:"ﵻ";s:6:"غمى";s:3:"ﵼ";s:6:"فخم";s:3:"ﵽ";s:6:"فخم";s:3:"ﵾ";s:6:"قمح";s:3:"ﵿ";s:6:"قمم";s:3:"ﶀ";s:6:"لحم";s:3:"ﶁ";s:6:"لحي";s:3:"ﶂ";s:6:"لحى";s:3:"ﶃ";s:6:"لجج";s:3:"ﶄ";s:6:"لجج";s:3:"ﶅ";s:6:"لخم";s:3:"ﶆ";s:6:"لخم";s:3:"ﶇ";s:6:"لمح";s:3:"ﶈ";s:6:"لمح";s:3:"ﶉ";s:6:"محج";s:3:"ﶊ";s:6:"محم";s:3:"ﶋ";s:6:"محي";s:3:"ﶌ";s:6:"مجح";s:3:"ﶍ";s:6:"مجم";s:3:"ﶎ";s:6:"مخج";s:3:"ﶏ";s:6:"مخم";s:3:"ﶒ";s:6:"مجخ";s:3:"ﶓ";s:6:"همج";s:3:"ﶔ";s:6:"همم";s:3:"ﶕ";s:6:"نحم";s:3:"ﶖ";s:6:"نحى";s:3:"ﶗ";s:6:"نجم";s:3:"ﶘ";s:6:"نجم";s:3:"ﶙ";s:6:"نجى";s:3:"ﶚ";s:6:"نمي";s:3:"ﶛ";s:6:"نمى";s:3:"ﶜ";s:6:"يمم";s:3:"ﶝ";s:6:"يمم";s:3:"ﶞ";s:6:"بخي";s:3:"ﶟ";s:6:"تجي";s:3:"ﶠ";s:6:"تجى";s:3:"ﶡ";s:6:"تخي";s:3:"ﶢ";s:6:"تخى";s:3:"ﶣ";s:6:"تمي";s:3:"ﶤ";s:6:"تمى";s:3:"ﶥ";s:6:"جمي";s:3:"ﶦ";s:6:"جحى";s:3:"ﶧ";s:6:"جمى";s:3:"ﶨ";s:6:"سخى";s:3:"ﶩ";s:6:"صحي";s:3:"ﶪ";s:6:"شحي";s:3:"ﶫ";s:6:"ضحي";s:3:"ﶬ";s:6:"لجي";s:3:"ﶭ";s:6:"لمي";s:3:"ﶮ";s:6:"يحي";s:3:"ﶯ";s:6:"يجي";s:3:"ﶰ";s:6:"يمي";s:3:"ﶱ";s:6:"ممي";s:3:"ﶲ";s:6:"قمي";s:3:"ﶳ";s:6:"نحي";s:3:"ﶴ";s:6:"قمح";s:3:"ﶵ";s:6:"لحم";s:3:"ﶶ";s:6:"عمي";s:3:"ﶷ";s:6:"كمي";s:3:"ﶸ";s:6:"نجح";s:3:"ﶹ";s:6:"مخي";s:3:"ﶺ";s:6:"لجم";s:3:"ﶻ";s:6:"كمم";s:3:"ﶼ";s:6:"لجم";s:3:"ﶽ";s:6:"نجح";s:3:"ﶾ";s:6:"جحي";s:3:"ﶿ";s:6:"حجي";s:3:"ﷀ";s:6:"مجي";s:3:"ﷁ";s:6:"فمي";s:3:"ﷂ";s:6:"بحي";s:3:"ﷃ";s:6:"كمم";s:3:"ﷄ";s:6:"عجم";s:3:"ﷅ";s:6:"صمم";s:3:"ﷆ";s:6:"سخي";s:3:"ﷇ";s:6:"نجي";s:3:"ﷰ";s:6:"صلے";s:3:"ﷱ";s:6:"قلے";s:3:"ﷲ";s:8:"الله";s:3:"ﷳ";s:8:"اكبر";s:3:"ﷴ";s:8:"محمد";s:3:"ﷵ";s:8:"صلعم";s:3:"ﷶ";s:8:"رسول";s:3:"ﷷ";s:8:"عليه";s:3:"ﷸ";s:8:"وسلم";s:3:"ﷹ";s:6:"صلى";s:3:"ﷺ";s:33:"صلى الله عليه وسلم";s:3:"ﷻ";s:15:"جل جلاله";s:3:"﷼";s:8:"ریال";s:3:"︐";s:1:",";s:3:"︑";s:3:"、";s:3:"︒";s:3:"。";s:3:"︓";s:1:":";s:3:"︔";s:1:";";s:3:"︕";s:1:"!";s:3:"︖";s:1:"?";s:3:"︗";s:3:"〖";s:3:"︘";s:3:"〗";s:3:"︙";s:3:"...";s:3:"︰";s:2:"..";s:3:"︱";s:3:"—";s:3:"︲";s:3:"–";s:3:"︳";s:1:"_";s:3:"︴";s:1:"_";s:3:"︵";s:1:"(";s:3:"︶";s:1:")";s:3:"︷";s:1:"{";s:3:"︸";s:1:"}";s:3:"︹";s:3:"〔";s:3:"︺";s:3:"〕";s:3:"︻";s:3:"【";s:3:"︼";s:3:"】";s:3:"︽";s:3:"《";s:3:"︾";s:3:"》";s:3:"︿";s:3:"〈";s:3:"﹀";s:3:"〉";s:3:"﹁";s:3:"「";s:3:"﹂";s:3:"」";s:3:"﹃";s:3:"『";s:3:"﹄";s:3:"』";s:3:"﹇";s:1:"[";s:3:"﹈";s:1:"]";s:3:"﹉";s:3:" ̅";s:3:"﹊";s:3:" ̅";s:3:"﹋";s:3:" ̅";s:3:"﹌";s:3:" ̅";s:3:"﹍";s:1:"_";s:3:"﹎";s:1:"_";s:3:"﹏";s:1:"_";s:3:"﹐";s:1:",";s:3:"﹑";s:3:"、";s:3:"﹒";s:1:".";s:3:"﹔";s:1:";";s:3:"﹕";s:1:":";s:3:"﹖";s:1:"?";s:3:"﹗";s:1:"!";s:3:"﹘";s:3:"—";s:3:"﹙";s:1:"(";s:3:"﹚";s:1:")";s:3:"﹛";s:1:"{";s:3:"﹜";s:1:"}";s:3:"﹝";s:3:"〔";s:3:"﹞";s:3:"〕";s:3:"﹟";s:1:"#";s:3:"﹠";s:1:"&";s:3:"﹡";s:1:"*";s:3:"﹢";s:1:"+";s:3:"﹣";s:1:"-";s:3:"﹤";s:1:"<";s:3:"﹥";s:1:">";s:3:"﹦";s:1:"=";s:3:"﹨";s:1:"\\";s:3:"﹩";s:1:"$";s:3:"﹪";s:1:"%";s:3:"﹫";s:1:"@";s:3:"ﹰ";s:3:" ً";s:3:"ﹱ";s:4:"ـً";s:3:"ﹲ";s:3:" ٌ";s:3:"ﹴ";s:3:" ٍ";s:3:"ﹶ";s:3:" َ";s:3:"ﹷ";s:4:"ـَ";s:3:"ﹸ";s:3:" ُ";s:3:"ﹹ";s:4:"ـُ";s:3:"ﹺ";s:3:" ِ";s:3:"ﹻ";s:4:"ـِ";s:3:"ﹼ";s:3:" ّ";s:3:"ﹽ";s:4:"ـّ";s:3:"ﹾ";s:3:" ْ";s:3:"ﹿ";s:4:"ـْ";s:3:"ﺀ";s:2:"ء";s:3:"ﺁ";s:4:"آ";s:3:"ﺂ";s:4:"آ";s:3:"ﺃ";s:4:"أ";s:3:"ﺄ";s:4:"أ";s:3:"ﺅ";s:4:"ؤ";s:3:"ﺆ";s:4:"ؤ";s:3:"ﺇ";s:4:"إ";s:3:"ﺈ";s:4:"إ";s:3:"ﺉ";s:4:"ئ";s:3:"ﺊ";s:4:"ئ";s:3:"ﺋ";s:4:"ئ";s:3:"ﺌ";s:4:"ئ";s:3:"ﺍ";s:2:"ا";s:3:"ﺎ";s:2:"ا";s:3:"ﺏ";s:2:"ب";s:3:"ﺐ";s:2:"ب";s:3:"ﺑ";s:2:"ب";s:3:"ﺒ";s:2:"ب";s:3:"ﺓ";s:2:"ة";s:3:"ﺔ";s:2:"ة";s:3:"ﺕ";s:2:"ت";s:3:"ﺖ";s:2:"ت";s:3:"ﺗ";s:2:"ت";s:3:"ﺘ";s:2:"ت";s:3:"ﺙ";s:2:"ث";s:3:"ﺚ";s:2:"ث";s:3:"ﺛ";s:2:"ث";s:3:"ﺜ";s:2:"ث";s:3:"ﺝ";s:2:"ج";s:3:"ﺞ";s:2:"ج";s:3:"ﺟ";s:2:"ج";s:3:"ﺠ";s:2:"ج";s:3:"ﺡ";s:2:"ح";s:3:"ﺢ";s:2:"ح";s:3:"ﺣ";s:2:"ح";s:3:"ﺤ";s:2:"ح";s:3:"ﺥ";s:2:"خ";s:3:"ﺦ";s:2:"خ";s:3:"ﺧ";s:2:"خ";s:3:"ﺨ";s:2:"خ";s:3:"ﺩ";s:2:"د";s:3:"ﺪ";s:2:"د";s:3:"ﺫ";s:2:"ذ";s:3:"ﺬ";s:2:"ذ";s:3:"ﺭ";s:2:"ر";s:3:"ﺮ";s:2:"ر";s:3:"ﺯ";s:2:"ز";s:3:"ﺰ";s:2:"ز";s:3:"ﺱ";s:2:"س";s:3:"ﺲ";s:2:"س";s:3:"ﺳ";s:2:"س";s:3:"ﺴ";s:2:"س";s:3:"ﺵ";s:2:"ش";s:3:"ﺶ";s:2:"ش";s:3:"ﺷ";s:2:"ش";s:3:"ﺸ";s:2:"ش";s:3:"ﺹ";s:2:"ص";s:3:"ﺺ";s:2:"ص";s:3:"ﺻ";s:2:"ص";s:3:"ﺼ";s:2:"ص";s:3:"ﺽ";s:2:"ض";s:3:"ﺾ";s:2:"ض";s:3:"ﺿ";s:2:"ض";s:3:"ﻀ";s:2:"ض";s:3:"ﻁ";s:2:"ط";s:3:"ﻂ";s:2:"ط";s:3:"ﻃ";s:2:"ط";s:3:"ﻄ";s:2:"ط";s:3:"ﻅ";s:2:"ظ";s:3:"ﻆ";s:2:"ظ";s:3:"ﻇ";s:2:"ظ";s:3:"ﻈ";s:2:"ظ";s:3:"ﻉ";s:2:"ع";s:3:"ﻊ";s:2:"ع";s:3:"ﻋ";s:2:"ع";s:3:"ﻌ";s:2:"ع";s:3:"ﻍ";s:2:"غ";s:3:"ﻎ";s:2:"غ";s:3:"ﻏ";s:2:"غ";s:3:"ﻐ";s:2:"غ";s:3:"ﻑ";s:2:"ف";s:3:"ﻒ";s:2:"ف";s:3:"ﻓ";s:2:"ف";s:3:"ﻔ";s:2:"ف";s:3:"ﻕ";s:2:"ق";s:3:"ﻖ";s:2:"ق";s:3:"ﻗ";s:2:"ق";s:3:"ﻘ";s:2:"ق";s:3:"ﻙ";s:2:"ك";s:3:"ﻚ";s:2:"ك";s:3:"ﻛ";s:2:"ك";s:3:"ﻜ";s:2:"ك";s:3:"ﻝ";s:2:"ل";s:3:"ﻞ";s:2:"ل";s:3:"ﻟ";s:2:"ل";s:3:"ﻠ";s:2:"ل";s:3:"ﻡ";s:2:"م";s:3:"ﻢ";s:2:"م";s:3:"ﻣ";s:2:"م";s:3:"ﻤ";s:2:"م";s:3:"ﻥ";s:2:"ن";s:3:"ﻦ";s:2:"ن";s:3:"ﻧ";s:2:"ن";s:3:"ﻨ";s:2:"ن";s:3:"ﻩ";s:2:"ه";s:3:"ﻪ";s:2:"ه";s:3:"ﻫ";s:2:"ه";s:3:"ﻬ";s:2:"ه";s:3:"ﻭ";s:2:"و";s:3:"ﻮ";s:2:"و";s:3:"ﻯ";s:2:"ى";s:3:"ﻰ";s:2:"ى";s:3:"ﻱ";s:2:"ي";s:3:"ﻲ";s:2:"ي";s:3:"ﻳ";s:2:"ي";s:3:"ﻴ";s:2:"ي";s:3:"ﻵ";s:6:"لآ";s:3:"ﻶ";s:6:"لآ";s:3:"ﻷ";s:6:"لأ";s:3:"ﻸ";s:6:"لأ";s:3:"ﻹ";s:6:"لإ";s:3:"ﻺ";s:6:"لإ";s:3:"ﻻ";s:4:"لا";s:3:"ﻼ";s:4:"لا";s:3:"!";s:1:"!";s:3:""";s:1:""";s:3:"#";s:1:"#";s:3:"$";s:1:"$";s:3:"%";s:1:"%";s:3:"&";s:1:"&";s:3:"'";s:1:"\'";s:3:"(";s:1:"(";s:3:")";s:1:")";s:3:"*";s:1:"*";s:3:"+";s:1:"+";s:3:",";s:1:",";s:3:"-";s:1:"-";s:3:".";s:1:".";s:3:"/";s:1:"/";s:3:"0";s:1:"0";s:3:"1";s:1:"1";s:3:"2";s:1:"2";s:3:"3";s:1:"3";s:3:"4";s:1:"4";s:3:"5";s:1:"5";s:3:"6";s:1:"6";s:3:"7";s:1:"7";s:3:"8";s:1:"8";s:3:"9";s:1:"9";s:3:":";s:1:":";s:3:";";s:1:";";s:3:"<";s:1:"<";s:3:"=";s:1:"=";s:3:">";s:1:">";s:3:"?";s:1:"?";s:3:"@";s:1:"@";s:3:"A";s:1:"A";s:3:"B";s:1:"B";s:3:"C";s:1:"C";s:3:"D";s:1:"D";s:3:"E";s:1:"E";s:3:"F";s:1:"F";s:3:"G";s:1:"G";s:3:"H";s:1:"H";s:3:"I";s:1:"I";s:3:"J";s:1:"J";s:3:"K";s:1:"K";s:3:"L";s:1:"L";s:3:"M";s:1:"M";s:3:"N";s:1:"N";s:3:"O";s:1:"O";s:3:"P";s:1:"P";s:3:"Q";s:1:"Q";s:3:"R";s:1:"R";s:3:"S";s:1:"S";s:3:"T";s:1:"T";s:3:"U";s:1:"U";s:3:"V";s:1:"V";s:3:"W";s:1:"W";s:3:"X";s:1:"X";s:3:"Y";s:1:"Y";s:3:"Z";s:1:"Z";s:3:"[";s:1:"[";s:3:"\";s:1:"\\";s:3:"]";s:1:"]";s:3:"^";s:1:"^";s:3:"_";s:1:"_";s:3:"`";s:1:"`";s:3:"a";s:1:"a";s:3:"b";s:1:"b";s:3:"c";s:1:"c";s:3:"d";s:1:"d";s:3:"e";s:1:"e";s:3:"f";s:1:"f";s:3:"g";s:1:"g";s:3:"h";s:1:"h";s:3:"i";s:1:"i";s:3:"j";s:1:"j";s:3:"k";s:1:"k";s:3:"l";s:1:"l";s:3:"m";s:1:"m";s:3:"n";s:1:"n";s:3:"o";s:1:"o";s:3:"p";s:1:"p";s:3:"q";s:1:"q";s:3:"r";s:1:"r";s:3:"s";s:1:"s";s:3:"t";s:1:"t";s:3:"u";s:1:"u";s:3:"v";s:1:"v";s:3:"w";s:1:"w";s:3:"x";s:1:"x";s:3:"y";s:1:"y";s:3:"z";s:1:"z";s:3:"{";s:1:"{";s:3:"|";s:1:"|";s:3:"}";s:1:"}";s:3:"~";s:1:"~";s:3:"⦅";s:3:"⦅";s:3:"⦆";s:3:"⦆";s:3:"。";s:3:"。";s:3:"「";s:3:"「";s:3:"」";s:3:"」";s:3:"、";s:3:"、";s:3:"・";s:3:"・";s:3:"ヲ";s:3:"ヲ";s:3:"ァ";s:3:"ァ";s:3:"ィ";s:3:"ィ";s:3:"ゥ";s:3:"ゥ";s:3:"ェ";s:3:"ェ";s:3:"ォ";s:3:"ォ";s:3:"ャ";s:3:"ャ";s:3:"ュ";s:3:"ュ";s:3:"ョ";s:3:"ョ";s:3:"ッ";s:3:"ッ";s:3:"ー";s:3:"ー";s:3:"ア";s:3:"ア";s:3:"イ";s:3:"イ";s:3:"ウ";s:3:"ウ";s:3:"エ";s:3:"エ";s:3:"オ";s:3:"オ";s:3:"カ";s:3:"カ";s:3:"キ";s:3:"キ";s:3:"ク";s:3:"ク";s:3:"ケ";s:3:"ケ";s:3:"コ";s:3:"コ";s:3:"サ";s:3:"サ";s:3:"シ";s:3:"シ";s:3:"ス";s:3:"ス";s:3:"セ";s:3:"セ";s:3:"ソ";s:3:"ソ";s:3:"タ";s:3:"タ";s:3:"チ";s:3:"チ";s:3:"ツ";s:3:"ツ";s:3:"テ";s:3:"テ";s:3:"ト";s:3:"ト";s:3:"ナ";s:3:"ナ";s:3:"ニ";s:3:"ニ";s:3:"ヌ";s:3:"ヌ";s:3:"ネ";s:3:"ネ";s:3:"ノ";s:3:"ノ";s:3:"ハ";s:3:"ハ";s:3:"ヒ";s:3:"ヒ";s:3:"フ";s:3:"フ";s:3:"ヘ";s:3:"ヘ";s:3:"ホ";s:3:"ホ";s:3:"マ";s:3:"マ";s:3:"ミ";s:3:"ミ";s:3:"ム";s:3:"ム";s:3:"メ";s:3:"メ";s:3:"モ";s:3:"モ";s:3:"ヤ";s:3:"ヤ";s:3:"ユ";s:3:"ユ";s:3:"ヨ";s:3:"ヨ";s:3:"ラ";s:3:"ラ";s:3:"リ";s:3:"リ";s:3:"ル";s:3:"ル";s:3:"レ";s:3:"レ";s:3:"ロ";s:3:"ロ";s:3:"ワ";s:3:"ワ";s:3:"ン";s:3:"ン";s:3:"゙";s:3:"゙";s:3:"゚";s:3:"゚";s:3:"ᅠ";s:3:"ᅠ";s:3:"ᄀ";s:3:"ᄀ";s:3:"ᄁ";s:3:"ᄁ";s:3:"ᆪ";s:3:"ᆪ";s:3:"ᄂ";s:3:"ᄂ";s:3:"ᆬ";s:3:"ᆬ";s:3:"ᆭ";s:3:"ᆭ";s:3:"ᄃ";s:3:"ᄃ";s:3:"ᄄ";s:3:"ᄄ";s:3:"ᄅ";s:3:"ᄅ";s:3:"ᆰ";s:3:"ᆰ";s:3:"ᆱ";s:3:"ᆱ";s:3:"ᆲ";s:3:"ᆲ";s:3:"ᆳ";s:3:"ᆳ";s:3:"ᆴ";s:3:"ᆴ";s:3:"ᆵ";s:3:"ᆵ";s:3:"ᄚ";s:3:"ᄚ";s:3:"ᄆ";s:3:"ᄆ";s:3:"ᄇ";s:3:"ᄇ";s:3:"ᄈ";s:3:"ᄈ";s:3:"ᄡ";s:3:"ᄡ";s:3:"ᄉ";s:3:"ᄉ";s:3:"ᄊ";s:3:"ᄊ";s:3:"ᄋ";s:3:"ᄋ";s:3:"ᄌ";s:3:"ᄌ";s:3:"ᄍ";s:3:"ᄍ";s:3:"ᄎ";s:3:"ᄎ";s:3:"ᄏ";s:3:"ᄏ";s:3:"ᄐ";s:3:"ᄐ";s:3:"ᄑ";s:3:"ᄑ";s:3:"ᄒ";s:3:"ᄒ";s:3:"ᅡ";s:3:"ᅡ";s:3:"ᅢ";s:3:"ᅢ";s:3:"ᅣ";s:3:"ᅣ";s:3:"ᅤ";s:3:"ᅤ";s:3:"ᅥ";s:3:"ᅥ";s:3:"ᅦ";s:3:"ᅦ";s:3:"ᅧ";s:3:"ᅧ";s:3:"ᅨ";s:3:"ᅨ";s:3:"ᅩ";s:3:"ᅩ";s:3:"ᅪ";s:3:"ᅪ";s:3:"ᅫ";s:3:"ᅫ";s:3:"ᅬ";s:3:"ᅬ";s:3:"ᅭ";s:3:"ᅭ";s:3:"ᅮ";s:3:"ᅮ";s:3:"ᅯ";s:3:"ᅯ";s:3:"ᅰ";s:3:"ᅰ";s:3:"ᅱ";s:3:"ᅱ";s:3:"ᅲ";s:3:"ᅲ";s:3:"ᅳ";s:3:"ᅳ";s:3:"ᅴ";s:3:"ᅴ";s:3:"ᅵ";s:3:"ᅵ";s:3:"¢";s:2:"¢";s:3:"£";s:2:"£";s:3:"¬";s:2:"¬";s:3:" ̄";s:3:" ̄";s:3:"¦";s:2:"¦";s:3:"¥";s:2:"¥";s:3:"₩";s:3:"₩";s:3:"│";s:3:"│";s:3:"←";s:3:"←";s:3:"↑";s:3:"↑";s:3:"→";s:3:"→";s:3:"↓";s:3:"↓";s:3:"■";s:3:"■";s:3:"○";s:3:"○";s:4:"𝅗𝅥";s:8:"𝅗𝅥";s:4:"𝅘𝅥";s:8:"𝅘𝅥";s:4:"𝅘𝅥𝅮";s:12:"𝅘𝅥𝅮";s:4:"𝅘𝅥𝅯";s:12:"𝅘𝅥𝅯";s:4:"𝅘𝅥𝅰";s:12:"𝅘𝅥𝅰";s:4:"𝅘𝅥𝅱";s:12:"𝅘𝅥𝅱";s:4:"𝅘𝅥𝅲";s:12:"𝅘𝅥𝅲";s:4:"𝆹𝅥";s:8:"𝆹𝅥";s:4:"𝆺𝅥";s:8:"𝆺𝅥";s:4:"𝆹𝅥𝅮";s:12:"𝆹𝅥𝅮";s:4:"𝆺𝅥𝅮";s:12:"𝆺𝅥𝅮";s:4:"𝆹𝅥𝅯";s:12:"𝆹𝅥𝅯";s:4:"𝆺𝅥𝅯";s:12:"𝆺𝅥𝅯";s:4:"𝐀";s:1:"A";s:4:"𝐁";s:1:"B";s:4:"𝐂";s:1:"C";s:4:"𝐃";s:1:"D";s:4:"𝐄";s:1:"E";s:4:"𝐅";s:1:"F";s:4:"𝐆";s:1:"G";s:4:"𝐇";s:1:"H";s:4:"𝐈";s:1:"I";s:4:"𝐉";s:1:"J";s:4:"𝐊";s:1:"K";s:4:"𝐋";s:1:"L";s:4:"𝐌";s:1:"M";s:4:"𝐍";s:1:"N";s:4:"𝐎";s:1:"O";s:4:"𝐏";s:1:"P";s:4:"𝐐";s:1:"Q";s:4:"𝐑";s:1:"R";s:4:"𝐒";s:1:"S";s:4:"𝐓";s:1:"T";s:4:"𝐔";s:1:"U";s:4:"𝐕";s:1:"V";s:4:"𝐖";s:1:"W";s:4:"𝐗";s:1:"X";s:4:"𝐘";s:1:"Y";s:4:"𝐙";s:1:"Z";s:4:"𝐚";s:1:"a";s:4:"𝐛";s:1:"b";s:4:"𝐜";s:1:"c";s:4:"𝐝";s:1:"d";s:4:"𝐞";s:1:"e";s:4:"𝐟";s:1:"f";s:4:"𝐠";s:1:"g";s:4:"𝐡";s:1:"h";s:4:"𝐢";s:1:"i";s:4:"𝐣";s:1:"j";s:4:"𝐤";s:1:"k";s:4:"𝐥";s:1:"l";s:4:"𝐦";s:1:"m";s:4:"𝐧";s:1:"n";s:4:"𝐨";s:1:"o";s:4:"𝐩";s:1:"p";s:4:"𝐪";s:1:"q";s:4:"𝐫";s:1:"r";s:4:"𝐬";s:1:"s";s:4:"𝐭";s:1:"t";s:4:"𝐮";s:1:"u";s:4:"𝐯";s:1:"v";s:4:"𝐰";s:1:"w";s:4:"𝐱";s:1:"x";s:4:"𝐲";s:1:"y";s:4:"𝐳";s:1:"z";s:4:"𝐴";s:1:"A";s:4:"𝐵";s:1:"B";s:4:"𝐶";s:1:"C";s:4:"𝐷";s:1:"D";s:4:"𝐸";s:1:"E";s:4:"𝐹";s:1:"F";s:4:"𝐺";s:1:"G";s:4:"𝐻";s:1:"H";s:4:"𝐼";s:1:"I";s:4:"𝐽";s:1:"J";s:4:"𝐾";s:1:"K";s:4:"𝐿";s:1:"L";s:4:"𝑀";s:1:"M";s:4:"𝑁";s:1:"N";s:4:"𝑂";s:1:"O";s:4:"𝑃";s:1:"P";s:4:"𝑄";s:1:"Q";s:4:"𝑅";s:1:"R";s:4:"𝑆";s:1:"S";s:4:"𝑇";s:1:"T";s:4:"𝑈";s:1:"U";s:4:"𝑉";s:1:"V";s:4:"𝑊";s:1:"W";s:4:"𝑋";s:1:"X";s:4:"𝑌";s:1:"Y";s:4:"𝑍";s:1:"Z";s:4:"𝑎";s:1:"a";s:4:"𝑏";s:1:"b";s:4:"𝑐";s:1:"c";s:4:"𝑑";s:1:"d";s:4:"𝑒";s:1:"e";s:4:"𝑓";s:1:"f";s:4:"𝑔";s:1:"g";s:4:"𝑖";s:1:"i";s:4:"𝑗";s:1:"j";s:4:"𝑘";s:1:"k";s:4:"𝑙";s:1:"l";s:4:"𝑚";s:1:"m";s:4:"𝑛";s:1:"n";s:4:"𝑜";s:1:"o";s:4:"𝑝";s:1:"p";s:4:"𝑞";s:1:"q";s:4:"𝑟";s:1:"r";s:4:"𝑠";s:1:"s";s:4:"𝑡";s:1:"t";s:4:"𝑢";s:1:"u";s:4:"𝑣";s:1:"v";s:4:"𝑤";s:1:"w";s:4:"𝑥";s:1:"x";s:4:"𝑦";s:1:"y";s:4:"𝑧";s:1:"z";s:4:"𝑨";s:1:"A";s:4:"𝑩";s:1:"B";s:4:"𝑪";s:1:"C";s:4:"𝑫";s:1:"D";s:4:"𝑬";s:1:"E";s:4:"𝑭";s:1:"F";s:4:"𝑮";s:1:"G";s:4:"𝑯";s:1:"H";s:4:"𝑰";s:1:"I";s:4:"𝑱";s:1:"J";s:4:"𝑲";s:1:"K";s:4:"𝑳";s:1:"L";s:4:"𝑴";s:1:"M";s:4:"𝑵";s:1:"N";s:4:"𝑶";s:1:"O";s:4:"𝑷";s:1:"P";s:4:"𝑸";s:1:"Q";s:4:"𝑹";s:1:"R";s:4:"𝑺";s:1:"S";s:4:"𝑻";s:1:"T";s:4:"𝑼";s:1:"U";s:4:"𝑽";s:1:"V";s:4:"𝑾";s:1:"W";s:4:"𝑿";s:1:"X";s:4:"𝒀";s:1:"Y";s:4:"𝒁";s:1:"Z";s:4:"𝒂";s:1:"a";s:4:"𝒃";s:1:"b";s:4:"𝒄";s:1:"c";s:4:"𝒅";s:1:"d";s:4:"𝒆";s:1:"e";s:4:"𝒇";s:1:"f";s:4:"𝒈";s:1:"g";s:4:"𝒉";s:1:"h";s:4:"𝒊";s:1:"i";s:4:"𝒋";s:1:"j";s:4:"𝒌";s:1:"k";s:4:"𝒍";s:1:"l";s:4:"𝒎";s:1:"m";s:4:"𝒏";s:1:"n";s:4:"𝒐";s:1:"o";s:4:"𝒑";s:1:"p";s:4:"𝒒";s:1:"q";s:4:"𝒓";s:1:"r";s:4:"𝒔";s:1:"s";s:4:"𝒕";s:1:"t";s:4:"𝒖";s:1:"u";s:4:"𝒗";s:1:"v";s:4:"𝒘";s:1:"w";s:4:"𝒙";s:1:"x";s:4:"𝒚";s:1:"y";s:4:"𝒛";s:1:"z";s:4:"𝒜";s:1:"A";s:4:"𝒞";s:1:"C";s:4:"𝒟";s:1:"D";s:4:"𝒢";s:1:"G";s:4:"𝒥";s:1:"J";s:4:"𝒦";s:1:"K";s:4:"𝒩";s:1:"N";s:4:"𝒪";s:1:"O";s:4:"𝒫";s:1:"P";s:4:"𝒬";s:1:"Q";s:4:"𝒮";s:1:"S";s:4:"𝒯";s:1:"T";s:4:"𝒰";s:1:"U";s:4:"𝒱";s:1:"V";s:4:"𝒲";s:1:"W";s:4:"𝒳";s:1:"X";s:4:"𝒴";s:1:"Y";s:4:"𝒵";s:1:"Z";s:4:"𝒶";s:1:"a";s:4:"𝒷";s:1:"b";s:4:"𝒸";s:1:"c";s:4:"𝒹";s:1:"d";s:4:"𝒻";s:1:"f";s:4:"𝒽";s:1:"h";s:4:"𝒾";s:1:"i";s:4:"𝒿";s:1:"j";s:4:"𝓀";s:1:"k";s:4:"𝓁";s:1:"l";s:4:"𝓂";s:1:"m";s:4:"𝓃";s:1:"n";s:4:"𝓅";s:1:"p";s:4:"𝓆";s:1:"q";s:4:"𝓇";s:1:"r";s:4:"𝓈";s:1:"s";s:4:"𝓉";s:1:"t";s:4:"𝓊";s:1:"u";s:4:"𝓋";s:1:"v";s:4:"𝓌";s:1:"w";s:4:"𝓍";s:1:"x";s:4:"𝓎";s:1:"y";s:4:"𝓏";s:1:"z";s:4:"𝓐";s:1:"A";s:4:"𝓑";s:1:"B";s:4:"𝓒";s:1:"C";s:4:"𝓓";s:1:"D";s:4:"𝓔";s:1:"E";s:4:"𝓕";s:1:"F";s:4:"𝓖";s:1:"G";s:4:"𝓗";s:1:"H";s:4:"𝓘";s:1:"I";s:4:"𝓙";s:1:"J";s:4:"𝓚";s:1:"K";s:4:"𝓛";s:1:"L";s:4:"𝓜";s:1:"M";s:4:"𝓝";s:1:"N";s:4:"𝓞";s:1:"O";s:4:"𝓟";s:1:"P";s:4:"𝓠";s:1:"Q";s:4:"𝓡";s:1:"R";s:4:"𝓢";s:1:"S";s:4:"𝓣";s:1:"T";s:4:"𝓤";s:1:"U";s:4:"𝓥";s:1:"V";s:4:"𝓦";s:1:"W";s:4:"𝓧";s:1:"X";s:4:"𝓨";s:1:"Y";s:4:"𝓩";s:1:"Z";s:4:"𝓪";s:1:"a";s:4:"𝓫";s:1:"b";s:4:"𝓬";s:1:"c";s:4:"𝓭";s:1:"d";s:4:"𝓮";s:1:"e";s:4:"𝓯";s:1:"f";s:4:"𝓰";s:1:"g";s:4:"𝓱";s:1:"h";s:4:"𝓲";s:1:"i";s:4:"𝓳";s:1:"j";s:4:"𝓴";s:1:"k";s:4:"𝓵";s:1:"l";s:4:"𝓶";s:1:"m";s:4:"𝓷";s:1:"n";s:4:"𝓸";s:1:"o";s:4:"𝓹";s:1:"p";s:4:"𝓺";s:1:"q";s:4:"𝓻";s:1:"r";s:4:"𝓼";s:1:"s";s:4:"𝓽";s:1:"t";s:4:"𝓾";s:1:"u";s:4:"𝓿";s:1:"v";s:4:"𝔀";s:1:"w";s:4:"𝔁";s:1:"x";s:4:"𝔂";s:1:"y";s:4:"𝔃";s:1:"z";s:4:"𝔄";s:1:"A";s:4:"𝔅";s:1:"B";s:4:"𝔇";s:1:"D";s:4:"𝔈";s:1:"E";s:4:"𝔉";s:1:"F";s:4:"𝔊";s:1:"G";s:4:"𝔍";s:1:"J";s:4:"𝔎";s:1:"K";s:4:"𝔏";s:1:"L";s:4:"𝔐";s:1:"M";s:4:"𝔑";s:1:"N";s:4:"𝔒";s:1:"O";s:4:"𝔓";s:1:"P";s:4:"𝔔";s:1:"Q";s:4:"𝔖";s:1:"S";s:4:"𝔗";s:1:"T";s:4:"𝔘";s:1:"U";s:4:"𝔙";s:1:"V";s:4:"𝔚";s:1:"W";s:4:"𝔛";s:1:"X";s:4:"𝔜";s:1:"Y";s:4:"𝔞";s:1:"a";s:4:"𝔟";s:1:"b";s:4:"𝔠";s:1:"c";s:4:"𝔡";s:1:"d";s:4:"𝔢";s:1:"e";s:4:"𝔣";s:1:"f";s:4:"𝔤";s:1:"g";s:4:"𝔥";s:1:"h";s:4:"𝔦";s:1:"i";s:4:"𝔧";s:1:"j";s:4:"𝔨";s:1:"k";s:4:"𝔩";s:1:"l";s:4:"𝔪";s:1:"m";s:4:"𝔫";s:1:"n";s:4:"𝔬";s:1:"o";s:4:"𝔭";s:1:"p";s:4:"𝔮";s:1:"q";s:4:"𝔯";s:1:"r";s:4:"𝔰";s:1:"s";s:4:"𝔱";s:1:"t";s:4:"𝔲";s:1:"u";s:4:"𝔳";s:1:"v";s:4:"𝔴";s:1:"w";s:4:"𝔵";s:1:"x";s:4:"𝔶";s:1:"y";s:4:"𝔷";s:1:"z";s:4:"𝔸";s:1:"A";s:4:"𝔹";s:1:"B";s:4:"𝔻";s:1:"D";s:4:"𝔼";s:1:"E";s:4:"𝔽";s:1:"F";s:4:"𝔾";s:1:"G";s:4:"𝕀";s:1:"I";s:4:"𝕁";s:1:"J";s:4:"𝕂";s:1:"K";s:4:"𝕃";s:1:"L";s:4:"𝕄";s:1:"M";s:4:"𝕆";s:1:"O";s:4:"𝕊";s:1:"S";s:4:"𝕋";s:1:"T";s:4:"𝕌";s:1:"U";s:4:"𝕍";s:1:"V";s:4:"𝕎";s:1:"W";s:4:"𝕏";s:1:"X";s:4:"𝕐";s:1:"Y";s:4:"𝕒";s:1:"a";s:4:"𝕓";s:1:"b";s:4:"𝕔";s:1:"c";s:4:"𝕕";s:1:"d";s:4:"𝕖";s:1:"e";s:4:"𝕗";s:1:"f";s:4:"𝕘";s:1:"g";s:4:"𝕙";s:1:"h";s:4:"𝕚";s:1:"i";s:4:"𝕛";s:1:"j";s:4:"𝕜";s:1:"k";s:4:"𝕝";s:1:"l";s:4:"𝕞";s:1:"m";s:4:"𝕟";s:1:"n";s:4:"𝕠";s:1:"o";s:4:"𝕡";s:1:"p";s:4:"𝕢";s:1:"q";s:4:"𝕣";s:1:"r";s:4:"𝕤";s:1:"s";s:4:"𝕥";s:1:"t";s:4:"𝕦";s:1:"u";s:4:"𝕧";s:1:"v";s:4:"𝕨";s:1:"w";s:4:"𝕩";s:1:"x";s:4:"𝕪";s:1:"y";s:4:"𝕫";s:1:"z";s:4:"𝕬";s:1:"A";s:4:"𝕭";s:1:"B";s:4:"𝕮";s:1:"C";s:4:"𝕯";s:1:"D";s:4:"𝕰";s:1:"E";s:4:"𝕱";s:1:"F";s:4:"𝕲";s:1:"G";s:4:"𝕳";s:1:"H";s:4:"𝕴";s:1:"I";s:4:"𝕵";s:1:"J";s:4:"𝕶";s:1:"K";s:4:"𝕷";s:1:"L";s:4:"𝕸";s:1:"M";s:4:"𝕹";s:1:"N";s:4:"𝕺";s:1:"O";s:4:"𝕻";s:1:"P";s:4:"𝕼";s:1:"Q";s:4:"𝕽";s:1:"R";s:4:"𝕾";s:1:"S";s:4:"𝕿";s:1:"T";s:4:"𝖀";s:1:"U";s:4:"𝖁";s:1:"V";s:4:"𝖂";s:1:"W";s:4:"𝖃";s:1:"X";s:4:"𝖄";s:1:"Y";s:4:"𝖅";s:1:"Z";s:4:"𝖆";s:1:"a";s:4:"𝖇";s:1:"b";s:4:"𝖈";s:1:"c";s:4:"𝖉";s:1:"d";s:4:"𝖊";s:1:"e";s:4:"𝖋";s:1:"f";s:4:"𝖌";s:1:"g";s:4:"𝖍";s:1:"h";s:4:"𝖎";s:1:"i";s:4:"𝖏";s:1:"j";s:4:"𝖐";s:1:"k";s:4:"𝖑";s:1:"l";s:4:"𝖒";s:1:"m";s:4:"𝖓";s:1:"n";s:4:"𝖔";s:1:"o";s:4:"𝖕";s:1:"p";s:4:"𝖖";s:1:"q";s:4:"𝖗";s:1:"r";s:4:"𝖘";s:1:"s";s:4:"𝖙";s:1:"t";s:4:"𝖚";s:1:"u";s:4:"𝖛";s:1:"v";s:4:"𝖜";s:1:"w";s:4:"𝖝";s:1:"x";s:4:"𝖞";s:1:"y";s:4:"𝖟";s:1:"z";s:4:"𝖠";s:1:"A";s:4:"𝖡";s:1:"B";s:4:"𝖢";s:1:"C";s:4:"𝖣";s:1:"D";s:4:"𝖤";s:1:"E";s:4:"𝖥";s:1:"F";s:4:"𝖦";s:1:"G";s:4:"𝖧";s:1:"H";s:4:"𝖨";s:1:"I";s:4:"𝖩";s:1:"J";s:4:"𝖪";s:1:"K";s:4:"𝖫";s:1:"L";s:4:"𝖬";s:1:"M";s:4:"𝖭";s:1:"N";s:4:"𝖮";s:1:"O";s:4:"𝖯";s:1:"P";s:4:"𝖰";s:1:"Q";s:4:"𝖱";s:1:"R";s:4:"𝖲";s:1:"S";s:4:"𝖳";s:1:"T";s:4:"𝖴";s:1:"U";s:4:"𝖵";s:1:"V";s:4:"𝖶";s:1:"W";s:4:"𝖷";s:1:"X";s:4:"𝖸";s:1:"Y";s:4:"𝖹";s:1:"Z";s:4:"𝖺";s:1:"a";s:4:"𝖻";s:1:"b";s:4:"𝖼";s:1:"c";s:4:"𝖽";s:1:"d";s:4:"𝖾";s:1:"e";s:4:"𝖿";s:1:"f";s:4:"𝗀";s:1:"g";s:4:"𝗁";s:1:"h";s:4:"𝗂";s:1:"i";s:4:"𝗃";s:1:"j";s:4:"𝗄";s:1:"k";s:4:"𝗅";s:1:"l";s:4:"𝗆";s:1:"m";s:4:"𝗇";s:1:"n";s:4:"𝗈";s:1:"o";s:4:"𝗉";s:1:"p";s:4:"𝗊";s:1:"q";s:4:"𝗋";s:1:"r";s:4:"𝗌";s:1:"s";s:4:"𝗍";s:1:"t";s:4:"𝗎";s:1:"u";s:4:"𝗏";s:1:"v";s:4:"𝗐";s:1:"w";s:4:"𝗑";s:1:"x";s:4:"𝗒";s:1:"y";s:4:"𝗓";s:1:"z";s:4:"𝗔";s:1:"A";s:4:"𝗕";s:1:"B";s:4:"𝗖";s:1:"C";s:4:"𝗗";s:1:"D";s:4:"𝗘";s:1:"E";s:4:"𝗙";s:1:"F";s:4:"𝗚";s:1:"G";s:4:"𝗛";s:1:"H";s:4:"𝗜";s:1:"I";s:4:"𝗝";s:1:"J";s:4:"𝗞";s:1:"K";s:4:"𝗟";s:1:"L";s:4:"𝗠";s:1:"M";s:4:"𝗡";s:1:"N";s:4:"𝗢";s:1:"O";s:4:"𝗣";s:1:"P";s:4:"𝗤";s:1:"Q";s:4:"𝗥";s:1:"R";s:4:"𝗦";s:1:"S";s:4:"𝗧";s:1:"T";s:4:"𝗨";s:1:"U";s:4:"𝗩";s:1:"V";s:4:"𝗪";s:1:"W";s:4:"𝗫";s:1:"X";s:4:"𝗬";s:1:"Y";s:4:"𝗭";s:1:"Z";s:4:"𝗮";s:1:"a";s:4:"𝗯";s:1:"b";s:4:"𝗰";s:1:"c";s:4:"𝗱";s:1:"d";s:4:"𝗲";s:1:"e";s:4:"𝗳";s:1:"f";s:4:"𝗴";s:1:"g";s:4:"𝗵";s:1:"h";s:4:"𝗶";s:1:"i";s:4:"𝗷";s:1:"j";s:4:"𝗸";s:1:"k";s:4:"𝗹";s:1:"l";s:4:"𝗺";s:1:"m";s:4:"𝗻";s:1:"n";s:4:"𝗼";s:1:"o";s:4:"𝗽";s:1:"p";s:4:"𝗾";s:1:"q";s:4:"𝗿";s:1:"r";s:4:"𝘀";s:1:"s";s:4:"𝘁";s:1:"t";s:4:"𝘂";s:1:"u";s:4:"𝘃";s:1:"v";s:4:"𝘄";s:1:"w";s:4:"𝘅";s:1:"x";s:4:"𝘆";s:1:"y";s:4:"𝘇";s:1:"z";s:4:"𝘈";s:1:"A";s:4:"𝘉";s:1:"B";s:4:"𝘊";s:1:"C";s:4:"𝘋";s:1:"D";s:4:"𝘌";s:1:"E";s:4:"𝘍";s:1:"F";s:4:"𝘎";s:1:"G";s:4:"𝘏";s:1:"H";s:4:"𝘐";s:1:"I";s:4:"𝘑";s:1:"J";s:4:"𝘒";s:1:"K";s:4:"𝘓";s:1:"L";s:4:"𝘔";s:1:"M";s:4:"𝘕";s:1:"N";s:4:"𝘖";s:1:"O";s:4:"𝘗";s:1:"P";s:4:"𝘘";s:1:"Q";s:4:"𝘙";s:1:"R";s:4:"𝘚";s:1:"S";s:4:"𝘛";s:1:"T";s:4:"𝘜";s:1:"U";s:4:"𝘝";s:1:"V";s:4:"𝘞";s:1:"W";s:4:"𝘟";s:1:"X";s:4:"𝘠";s:1:"Y";s:4:"𝘡";s:1:"Z";s:4:"𝘢";s:1:"a";s:4:"𝘣";s:1:"b";s:4:"𝘤";s:1:"c";s:4:"𝘥";s:1:"d";s:4:"𝘦";s:1:"e";s:4:"𝘧";s:1:"f";s:4:"𝘨";s:1:"g";s:4:"𝘩";s:1:"h";s:4:"𝘪";s:1:"i";s:4:"𝘫";s:1:"j";s:4:"𝘬";s:1:"k";s:4:"𝘭";s:1:"l";s:4:"𝘮";s:1:"m";s:4:"𝘯";s:1:"n";s:4:"𝘰";s:1:"o";s:4:"𝘱";s:1:"p";s:4:"𝘲";s:1:"q";s:4:"𝘳";s:1:"r";s:4:"𝘴";s:1:"s";s:4:"𝘵";s:1:"t";s:4:"𝘶";s:1:"u";s:4:"𝘷";s:1:"v";s:4:"𝘸";s:1:"w";s:4:"𝘹";s:1:"x";s:4:"𝘺";s:1:"y";s:4:"𝘻";s:1:"z";s:4:"𝘼";s:1:"A";s:4:"𝘽";s:1:"B";s:4:"𝘾";s:1:"C";s:4:"𝘿";s:1:"D";s:4:"𝙀";s:1:"E";s:4:"𝙁";s:1:"F";s:4:"𝙂";s:1:"G";s:4:"𝙃";s:1:"H";s:4:"𝙄";s:1:"I";s:4:"𝙅";s:1:"J";s:4:"𝙆";s:1:"K";s:4:"𝙇";s:1:"L";s:4:"𝙈";s:1:"M";s:4:"𝙉";s:1:"N";s:4:"𝙊";s:1:"O";s:4:"𝙋";s:1:"P";s:4:"𝙌";s:1:"Q";s:4:"𝙍";s:1:"R";s:4:"𝙎";s:1:"S";s:4:"𝙏";s:1:"T";s:4:"𝙐";s:1:"U";s:4:"𝙑";s:1:"V";s:4:"𝙒";s:1:"W";s:4:"𝙓";s:1:"X";s:4:"𝙔";s:1:"Y";s:4:"𝙕";s:1:"Z";s:4:"𝙖";s:1:"a";s:4:"𝙗";s:1:"b";s:4:"𝙘";s:1:"c";s:4:"𝙙";s:1:"d";s:4:"𝙚";s:1:"e";s:4:"𝙛";s:1:"f";s:4:"𝙜";s:1:"g";s:4:"𝙝";s:1:"h";s:4:"𝙞";s:1:"i";s:4:"𝙟";s:1:"j";s:4:"𝙠";s:1:"k";s:4:"𝙡";s:1:"l";s:4:"𝙢";s:1:"m";s:4:"𝙣";s:1:"n";s:4:"𝙤";s:1:"o";s:4:"𝙥";s:1:"p";s:4:"𝙦";s:1:"q";s:4:"𝙧";s:1:"r";s:4:"𝙨";s:1:"s";s:4:"𝙩";s:1:"t";s:4:"𝙪";s:1:"u";s:4:"𝙫";s:1:"v";s:4:"𝙬";s:1:"w";s:4:"𝙭";s:1:"x";s:4:"𝙮";s:1:"y";s:4:"𝙯";s:1:"z";s:4:"𝙰";s:1:"A";s:4:"𝙱";s:1:"B";s:4:"𝙲";s:1:"C";s:4:"𝙳";s:1:"D";s:4:"𝙴";s:1:"E";s:4:"𝙵";s:1:"F";s:4:"𝙶";s:1:"G";s:4:"𝙷";s:1:"H";s:4:"𝙸";s:1:"I";s:4:"𝙹";s:1:"J";s:4:"𝙺";s:1:"K";s:4:"𝙻";s:1:"L";s:4:"𝙼";s:1:"M";s:4:"𝙽";s:1:"N";s:4:"𝙾";s:1:"O";s:4:"𝙿";s:1:"P";s:4:"𝚀";s:1:"Q";s:4:"𝚁";s:1:"R";s:4:"𝚂";s:1:"S";s:4:"𝚃";s:1:"T";s:4:"𝚄";s:1:"U";s:4:"𝚅";s:1:"V";s:4:"𝚆";s:1:"W";s:4:"𝚇";s:1:"X";s:4:"𝚈";s:1:"Y";s:4:"𝚉";s:1:"Z";s:4:"𝚊";s:1:"a";s:4:"𝚋";s:1:"b";s:4:"𝚌";s:1:"c";s:4:"𝚍";s:1:"d";s:4:"𝚎";s:1:"e";s:4:"𝚏";s:1:"f";s:4:"𝚐";s:1:"g";s:4:"𝚑";s:1:"h";s:4:"𝚒";s:1:"i";s:4:"𝚓";s:1:"j";s:4:"𝚔";s:1:"k";s:4:"𝚕";s:1:"l";s:4:"𝚖";s:1:"m";s:4:"𝚗";s:1:"n";s:4:"𝚘";s:1:"o";s:4:"𝚙";s:1:"p";s:4:"𝚚";s:1:"q";s:4:"𝚛";s:1:"r";s:4:"𝚜";s:1:"s";s:4:"𝚝";s:1:"t";s:4:"𝚞";s:1:"u";s:4:"𝚟";s:1:"v";s:4:"𝚠";s:1:"w";s:4:"𝚡";s:1:"x";s:4:"𝚢";s:1:"y";s:4:"𝚣";s:1:"z";s:4:"𝚤";s:2:"ı";s:4:"𝚥";s:2:"ȷ";s:4:"𝚨";s:2:"Α";s:4:"𝚩";s:2:"Β";s:4:"𝚪";s:2:"Γ";s:4:"𝚫";s:2:"Δ";s:4:"𝚬";s:2:"Ε";s:4:"𝚭";s:2:"Ζ";s:4:"𝚮";s:2:"Η";s:4:"𝚯";s:2:"Θ";s:4:"𝚰";s:2:"Ι";s:4:"𝚱";s:2:"Κ";s:4:"𝚲";s:2:"Λ";s:4:"𝚳";s:2:"Μ";s:4:"𝚴";s:2:"Ν";s:4:"𝚵";s:2:"Ξ";s:4:"𝚶";s:2:"Ο";s:4:"𝚷";s:2:"Π";s:4:"𝚸";s:2:"Ρ";s:4:"𝚹";s:2:"Θ";s:4:"𝚺";s:2:"Σ";s:4:"𝚻";s:2:"Τ";s:4:"𝚼";s:2:"Υ";s:4:"𝚽";s:2:"Φ";s:4:"𝚾";s:2:"Χ";s:4:"𝚿";s:2:"Ψ";s:4:"𝛀";s:2:"Ω";s:4:"𝛁";s:3:"∇";s:4:"𝛂";s:2:"α";s:4:"𝛃";s:2:"β";s:4:"𝛄";s:2:"γ";s:4:"𝛅";s:2:"δ";s:4:"𝛆";s:2:"ε";s:4:"𝛇";s:2:"ζ";s:4:"𝛈";s:2:"η";s:4:"𝛉";s:2:"θ";s:4:"𝛊";s:2:"ι";s:4:"𝛋";s:2:"κ";s:4:"𝛌";s:2:"λ";s:4:"𝛍";s:2:"μ";s:4:"𝛎";s:2:"ν";s:4:"𝛏";s:2:"ξ";s:4:"𝛐";s:2:"ο";s:4:"𝛑";s:2:"π";s:4:"𝛒";s:2:"ρ";s:4:"𝛓";s:2:"ς";s:4:"𝛔";s:2:"σ";s:4:"𝛕";s:2:"τ";s:4:"𝛖";s:2:"υ";s:4:"𝛗";s:2:"φ";s:4:"𝛘";s:2:"χ";s:4:"𝛙";s:2:"ψ";s:4:"𝛚";s:2:"ω";s:4:"𝛛";s:3:"∂";s:4:"𝛜";s:2:"ε";s:4:"𝛝";s:2:"θ";s:4:"𝛞";s:2:"κ";s:4:"𝛟";s:2:"φ";s:4:"𝛠";s:2:"ρ";s:4:"𝛡";s:2:"π";s:4:"𝛢";s:2:"Α";s:4:"𝛣";s:2:"Β";s:4:"𝛤";s:2:"Γ";s:4:"𝛥";s:2:"Δ";s:4:"𝛦";s:2:"Ε";s:4:"𝛧";s:2:"Ζ";s:4:"𝛨";s:2:"Η";s:4:"𝛩";s:2:"Θ";s:4:"𝛪";s:2:"Ι";s:4:"𝛫";s:2:"Κ";s:4:"𝛬";s:2:"Λ";s:4:"𝛭";s:2:"Μ";s:4:"𝛮";s:2:"Ν";s:4:"𝛯";s:2:"Ξ";s:4:"𝛰";s:2:"Ο";s:4:"𝛱";s:2:"Π";s:4:"𝛲";s:2:"Ρ";s:4:"𝛳";s:2:"Θ";s:4:"𝛴";s:2:"Σ";s:4:"𝛵";s:2:"Τ";s:4:"𝛶";s:2:"Υ";s:4:"𝛷";s:2:"Φ";s:4:"𝛸";s:2:"Χ";s:4:"𝛹";s:2:"Ψ";s:4:"𝛺";s:2:"Ω";s:4:"𝛻";s:3:"∇";s:4:"𝛼";s:2:"α";s:4:"𝛽";s:2:"β";s:4:"𝛾";s:2:"γ";s:4:"𝛿";s:2:"δ";s:4:"𝜀";s:2:"ε";s:4:"𝜁";s:2:"ζ";s:4:"𝜂";s:2:"η";s:4:"𝜃";s:2:"θ";s:4:"𝜄";s:2:"ι";s:4:"𝜅";s:2:"κ";s:4:"𝜆";s:2:"λ";s:4:"𝜇";s:2:"μ";s:4:"𝜈";s:2:"ν";s:4:"𝜉";s:2:"ξ";s:4:"𝜊";s:2:"ο";s:4:"𝜋";s:2:"π";s:4:"𝜌";s:2:"ρ";s:4:"𝜍";s:2:"ς";s:4:"𝜎";s:2:"σ";s:4:"𝜏";s:2:"τ";s:4:"𝜐";s:2:"υ";s:4:"𝜑";s:2:"φ";s:4:"𝜒";s:2:"χ";s:4:"𝜓";s:2:"ψ";s:4:"𝜔";s:2:"ω";s:4:"𝜕";s:3:"∂";s:4:"𝜖";s:2:"ε";s:4:"𝜗";s:2:"θ";s:4:"𝜘";s:2:"κ";s:4:"𝜙";s:2:"φ";s:4:"𝜚";s:2:"ρ";s:4:"𝜛";s:2:"π";s:4:"𝜜";s:2:"Α";s:4:"𝜝";s:2:"Β";s:4:"𝜞";s:2:"Γ";s:4:"𝜟";s:2:"Δ";s:4:"𝜠";s:2:"Ε";s:4:"𝜡";s:2:"Ζ";s:4:"𝜢";s:2:"Η";s:4:"𝜣";s:2:"Θ";s:4:"𝜤";s:2:"Ι";s:4:"𝜥";s:2:"Κ";s:4:"𝜦";s:2:"Λ";s:4:"𝜧";s:2:"Μ";s:4:"𝜨";s:2:"Ν";s:4:"𝜩";s:2:"Ξ";s:4:"𝜪";s:2:"Ο";s:4:"𝜫";s:2:"Π";s:4:"𝜬";s:2:"Ρ";s:4:"𝜭";s:2:"Θ";s:4:"𝜮";s:2:"Σ";s:4:"𝜯";s:2:"Τ";s:4:"𝜰";s:2:"Υ";s:4:"𝜱";s:2:"Φ";s:4:"𝜲";s:2:"Χ";s:4:"𝜳";s:2:"Ψ";s:4:"𝜴";s:2:"Ω";s:4:"𝜵";s:3:"∇";s:4:"𝜶";s:2:"α";s:4:"𝜷";s:2:"β";s:4:"𝜸";s:2:"γ";s:4:"𝜹";s:2:"δ";s:4:"𝜺";s:2:"ε";s:4:"𝜻";s:2:"ζ";s:4:"𝜼";s:2:"η";s:4:"𝜽";s:2:"θ";s:4:"𝜾";s:2:"ι";s:4:"𝜿";s:2:"κ";s:4:"𝝀";s:2:"λ";s:4:"𝝁";s:2:"μ";s:4:"𝝂";s:2:"ν";s:4:"𝝃";s:2:"ξ";s:4:"𝝄";s:2:"ο";s:4:"𝝅";s:2:"π";s:4:"𝝆";s:2:"ρ";s:4:"𝝇";s:2:"ς";s:4:"𝝈";s:2:"σ";s:4:"𝝉";s:2:"τ";s:4:"𝝊";s:2:"υ";s:4:"𝝋";s:2:"φ";s:4:"𝝌";s:2:"χ";s:4:"𝝍";s:2:"ψ";s:4:"𝝎";s:2:"ω";s:4:"𝝏";s:3:"∂";s:4:"𝝐";s:2:"ε";s:4:"𝝑";s:2:"θ";s:4:"𝝒";s:2:"κ";s:4:"𝝓";s:2:"φ";s:4:"𝝔";s:2:"ρ";s:4:"𝝕";s:2:"π";s:4:"𝝖";s:2:"Α";s:4:"𝝗";s:2:"Β";s:4:"𝝘";s:2:"Γ";s:4:"𝝙";s:2:"Δ";s:4:"𝝚";s:2:"Ε";s:4:"𝝛";s:2:"Ζ";s:4:"𝝜";s:2:"Η";s:4:"𝝝";s:2:"Θ";s:4:"𝝞";s:2:"Ι";s:4:"𝝟";s:2:"Κ";s:4:"𝝠";s:2:"Λ";s:4:"𝝡";s:2:"Μ";s:4:"𝝢";s:2:"Ν";s:4:"𝝣";s:2:"Ξ";s:4:"𝝤";s:2:"Ο";s:4:"𝝥";s:2:"Π";s:4:"𝝦";s:2:"Ρ";s:4:"𝝧";s:2:"Θ";s:4:"𝝨";s:2:"Σ";s:4:"𝝩";s:2:"Τ";s:4:"𝝪";s:2:"Υ";s:4:"𝝫";s:2:"Φ";s:4:"𝝬";s:2:"Χ";s:4:"𝝭";s:2:"Ψ";s:4:"𝝮";s:2:"Ω";s:4:"𝝯";s:3:"∇";s:4:"𝝰";s:2:"α";s:4:"𝝱";s:2:"β";s:4:"𝝲";s:2:"γ";s:4:"𝝳";s:2:"δ";s:4:"𝝴";s:2:"ε";s:4:"𝝵";s:2:"ζ";s:4:"𝝶";s:2:"η";s:4:"𝝷";s:2:"θ";s:4:"𝝸";s:2:"ι";s:4:"𝝹";s:2:"κ";s:4:"𝝺";s:2:"λ";s:4:"𝝻";s:2:"μ";s:4:"𝝼";s:2:"ν";s:4:"𝝽";s:2:"ξ";s:4:"𝝾";s:2:"ο";s:4:"𝝿";s:2:"π";s:4:"𝞀";s:2:"ρ";s:4:"𝞁";s:2:"ς";s:4:"𝞂";s:2:"σ";s:4:"𝞃";s:2:"τ";s:4:"𝞄";s:2:"υ";s:4:"𝞅";s:2:"φ";s:4:"𝞆";s:2:"χ";s:4:"𝞇";s:2:"ψ";s:4:"𝞈";s:2:"ω";s:4:"𝞉";s:3:"∂";s:4:"𝞊";s:2:"ε";s:4:"𝞋";s:2:"θ";s:4:"𝞌";s:2:"κ";s:4:"𝞍";s:2:"φ";s:4:"𝞎";s:2:"ρ";s:4:"𝞏";s:2:"π";s:4:"𝞐";s:2:"Α";s:4:"𝞑";s:2:"Β";s:4:"𝞒";s:2:"Γ";s:4:"𝞓";s:2:"Δ";s:4:"𝞔";s:2:"Ε";s:4:"𝞕";s:2:"Ζ";s:4:"𝞖";s:2:"Η";s:4:"𝞗";s:2:"Θ";s:4:"𝞘";s:2:"Ι";s:4:"𝞙";s:2:"Κ";s:4:"𝞚";s:2:"Λ";s:4:"𝞛";s:2:"Μ";s:4:"𝞜";s:2:"Ν";s:4:"𝞝";s:2:"Ξ";s:4:"𝞞";s:2:"Ο";s:4:"𝞟";s:2:"Π";s:4:"𝞠";s:2:"Ρ";s:4:"𝞡";s:2:"Θ";s:4:"𝞢";s:2:"Σ";s:4:"𝞣";s:2:"Τ";s:4:"𝞤";s:2:"Υ";s:4:"𝞥";s:2:"Φ";s:4:"𝞦";s:2:"Χ";s:4:"𝞧";s:2:"Ψ";s:4:"𝞨";s:2:"Ω";s:4:"𝞩";s:3:"∇";s:4:"𝞪";s:2:"α";s:4:"𝞫";s:2:"β";s:4:"𝞬";s:2:"γ";s:4:"𝞭";s:2:"δ";s:4:"𝞮";s:2:"ε";s:4:"𝞯";s:2:"ζ";s:4:"𝞰";s:2:"η";s:4:"𝞱";s:2:"θ";s:4:"𝞲";s:2:"ι";s:4:"𝞳";s:2:"κ";s:4:"𝞴";s:2:"λ";s:4:"𝞵";s:2:"μ";s:4:"𝞶";s:2:"ν";s:4:"𝞷";s:2:"ξ";s:4:"𝞸";s:2:"ο";s:4:"𝞹";s:2:"π";s:4:"𝞺";s:2:"ρ";s:4:"𝞻";s:2:"ς";s:4:"𝞼";s:2:"σ";s:4:"𝞽";s:2:"τ";s:4:"𝞾";s:2:"υ";s:4:"𝞿";s:2:"φ";s:4:"𝟀";s:2:"χ";s:4:"𝟁";s:2:"ψ";s:4:"𝟂";s:2:"ω";s:4:"𝟃";s:3:"∂";s:4:"𝟄";s:2:"ε";s:4:"𝟅";s:2:"θ";s:4:"𝟆";s:2:"κ";s:4:"𝟇";s:2:"φ";s:4:"𝟈";s:2:"ρ";s:4:"𝟉";s:2:"π";s:4:"𝟊";s:2:"Ϝ";s:4:"𝟋";s:2:"ϝ";s:4:"𝟎";s:1:"0";s:4:"𝟏";s:1:"1";s:4:"𝟐";s:1:"2";s:4:"𝟑";s:1:"3";s:4:"𝟒";s:1:"4";s:4:"𝟓";s:1:"5";s:4:"𝟔";s:1:"6";s:4:"𝟕";s:1:"7";s:4:"𝟖";s:1:"8";s:4:"𝟗";s:1:"9";s:4:"𝟘";s:1:"0";s:4:"𝟙";s:1:"1";s:4:"𝟚";s:1:"2";s:4:"𝟛";s:1:"3";s:4:"𝟜";s:1:"4";s:4:"𝟝";s:1:"5";s:4:"𝟞";s:1:"6";s:4:"𝟟";s:1:"7";s:4:"𝟠";s:1:"8";s:4:"𝟡";s:1:"9";s:4:"𝟢";s:1:"0";s:4:"𝟣";s:1:"1";s:4:"𝟤";s:1:"2";s:4:"𝟥";s:1:"3";s:4:"𝟦";s:1:"4";s:4:"𝟧";s:1:"5";s:4:"𝟨";s:1:"6";s:4:"𝟩";s:1:"7";s:4:"𝟪";s:1:"8";s:4:"𝟫";s:1:"9";s:4:"𝟬";s:1:"0";s:4:"𝟭";s:1:"1";s:4:"𝟮";s:1:"2";s:4:"𝟯";s:1:"3";s:4:"𝟰";s:1:"4";s:4:"𝟱";s:1:"5";s:4:"𝟲";s:1:"6";s:4:"𝟳";s:1:"7";s:4:"𝟴";s:1:"8";s:4:"𝟵";s:1:"9";s:4:"𝟶";s:1:"0";s:4:"𝟷";s:1:"1";s:4:"𝟸";s:1:"2";s:4:"𝟹";s:1:"3";s:4:"𝟺";s:1:"4";s:4:"𝟻";s:1:"5";s:4:"𝟼";s:1:"6";s:4:"𝟽";s:1:"7";s:4:"𝟾";s:1:"8";s:4:"𝟿";s:1:"9";s:4:"丽";s:3:"丽";s:4:"丸";s:3:"丸";s:4:"乁";s:3:"乁";s:4:"𠄢";s:4:"𠄢";s:4:"你";s:3:"你";s:4:"侮";s:3:"侮";s:4:"侻";s:3:"侻";s:4:"倂";s:3:"倂";s:4:"偺";s:3:"偺";s:4:"備";s:3:"備";s:4:"僧";s:3:"僧";s:4:"像";s:3:"像";s:4:"㒞";s:3:"㒞";s:4:"𠘺";s:4:"𠘺";s:4:"免";s:3:"免";s:4:"兔";s:3:"兔";s:4:"兤";s:3:"兤";s:4:"具";s:3:"具";s:4:"𠔜";s:4:"𠔜";s:4:"㒹";s:3:"㒹";s:4:"內";s:3:"內";s:4:"再";s:3:"再";s:4:"𠕋";s:4:"𠕋";s:4:"冗";s:3:"冗";s:4:"冤";s:3:"冤";s:4:"仌";s:3:"仌";s:4:"冬";s:3:"冬";s:4:"况";s:3:"况";s:4:"𩇟";s:4:"𩇟";s:4:"凵";s:3:"凵";s:4:"刃";s:3:"刃";s:4:"㓟";s:3:"㓟";s:4:"刻";s:3:"刻";s:4:"剆";s:3:"剆";s:4:"割";s:3:"割";s:4:"剷";s:3:"剷";s:4:"㔕";s:3:"㔕";s:4:"勇";s:3:"勇";s:4:"勉";s:3:"勉";s:4:"勤";s:3:"勤";s:4:"勺";s:3:"勺";s:4:"包";s:3:"包";s:4:"匆";s:3:"匆";s:4:"北";s:3:"北";s:4:"卉";s:3:"卉";s:4:"卑";s:3:"卑";s:4:"博";s:3:"博";s:4:"即";s:3:"即";s:4:"卽";s:3:"卽";s:4:"卿";s:3:"卿";s:4:"卿";s:3:"卿";s:4:"卿";s:3:"卿";s:4:"𠨬";s:4:"𠨬";s:4:"灰";s:3:"灰";s:4:"及";s:3:"及";s:4:"叟";s:3:"叟";s:4:"𠭣";s:4:"𠭣";s:4:"叫";s:3:"叫";s:4:"叱";s:3:"叱";s:4:"吆";s:3:"吆";s:4:"咞";s:3:"咞";s:4:"吸";s:3:"吸";s:4:"呈";s:3:"呈";s:4:"周";s:3:"周";s:4:"咢";s:3:"咢";s:4:"哶";s:3:"哶";s:4:"唐";s:3:"唐";s:4:"啓";s:3:"啓";s:4:"啣";s:3:"啣";s:4:"善";s:3:"善";s:4:"善";s:3:"善";s:4:"喙";s:3:"喙";s:4:"喫";s:3:"喫";s:4:"喳";s:3:"喳";s:4:"嗂";s:3:"嗂";s:4:"圖";s:3:"圖";s:4:"嘆";s:3:"嘆";s:4:"圗";s:3:"圗";s:4:"噑";s:3:"噑";s:4:"噴";s:3:"噴";s:4:"切";s:3:"切";s:4:"壮";s:3:"壮";s:4:"城";s:3:"城";s:4:"埴";s:3:"埴";s:4:"堍";s:3:"堍";s:4:"型";s:3:"型";s:4:"堲";s:3:"堲";s:4:"報";s:3:"報";s:4:"墬";s:3:"墬";s:4:"𡓤";s:4:"𡓤";s:4:"売";s:3:"売";s:4:"壷";s:3:"壷";s:4:"夆";s:3:"夆";s:4:"多";s:3:"多";s:4:"夢";s:3:"夢";s:4:"奢";s:3:"奢";s:4:"𡚨";s:4:"𡚨";s:4:"𡛪";s:4:"𡛪";s:4:"姬";s:3:"姬";s:4:"娛";s:3:"娛";s:4:"娧";s:3:"娧";s:4:"姘";s:3:"姘";s:4:"婦";s:3:"婦";s:4:"㛮";s:3:"㛮";s:4:"㛼";s:3:"㛼";s:4:"嬈";s:3:"嬈";s:4:"嬾";s:3:"嬾";s:4:"嬾";s:3:"嬾";s:4:"𡧈";s:4:"𡧈";s:4:"寃";s:3:"寃";s:4:"寘";s:3:"寘";s:4:"寧";s:3:"寧";s:4:"寳";s:3:"寳";s:4:"𡬘";s:4:"𡬘";s:4:"寿";s:3:"寿";s:4:"将";s:3:"将";s:4:"当";s:3:"当";s:4:"尢";s:3:"尢";s:4:"㞁";s:3:"㞁";s:4:"屠";s:3:"屠";s:4:"屮";s:3:"屮";s:4:"峀";s:3:"峀";s:4:"岍";s:3:"岍";s:4:"𡷤";s:4:"𡷤";s:4:"嵃";s:3:"嵃";s:4:"𡷦";s:4:"𡷦";s:4:"嵮";s:3:"嵮";s:4:"嵫";s:3:"嵫";s:4:"嵼";s:3:"嵼";s:4:"巡";s:3:"巡";s:4:"巢";s:3:"巢";s:4:"㠯";s:3:"㠯";s:4:"巽";s:3:"巽";s:4:"帨";s:3:"帨";s:4:"帽";s:3:"帽";s:4:"幩";s:3:"幩";s:4:"㡢";s:3:"㡢";s:4:"𢆃";s:4:"𢆃";s:4:"㡼";s:3:"㡼";s:4:"庰";s:3:"庰";s:4:"庳";s:3:"庳";s:4:"庶";s:3:"庶";s:4:"廊";s:3:"廊";s:4:"𪎒";s:4:"𪎒";s:4:"廾";s:3:"廾";s:4:"𢌱";s:4:"𢌱";s:4:"𢌱";s:4:"𢌱";s:4:"舁";s:3:"舁";s:4:"弢";s:3:"弢";s:4:"弢";s:3:"弢";s:4:"㣇";s:3:"㣇";s:4:"𣊸";s:4:"𣊸";s:4:"𦇚";s:4:"𦇚";s:4:"形";s:3:"形";s:4:"彫";s:3:"彫";s:4:"㣣";s:3:"㣣";s:4:"徚";s:3:"徚";s:4:"忍";s:3:"忍";s:4:"志";s:3:"志";s:4:"忹";s:3:"忹";s:4:"悁";s:3:"悁";s:4:"㤺";s:3:"㤺";s:4:"㤜";s:3:"㤜";s:4:"悔";s:3:"悔";s:4:"𢛔";s:4:"𢛔";s:4:"惇";s:3:"惇";s:4:"慈";s:3:"慈";s:4:"慌";s:3:"慌";s:4:"慎";s:3:"慎";s:4:"慌";s:3:"慌";s:4:"慺";s:3:"慺";s:4:"憎";s:3:"憎";s:4:"憲";s:3:"憲";s:4:"憤";s:3:"憤";s:4:"憯";s:3:"憯";s:4:"懞";s:3:"懞";s:4:"懲";s:3:"懲";s:4:"懶";s:3:"懶";s:4:"成";s:3:"成";s:4:"戛";s:3:"戛";s:4:"扝";s:3:"扝";s:4:"抱";s:3:"抱";s:4:"拔";s:3:"拔";s:4:"捐";s:3:"捐";s:4:"𢬌";s:4:"𢬌";s:4:"挽";s:3:"挽";s:4:"拼";s:3:"拼";s:4:"捨";s:3:"捨";s:4:"掃";s:3:"掃";s:4:"揤";s:3:"揤";s:4:"𢯱";s:4:"𢯱";s:4:"搢";s:3:"搢";s:4:"揅";s:3:"揅";s:4:"掩";s:3:"掩";s:4:"㨮";s:3:"㨮";s:4:"摩";s:3:"摩";s:4:"摾";s:3:"摾";s:4:"撝";s:3:"撝";s:4:"摷";s:3:"摷";s:4:"㩬";s:3:"㩬";s:4:"敏";s:3:"敏";s:4:"敬";s:3:"敬";s:4:"𣀊";s:4:"𣀊";s:4:"旣";s:3:"旣";s:4:"書";s:3:"書";s:4:"晉";s:3:"晉";s:4:"㬙";s:3:"㬙";s:4:"暑";s:3:"暑";s:4:"㬈";s:3:"㬈";s:4:"㫤";s:3:"㫤";s:4:"冒";s:3:"冒";s:4:"冕";s:3:"冕";s:4:"最";s:3:"最";s:4:"暜";s:3:"暜";s:4:"肭";s:3:"肭";s:4:"䏙";s:3:"䏙";s:4:"朗";s:3:"朗";s:4:"望";s:3:"望";s:4:"朡";s:3:"朡";s:4:"杞";s:3:"杞";s:4:"杓";s:3:"杓";s:4:"𣏃";s:4:"𣏃";s:4:"㭉";s:3:"㭉";s:4:"柺";s:3:"柺";s:4:"枅";s:3:"枅";s:4:"桒";s:3:"桒";s:4:"梅";s:3:"梅";s:4:"𣑭";s:4:"𣑭";s:4:"梎";s:3:"梎";s:4:"栟";s:3:"栟";s:4:"椔";s:3:"椔";s:4:"㮝";s:3:"㮝";s:4:"楂";s:3:"楂";s:4:"榣";s:3:"榣";s:4:"槪";s:3:"槪";s:4:"檨";s:3:"檨";s:4:"𣚣";s:4:"𣚣";s:4:"櫛";s:3:"櫛";s:4:"㰘";s:3:"㰘";s:4:"次";s:3:"次";s:4:"𣢧";s:4:"𣢧";s:4:"歔";s:3:"歔";s:4:"㱎";s:3:"㱎";s:4:"歲";s:3:"歲";s:4:"殟";s:3:"殟";s:4:"殺";s:3:"殺";s:4:"殻";s:3:"殻";s:4:"𣪍";s:4:"𣪍";s:4:"𡴋";s:4:"𡴋";s:4:"𣫺";s:4:"𣫺";s:4:"汎";s:3:"汎";s:4:"𣲼";s:4:"𣲼";s:4:"沿";s:3:"沿";s:4:"泍";s:3:"泍";s:4:"汧";s:3:"汧";s:4:"洖";s:3:"洖";s:4:"派";s:3:"派";s:4:"海";s:3:"海";s:4:"流";s:3:"流";s:4:"浩";s:3:"浩";s:4:"浸";s:3:"浸";s:4:"涅";s:3:"涅";s:4:"𣴞";s:4:"𣴞";s:4:"洴";s:3:"洴";s:4:"港";s:3:"港";s:4:"湮";s:3:"湮";s:4:"㴳";s:3:"㴳";s:4:"滋";s:3:"滋";s:4:"滇";s:3:"滇";s:4:"𣻑";s:4:"𣻑";s:4:"淹";s:3:"淹";s:4:"潮";s:3:"潮";s:4:"𣽞";s:4:"𣽞";s:4:"𣾎";s:4:"𣾎";s:4:"濆";s:3:"濆";s:4:"瀹";s:3:"瀹";s:4:"瀞";s:3:"瀞";s:4:"瀛";s:3:"瀛";s:4:"㶖";s:3:"㶖";s:4:"灊";s:3:"灊";s:4:"災";s:3:"災";s:4:"灷";s:3:"灷";s:4:"炭";s:3:"炭";s:4:"𠔥";s:4:"𠔥";s:4:"煅";s:3:"煅";s:4:"𤉣";s:4:"𤉣";s:4:"熜";s:3:"熜";s:4:"𤎫";s:4:"𤎫";s:4:"爨";s:3:"爨";s:4:"爵";s:3:"爵";s:4:"牐";s:3:"牐";s:4:"𤘈";s:4:"𤘈";s:4:"犀";s:3:"犀";s:4:"犕";s:3:"犕";s:4:"𤜵";s:4:"𤜵";s:4:"𤠔";s:4:"𤠔";s:4:"獺";s:3:"獺";s:4:"王";s:3:"王";s:4:"㺬";s:3:"㺬";s:4:"玥";s:3:"玥";s:4:"㺸";s:3:"㺸";s:4:"㺸";s:3:"㺸";s:4:"瑇";s:3:"瑇";s:4:"瑜";s:3:"瑜";s:4:"瑱";s:3:"瑱";s:4:"璅";s:3:"璅";s:4:"瓊";s:3:"瓊";s:4:"㼛";s:3:"㼛";s:4:"甤";s:3:"甤";s:4:"𤰶";s:4:"𤰶";s:4:"甾";s:3:"甾";s:4:"𤲒";s:4:"𤲒";s:4:"異";s:3:"異";s:4:"𢆟";s:4:"𢆟";s:4:"瘐";s:3:"瘐";s:4:"𤾡";s:4:"𤾡";s:4:"𤾸";s:4:"𤾸";s:4:"𥁄";s:4:"𥁄";s:4:"㿼";s:3:"㿼";s:4:"䀈";s:3:"䀈";s:4:"直";s:3:"直";s:4:"𥃳";s:4:"𥃳";s:4:"𥃲";s:4:"𥃲";s:4:"𥄙";s:4:"𥄙";s:4:"𥄳";s:4:"𥄳";s:4:"眞";s:3:"眞";s:4:"真";s:3:"真";s:4:"真";s:3:"真";s:4:"睊";s:3:"睊";s:4:"䀹";s:3:"䀹";s:4:"瞋";s:3:"瞋";s:4:"䁆";s:3:"䁆";s:4:"䂖";s:3:"䂖";s:4:"𥐝";s:4:"𥐝";s:4:"硎";s:3:"硎";s:4:"碌";s:3:"碌";s:4:"磌";s:3:"磌";s:4:"䃣";s:3:"䃣";s:4:"𥘦";s:4:"𥘦";s:4:"祖";s:3:"祖";s:4:"𥚚";s:4:"𥚚";s:4:"𥛅";s:4:"𥛅";s:4:"福";s:3:"福";s:4:"秫";s:3:"秫";s:4:"䄯";s:3:"䄯";s:4:"穀";s:3:"穀";s:4:"穊";s:3:"穊";s:4:"穏";s:3:"穏";s:4:"𥥼";s:4:"𥥼";s:4:"𥪧";s:4:"𥪧";s:4:"𥪧";s:4:"𥪧";s:4:"竮";s:3:"竮";s:4:"䈂";s:3:"䈂";s:4:"𥮫";s:4:"𥮫";s:4:"篆";s:3:"篆";s:4:"築";s:3:"築";s:4:"䈧";s:3:"䈧";s:4:"𥲀";s:4:"𥲀";s:4:"糒";s:3:"糒";s:4:"䊠";s:3:"䊠";s:4:"糨";s:3:"糨";s:4:"糣";s:3:"糣";s:4:"紀";s:3:"紀";s:4:"𥾆";s:4:"𥾆";s:4:"絣";s:3:"絣";s:4:"䌁";s:3:"䌁";s:4:"緇";s:3:"緇";s:4:"縂";s:3:"縂";s:4:"繅";s:3:"繅";s:4:"䌴";s:3:"䌴";s:4:"𦈨";s:4:"𦈨";s:4:"𦉇";s:4:"𦉇";s:4:"䍙";s:3:"䍙";s:4:"𦋙";s:4:"𦋙";s:4:"罺";s:3:"罺";s:4:"𦌾";s:4:"𦌾";s:4:"羕";s:3:"羕";s:4:"翺";s:3:"翺";s:4:"者";s:3:"者";s:4:"𦓚";s:4:"𦓚";s:4:"𦔣";s:4:"𦔣";s:4:"聠";s:3:"聠";s:4:"𦖨";s:4:"𦖨";s:4:"聰";s:3:"聰";s:4:"𣍟";s:4:"𣍟";s:4:"䏕";s:3:"䏕";s:4:"育";s:3:"育";s:4:"脃";s:3:"脃";s:4:"䐋";s:3:"䐋";s:4:"脾";s:3:"脾";s:4:"媵";s:3:"媵";s:4:"𦞧";s:4:"𦞧";s:4:"𦞵";s:4:"𦞵";s:4:"𣎓";s:4:"𣎓";s:4:"𣎜";s:4:"𣎜";s:4:"舁";s:3:"舁";s:4:"舄";s:3:"舄";s:4:"辞";s:3:"辞";s:4:"䑫";s:3:"䑫";s:4:"芑";s:3:"芑";s:4:"芋";s:3:"芋";s:4:"芝";s:3:"芝";s:4:"劳";s:3:"劳";s:4:"花";s:3:"花";s:4:"芳";s:3:"芳";s:4:"芽";s:3:"芽";s:4:"苦";s:3:"苦";s:4:"𦬼";s:4:"𦬼";s:4:"若";s:3:"若";s:4:"茝";s:3:"茝";s:4:"荣";s:3:"荣";s:4:"莭";s:3:"莭";s:4:"茣";s:3:"茣";s:4:"莽";s:3:"莽";s:4:"菧";s:3:"菧";s:4:"著";s:3:"著";s:4:"荓";s:3:"荓";s:4:"菊";s:3:"菊";s:4:"菌";s:3:"菌";s:4:"菜";s:3:"菜";s:4:"𦰶";s:4:"𦰶";s:4:"𦵫";s:4:"𦵫";s:4:"𦳕";s:4:"𦳕";s:4:"䔫";s:3:"䔫";s:4:"蓱";s:3:"蓱";s:4:"蓳";s:3:"蓳";s:4:"蔖";s:3:"蔖";s:4:"𧏊";s:4:"𧏊";s:4:"蕤";s:3:"蕤";s:4:"𦼬";s:4:"𦼬";s:4:"䕝";s:3:"䕝";s:4:"䕡";s:3:"䕡";s:4:"𦾱";s:4:"𦾱";s:4:"𧃒";s:4:"𧃒";s:4:"䕫";s:3:"䕫";s:4:"虐";s:3:"虐";s:4:"虜";s:3:"虜";s:4:"虧";s:3:"虧";s:4:"虩";s:3:"虩";s:4:"蚩";s:3:"蚩";s:4:"蚈";s:3:"蚈";s:4:"蜎";s:3:"蜎";s:4:"蛢";s:3:"蛢";s:4:"蝹";s:3:"蝹";s:4:"蜨";s:3:"蜨";s:4:"蝫";s:3:"蝫";s:4:"螆";s:3:"螆";s:4:"䗗";s:3:"䗗";s:4:"蟡";s:3:"蟡";s:4:"蠁";s:3:"蠁";s:4:"䗹";s:3:"䗹";s:4:"衠";s:3:"衠";s:4:"衣";s:3:"衣";s:4:"𧙧";s:4:"𧙧";s:4:"裗";s:3:"裗";s:4:"裞";s:3:"裞";s:4:"䘵";s:3:"䘵";s:4:"裺";s:3:"裺";s:4:"㒻";s:3:"㒻";s:4:"𧢮";s:4:"𧢮";s:4:"𧥦";s:4:"𧥦";s:4:"䚾";s:3:"䚾";s:4:"䛇";s:3:"䛇";s:4:"誠";s:3:"誠";s:4:"諭";s:3:"諭";s:4:"變";s:3:"變";s:4:"豕";s:3:"豕";s:4:"𧲨";s:4:"𧲨";s:4:"貫";s:3:"貫";s:4:"賁";s:3:"賁";s:4:"贛";s:3:"贛";s:4:"起";s:3:"起";s:4:"𧼯";s:4:"𧼯";s:4:"𠠄";s:4:"𠠄";s:4:"跋";s:3:"跋";s:4:"趼";s:3:"趼";s:4:"跰";s:3:"跰";s:4:"𠣞";s:4:"𠣞";s:4:"軔";s:3:"軔";s:4:"輸";s:3:"輸";s:4:"𨗒";s:4:"𨗒";s:4:"𨗭";s:4:"𨗭";s:4:"邔";s:3:"邔";s:4:"郱";s:3:"郱";s:4:"鄑";s:3:"鄑";s:4:"𨜮";s:4:"𨜮";s:4:"鄛";s:3:"鄛";s:4:"鈸";s:3:"鈸";s:4:"鋗";s:3:"鋗";s:4:"鋘";s:3:"鋘";s:4:"鉼";s:3:"鉼";s:4:"鏹";s:3:"鏹";s:4:"鐕";s:3:"鐕";s:4:"𨯺";s:4:"𨯺";s:4:"開";s:3:"開";s:4:"䦕";s:3:"䦕";s:4:"閷";s:3:"閷";s:4:"𨵷";s:4:"𨵷";s:4:"䧦";s:3:"䧦";s:4:"雃";s:3:"雃";s:4:"嶲";s:3:"嶲";s:4:"霣";s:3:"霣";s:4:"𩅅";s:4:"𩅅";s:4:"𩈚";s:4:"𩈚";s:4:"䩮";s:3:"䩮";s:4:"䩶";s:3:"䩶";s:4:"韠";s:3:"韠";s:4:"𩐊";s:4:"𩐊";s:4:"䪲";s:3:"䪲";s:4:"𩒖";s:4:"𩒖";s:4:"頋";s:3:"頋";s:4:"頋";s:3:"頋";s:4:"頩";s:3:"頩";s:4:"𩖶";s:4:"𩖶";s:4:"飢";s:3:"飢";s:4:"䬳";s:3:"䬳";s:4:"餩";s:3:"餩";s:4:"馧";s:3:"馧";s:4:"駂";s:3:"駂";s:4:"駾";s:3:"駾";s:4:"䯎";s:3:"䯎";s:4:"𩬰";s:4:"𩬰";s:4:"鬒";s:3:"鬒";s:4:"鱀";s:3:"鱀";s:4:"鳽";s:3:"鳽";s:4:"䳎";s:3:"䳎";s:4:"䳭";s:3:"䳭";s:4:"鵧";s:3:"鵧";s:4:"𪃎";s:4:"𪃎";s:4:"䳸";s:3:"䳸";s:4:"𪄅";s:4:"𪄅";s:4:"𪈎";s:4:"𪈎";s:4:"𪊑";s:4:"𪊑";s:4:"麻";s:3:"麻";s:4:"䵖";s:3:"䵖";s:4:"黹";s:3:"黹";s:4:"黾";s:3:"黾";s:4:"鼅";s:3:"鼅";s:4:"鼏";s:3:"鼏";s:4:"鼖";s:3:"鼖";s:4:"鼻";s:3:"鼻";s:4:"𪘀";s:4:"𪘀";}' );
?>
diff --git a/includes/normal/UtfNormalGenerate.php b/includes/normal/UtfNormalGenerate.php
index f0eb5330..30f18675 100644
--- a/includes/normal/UtfNormalGenerate.php
+++ b/includes/normal/UtfNormalGenerate.php
@@ -21,7 +21,7 @@
* This script generates UniNormalData.inc from the Unicode Character Database
* and supplementary files.
*
- * @package UtfNormal
+ * @addtogroup UtfNormal
* @access private
*/
@@ -175,7 +175,6 @@ if( $out ) {
/**
* This file was automatically generated -- do not edit!
* Run UtfNormalGenerate.php to create this file again (make clean && make)
- * @package MediaWiki
*/
/** */
global \$utfCombiningClass, \$utfCanonicalComp, \$utfCanonicalDecomp, \$utfCheckNFC;
@@ -200,7 +199,6 @@ if( $out ) {
/**
* This file was automatically generated -- do not edit!
* Run UtfNormalGenerate.php to create this file again (make clean && make)
- * @package MediaWiki
*/
/** */
global \$utfCompatibilityDecomp;
diff --git a/includes/normal/UtfNormalTest.php b/includes/normal/UtfNormalTest.php
index 1181b633..6d0dce25 100644
--- a/includes/normal/UtfNormalTest.php
+++ b/includes/normal/UtfNormalTest.php
@@ -20,7 +20,7 @@
/**
* Implements the conformance test at:
* http://www.unicode.org/Public/UNIDATA/NormalizationTest.txt
- * @package UtfNormal
+ * @addtogroup UtfNormal
*/
/** */
diff --git a/includes/normal/UtfNormalUtil.php b/includes/normal/UtfNormalUtil.php
index 94224e3d..4ba05693 100644
--- a/includes/normal/UtfNormalUtil.php
+++ b/includes/normal/UtfNormalUtil.php
@@ -21,7 +21,7 @@
* Some of these functions are adapted from places in MediaWiki.
* Should probably merge them for consistency.
*
- * @package UtfNormal
+ * @addtogroup UtfNormal
* @public
*/
diff --git a/includes/proxy_check.php b/includes/proxy_check.php
index fb7fdb50..4c672760 100644
--- a/includes/proxy_check.php
+++ b/includes/proxy_check.php
@@ -1,7 +1,6 @@
<?php
/**
* Command line script to check for an open proxy at a specified location
- * @package MediaWiki
*/
if( php_sapi_name() != 'cli' ) {
diff --git a/includes/templates/Userlogin.php b/includes/templates/Userlogin.php
index 953fbd47..ccddfa66 100644
--- a/includes/templates/Userlogin.php
+++ b/includes/templates/Userlogin.php
@@ -1,7 +1,6 @@
<?php
/**
- * @package MediaWiki
- * @subpackage Templates
+ * @addtogroup Templates
*/
if( !defined( 'MEDIAWIKI' ) ) die( -1 );
@@ -10,8 +9,7 @@ require_once( 'includes/SkinTemplate.php' );
/**
* HTML template for Special:Userlogin form
- * @package MediaWiki
- * @subpackage Templates
+ * @addtogroup Templates
*/
class UserloginTemplate extends QuickTemplate {
function execute() {
@@ -94,6 +92,9 @@ class UserloginTemplate extends QuickTemplate {
}
}
+/**
+ * @addtogroup Templates
+ */
class UsercreateTemplate extends QuickTemplate {
function execute() {
if( $this->data['message'] ) {
diff --git a/includes/tidy.conf b/includes/tidy.conf
new file mode 100644
index 00000000..3cefcf8f
--- /dev/null
+++ b/includes/tidy.conf
@@ -0,0 +1,18 @@
+# html tidy (http://tidy.sf.net) configuration
+# tidy - validate, correct, and pretty-print HTML files
+# see: man 1 tidy, http://tidy.sourceforge.net/docs/quickref.html
+
+show-body-only: yes
+force-output: yes
+tidy-mark: no
+wrap: 0
+wrap-attributes: no
+literal-attributes: yes
+output-xhtml: yes
+numeric-entities: yes
+enclose-text: yes
+enclose-block-text: yes
+quiet: yes
+quote-nbsp: yes
+fix-backslash: no
+fix-uri: no