summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/AjaxResponse.php3
-rw-r--r--includes/Article.php253
-rw-r--r--includes/AutoLoader.php26
-rw-r--r--includes/BacklinkCache.php232
-rw-r--r--includes/Block.php3
-rw-r--r--includes/Category.php6
-rw-r--r--includes/CategoryPage.php22
-rw-r--r--includes/Categoryfinder.php8
-rw-r--r--includes/ChangeTags.php183
-rw-r--r--includes/ChangesFeed.php6
-rw-r--r--includes/ChangesList.php173
-rw-r--r--includes/DefaultSettings.php199
-rw-r--r--includes/EditPage.php283
-rw-r--r--includes/EnotifNotifyJob.php3
-rw-r--r--includes/Exception.php93
-rw-r--r--includes/Exif.php32
-rw-r--r--includes/Export.php141
-rw-r--r--includes/ExternalStore.php2
-rw-r--r--includes/FileDeleteForm.php5
-rw-r--r--includes/ForkController.php160
-rw-r--r--includes/GlobalFunctions.php208
-rw-r--r--includes/HTMLCacheUpdate.php163
-rw-r--r--includes/HTMLFileCache.php11
-rw-r--r--includes/ImageGallery.php2
-rw-r--r--includes/ImagePage.php84
-rw-r--r--includes/Import.php9
-rw-r--r--includes/Linker.php63
-rw-r--r--includes/LinksUpdate.php60
-rw-r--r--includes/LogEventsList.php150
-rw-r--r--includes/LogPage.php74
-rw-r--r--includes/MagicWord.php5
-rw-r--r--includes/MessageCache.php9
-rw-r--r--includes/MimeMagic.php16
-rw-r--r--includes/OutputHandler.php20
-rw-r--r--includes/OutputPage.php74
-rw-r--r--includes/PageHistory.php143
-rw-r--r--includes/Pager.php35
-rw-r--r--includes/PatrolLog.php27
-rw-r--r--includes/PrefixSearch.php2
-rw-r--r--includes/Profiler.php26
-rw-r--r--includes/ProfilerSimple.php4
-rw-r--r--includes/ProfilerSimpleTrace.php73
-rw-r--r--includes/ProtectionForm.php5
-rw-r--r--includes/QueryPage.php10
-rw-r--r--includes/RawPage.php11
-rw-r--r--includes/RecentChange.php64
-rw-r--r--includes/RefreshLinksJob.php22
-rw-r--r--includes/Revision.php33
-rw-r--r--includes/SearchEngine.php15
-rw-r--r--includes/SearchIBM_DB2.php247
-rw-r--r--includes/SearchPostgres.php6
-rw-r--r--includes/Setup.php4
-rw-r--r--includes/SiteStats.php4
-rw-r--r--includes/Skin.php164
-rw-r--r--includes/SkinTemplate.php40
-rw-r--r--includes/SpecialPage.php176
-rw-r--r--includes/SquidUpdate.php12
-rw-r--r--includes/StreamFile.php1
-rw-r--r--includes/StubObject.php2
-rw-r--r--includes/Title.php551
-rw-r--r--includes/UploadBase.php867
-rw-r--r--includes/UploadFromStash.php58
-rw-r--r--includes/UploadFromUpload.php20
-rw-r--r--includes/UploadFromUrl.php92
-rw-r--r--includes/User.php67
-rw-r--r--includes/UserArray.php11
-rw-r--r--includes/UserMailer.php114
-rw-r--r--includes/WatchedItem.php35
-rw-r--r--includes/WatchlistEditor.php8
-rw-r--r--includes/WebRequest.php3
-rw-r--r--includes/Wiki.php17
-rw-r--r--includes/WikiError.php10
-rw-r--r--includes/Xml.php33
-rw-r--r--includes/ZhConversion.php28274
-rw-r--r--includes/api/ApiBase.php245
-rw-r--r--includes/api/ApiBlock.php9
-rw-r--r--includes/api/ApiDelete.php22
-rw-r--r--includes/api/ApiDisabled.php6
-rw-r--r--includes/api/ApiEditPage.php84
-rw-r--r--includes/api/ApiEmailUser.php11
-rw-r--r--includes/api/ApiFeedWatchlist.php4
-rw-r--r--includes/api/ApiFormatBase.php82
-rw-r--r--includes/api/ApiFormatJson.php4
-rw-r--r--includes/api/ApiFormatJson_json.php1480
-rw-r--r--includes/api/ApiFormatRaw.php71
-rw-r--r--includes/api/ApiFormatWddx.php14
-rw-r--r--includes/api/ApiFormatXml.php15
-rw-r--r--includes/api/ApiHelp.php6
-rw-r--r--includes/api/ApiImport.php179
-rw-r--r--includes/api/ApiLogin.php10
-rw-r--r--includes/api/ApiLogout.php6
-rw-r--r--includes/api/ApiMain.php71
-rw-r--r--includes/api/ApiMove.php51
-rw-r--r--includes/api/ApiOpenSearch.php10
-rw-r--r--includes/api/ApiPageSet.php146
-rw-r--r--includes/api/ApiParamInfo.php27
-rw-r--r--includes/api/ApiParse.php15
-rw-r--r--includes/api/ApiPatrol.php11
-rw-r--r--includes/api/ApiProtect.php21
-rw-r--r--includes/api/ApiPurge.php11
-rw-r--r--includes/api/ApiQuery.php118
-rw-r--r--includes/api/ApiQueryAllCategories.php20
-rw-r--r--includes/api/ApiQueryAllLinks.php24
-rw-r--r--includes/api/ApiQueryAllUsers.php33
-rw-r--r--includes/api/ApiQueryAllimages.php19
-rw-r--r--includes/api/ApiQueryAllmessages.php23
-rw-r--r--includes/api/ApiQueryAllpages.php18
-rw-r--r--includes/api/ApiQueryBacklinks.php108
-rw-r--r--includes/api/ApiQueryBase.php145
-rw-r--r--includes/api/ApiQueryBlocks.php16
-rw-r--r--includes/api/ApiQueryCategories.php43
-rw-r--r--includes/api/ApiQueryCategoryInfo.php43
-rw-r--r--includes/api/ApiQueryCategoryMembers.php25
-rw-r--r--includes/api/ApiQueryDeletedrevs.php183
-rw-r--r--includes/api/ApiQueryDuplicateFiles.php28
-rw-r--r--includes/api/ApiQueryExtLinksUsage.php21
-rw-r--r--includes/api/ApiQueryExternalLinks.php26
-rw-r--r--includes/api/ApiQueryImageInfo.php148
-rw-r--r--includes/api/ApiQueryImages.php28
-rw-r--r--includes/api/ApiQueryInfo.php555
-rw-r--r--includes/api/ApiQueryLangLinks.php26
-rw-r--r--includes/api/ApiQueryLinks.php29
-rw-r--r--includes/api/ApiQueryLogEvents.php85
-rw-r--r--includes/api/ApiQueryProtectedTitles.php191
-rw-r--r--includes/api/ApiQueryRandom.php26
-rw-r--r--includes/api/ApiQueryRecentChanges.php45
-rw-r--r--includes/api/ApiQueryRevisions.php155
-rw-r--r--includes/api/ApiQuerySearch.php25
-rw-r--r--includes/api/ApiQuerySiteinfo.php133
-rw-r--r--includes/api/ApiQueryUserContributions.php101
-rw-r--r--includes/api/ApiQueryUserInfo.php19
-rw-r--r--includes/api/ApiQueryUsers.php121
-rw-r--r--includes/api/ApiQueryWatchlist.php31
-rw-r--r--includes/api/ApiQueryWatchlistRaw.php18
-rw-r--r--includes/api/ApiResult.php144
-rw-r--r--includes/api/ApiRollback.php17
-rw-r--r--includes/api/ApiUnblock.php9
-rw-r--r--includes/api/ApiUndelete.php13
-rw-r--r--includes/api/ApiWatch.php10
-rw-r--r--includes/db/Database.php248
-rw-r--r--includes/db/DatabaseIbm_db2.php1796
-rw-r--r--includes/db/DatabasePostgres.php65
-rw-r--r--includes/db/DatabaseSqlite.php238
-rw-r--r--includes/db/LBFactory.php26
-rw-r--r--includes/db/LoadBalancer.php2
-rw-r--r--includes/diff/DifferenceEngine.php211
-rw-r--r--includes/diff/HTMLDiff.php4
-rw-r--r--includes/filerepo/ArchivedFile.php12
-rw-r--r--includes/filerepo/File.php28
-rw-r--r--includes/filerepo/FileRepo.php80
-rw-r--r--includes/filerepo/ForeignAPIFile.php26
-rw-r--r--includes/filerepo/ForeignAPIRepo.php13
-rw-r--r--includes/filerepo/ForeignDBFile.php2
-rw-r--r--includes/filerepo/LocalFile.php4
-rw-r--r--includes/filerepo/LocalRepo.php51
-rw-r--r--includes/media/Bitmap.php4
-rw-r--r--includes/media/Tiff.php33
-rw-r--r--includes/mime.types16
-rw-r--r--includes/parser/CoreParserFunctions.php231
-rw-r--r--includes/parser/DateFormatter.php122
-rw-r--r--includes/parser/Parser.php360
-rw-r--r--includes/parser/ParserCache.php26
-rw-r--r--includes/parser/ParserOptions.php17
-rw-r--r--includes/parser/ParserOutput.php10
-rw-r--r--includes/parser/Preprocessor_DOM.php82
-rw-r--r--includes/parser/Preprocessor_Hash.php41
-rw-r--r--includes/parser/Tidy.php170
-rw-r--r--includes/specials/SpecialAllmessages.php16
-rw-r--r--includes/specials/SpecialAllpages.php20
-rw-r--r--includes/specials/SpecialBlankpage.php2
-rw-r--r--includes/specials/SpecialBlockip.php137
-rw-r--r--includes/specials/SpecialBooksources.php2
-rw-r--r--includes/specials/SpecialCategories.php2
-rw-r--r--includes/specials/SpecialConfirmemail.php4
-rw-r--r--includes/specials/SpecialContributions.php144
-rw-r--r--includes/specials/SpecialDeletedContributions.php20
-rw-r--r--includes/specials/SpecialEmailuser.php2
-rw-r--r--includes/specials/SpecialExport.php551
-rw-r--r--includes/specials/SpecialFileDuplicateSearch.php5
-rw-r--r--includes/specials/SpecialImport.php52
-rw-r--r--includes/specials/SpecialIpblocklist.php27
-rw-r--r--includes/specials/SpecialLinkSearch.php4
-rw-r--r--includes/specials/SpecialListUserRestrictions.php3
-rw-r--r--includes/specials/SpecialListfiles.php24
-rw-r--r--includes/specials/SpecialListgrouprights.php7
-rw-r--r--includes/specials/SpecialListusers.php58
-rw-r--r--includes/specials/SpecialLog.php5
-rw-r--r--includes/specials/SpecialMergeHistory.php27
-rw-r--r--includes/specials/SpecialMovepage.php48
-rw-r--r--includes/specials/SpecialNewpages.php64
-rw-r--r--includes/specials/SpecialPreferences.php183
-rw-r--r--includes/specials/SpecialPrefixindex.php43
-rw-r--r--includes/specials/SpecialProtectedpages.php40
-rw-r--r--includes/specials/SpecialRandompage.php2
-rw-r--r--includes/specials/SpecialRecentchanges.php61
-rw-r--r--includes/specials/SpecialRecentchangeslinked.php22
-rw-r--r--includes/specials/SpecialRestrictUser.php3
-rw-r--r--includes/specials/SpecialRevisiondelete.php428
-rw-r--r--includes/specials/SpecialSearch.php85
-rw-r--r--includes/specials/SpecialSpecialpages.php13
-rw-r--r--includes/specials/SpecialTags.php73
-rw-r--r--includes/specials/SpecialUndelete.php38
-rw-r--r--includes/specials/SpecialUnlockdb.php3
-rw-r--r--includes/specials/SpecialUnusedimages.php12
-rw-r--r--includes/specials/SpecialUpload.php106
-rw-r--r--includes/specials/SpecialUserlogin.php22
-rw-r--r--includes/specials/SpecialUserrights.php17
-rw-r--r--includes/specials/SpecialVersion.php2
-rw-r--r--includes/specials/SpecialWantedfiles.php23
-rw-r--r--includes/specials/SpecialWantedpages.php8
-rw-r--r--includes/specials/SpecialWantedtemplates.php4
-rw-r--r--includes/specials/SpecialWatchlist.php32
-rw-r--r--includes/specials/SpecialWhatlinkshere.php5
-rw-r--r--includes/templates/NoLocalSettings.php13
-rw-r--r--includes/templates/Userlogin.php2
-rw-r--r--includes/zhtable/Makefile2
-rw-r--r--includes/zhtable/Makefile.py438
-rw-r--r--includes/zhtable/simp2trad.manual157
-rw-r--r--includes/zhtable/simp2trad_supp_set.manual2
-rw-r--r--includes/zhtable/simpphrases.manual2451
-rw-r--r--includes/zhtable/simpphrases_exclude.manual18
-rw-r--r--includes/zhtable/toCN.manual42
-rw-r--r--includes/zhtable/toHK.manual2002
-rw-r--r--includes/zhtable/toSG.manual2
-rw-r--r--includes/zhtable/toSimp.manual100
-rw-r--r--includes/zhtable/toTW.manual40
-rw-r--r--includes/zhtable/toTrad.manual25
-rw-r--r--includes/zhtable/trad2simp.manual253
-rw-r--r--includes/zhtable/trad2simp_supp_set.manual2
-rw-r--r--includes/zhtable/tradphrases.manual1411
-rw-r--r--includes/zhtable/tradphrases_exclude.manual106
231 files changed, 35363 insertions, 17570 deletions
diff --git a/includes/AjaxResponse.php b/includes/AjaxResponse.php
index 63468a14..26b6f443 100644
--- a/includes/AjaxResponse.php
+++ b/includes/AjaxResponse.php
@@ -45,7 +45,7 @@ class AjaxResponse {
$this->mText = '';
$this->mResponseCode = '200 OK';
$this->mLastModified = false;
- $this->mContentType= 'text/html; charset=utf-8';
+ $this->mContentType= 'application/x-wiki';
if ( $text ) {
$this->addText( $text );
@@ -178,6 +178,7 @@ class AjaxResponse {
wfDebug( "$fname: -- client send If-Modified-Since: " . $modsince . "\n", false );
wfDebug( "$fname: -- we might send Last-Modified : $lastmod\n", false );
if( ($ismodsince >= $timestamp ) && $wgUser->validateCache( $ismodsince ) && $ismodsince >= $wgCacheEpoch ) {
+ ini_set('zlib.output_compression', 0);
$this->setResponseCode( "304 Not Modified" );
$this->disable();
$this->mLastModified = $lastmod;
diff --git a/includes/Article.php b/includes/Article.php
index 3d9c2147..ef219ea3 100644
--- a/includes/Article.php
+++ b/includes/Article.php
@@ -84,12 +84,12 @@ class Article {
return $this->mRedirectTarget;
# Query the redirect table
$dbr = wfGetDB( DB_SLAVE );
- $res = $dbr->select( 'redirect',
+ $row = $dbr->selectRow( 'redirect',
array('rd_namespace', 'rd_title'),
- array('rd_from' => $this->getID()),
+ array('rd_from' => $this->getID() ),
__METHOD__
);
- if( $row = $dbr->fetchObject($res) ) {
+ if( $row ) {
return $this->mRedirectTarget = Title::makeTitle($row->rd_namespace, $row->rd_title);
}
# This page doesn't have an entry in the redirect table
@@ -135,7 +135,7 @@ class Article {
* @return mixed false, Title of in-wiki target, or string with URL
*/
public function followRedirectText( $text ) {
- $rt = Title::newFromRedirect( $text );
+ $rt = Title::newFromRedirectRecurse( $text ); // recurse through to only get the final target
# process if title object is valid and not special:userlogout
if( $rt ) {
if( $rt->getInterwiki() != '' ) {
@@ -218,7 +218,7 @@ class Article {
if( wfEmptyMsg( $message, $text ) )
$text = '';
} else {
- $text = wfMsg( $wgUser->isLoggedIn() ? 'noarticletext' : 'noarticletextanon' );
+ $text = wfMsgExt( $wgUser->isLoggedIn() ? 'noarticletext' : 'noarticletextanon', 'parsemag' );
}
wfProfileOut( __METHOD__ );
return $text;
@@ -228,6 +228,21 @@ class Article {
return $this->mContent;
}
}
+
+ /**
+ * Get the text of the current revision. No side-effects...
+ *
+ * @return Return the text of the current revision
+ */
+ public function getRawText() {
+ // Check process cache for current revision
+ if( $this->mContentLoaded && $this->mOldId == 0 ) {
+ return $this->mContent;
+ }
+ $rev = Revision::newFromTitle( $this->mTitle );
+ $text = $rev ? $rev->getRawText() : false;
+ return $text;
+ }
/**
* This function returns the text of a section, specified by a number ($section).
@@ -245,6 +260,28 @@ class Article {
global $wgParser;
return $wgParser->getSection( $text, $section );
}
+
+ /**
+ * Get the text that needs to be saved in order to undo all revisions
+ * between $undo and $undoafter. Revisions must belong to the same page,
+ * must exist and must not be deleted
+ * @param $undo Revision
+ * @param $undoafter Revision Must be an earlier revision than $undo
+ * @return mixed string on success, false on failure
+ */
+ public function getUndoText( Revision $undo, Revision $undoafter = null ) {
+ $undo_text = $undo->getText();
+ $undoafter_text = $undoafter->getText();
+ $cur_text = $this->getContent();
+ if ( $cur_text == $undo_text ) {
+ # No use doing a merge if it's just a straight revert.
+ return $undoafter_text;
+ }
+ $undone_text = '';
+ if ( !wfMerge( $undo_text, $undoafter_text, $cur_text, $undone_text ) )
+ return false;
+ return $undone_text;
+ }
/**
* @return int The oldid of the article that is to be shown, 0 for the
@@ -569,7 +606,7 @@ class Article {
}
// Apparently loadPageData was never called
$this->loadContent();
- $titleObj = Title::newFromRedirect( $this->fetchContent() );
+ $titleObj = Title::newFromRedirectRecurse( $this->fetchContent() );
} else {
$titleObj = Title::newFromRedirect( $text );
}
@@ -660,10 +697,13 @@ class Article {
$user = $this->getUser();
$pageId = $this->getId();
+ $hideBit = Revision::DELETED_USER; // username hidden?
+
$sql = "SELECT {$userTable}.*, MAX(rev_timestamp) as timestamp
FROM $revTable LEFT JOIN $userTable ON rev_user = user_id
WHERE rev_page = $pageId
AND rev_user != $user
+ AND rev_deleted & $hideBit = 0
GROUP BY rev_user, rev_user_text, user_real_name
ORDER BY timestamp DESC";
@@ -687,21 +727,28 @@ class Article {
global $wgUseTrackbacks, $wgNamespaceRobotPolicies, $wgArticleRobotPolicies;
global $wgDefaultRobotPolicy;
+ # Let the parser know if this is the printable version
+ if( $wgOut->isPrintable() ) {
+ $wgOut->parserOptions()->setIsPrintable( true );
+ }
+
wfProfileIn( __METHOD__ );
# Get variables from query string
$oldid = $this->getOldID();
- # Try file cache
+ # Try client and file cache
if( $oldid === 0 && $this->checkTouched() ) {
global $wgUseETag;
if( $wgUseETag ) {
$parserCache = ParserCache::singleton();
- $wgOut->setETag( $parserCache->getETag($this,$wgUser) );
+ $wgOut->setETag( $parserCache->getETag($this, $wgOut->parserOptions()) );
}
+ # Is is client cached?
if( $wgOut->checkLastModified( $this->getTouched() ) ) {
wfProfileOut( __METHOD__ );
return;
+ # Try file cache
} else if( $this->tryFileCache() ) {
# tell wgOut that output is taken care of
$wgOut->disable();
@@ -743,15 +790,17 @@ class Article {
}
$wgOut->setRobotPolicy( $policy );
+ # Allow admins to see deleted content if explicitly requested
+ $delId = $diff ? $diff : $oldid;
+ $unhide = $wgRequest->getInt('unhide') == 1
+ && $wgUser->matchEditToken( $wgRequest->getVal('token'), $delId );
# If we got diff and oldid in the query, we want to see a
# diff page instead of the article.
-
if( !is_null( $diff ) ) {
$wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
- $diff = $wgRequest->getVal( 'diff' );
$htmldiff = $wgRequest->getVal( 'htmldiff' , false);
- $de = new DifferenceEngine( $this->mTitle, $oldid, $diff, $rcid, $purge, $htmldiff);
+ $de = new DifferenceEngine( $this->mTitle, $oldid, $diff, $rcid, $purge, $htmldiff, $unhide );
// DifferenceEngine directly fetched the revision:
$this->mRevIdFetched = $de->mNewid;
$de->showDiffPage( $diffOnly );
@@ -765,6 +814,16 @@ class Article {
wfProfileOut( __METHOD__ );
return;
}
+
+ if( $ns == NS_USER || $ns == NS_USER_TALK ) {
+ # User/User_talk subpages are not modified. (bug 11443)
+ if( !$this->mTitle->isSubpage() ) {
+ $block = new Block();
+ if( $block->load( $this->mTitle->getBaseText() ) ) {
+ $wgOut->setRobotpolicy( 'noindex,nofollow' );
+ }
+ }
+ }
# Should the parser cache be used?
$pcache = $this->useParserCache( $oldid );
@@ -787,6 +846,11 @@ class Article {
$fragment = Xml::escapeJsString( $this->mTitle->getFragmentForURL() );
$wgOut->addInlineScript( "redirectToFragment(\"$fragment\");" );
}
+
+ // Add a <link rel="canonical"> tag
+ $wgOut->addLink( array( 'rel' => 'canonical',
+ 'href' => $this->mTitle->getLocalURL() )
+ );
$wasRedirected = true;
}
} elseif( !empty( $rdfrom ) ) {
@@ -803,7 +867,7 @@ class Article {
$outputDone = false;
wfRunHooks( 'ArticleViewHeader', array( &$this, &$outputDone, &$pcache ) );
- if( $pcache && $wgOut->tryParserCache( $this, $wgUser ) ) {
+ if( $pcache && $wgOut->tryParserCache( $this ) ) {
// Ensure that UI elements requiring revision ID have
// the correct version information.
$wgOut->setRevisionId( $this->mLatest );
@@ -816,14 +880,18 @@ class Article {
$this->showDeletionLog();
}
$text = $this->getContent();
- if( $text === false ) {
+ // For now, check also for ID until getContent actually returns
+ // false for pages that do not exists
+ if( $text === false || $this->getID() === 0 ) {
# Failed to load, replace text with error message
$t = $this->mTitle->getPrefixedText();
if( $oldid ) {
- $d = wfMsgExt( 'missingarticle-rev', array( 'escape' ), $oldid );
- $text = wfMsg( 'missing-article', $t, $d );
- } else {
- $text = wfMsg( 'noarticletext' );
+ $d = wfMsgExt( 'missingarticle-rev', 'escape', $oldid );
+ $text = wfMsgExt( 'missing-article', 'parsemag', $t, $d );
+ // Always use page content for pages in the MediaWiki namespace
+ // since it contains the default message
+ } elseif ( $this->mTitle->getNamespace() != NS_MEDIAWIKI ) {
+ $text = wfMsgExt( 'noarticletext', 'parsemag' );
}
}
@@ -836,7 +904,7 @@ class Article {
// for better machine handling of broken links.
$return404 = true;
}
- }
+ }
if( $return404 ) {
$wgRequest->response()->header( "HTTP/1.x 404 Not Found" );
@@ -862,24 +930,35 @@ class Article {
// FIXME: This would be a nice place to load the 'no such page' text.
} else {
$this->setOldSubtitle( isset($this->mOldId) ? $this->mOldId : $oldid );
+ # Allow admins to see deleted content if explicitly requested
if( $this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
- if( !$this->mRevision->userCan( Revision::DELETED_TEXT ) ) {
- $wgOut->addWikiMsg( 'rev-deleted-text-permission' );
+ if( !$unhide || !$this->mRevision->userCan(Revision::DELETED_TEXT) ) {
+ $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-permission' );
$wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
wfProfileOut( __METHOD__ );
return;
} else {
- $wgOut->addWikiMsg( 'rev-deleted-text-view' );
+ $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-view' );
// and we are allowed to see...
}
}
+ // Is this the current revision and otherwise cacheable? Try the parser cache...
+ if( $oldid === $this->getLatest() && $this->useParserCache( false )
+ && $wgOut->tryParserCache( $this ) )
+ {
+ $outputDone = true;
+ }
}
}
+ // Ensure that UI elements requiring revision ID have
+ // the correct version information.
$wgOut->setRevisionId( $this->getRevIdFetched() );
- // Pages containing custom CSS or JavaScript get special treatment
- if( $this->mTitle->isCssOrJsPage() || $this->mTitle->isCssJsSubpage() ) {
+ if( $outputDone ) {
+ // do nothing...
+ // Pages containing custom CSS or JavaScript get special treatment
+ } else if( $this->mTitle->isCssOrJsPage() || $this->mTitle->isCssJsSubpage() ) {
$wgOut->addHTML( wfMsgExt( 'clearyourcache', 'parse' ) );
// Give hooks a chance to customise the output
if( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->mTitle, $wgOut ) ) ) {
@@ -890,7 +969,7 @@ class Article {
$wgOut->addHTML( htmlspecialchars( $this->mContent ) );
$wgOut->addHTML( "\n</pre>\n" );
}
- } else if( $rt = Title::newFromRedirect( $text ) ) {
+ } else if( $rt = Title::newFromRedirectArray( $text ) ) { # get an array of redirect targets
# Don't append the subtitle if this was an old revision
$wgOut->addHTML( $this->viewRedirect( $rt, !$wasRedirected && $this->isCurrent() ) );
$parseout = $wgParser->parse($text, $this->mTitle, ParserOptions::newFromUser($wgUser));
@@ -942,7 +1021,7 @@ class Article {
# If we have been passed an &rcid= parameter, we want to give the user a
# chance to mark this new article as patrolled.
- if( !empty($rcid) && $this->mTitle->exists() && $this->mTitle->userCan('patrol') ) {
+ if( !empty($rcid) && $this->mTitle->exists() && $this->mTitle->quickUserCan('patrol') ) {
$wgOut->addHTML(
"<div class='patrollink'>" .
wfMsgHtml( 'markaspatrolledlink',
@@ -1003,24 +1082,41 @@ class Article {
/**
* View redirect
- * @param $target Title object of destination to redirect
+ * @param $target Title object or Array of destination(s) to redirect
* @param $appendSubtitle Boolean [optional]
* @param $forceKnown Boolean: should the image be shown as a bluelink regardless of existence?
*/
public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
global $wgParser, $wgOut, $wgContLang, $wgStylePath, $wgUser;
# Display redirect
+ if( !is_array( $target ) ) {
+ $target = array( $target );
+ }
$imageDir = $wgContLang->isRTL() ? 'rtl' : 'ltr';
- $imageUrl = $wgStylePath.'/common/images/redirect' . $imageDir . '.png';
-
+ $imageUrl = $wgStylePath . '/common/images/redirect' . $imageDir . '.png';
+ $imageUrl2 = $wgStylePath . '/common/images/nextredirect' . $imageDir . '.png';
+ $alt2 = $wgContLang->isRTL() ? '&larr;' : '&rarr;'; // should -> and <- be used instead of entities?
+
if( $appendSubtitle ) {
$wgOut->appendSubtitle( wfMsgHtml( 'redirectpagesub' ) );
}
$sk = $wgUser->getSkin();
+ // the loop prepends the arrow image before the link, so the first case needs to be outside
+ $title = array_shift( $target );
if( $forceKnown ) {
- $link = $sk->makeKnownLinkObj( $target, htmlspecialchars( $target->getFullText() ) );
+ $link = $sk->makeKnownLinkObj( $title, htmlspecialchars( $title->getFullText() ) );
} else {
- $link = $sk->makeLinkObj( $target, htmlspecialchars( $target->getFullText() ) );
+ $link = $sk->makeLinkObj( $title, htmlspecialchars( $title->getFullText() ) );
+ }
+ // automatically append redirect=no to each link, since most of them are redirect pages themselves
+ foreach( $target as $rt ) {
+ if( $forceKnown ) {
+ $link .= '<img src="'.$imageUrl2.'" alt="'.$alt2.' " />'
+ . $sk->makeKnownLinkObj( $rt, htmlspecialchars( $rt->getFullText() ) );
+ } else {
+ $link .= '<img src="'.$imageUrl2.'" alt="'.$alt2.' " />'
+ . $sk->makeLinkObj( $rt, htmlspecialchars( $rt->getFullText() ) );
+ }
}
return '<img src="'.$imageUrl.'" alt="#REDIRECT " />' .
'<span class="redirectText">'.$link.'</span>';
@@ -1052,7 +1148,7 @@ class Article {
$o->tb_name,
$rmvtxt);
}
- $wgOut->addWikiMsg( 'trackbackbox', $tbtext );
+ $wgOut->wrapWikiMsg( "<div id='mw_trackbacks'>$1</div>\n", array( 'trackbackbox', $tbtext ) );
$this->mTitle->invalidateCache();
}
@@ -1128,7 +1224,7 @@ class Article {
if( $this->getID() == 0 ) {
$text = false;
} else {
- $text = $this->getContent();
+ $text = $this->getRawText();
}
$wgMessageCache->replace( $this->mTitle->getDBkey(), $text );
}
@@ -1490,7 +1586,7 @@ class Article {
$isminor = ( $flags & EDIT_MINOR ) && $user->isAllowed('minoredit');
$bot = $flags & EDIT_FORCE_BOT;
- $oldtext = $this->getContent();
+ $oldtext = $this->getRawText(); // current revision
$oldsize = strlen( $oldtext );
# Provide autosummaries if one is not provided and autosummaries are enabled.
@@ -1601,8 +1697,8 @@ class Article {
}
# Invalidate cache of this article and all pages using this article
- # as a template. Partly deferred. Leave templatelinks for editUpdates().
- Article::onArticleEdit( $this->mTitle, 'skiptransclusions' );
+ # as a template. Partly deferred.
+ Article::onArticleEdit( $this->mTitle );
# Update links tables, site stats, etc.
$this->editUpdates( $text, $summary, $isminor, $now, $revisionId, $changed );
} else {
@@ -1680,7 +1776,7 @@ class Article {
$status->value['revision'] = $revision;
wfRunHooks( 'ArticleSaveComplete', array( &$this, &$user, $text, $summary,
- $flags & EDIT_MINOR, null, null, &$flags, $revision, &$status ) );
+ $flags & EDIT_MINOR, null, null, &$flags, $revision, &$status, $baseRevId ) );
wfProfileOut( __METHOD__ );
return $status;
@@ -1731,7 +1827,7 @@ class Article {
#It would be nice to see where the user had actually come from, but for now just guess
$returnto = $rc->getAttribute( 'rc_type' ) == RC_NEW ? 'Newpages' : 'Recentchanges';
- $return = Title::makeTitle( NS_SPECIAL, $returnto );
+ $return = SpecialPage::getTitleFor( $returnto );
$dbw = wfGetDB( DB_MASTER );
$errors = $rc->doMarkPatrolled();
@@ -1867,7 +1963,18 @@ class Article {
global $wgUser, $wgRestrictionTypes, $wgContLang;
$id = $this->mTitle->getArticleID();
- if( $id <= 0 || wfReadOnly() || !$this->mTitle->userCan('protect') ) {
+ if ( $id <= 0 ) {
+ wfDebug( "updateRestrictions failed: $id <= 0\n" );
+ return false;
+ }
+
+ if ( wfReadOnly() ) {
+ wfDebug( "updateRestrictions failed: read-only\n" );
+ return false;
+ }
+
+ if ( !$this->mTitle->userCan( 'protect' ) ) {
+ wfDebug( "updateRestrictions failed: insufficient permissions\n" );
return false;
}
@@ -1938,6 +2045,9 @@ class Article {
$encodedExpiry = array();
$protect_description = '';
foreach( $limit as $action => $restrictions ) {
+ if ( !isset($expiry[$action]) )
+ $expiry[$action] = 'infinite';
+
$encodedExpiry[$action] = Block::encodeExpiry($expiry[$action], $dbw );
if( $restrictions != '' ) {
$protect_description .= "[$action=$restrictions] (";
@@ -2099,7 +2209,7 @@ class Article {
// Calculate the maximum amount of chars to get
// Max content length = max comment length - length of the comment (excl. $1) - '...'
$maxLength = 255 - (strlen( $reason ) - 2) - 3;
- $contents = $wgContLang->truncate( $contents, $maxLength, '...' );
+ $contents = $wgContLang->truncate( $contents, $maxLength );
// Remove possible unfinished links
$contents = preg_replace( '/\[\[([^\]]*)\]?$/', '$1', $contents );
// Now replace the '$1' placeholder
@@ -2124,7 +2234,7 @@ class Article {
if( $reason != 'other' && $this->DeleteReason != '' ) {
// Entry from drop down menu + additional comment
- $reason .= ': ' . $this->DeleteReason;
+ $reason .= wfMsgForContent( 'colon-separator' ) . $this->DeleteReason;
} elseif( $reason == 'other' ) {
$reason = $this->DeleteReason;
}
@@ -2398,7 +2508,7 @@ class Article {
return false;
}
- $u = new SiteStatsUpdate( 0, 1, -(int)$this->isCountable( $this->getContent() ), -1 );
+ $u = new SiteStatsUpdate( 0, 1, -(int)$this->isCountable( $this->getRawText() ), -1 );
array_push( $wgDeferredUpdateList, $u );
// Bitfields to further suppress the content
@@ -2500,7 +2610,6 @@ class Article {
# Clear the cached article id so the interface doesn't act like we exist
$this->mTitle->resetArticleID( 0 );
- $this->mTitle->mArticleID = 0;
# Log the deletion, if the page was suppressed, log it at Oversight instead
$logtype = $suppress ? 'suppress' : 'delete';
@@ -2812,13 +2921,15 @@ class Article {
# Save it to the parser cache
if( $wgEnableParserCache ) {
+ $popts = new ParserOptions;
+ $popts->setTidy( true );
+ $popts->enableLimitReport();
$parserCache = ParserCache::singleton();
- $parserCache->save( $editInfo->output, $this, $wgUser );
+ $parserCache->save( $editInfo->output, $this, $popts );
}
# Update the links tables
- $u = new LinksUpdate( $this->mTitle, $editInfo->output, false );
- $u->setRecursiveTouch( $changed ); // refresh/invalidate including pages too
+ $u = new LinksUpdate( $this->mTitle, $editInfo->output );
$u->doUpdate();
wfRunHooks( 'ArticleEditUpdates', array( &$this, &$editInfo, $changed ) );
@@ -2903,7 +3014,7 @@ class Article {
* @param $oldid String: revision ID of this article revision
*/
public function setOldSubtitle( $oldid = 0 ) {
- global $wgLang, $wgOut, $wgUser;
+ global $wgLang, $wgOut, $wgUser, $wgRequest;
if( !wfRunHooks( 'DisplayOldSubtitle', array( &$this, &$oldid ) ) ) {
return;
@@ -2954,16 +3065,17 @@ class Article {
}
$cdel = "(<small>$cdel</small>) ";
}
- # Show user links if allowed to see them. Normally they
- # are hidden regardless, but since we can already see the text here...
- $userlinks = $sk->revUserTools( $revision, false );
+ $unhide = $wgRequest->getInt('unhide') == 1 && $wgUser->matchEditToken( $wgRequest->getVal('token'), $oldid );
+ # Show user links if allowed to see them. If hidden, then show them only if requested...
+ $userlinks = $sk->revUserTools( $revision, !$unhide );
$m = wfMsg( 'revision-info-current' );
$infomsg = $current && !wfEmptyMsg( 'revision-info-current', $m ) && $m != '-'
? 'revision-info-current'
: 'revision-info';
- $r = "\n\t\t\t\t<div id=\"mw-{$infomsg}\">" . wfMsgExt( $infomsg, array( 'parseinline', 'replaceafter' ), $td, $userlinks, $revision->getID() ) . "</div>\n" .
+ $r = "\n\t\t\t\t<div id=\"mw-{$infomsg}\">" . wfMsgExt( $infomsg, array( 'parseinline', 'replaceafter' ),
+ $td, $userlinks, $revision->getID() ) . "</div>\n" .
"\n\t\t\t\t<div id=\"mw-revision-nav\">" . $cdel . wfMsgExt( 'revision-nav', array( 'escapenoentities', 'parsemag', 'replaceafter' ),
$prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>\n\t\t\t";
@@ -3056,7 +3168,7 @@ class Article {
if( !$this->mDataLoaded ) {
$this->loadPageData();
}
- return $this->mLatest;
+ return (int)$this->mLatest;
}
/**
@@ -3206,17 +3318,18 @@ class Article {
$user = User::newFromName( $title->getText(), false );
$user->setNewtalk( false );
}
+ # Image redirects
+ RepoGroup::singleton()->getLocalRepo()->invalidateImageRedirect( $title );
}
/**
* Purge caches on page update etc
*/
- public static function onArticleEdit( $title, $transclusions = 'transclusions' ) {
+ public static function onArticleEdit( $title, $flags = '' ) {
global $wgDeferredUpdateList;
// Invalidate caches of articles which include this page
- if( $transclusions !== 'skiptransclusions' )
- $wgDeferredUpdateList[] = new HTMLCacheUpdate( $title, 'templatelinks' );
+ $wgDeferredUpdateList[] = new HTMLCacheUpdate( $title, 'templatelinks' );
// Invalidate the caches of all pages which redirect here
$wgDeferredUpdateList[] = new HTMLCacheUpdate( $title, 'redirect' );
@@ -3405,8 +3518,7 @@ class Article {
global $wgContLang;
$truncatedtext = $wgContLang->truncate(
str_replace("\n", ' ', $newtext),
- max( 0, 200 - strlen( wfMsgForContent( 'autosumm-new' ) ) ),
- '...' );
+ max( 0, 200 - strlen( wfMsgForContent( 'autosumm-new' ) ) ) );
return wfMsgForContent( 'autosumm-new', $truncatedtext );
}
@@ -3418,9 +3530,7 @@ class Article {
global $wgContLang;
$truncatedtext = $wgContLang->truncate(
$newtext,
- max( 0, 200 - strlen( wfMsgForContent( 'autosumm-replace' ) ) ),
- '...'
- );
+ max( 0, 200 - strlen( wfMsgForContent( 'autosumm-replace' ) ) ) );
return wfMsgForContent( 'autosumm-replace', $truncatedtext );
}
@@ -3449,7 +3559,7 @@ class Article {
$popts->enableLimitReport( false );
if( $wgEnableParserCache && $cache && $this && $parserOutput->getCacheTime() != -1 ) {
$parserCache = ParserCache::singleton();
- $parserCache->save( $parserOutput, $this, $wgUser );
+ $parserCache->save( $parserOutput, $this, $popts );
}
// Make sure file cache is not used on uncacheable content.
// Output that has magic words in it can still use the parser cache
@@ -3478,27 +3588,26 @@ class Article {
__METHOD__ );
global $wgContLang;
-
- if( $res !== false ) {
- foreach( $res as $row ) {
- $tlTemplates[] = $wgContLang->getNsText( $row->tl_namespace ) . ':' . $row->tl_title ;
- }
+ foreach( $res as $row ) {
+ $tlTemplates["{$row->tl_namespace}:{$row->tl_title}"] = true;
}
# Get templates from parser output.
- $poTemplates_allns = $parserOutput->getTemplates();
-
- $poTemplates = array ();
- foreach ( $poTemplates_allns as $ns_templates ) {
- $poTemplates = array_merge( $poTemplates, $ns_templates );
+ $poTemplates = array();
+ foreach ( $parserOutput->getTemplates() as $ns => $templates ) {
+ foreach ( $templates as $dbk => $id ) {
+ $key = $row->tl_namespace . ':'. $row->tl_title;
+ $poTemplates["$ns:$dbk"] = true;
+ }
}
# Get the diff
- $templates_diff = array_diff( $poTemplates, $tlTemplates );
+ # Note that we simulate array_diff_key in PHP <5.0.x
+ $templates_diff = array_diff_key( $poTemplates, $tlTemplates );
if( count( $templates_diff ) > 0 ) {
# Whee, link updates time.
- $u = new LinksUpdate( $this->mTitle, $parserOutput );
+ $u = new LinksUpdate( $this->mTitle, $parserOutput, false );
$u->doUpdate();
}
}
diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php
index ce1912ea..85e7e668 100644
--- a/includes/AutoLoader.php
+++ b/includes/AutoLoader.php
@@ -19,6 +19,7 @@ $wgAutoloadLocalClasses = array(
'AuthPlugin' => 'includes/AuthPlugin.php',
'AuthPluginUser' => 'includes/AuthPlugin.php',
'Autopromote' => 'includes/Autopromote.php',
+ 'BacklinkCache' => 'includes/BacklinkCache.php',
'BagOStuff' => 'includes/BagOStuff.php',
'Block' => 'includes/Block.php',
'CacheDependency' => 'includes/CacheDependency.php',
@@ -28,6 +29,7 @@ $wgAutoloadLocalClasses = array(
'CategoryViewer' => 'includes/CategoryPage.php',
'ChangesList' => 'includes/ChangesList.php',
'ChangesFeed' => 'includes/ChangesFeed.php',
+ 'ChangeTags' => 'includes/ChangeTags.php',
'ChannelFeed' => 'includes/Feed.php',
'ConcatenatedGzipHistoryBlob' => 'includes/HistoryBlob.php',
'ConstantDependency' => 'includes/CacheDependency.php',
@@ -73,6 +75,7 @@ $wgAutoloadLocalClasses = array(
'FileDependency' => 'includes/CacheDependency.php',
'FileRevertForm' => 'includes/FileRevertForm.php',
'FileStore' => 'includes/FileStore.php',
+ 'ForkController' => 'includes/ForkController.php',
'FormatExif' => 'includes/Exif.php',
'FormOptions' => 'includes/FormOptions.php',
'FSException' => 'includes/FileStore.php',
@@ -186,6 +189,7 @@ $wgAutoloadLocalClasses = array(
'StringUtils' => 'includes/StringUtils.php',
'TablePager' => 'includes/Pager.php',
'ThumbnailImage' => 'includes/MediaTransformOutput.php',
+ 'TiffHandler' => 'includes/media/Tiff.php',
'TitleDependency' => 'includes/CacheDependency.php',
'Title' => 'includes/Title.php',
'TitleArray' => 'includes/TitleArray.php',
@@ -193,10 +197,6 @@ $wgAutoloadLocalClasses = array(
'TransformParameterError' => 'includes/MediaTransformOutput.php',
'TurckBagOStuff' => 'includes/BagOStuff.php',
'UnlistedSpecialPage' => 'includes/SpecialPage.php',
- 'UploadBase' => 'includes/UploadBase.php',
- 'UploadFromStash' => 'includes/UploadFromStash.php',
- 'UploadFromUpload' => 'includes/UploadFromUpload.php',
- 'UploadFromUrl' => 'includes/UploadFromUrl.php',
'User' => 'includes/User.php',
'UserArray' => 'includes/UserArray.php',
'UserArrayFromResult' => 'includes/UserArray.php',
@@ -231,11 +231,14 @@ $wgAutoloadLocalClasses = array(
'ApiFormatFeedWrapper' => 'includes/api/ApiFormatBase.php',
'ApiFormatJson' => 'includes/api/ApiFormatJson.php',
'ApiFormatPhp' => 'includes/api/ApiFormatPhp.php',
+ 'ApiFormatRaw' => 'includes/api/ApiFormatRaw.php',
'ApiFormatTxt' => 'includes/api/ApiFormatTxt.php',
'ApiFormatWddx' => 'includes/api/ApiFormatWddx.php',
'ApiFormatXml' => 'includes/api/ApiFormatXml.php',
'ApiFormatYaml' => 'includes/api/ApiFormatYaml.php',
'ApiHelp' => 'includes/api/ApiHelp.php',
+ 'ApiImport' => 'includes/api/ApiImport.php',
+ 'ApiImportReporter' => 'includes/api/ApiImport.php',
'ApiLogin' => 'includes/api/ApiLogin.php',
'ApiLogout' => 'includes/api/ApiLogout.php',
'ApiMain' => 'includes/api/ApiMain.php',
@@ -273,6 +276,7 @@ $wgAutoloadLocalClasses = array(
'ApiQueryLangLinks' => 'includes/api/ApiQueryLangLinks.php',
'ApiQueryLinks' => 'includes/api/ApiQueryLinks.php',
'ApiQueryLogEvents' => 'includes/api/ApiQueryLogEvents.php',
+ 'ApiQueryProtectedTitles' => 'includes/api/ApiQueryProtectedTitles.php',
'ApiQueryRandom' => 'includes/api/ApiQueryRandom.php',
'ApiQueryRecentChanges'=> 'includes/api/ApiQueryRecentChanges.php',
'ApiQueryRevisions' => 'includes/api/ApiQueryRevisions.php',
@@ -320,6 +324,11 @@ $wgAutoloadLocalClasses = array(
'PostgresField' => 'includes/db/DatabasePostgres.php',
'ResultWrapper' => 'includes/db/Database.php',
'SQLiteField' => 'includes/db/DatabaseSqlite.php',
+
+ 'DatabaseIbm_db2' => 'includes/db/DatabaseIbm_db2.php',
+ 'IBM_DB2Field' => 'includes/db/DatabaseIbm_db2.php',
+ 'IBM_DB2SearchResultSet' => 'includes/SearchIBM_DB2.php',
+ 'SearchIBM_DB2' => 'includes/SearchIBM_DB2.php',
# includes/diff
'AncestorComparator' => 'includes/diff/HTMLDiff.php',
@@ -431,6 +440,7 @@ $wgAutoloadLocalClasses = array(
'Preprocessor_DOM' => 'includes/parser/Preprocessor_DOM.php',
'Preprocessor_Hash' => 'includes/parser/Preprocessor_Hash.php',
'StripState' => 'includes/parser/Parser.php',
+ 'MWTidy' => 'includes/parser/Tidy.php',
# includes/specials
'AncientPagesPage' => 'includes/specials/SpecialAncientpages.php',
@@ -474,11 +484,12 @@ $wgAutoloadLocalClasses = array(
'PopularPagesPage' => 'includes/specials/SpecialPopularpages.php',
'PreferencesForm' => 'includes/specials/SpecialPreferences.php',
'RandomPage' => 'includes/specials/SpecialRandompage.php',
- 'RevisionDeleteForm' => 'includes/specials/SpecialRevisiondelete.php',
+ 'SpecialRevisionDelete' => 'includes/specials/SpecialRevisiondelete.php',
'RevisionDeleter' => 'includes/specials/SpecialRevisiondelete.php',
'ShortPagesPage' => 'includes/specials/SpecialShortpages.php',
'SpecialAllpages' => 'includes/specials/SpecialAllpages.php',
'SpecialBookSources' => 'includes/specials/SpecialBooksources.php',
+ 'SpecialExport' => 'includes/specials/SpecialExport.php',
'SpecialImport' => 'includes/specials/SpecialImport.php',
'SpecialListGroupRights' => 'includes/specials/SpecialListgrouprights.php',
'SpecialMostlinkedtemplates' => 'includes/specials/SpecialMostlinkedtemplates.php',
@@ -489,6 +500,7 @@ $wgAutoloadLocalClasses = array(
'SpecialSearch' => 'includes/specials/SpecialSearch.php',
'SpecialSearchOld' => 'includes/specials/SpecialSearch.php',
'SpecialStatistics' => 'includes/specials/SpecialStatistics.php',
+ 'SpecialTags' => 'includes/specials/SpecialTags.php',
'SpecialVersion' => 'includes/specials/SpecialVersion.php',
'UncategorizedCategoriesPage' => 'includes/specials/SpecialUncategorizedcategories.php',
'UncategorizedPagesPage' => 'includes/specials/SpecialUncategorizedpages.php',
@@ -556,7 +568,7 @@ class AutoLoader {
}
if ( !$filename ) {
if( function_exists( 'wfDebug' ) )
- wfDebug( "Class {$className} not found; skipped loading" );
+ wfDebug( "Class {$className} not found; skipped loading\n" );
# Give up
return false;
}
@@ -575,7 +587,7 @@ class AutoLoader {
global $wgAutoloadClasses;
foreach( $wgAutoloadClasses as $class => $file ) {
- if( !( class_exists( $class ) || interface_exists( $class ) ) ) {
+ if( !( class_exists( $class, false ) || interface_exists( $class, false ) ) ) {
require( $file );
}
}
diff --git a/includes/BacklinkCache.php b/includes/BacklinkCache.php
new file mode 100644
index 00000000..a7bcd858
--- /dev/null
+++ b/includes/BacklinkCache.php
@@ -0,0 +1,232 @@
+<?php
+
+/**
+ * Class for fetching backlink lists, approximate backlink counts and partitions.
+ * Instances of this class should typically be fetched with $title->getBacklinkCache().
+ *
+ * Ideally you should only get your backlinks from here when you think there is some
+ * advantage in caching them. Otherwise it's just a waste of memory.
+ */
+class BacklinkCache {
+ var $partitionCache = array();
+ var $fullResultCache = array();
+ var $title;
+ var $db;
+
+ const CACHE_EXPIRY = 3600;
+
+ /**
+ * Create a new BacklinkCache
+ */
+ function __construct( $title ) {
+ $this->title = $title;
+ }
+
+ /**
+ * Clear locally stored data
+ */
+ function clear() {
+ $this->partitionCache = array();
+ $this->fullResultCache = array();
+ unset( $this->db );
+ }
+
+ /**
+ * Set the Database object to use
+ */
+ public function setDB( $db ) {
+ $this->db = $db;
+ }
+
+ protected function getDB() {
+ if ( !isset( $this->db ) ) {
+ $this->db = wfGetDB( DB_SLAVE );
+ }
+ return $this->db;
+ }
+
+ /**
+ * Get the backlinks for a given table. Cached in process memory only.
+ * @param string $table
+ * @return TitleArray
+ */
+ public function getLinks( $table, $startId = false, $endId = false ) {
+ wfProfileIn( __METHOD__ );
+
+ if ( $startId || $endId ) {
+ // Partial range, not cached
+ wfDebug( __METHOD__.": from DB (uncacheable range)\n" );
+ $conds = $this->getConditions( $table );
+ // Use the from field in the condition rather than the joined page_id,
+ // because databases are stupid and don't necessarily propagate indexes.
+ $fromField = $this->getPrefix( $table ) . '_from';
+ if ( $startId ) {
+ $conds[] = "$fromField >= " . intval( $startId );
+ }
+ if ( $endId ) {
+ $conds[] = "$fromField <= " . intval( $endId );
+ }
+ $res = $this->getDB()->select(
+ array( $table, 'page' ),
+ array( 'page_namespace', 'page_title', 'page_id'),
+ $conds,
+ __METHOD__,
+ array('STRAIGHT_JOIN') );
+ $ta = TitleArray::newFromResult( $res );
+ wfProfileOut( __METHOD__ );
+ return $ta;
+ }
+
+ if ( !isset( $this->fullResultCache[$table] ) ) {
+ wfDebug( __METHOD__.": from DB\n" );
+ $res = $this->getDB()->select(
+ array( $table, 'page' ),
+ array( 'page_namespace', 'page_title', 'page_id' ),
+ $this->getConditions( $table ),
+ __METHOD__,
+ array('STRAIGHT_JOIN') );
+ $this->fullResultCache[$table] = $res;
+ }
+ $ta = TitleArray::newFromResult( $this->fullResultCache[$table] );
+ wfProfileOut( __METHOD__ );
+ return $ta;
+ }
+
+ /**
+ * Get the field name prefix for a given table
+ */
+ protected function getPrefix( $table ) {
+ static $prefixes = array(
+ 'pagelinks' => 'pl',
+ 'imagelinks' => 'il',
+ 'categorylinks' => 'cl',
+ 'templatelinks' => 'tl',
+ 'redirect' => 'rd',
+ );
+ if ( isset( $prefixes[$table] ) ) {
+ return $prefixes[$table];
+ } else {
+ throw new MWException( "Invalid table \"$table\" in " . __CLASS__ );
+ }
+ }
+
+ /**
+ * Get the SQL condition array for selecting backlinks, with a join on the page table
+ */
+ protected function getConditions( $table ) {
+ $prefix = $this->getPrefix( $table );
+ switch ( $table ) {
+ case 'pagelinks':
+ case 'templatelinks':
+ case 'redirect':
+ $conds = array(
+ "{$prefix}_namespace" => $this->title->getNamespace(),
+ "{$prefix}_title" => $this->title->getDBkey(),
+ "page_id={$prefix}_from"
+ );
+ break;
+ case 'imagelinks':
+ $conds = array(
+ 'il_to' => $this->title->getDBkey(),
+ 'page_id=il_from'
+ );
+ break;
+ case 'categorylinks':
+ $conds = array(
+ 'cl_to' => $this->title->getDBkey(),
+ 'page_id=cl_from',
+ );
+ break;
+ default:
+ throw new MWException( "Invalid table \"$table\" in " . __CLASS__ );
+ }
+ return $conds;
+ }
+
+ /**
+ * Get the approximate number of backlinks
+ */
+ public function getNumLinks( $table ) {
+ if ( isset( $this->fullResultCache[$table] ) ) {
+ return $this->fullResultCache[$table]->numRows();
+ }
+ if ( isset( $this->partitionCache[$table] ) ) {
+ $entry = reset( $this->partitionCache[$table] );
+ return $entry['numRows'];
+ }
+ $titleArray = $this->getLinks( $table );
+ return $titleArray->count();
+ }
+
+ /**
+ * Partition the backlinks into batches.
+ * Returns an array giving the start and end of each range. The first batch has
+ * a start of false, and the last batch has an end of false.
+ *
+ * @param string $table The links table name
+ * @param integer $batchSize
+ * @return array
+ */
+ public function partition( $table, $batchSize ) {
+ // Try cache
+ if ( isset( $this->partitionCache[$table][$batchSize] ) ) {
+ wfDebug( __METHOD__.": got from partition cache\n" );
+ return $this->partitionCache[$table][$batchSize]['batches'];
+ }
+ $this->partitionCache[$table][$batchSize] = false;
+ $cacheEntry =& $this->partitionCache[$table][$batchSize];
+
+ // Try full result cache
+ if ( isset( $this->fullResultCache[$table] ) ) {
+ $cacheEntry = $this->partitionResult( $this->fullResultCache[$table], $batchSize );
+ wfDebug( __METHOD__.": got from full result cache\n" );
+ return $cacheEntry['batches'];
+ }
+ // Try memcached
+ global $wgMemc;
+ $memcKey = wfMemcKey( 'backlinks', md5( $this->title->getPrefixedDBkey() ),
+ $table, $batchSize );
+ $memcValue = $wgMemc->get( $memcKey );
+ if ( is_array( $memcValue ) ) {
+ $cacheEntry = $memcValue;
+ wfDebug( __METHOD__.": got from memcached $memcKey\n" );
+ return $cacheEntry['batches'];
+ }
+ // Fetch from database
+ $this->getLinks( $table );
+ $cacheEntry = $this->partitionResult( $this->fullResultCache[$table], $batchSize );
+ // Save to memcached
+ $wgMemc->set( $memcKey, $cacheEntry, self::CACHE_EXPIRY );
+ wfDebug( __METHOD__.": got from database\n" );
+ return $cacheEntry['batches'];
+ }
+
+ /**
+ * Partition a DB result with backlinks in it into batches
+ */
+ protected function partitionResult( $res, $batchSize ) {
+ $batches = array();
+ $numRows = $res->numRows();
+ $numBatches = ceil( $numRows / $batchSize );
+ for ( $i = 0; $i < $numBatches; $i++ ) {
+ if ( $i == 0 ) {
+ $start = false;
+ } else {
+ $rowNum = intval( $numRows * $i / $numBatches );
+ $res->seek( $rowNum );
+ $row = $res->fetchObject();
+ $start = $row->page_id;
+ }
+ if ( $i == $numBatches - 1 ) {
+ $end = false;
+ } else {
+ $rowNum = intval( $numRows * ( $i + 1 ) / $numBatches );
+ $res->seek( $rowNum );
+ $row = $res->fetchObject();
+ $end = $row->page_id - 1;
+ }
+ $batches[] = array( $start, $end );
+ }
+ return array( 'numRows' => $numRows, 'batches' => $batches );
+ }
+}
diff --git a/includes/Block.php b/includes/Block.php
index 2c2227e2..a44941f1 100644
--- a/includes/Block.php
+++ b/includes/Block.php
@@ -105,6 +105,7 @@ class Block {
&& $this->mHideName == $block->mHideName
&& $this->mBlockEmail == $block->mBlockEmail
&& $this->mAllowUsertalk == $block->mAllowUsertalk
+ && $this->mReason == $block->mReason
);
}
@@ -570,7 +571,7 @@ class Block {
## Allow hooks to cancel the autoblock.
if (!wfRunHooks( 'AbortAutoblock', array( $autoblockIP, &$this ) )) {
- wfDebug( "Autoblock aborted by hook." );
+ wfDebug( "Autoblock aborted by hook.\n" );
return false;
}
diff --git a/includes/Category.php b/includes/Category.php
index 78567add..50efdbc1 100644
--- a/includes/Category.php
+++ b/includes/Category.php
@@ -41,8 +41,7 @@ class Category {
$dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow(
'category',
- array( 'cat_id', 'cat_title', 'cat_pages', 'cat_subcats',
- 'cat_files' ),
+ array( 'cat_id', 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files' ),
$where,
__METHOD__
);
@@ -70,8 +69,7 @@ class Category {
# (bug 13683) If the count is negative, then 1) it's obviously wrong
# and should not be kept, and 2) we *probably* don't have to scan many
# rows to obtain the correct figure, so let's risk a one-time recount.
- if( $this->mPages < 0 || $this->mSubcats < 0 ||
- $this->mFiles < 0 ) {
+ if( $this->mPages < 0 || $this->mSubcats < 0 || $this->mFiles < 0 ) {
$this->refreshCounts();
}
diff --git a/includes/CategoryPage.php b/includes/CategoryPage.php
index 4ac24b5f..03ecb5dc 100644
--- a/includes/CategoryPage.php
+++ b/includes/CategoryPage.php
@@ -20,7 +20,8 @@ class CategoryPage extends Article {
if ( isset( $diff ) && $diffOnly )
return Article::view();
- if(!wfRunHooks('CategoryPageView', array(&$this))) return;
+ if( !wfRunHooks( 'CategoryPageView', array( &$this ) ) )
+ return;
if ( NS_CATEGORY == $this->mTitle->getNamespace() ) {
$this->openShowCategory();
@@ -28,10 +29,6 @@ class CategoryPage extends Article {
Article::view();
- # If the article we've just shown is in the "Image" namespace,
- # follow it with the history list and link list for the image
- # it describes.
-
if ( NS_CATEGORY == $this->mTitle->getNamespace() ) {
$this->closeShowCategory();
}
@@ -79,7 +76,7 @@ class CategoryViewer {
$this->from = $from;
$this->until = $until;
$this->limit = $wgCategoryPagingLimit;
- $this->cat = Category::newFromName( $title->getDBKey() );
+ $this->cat = Category::newFromTitle( $title );
}
/**
@@ -192,9 +189,10 @@ class CategoryViewer {
*/
function addPage( $title, $sortkey, $pageLength, $isRedirect = false ) {
global $wgContLang;
+ $titletext = $wgContLang->convert( $title->getPrefixedText() );
$this->articles[] = $isRedirect
- ? '<span class="redirect-in-category">' . $this->getSkin()->makeKnownLinkObj( $title ) . '</span>'
- : $this->getSkin()->makeSizeLinkObj( $pageLength, $title );
+ ? '<span class="redirect-in-category">' . $this->getSkin()->makeKnownLinkObj( $title, $titletext ) . '</span>'
+ : $this->getSkin()->makeSizeLinkObj( $pageLength, $title, $titletext );
$this->articles_start_char[] = $wgContLang->convert( $wgContLang->firstChar( $sortkey ) );
}
@@ -208,7 +206,7 @@ class CategoryViewer {
}
function doCategoryQuery() {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE, 'category' );
if( $this->from != '' ) {
$pageCondition = 'cl_sortkey >= ' . $dbr->addQuotes( $this->from );
$this->flip = false;
@@ -316,7 +314,7 @@ class CategoryViewer {
$countmsg = $this->getCountMessage( $rescnt, $dbcnt, 'file' );
return "<div id=\"mw-category-media\">\n" .
- '<h2>' . wfMsg( 'category-media-header', htmlspecialchars($this->title->getText()) ) . "</h2>\n" .
+ '<h2>' . wfMsg( 'category-media-header', htmlspecialchars( $this->title->getText() ) ) . "</h2>\n" .
$countmsg . $this->gallery->toHTML() . "\n</div>";
} else {
return '';
@@ -451,12 +449,12 @@ class CategoryViewer {
$sk = $this->getSkin();
$limitText = $wgLang->formatNum( $limit );
- $prevLink = htmlspecialchars( wfMsg( 'prevn', $limitText ) );
+ $prevLink = wfMsgExt( 'prevn', array( 'escape', 'parsemag' ), $limitText );
if( $first != '' ) {
$prevLink = $sk->makeLinkObj( $title, $prevLink,
wfArrayToCGI( $query + array( 'until' => $first ) ) );
}
- $nextLink = htmlspecialchars( wfMsg( 'nextn', $limitText ) );
+ $nextLink = wfMsgExt( 'nextn', array( 'escape', 'parsemag' ), $limitText );
if( $last != '' ) {
$nextLink = $sk->makeLinkObj( $title, $nextLink,
wfArrayToCGI( $query + array( 'from' => $last ) ) );
diff --git a/includes/Categoryfinder.php b/includes/Categoryfinder.php
index 4413bd1a..7c1c2856 100644
--- a/includes/Categoryfinder.php
+++ b/includes/Categoryfinder.php
@@ -53,9 +53,11 @@ class Categoryfinder {
# Set the list of target categories; convert them to DBKEY form first
$this->targets = array () ;
foreach ( $categories AS $c ) {
- $ct = Title::newFromText ( $c , NS_CATEGORY ) ;
- $c = $ct->getDBkey () ;
- $this->targets[$c] = $c ;
+ $ct = Title::makeTitleSafe( NS_CATEGORY, $c );
+ if( $ct ) {
+ $c = $ct->getDBkey();
+ $this->targets[$c] = $c;
+ }
}
}
diff --git a/includes/ChangeTags.php b/includes/ChangeTags.php
new file mode 100644
index 00000000..de804c5c
--- /dev/null
+++ b/includes/ChangeTags.php
@@ -0,0 +1,183 @@
+<?php
+
+if (!defined( 'MEDIAWIKI' ))
+ die;
+
+class ChangeTags {
+ static function formatSummaryRow( $tags, $page ) {
+ if (!$tags)
+ return array('',array());
+
+ $classes = array();
+
+ $tags = explode( ',', $tags );
+ $displayTags = array();
+ foreach( $tags as $tag ) {
+ $displayTags[] = self::tagDescription( $tag );
+ $classes[] = "mw-tag-$tag";
+ }
+
+ return array( '(' . implode( ', ', $displayTags ) . ')', $classes );
+ }
+
+ static function tagDescription( $tag ) {
+ $msg = wfMsgExt( "tag-$tag", 'parseinline' );
+ if ( wfEmptyMsg( "tag-$tag", $msg ) ) {
+ return htmlspecialchars($tag);
+ }
+ return $msg;
+ }
+
+ ## Basic utility method to add tags to a particular change, given its rc_id, rev_id and/or log_id.
+ static function addTags( $tags, $rc_id=null, $rev_id=null, $log_id=null, $params = null ) {
+ if ( !is_array($tags) ) {
+ $tags = array( $tags );
+ }
+
+ $tags = array_filter( $tags ); // Make sure we're submitting all tags...
+
+ if (!$rc_id && !$rev_id && !$log_id) {
+ throw new MWException( "At least one of: RCID, revision ID, and log ID MUST be specified when adding a tag to a change!" );
+ }
+
+ $dbr = wfGetDB( DB_SLAVE );
+
+ // Might as well look for rcids and so on.
+ if (!$rc_id) {
+ $dbr = wfGetDB( DB_MASTER ); // Info might be out of date, somewhat fractionally, on slave.
+ if ($log_id) {
+ $rc_id = $dbr->selectField( 'recentchanges', 'rc_id', array( 'rc_logid' => $log_id ), __METHOD__ );
+ } elseif ($rev_id) {
+ $rc_id = $dbr->selectField( 'recentchanges', 'rc_id', array( 'rc_this_oldid' => $rev_id ), __METHOD__ );
+ }
+ } elseif (!$log_id && !$rev_id) {
+ $dbr = wfGetDB( DB_MASTER ); // Info might be out of date, somewhat fractionally, on slave.
+ $log_id = $dbr->selectField( 'recentchanges', 'rc_logid', array( 'rc_id' => $rc_id ), __METHOD__ );
+ $rev_id = $dbr->selectField( 'recentchanges', 'rc_this_oldid', array( 'rc_id' => $rc_id ), __METHOD__ );
+ }
+
+ $tsConds = array_filter( array( 'ts_rc_id' => $rc_id, 'ts_rev_id' => $rev_id, 'ts_log_id' => $log_id ) );
+
+ ## Update the summary row.
+ $prevTags = $dbr->selectField( 'tag_summary', 'ts_tags', $tsConds, __METHOD__ );
+ $prevTags = $prevTags ? $prevTags : '';
+ $prevTags = array_filter( explode( ',', $prevTags ) );
+ $newTags = array_unique( array_merge( $prevTags, $tags ) );
+ sort($prevTags);
+ sort($newTags);
+
+ if ( $prevTags == $newTags ) {
+ // No change.
+ return false;
+ }
+
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->replace( 'tag_summary', array( 'ts_rev_id', 'ts_rc_id', 'ts_log_id' ), array_filter( array_merge( $tsConds, array( 'ts_tags' => implode( ',', $newTags ) ) ) ), __METHOD__ );
+
+ // Insert the tags rows.
+ $tagsRows = array();
+ foreach( $tags as $tag ) { // Filter so we don't insert NULLs as zero accidentally.
+ $tagsRows[] = array_filter( array( 'ct_tag' => $tag, 'ct_rc_id' => $rc_id, 'ct_log_id' => $log_id, 'ct_rev_id' => $rev_id, 'ct_params' => $params ) );
+ }
+
+ $dbw->insert( 'change_tag', $tagsRows, __METHOD__, array('IGNORE') );
+
+ return true;
+ }
+
+ /**
+ * Applies all tags-related changes to a query.
+ * Handles selecting tags, and filtering.
+ * Needs $tables to be set up properly, so we can figure out which join conditions to use.
+ */
+ static function modifyDisplayQuery( &$tables, &$fields, &$conds,
+ &$join_conds, &$options, $filter_tag = false ) {
+ global $wgRequest, $wgUseTagFilter;
+
+ if ($filter_tag === false) {
+ $filter_tag = $wgRequest->getVal( 'tagfilter' );
+ }
+
+ // Figure out which conditions can be done.
+ $join_field = '';
+ if ( in_array('recentchanges', $tables) ) {
+ $join_cond = 'rc_id';
+ } elseif( in_array('logging', $tables) ) {
+ $join_cond = 'log_id';
+ } elseif ( in_array('revision', $tables) ) {
+ $join_cond = 'rev_id';
+ } else {
+ throw new MWException( "Unable to determine appropriate JOIN condition for tagging." );
+ }
+
+ // JOIN on tag_summary
+ $tables[] = 'tag_summary';
+ $join_conds['tag_summary'] = array( 'LEFT JOIN', "ts_$join_cond=$join_cond" );
+ $fields[] = 'ts_tags';
+
+ if ($wgUseTagFilter && $filter_tag) {
+ // Somebody wants to filter on a tag.
+ // Add an INNER JOIN on change_tag
+
+ // FORCE INDEX -- change_tags will almost ALWAYS be the correct query plan.
+ $options['USE INDEX'] = array( 'change_tag' => 'change_tag_tag_id' );
+ unset( $options['FORCE INDEX'] );
+ $tables[] = 'change_tag';
+ $join_conds['change_tag'] = array( 'INNER JOIN', "ct_$join_cond=$join_cond" );
+ $conds['ct_tag'] = $filter_tag;
+ }
+ }
+
+ /**
+ * If $fullForm is set to false, then it returns an array of (label, form).
+ * If $fullForm is true, it returns an entire form.
+ */
+ static function buildTagFilterSelector( $selected='', $fullForm = false /* used to put a full form around the selector */ ) {
+ global $wgUseTagFilter;
+
+ if ( !$wgUseTagFilter || !count( self::listDefinedTags() ) )
+ return $fullForm ? '' : array();
+
+ global $wgTitle;
+
+ $data = array( wfMsgExt( 'tag-filter', 'parseinline' ), Xml::input( 'tagfilter', 20, $selected ) );
+
+ if (!$fullForm) {
+ return $data;
+ }
+
+ $html = implode( '&nbsp;', $data );
+ $html .= "\n" . Xml::element( 'input', array( 'type' => 'submit', 'value' => wfMsg( 'tag-filter-submit' ) ) );
+ $html .= "\n" . Xml::hidden( 'title', $wgTitle-> getPrefixedText() );
+ $html = Xml::tags( 'form', array( 'action' => $wgTitle->getLocalURL(), 'method' => 'get' ), $html );
+
+ return $html;
+ }
+
+ /** Basically lists defined tags which count even if they aren't applied to anything */
+ static function listDefinedTags() {
+ // Caching...
+ global $wgMemc;
+ $key = wfMemcKey( 'valid-tags' );
+
+ if ($tags = $wgMemc->get( $key ))
+ return $tags;
+
+ $emptyTags = array();
+
+ // Some DB stuff
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select( 'valid_tag', 'vt_tag', array(), __METHOD__ );
+ while( $row = $res->fetchObject() ) {
+ $emptyTags[] = $row->vt_tag;
+ }
+
+ wfRunHooks( 'ListDefinedTags', array(&$emptyTags) );
+
+ $emptyTags = array_filter( array_unique( $emptyTags ) );
+
+ // Short-term caching.
+ $wgMemc->set( $key, $emptyTags, 300 );
+ return $emptyTags;
+ }
+}
diff --git a/includes/ChangesFeed.php b/includes/ChangesFeed.php
index f3c3e429..a0c2767a 100644
--- a/includes/ChangesFeed.php
+++ b/includes/ChangesFeed.php
@@ -18,16 +18,16 @@ class ChangesFeed {
$feedTitle, htmlspecialchars( $description ), $wgTitle->getFullUrl() );
}
- public function execute( $feed, $rows, $limit = 0 , $hideminor = false, $lastmod = false ) {
+ public function execute( $feed, $rows, $limit=0, $hideminor=false, $lastmod=false, $target='' ) {
global $messageMemc, $wgFeedCacheTimeout;
- global $wgFeedClasses, $wgSitename, $wgContLanguageCode;
+ global $wgSitename, $wgContLanguageCode;
if ( !FeedUtils::checkFeedOutput( $this->format ) ) {
return;
}
$timekey = wfMemcKey( $this->type, $this->format, 'timestamp' );
- $key = wfMemcKey( $this->type, $this->format, 'limit', $limit, 'minor', $hideminor );
+ $key = wfMemcKey( $this->type, $this->format, $limit, $hideminor, $target );
FeedUtils::checkPurge($timekey, $key);
diff --git a/includes/ChangesList.php b/includes/ChangesList.php
index a8f5fff0..4eda1dbd 100644
--- a/includes/ChangesList.php
+++ b/includes/ChangesList.php
@@ -108,7 +108,7 @@ class ChangesList {
public static function showCharacterDifference( $old, $new ) {
global $wgRCChangedSizeThreshold, $wgLang;
$szdiff = $new - $old;
- $formatedSize = wfMsgExt( 'rc-change-size', array( 'parsemag', 'escape'), $wgLang->formatNum($szdiff) );
+ $formatedSize = wfMsgExt( 'rc-change-size', array( 'parsemag', 'escape' ), $wgLang->formatNum( $szdiff ) );
if( abs( $szdiff ) > abs( $wgRCChangedSizeThreshold ) ) {
$tag = 'strong';
} else {
@@ -223,9 +223,9 @@ class ChangesList {
}
/** Insert links to user page, user talk page and eventually a blocking link */
- public function insertUserRelatedLinks(&$s, &$rc) {
- if( $this->isDeleted($rc,Revision::DELETED_USER) ) {
- $s .= ' <span class="history-deleted">' . wfMsgHtml('rev-deleted-user') . '</span>';
+ public function insertUserRelatedLinks( &$s, &$rc ) {
+ if( $this->isDeleted( $rc, Revision::DELETED_USER ) ) {
+ $s .= ' <span class="history-deleted">' . wfMsgHtml( 'rev-deleted-user' ) . '</span>';
} else {
$s .= $this->skin->userLink( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
$s .= $this->skin->userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
@@ -233,22 +233,22 @@ class ChangesList {
}
/** insert a formatted action */
- protected function insertAction(&$s, &$rc) {
+ protected function insertAction( &$s, &$rc ) {
if( $rc->mAttribs['rc_type'] == RC_LOG ) {
- if( $this->isDeleted($rc,LogPage::DELETED_ACTION) ) {
- $s .= ' <span class="history-deleted">' . wfMsgHtml('rev-deleted-event') . '</span>';
+ if( $this->isDeleted( $rc, LogPage::DELETED_ACTION ) ) {
+ $s .= ' <span class="history-deleted">' . wfMsgHtml( 'rev-deleted-event' ) . '</span>';
} else {
$s .= ' '.LogPage::actionText( $rc->mAttribs['rc_log_type'], $rc->mAttribs['rc_log_action'],
- $rc->getTitle(), $this->skin, LogPage::extractParams($rc->mAttribs['rc_params']), true, true );
+ $rc->getTitle(), $this->skin, LogPage::extractParams( $rc->mAttribs['rc_params'] ), true, true );
}
}
}
/** insert a formatted comment */
- protected function insertComment(&$s, &$rc) {
+ protected function insertComment( &$s, &$rc ) {
if( $rc->mAttribs['rc_type'] != RC_MOVE && $rc->mAttribs['rc_type'] != RC_MOVE_OVER_REDIRECT ) {
- if( $this->isDeleted($rc,Revision::DELETED_COMMENT) ) {
- $s .= ' <span class="history-deleted">' . wfMsgHtml('rev-deleted-comment') . '</span>';
+ if( $this->isDeleted( $rc, Revision::DELETED_COMMENT ) ) {
+ $s .= ' <span class="history-deleted">' . wfMsgHtml( 'rev-deleted-comment' ) . '</span>';
} else {
$s .= $this->skin->commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle() );
}
@@ -272,8 +272,8 @@ class ChangesList {
static $cache = array();
if( $count > 0 ) {
if( !isset( $cache[$count] ) ) {
- $cache[$count] = wfMsgExt('number_of_watching_users_RCview',
- array('parsemag', 'escape'), $wgLang->formatNum($count));
+ $cache[$count] = wfMsgExt( 'number_of_watching_users_RCview',
+ array('parsemag', 'escape' ), $wgLang->formatNum( $count ) );
}
return $cache[$count];
} else {
@@ -288,7 +288,7 @@ class ChangesList {
* @return bool
*/
public static function isDeleted( $rc, $field ) {
- return ($rc->mAttribs['rc_deleted'] & $field) == $field;
+ return ( $rc->mAttribs['rc_deleted'] & $field ) == $field;
}
/**
@@ -318,6 +318,40 @@ class ChangesList {
return '<span class="mw-rc-unwatched">' . $link . '</span>';
}
}
+
+ /** Inserts a rollback link */
+ protected function insertRollback( &$s, &$rc ) {
+ global $wgUser;
+ if( !$rc->mAttribs['rc_new'] && $rc->mAttribs['rc_this_oldid'] && $rc->mAttribs['rc_cur_id'] ) {
+ $page = $rc->getTitle();
+ /** Check for rollback and edit permissions, disallow special pages, and only
+ * show a link on the top-most revision */
+ if ($wgUser->isAllowed('rollback') && $rc->mAttribs['page_latest'] == $rc->mAttribs['rc_this_oldid'] )
+ {
+ $rev = new Revision( array(
+ 'id' => $rc->mAttribs['rc_this_oldid'],
+ 'user' => $rc->mAttribs['rc_user'],
+ 'user_text' => $rc->mAttribs['rc_user_text'],
+ 'deleted' => $rc->mAttribs['rc_deleted']
+ ) );
+ $rev->setTitle( $page );
+ $s .= ' '.$this->skin->generateRollback( $rev );
+ }
+ }
+ }
+
+ protected function insertTags( &$s, &$rc, &$classes ) {
+ if ( empty($rc->mAttribs['ts_tags']) )
+ return;
+
+ list($tagSummary, $newClasses) = ChangeTags::formatSummaryRow( $rc->mAttribs['ts_tags'], 'changeslist' );
+ $classes = array_merge( $classes, $newClasses );
+ $s .= ' ' . $tagSummary;
+ }
+
+ protected function insertExtra( &$s, &$rc, &$classes ) {
+ ## Empty, used for subclassers to add anything special.
+ }
}
@@ -328,8 +362,8 @@ class OldChangesList extends ChangesList {
/**
* Format a line using the old system (aka without any javascript).
*/
- public function recentChangesLine( &$rc, $watched = false ) {
- global $wgContLang, $wgRCShowChangedSize, $wgUser;
+ public function recentChangesLine( &$rc, $watched = false, $linenumber = NULL ) {
+ global $wgContLang, $wgLang, $wgRCShowChangedSize, $wgUser;
wfProfileIn( __METHOD__ );
# Should patrol-related stuff be shown?
$unpatrolled = $wgUser->useRCPatrol() && !$rc->mAttribs['rc_patrolled'];
@@ -338,6 +372,17 @@ class OldChangesList extends ChangesList {
$this->insertDateHeader( $dateheader, $rc->mAttribs['rc_timestamp'] );
$s = '';
+ $classes = array();
+ // use mw-line-even/mw-line-odd class only if linenumber is given (feature from bug 14468)
+ if( $linenumber ) {
+ if( $linenumber & 1 ) {
+ $classes[] = 'mw-line-odd';
+ }
+ else {
+ $classes[] = 'mw-line-even';
+ }
+ }
+
// Moved pages
if( $rc->mAttribs['rc_type'] == RC_MOVE || $rc->mAttribs['rc_type'] == RC_MOVE_OVER_REDIRECT ) {
$this->insertMove( $s, $rc );
@@ -369,25 +414,32 @@ class OldChangesList extends ChangesList {
}
}
# User tool links
- $this->insertUserRelatedLinks($s,$rc);
+ $this->insertUserRelatedLinks( $s, $rc );
# Log action text (if any)
- $this->insertAction($s, $rc);
+ $this->insertAction( $s, $rc );
# Edit or log comment
- $this->insertComment($s, $rc);
+ $this->insertComment( $s, $rc );
+ # Tags
+ $this->insertTags( $s, $rc, $classes );
+ # Rollback
+ $this->insertRollback( $s, $rc );
+ # For subclasses
+ $this->insertExtra( $s, $rc, $classes );
+
# Mark revision as deleted if so
if( !$rc->mAttribs['rc_log_type'] && $this->isDeleted($rc,Revision::DELETED_TEXT) ) {
$s .= ' <tt>' . wfMsgHtml( 'deletedrev' ) . '</tt>';
}
# How many users watch this page
if( $rc->numberofWatchingusers > 0 ) {
- $s .= ' ' . wfMsg( 'number_of_watching_users_RCview',
- $wgContLang->formatNum($rc->numberofWatchingusers) );
+ $s .= ' ' . wfMsgExt( 'number_of_watching_users_RCview',
+ array( 'parsemag', 'escape' ), $wgLang->formatNum( $rc->numberofWatchingusers ) );
}
wfRunHooks( 'OldChangesListRecentChangesLine', array(&$this, &$s, $rc) );
wfProfileOut( __METHOD__ );
- return "$dateheader<li>$s</li>\n";
+ return "$dateheader<li class=\"".implode( ' ', $classes )."\">$s</li>\n";
}
}
@@ -417,6 +469,8 @@ class EnhancedChangesList extends ChangesList {
*/
public function recentChangesLine( &$baseRC, $watched = false ) {
global $wgLang, $wgContLang, $wgUser;
+
+ wfProfileIn( __METHOD__ );
# Create a specialised object
$rc = RCCacheEntry::newFromParent( $baseRC );
@@ -508,10 +562,8 @@ class EnhancedChangesList extends ChangesList {
if( !$showdifflinks ) {
$curLink = $this->message['cur'];
$diffLink = $this->message['diff'];
- } else if( $rc_type == RC_NEW || $rc_type == RC_LOG || $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
- if( $rc_type != RC_NEW ) {
- $curLink = $this->message['cur'];
- }
+ } else if( in_array( $rc_type, array(RC_NEW,RC_LOG,RC_MOVE,RC_MOVE_OVER_REDIRECT) ) ) {
+ $curLink = ($rc_type != RC_NEW) ? $this->message['cur'] : $curLink;
$diffLink = $this->message['diff'];
} else {
$diffLink = $this->skin->makeKnownLinkObj( $rc->getTitle(), $this->message['diff'],
@@ -519,9 +571,9 @@ class EnhancedChangesList extends ChangesList {
}
# Make "last" link
- if( !$showdifflinks ) {
+ if( !$showdifflinks || !$rc_last_oldid ) {
$lastLink = $this->message['last'];
- } else if( $rc_last_oldid == 0 || $rc_type == RC_LOG || $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
+ } else if( $rc_type == RC_LOG || $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
$lastLink = $this->message['last'];
} else {
$lastLink = $this->skin->makeKnownLinkObj( $rc->getTitle(), $this->message['last'],
@@ -530,7 +582,7 @@ class EnhancedChangesList extends ChangesList {
# Make user links
if( $this->isDeleted($rc,Revision::DELETED_USER) ) {
- $rc->userlink = ' <span class="history-deleted">' . wfMsgHtml('rev-deleted-user') . '</span>';
+ $rc->userlink = ' <span class="history-deleted">' . wfMsgHtml( 'rev-deleted-user' ) . '</span>';
} else {
$rc->userlink = $this->skin->userLink( $rc_user, $rc_user_text );
$rc->usertalklink = $this->skin->userToolLinks( $rc_user, $rc_user_text );
@@ -555,8 +607,12 @@ class EnhancedChangesList extends ChangesList {
if( !isset( $this->rc_cache[$secureName] ) ) {
$this->rc_cache[$secureName] = array();
}
+
array_push( $this->rc_cache[$secureName], $rc );
}
+
+ wfProfileOut( __METHOD__ );
+
return $ret;
}
@@ -565,6 +621,9 @@ class EnhancedChangesList extends ChangesList {
*/
protected function recentChangesBlockGroup( $block ) {
global $wgLang, $wgContLang, $wgRCShowChangedSize;
+
+ wfProfileIn( __METHOD__ );
+
$r = '<table cellpadding="0" cellspacing="0" border="0" style="background: none"><tr>';
# Collate list of users
@@ -630,10 +689,10 @@ class EnhancedChangesList extends ChangesList {
# onclick handler to toggle hidden/expanded
$toggleLink = "onclick='toggleVisibility($jsid); return false'";
# Title for <a> tags
- $expandTitle = htmlspecialchars( wfMsg('rc-enhanced-expand') );
- $closeTitle = htmlspecialchars( wfMsg('rc-enhanced-hide') );
+ $expandTitle = htmlspecialchars( wfMsg( 'rc-enhanced-expand' ) );
+ $closeTitle = htmlspecialchars( wfMsg( 'rc-enhanced-hide' ) );
- $tl = "<span id='mw-rc-openarrow-$jsid' class='mw-changeslist-expanded' style='visibility:hidden'><a href='#' $toggleLink title='$expandTitle'>" . $this->sideArrow() . "</a></span>";
+ $tl = "<span id='mw-rc-openarrow-$jsid' class='mw-changeslist-expanded' style='visibility:hidden'><a href='#' $toggleLink title='$expandTitle'>" . $this->sideArrow() . "</a></span>";
$tl .= "<span id='mw-rc-closearrow-$jsid' class='mw-changeslist-hidden' style='display:none'><a href='#' $toggleLink title='$closeTitle'>" . $this->downArrow() . "</a></span>";
$r .= '<td valign="top" style="white-space: nowrap"><tt>'.$tl.'&nbsp;';
@@ -645,7 +704,7 @@ class EnhancedChangesList extends ChangesList {
# Article link
if( $namehidden ) {
- $r .= ' <span class="history-deleted">' . wfMsgHtml('rev-deleted-event') . '</span>';
+ $r .= ' <span class="history-deleted">' . wfMsgHtml( 'rev-deleted-event' ) . '</span>';
} else if( $allLogs ) {
$r .= $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
} else {
@@ -665,7 +724,7 @@ class EnhancedChangesList extends ChangesList {
$r .= ' ';
if( !$allLogs ) {
$r .= '(';
- if( !ChangesList::userCan($rcObj,Revision::DELETED_TEXT) ) {
+ if( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT ) ) {
$r .= $nchanges[$n];
} else if( $isnew ) {
$r .= $nchanges[$n];
@@ -720,6 +779,8 @@ class EnhancedChangesList extends ChangesList {
# 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 variables.
+ # Classes to apply -- TODO implement
+ $classes = array();
extract( $rcObj->mAttribs );
#$r .= '<tr><td valign="top">'.$this->spacerArrow();
@@ -765,9 +826,14 @@ class EnhancedChangesList extends ChangesList {
$r .= $rcObj->userlink;
$r .= $rcObj->usertalklink;
// log action
- parent::insertAction( $r, $rcObj );
+ $this->insertAction( $r, $rcObj );
// log comment
- parent::insertComment( $r, $rcObj );
+ $this->insertComment( $r, $rcObj );
+ # Rollback
+ $this->insertRollback( $r, $rcObj );
+ # Tags
+ $this->insertTags( $r, $rcObj, $classes );
+
# Mark revision as deleted
if( !$rc_log_type && $this->isDeleted($rcObj,Revision::DELETED_TEXT) ) {
$r .= ' <tt>' . wfMsgHtml( 'deletedrev' ) . '</tt>';
@@ -778,6 +844,9 @@ class EnhancedChangesList extends ChangesList {
$r .= "</table></div>\n";
$this->rcCacheIndex++;
+
+ wfProfileOut( __METHOD__ );
+
return $r;
}
@@ -804,7 +873,7 @@ class EnhancedChangesList extends ChangesList {
protected function sideArrow() {
global $wgContLang;
$dir = $wgContLang->isRTL() ? 'l' : 'r';
- return $this->arrow( $dir, '+', wfMsg('rc-enhanced-expand') );
+ return $this->arrow( $dir, '+', wfMsg( 'rc-enhanced-expand' ) );
}
/**
@@ -813,7 +882,7 @@ class EnhancedChangesList extends ChangesList {
* @return string HTML <img> tag
*/
protected function downArrow() {
- return $this->arrow( 'd', '-', wfMsg('rc-enhanced-hide') );
+ return $this->arrow( 'd', '-', wfMsg( 'rc-enhanced-hide' ) );
}
/**
@@ -838,9 +907,13 @@ class EnhancedChangesList extends ChangesList {
*/
protected function recentChangesBlockLine( $rcObj ) {
global $wgContLang, $wgRCShowChangedSize;
+
+ wfProfileIn( __METHOD__ );
+
# 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 variables.
+ $classes = array(); // TODO implement
extract( $rcObj->mAttribs );
$curIdEq = "curid={$rc_cur_id}";
@@ -864,7 +937,7 @@ class EnhancedChangesList extends ChangesList {
# Diff and hist links
if ( $rc_type != RC_LOG ) {
$r .= ' ('. $rcObj->difflink . $this->message['semicolon-separator'];
- $r .= $this->skin->makeKnownLinkObj( $rcObj->getTitle(), wfMsg( 'hist' ),
+ $r .= $this->skin->makeKnownLinkObj( $rcObj->getTitle(), $this->message['hist'],
$curIdEq.'&action=history' ) . ')';
}
$r .= ' . . ';
@@ -883,19 +956,17 @@ class EnhancedChangesList extends ChangesList {
$this->skin, LogPage::extractParams($rc_params), true, true );
}
}
- # Edit or log comment
- if( $rc_type != RC_MOVE && $rc_type != RC_MOVE_OVER_REDIRECT ) {
- // log comment
- if ( $this->isDeleted($rcObj,LogPage::DELETED_COMMENT) ) {
- $r .= ' <span class="history-deleted">' . wfMsg('rev-deleted-comment') . '</span>';
- } else {
- $r .= $this->skin->commentBlock( $rc_comment, $rcObj->getTitle() );
- }
- }
+ $this->insertComment( $r, $rcObj );
+ $this->insertRollback( $r, $rcObj );
+ # Tags
+ $this->insertTags( $r, $rcObj, $classes );
# Show how many people are watching this if enabled
$r .= $this->numberofWatchingusers($rcObj->numberofWatchingusers);
$r .= "</td></tr></table>\n";
+
+ wfProfileOut( __METHOD__ );
+
return $r;
}
@@ -907,6 +978,9 @@ class EnhancedChangesList extends ChangesList {
if( count ( $this->rc_cache ) == 0 ) {
return '';
}
+
+ wfProfileIn( __METHOD__ );
+
$blockOut = '';
foreach( $this->rc_cache as $block ) {
if( count( $block ) < 2 ) {
@@ -915,6 +989,9 @@ class EnhancedChangesList extends ChangesList {
$blockOut .= $this->recentChangesBlockGroup( $block );
}
}
+
+ wfProfileOut( __METHOD__ );
+
return '<div>'.$blockOut.'</div>';
}
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index ed68fe7a..19878f76 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -33,7 +33,7 @@ if ( !defined( 'MW_PHP4' ) ) {
}
/** MediaWiki version number */
-$wgVersion = '1.14.0';
+$wgVersion = '1.15.0';
/** Name of the site. It must be changed in LocalSettings.php */
$wgSitename = 'MediaWiki';
@@ -224,6 +224,10 @@ $wgFileStore['deleted']['hash'] = 3; ///< 3-level subdirectory split
* equivalent to the corresponding member of $wgDBservers
* tablePrefix Table prefix, the foreign wiki's $wgDBprefix
* hasSharedCache True if the wiki's shared cache is accessible via the local $wgMemc
+ *
+ * ForeignAPIRepo:
+ * apibase Use for the foreign API's URL
+ * apiThumbCacheExpiry How long to locally cache thumbs for
*
* The default is to initialise these arrays from the MW<1.11 backwards compatible settings:
* $wgUploadPath, $wgThumbnailScriptPath, $wgSharedUploadDirectory, etc.
@@ -274,7 +278,8 @@ $wgUrlProtocols = array(
'nntp://', // @bug 3808 RFC 1738
'worldwind://',
'mailto:',
- 'news:'
+ 'news:',
+ 'svn://',
);
/** internal name of virus scanner. This servers as a key to the $wgAntivirusSetup array.
@@ -521,6 +526,11 @@ $wgUserEmailUseReplyTo = false;
$wgPasswordReminderResendTime = 24;
/**
+ * The time, in seconds, when an emailed temporary password expires.
+ */
+$wgNewPasswordExpiry = 3600 * 24 * 7;
+
+/**
* SMTP Mode
* For using a direct (authenticated) SMTP server connection.
* Default to false or fill an array :
@@ -977,7 +987,7 @@ $wgReadOnly = null;
$wgReadOnlyFile = false; ///< defaults to "{$wgUploadDirectory}/lock_yBgMBwiR";
/**
- * Filename for debug logging.
+ * Filename for debug logging. See http://www.mediawiki.org/wiki/How_to_debug
* The debug log file should be not be publicly accessible if it is used, as it
* may contain private data.
*/
@@ -1029,6 +1039,13 @@ $wgDebugDumpSql = false;
$wgDebugLogGroups = array();
/**
+ * Display debug data at the bottom of the main content area.
+ *
+ * Useful for developers and technical users trying to working on a closed wiki.
+ */
+$wgShowDebug = false;
+
+/**
* Show the contents of $wgHooks in Special:Version
*/
$wgSpecialVersionShowHooks = false;
@@ -1240,6 +1257,8 @@ $wgGroupPermissions['bureaucrat']['userrights'] = true;
$wgGroupPermissions['bureaucrat']['noratelimit'] = true;
// Permission to change users' groups assignments across wikis
#$wgGroupPermissions['bureaucrat']['userrights-interwiki'] = true;
+// Permission to export pages including linked pages regardless of $wgExportMaxLinkDepth
+#$wgGroupPermissions['bureaucrat']['override-export-depth'] = true;
#$wgGroupPermissions['sysop']['deleterevision'] = true;
// To hide usernames from users and Sysops
@@ -1287,7 +1306,7 @@ $wgGroupsRemoveFromSelf = array();
/**
* Set of available actions that can be restricted via action=protect
* You probably shouldn't change this.
- * Translated trough restriction-* messages.
+ * Translated through restriction-* messages.
*/
$wgRestrictionTypes = array( 'edit', 'move' );
@@ -1349,6 +1368,10 @@ $wgAutoConfirmCount = 0;
* array( APCOND_EMAILCONFIRMED ), *OR*
* array( APCOND_EDITCOUNT, number of edits ), *OR*
* array( APCOND_AGE, seconds since registration ), *OR*
+ * array( APCOND_INGROUPS, group1, group2, ... ), *OR*
+ * array( APCOND_ISIP, ip ), *OR*
+ * array( APCOND_IPINRANGE, range ), *OR*
+ * array( APCOND_AGE_FROM_EDIT, seconds since first edit ), *OR*
* similar constructs defined by extensions.
*
* If $wgEmailAuthentication is off, APCOND_EMAILCONFIRMED will be true for any
@@ -1446,7 +1469,7 @@ $wgCacheEpoch = '20030516000000';
* to ensure that client-side caches don't keep obsolete copies of global
* styles.
*/
-$wgStyleVersion = '195';
+$wgStyleVersion = '207';
# Server-side caching:
@@ -1974,6 +1997,7 @@ $wgMediaHandlers = array(
'image/jpeg' => 'BitmapHandler',
'image/png' => 'BitmapHandler',
'image/gif' => 'BitmapHandler',
+ 'image/tiff' => 'TiffHandler',
'image/x-ms-bmp' => 'BmpHandler',
'image/x-bmp' => 'BmpHandler',
'image/svg+xml' => 'SvgHandler', // official
@@ -2052,6 +2076,16 @@ $wgMaxImageArea = 1.25e7;
*/
$wgMaxAnimatedGifArea = 1.0e6;
/**
+ * Browsers don't support TIFF inline generally...
+ * For inline display, we need to convert to PNG or JPEG.
+ * Note scaling should work with ImageMagick, but may not with GD scaling.
+ * // PNG is lossless, but inefficient for photos
+ * $wgTiffThumbnailType = array( 'png', 'image/png' );
+ * // JPEG is good for photos, but has no transparency support. Bad for diagrams.
+ * $wgTiffThumbnailType = array( 'jpg', 'image/jpeg' );
+ */
+$wgTiffThumbnailType = false;
+/**
* If rendered thumbnail files are older than this timestamp, they
* will be rerendered on demand as if the file didn't already exist.
* Update if there is some need to force thumbs and SVG rasterizations
@@ -2081,10 +2115,9 @@ $wgIgnoreImageErrors = false;
*/
$wgGenerateThumbnailOnParse = true;
-/** Obsolete, always true, kept for compatibility with extensions */
+/** Whether or not to use image resizing */
$wgUseImageResize = true;
-
/** Set $wgCommandLineMode if it's not set already, to avoid notices */
if( !isset( $wgCommandLineMode ) ) {
$wgCommandLineMode = false;
@@ -2252,9 +2285,26 @@ $wgExportMaxHistory = 0;
$wgExportAllowListContributors = false ;
+/**
+ * If non-zero, Special:Export accepts a "pagelink-depth" parameter
+ * up to this specified level, which will cause it to include all
+ * pages linked to from the pages you specify. Since this number
+ * can become *insanely large* and could easily break your wiki,
+ * it's disabled by default for now.
+ *
+ * There's a HARD CODED limit of 5 levels of recursion to prevent a
+ * crazy-big export from being done by someone setting the depth
+ * number too high. In other words, last resort safety net.
+ */
+$wgExportMaxLinkDepth = 0;
/**
- * Edits matching these regular expressions in body text or edit summary
+ * Whether to allow the "export all pages in namespace" option
+ */
+$wgExportFromNamespaces = false;
+
+/**
+ * Edits matching these regular expressions in body text
* will be recognised as spam and rejected automatically.
*
* There's no administrator override on-wiki, so be careful what you set. :)
@@ -2264,6 +2314,9 @@ $wgExportAllowListContributors = false ;
*/
$wgSpamRegex = array();
+/** Same as the above except for edit summaries */
+$wgSummarySpamRegex = array();
+
/** 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
@@ -2374,6 +2427,8 @@ $wgDefaultUserOptions = array(
'rclimit' => 50,
'wllimit' => 250,
'hideminor' => 0,
+ 'hidepatrolled' => 0,
+ 'newpageshidepatrolled' => 0,
'highlightbroken' => 1,
'stubthreshold' => 0,
'previewontop' => 1,
@@ -2413,11 +2468,13 @@ $wgDefaultUserOptions = array(
'watchlisthideown' => 0,
'watchlisthideanons' => 0,
'watchlisthideliu' => 0,
+ 'watchlisthidepatrolled' => 0,
'watchcreations' => 0,
'watchdefault' => 0,
'watchmoves' => 0,
'watchdeletion' => 0,
'noconvertlink' => 0,
+ 'gender' => 'unknown',
);
/** Whether or not to allow and use real name fields. Defaults to true. */
@@ -2504,7 +2561,7 @@ $wgAutoloadClasses = array();
* $wgExtensionCredits[$type][] = array(
* 'name' => 'Example extension',
* 'version' => 1.9,
- * 'svn-revision' => '$LastChangedRevision: 47653 $',
+ * 'svn-revision' => '$LastChangedRevision: 51678 $',
* 'author' => 'Foo Barstein',
* 'url' => 'http://wwww.example.com/Example%20Extension/',
* 'description' => 'An example extension',
@@ -2724,6 +2781,9 @@ $wgBrowserBlackList = array(
*
* This variable is currently used ONLY for signature formatting, not for
* anything else.
+ *
+ * Timezones can be translated by editing MediaWiki messages of type
+ * timezone-nameinlowercase like timezone-utc.
*/
# $wgLocaltimezone = 'GMT';
# $wgLocaltimezone = 'PST8PDT';
@@ -2754,17 +2814,17 @@ $wgLocalTZoffset = null;
/**
- * When translating messages with wfMsg(), it is not always clear what should be
- * considered UI messages and what shoud be content messages.
+ * When translating messages with wfMsg(), it is not always clear what should
+ * be considered UI messages and what should be content messages.
*
- * For example, for regular wikipedia site like en, there should be only one
- * 'mainpage', therefore when getting the link of 'mainpage', we should treate
- * it as content of the site and call wfMsgForContent(), while for rendering the
- * text of the link, we call wfMsg(). The code in default behaves this way.
- * However, sites like common do offer different versions of 'mainpage' and the
- * like for different languages. This array provides a way to override the
- * default behavior. For example, to allow language specific mainpage and
- * community portal, set
+ * For example, for the English Wikipedia, there should be only one 'mainpage',
+ * so when getting the link for 'mainpage', we should treat it as site content
+ * and call wfMsgForContent(), but for rendering the text of the link, we call
+ * wfMsg(). The code behaves this way by default. However, sites like the
+ * Wikimedia Commons do offer different versions of 'mainpage' and the like for
+ * different languages. This array provides a way to override the default
+ * behavior. For example, to allow language-specific main page and community
+ * portal, set
*
* $wgForceUIMsgAsContentMsg = array( 'mainpage', 'portal-url' );
*/
@@ -2965,6 +3025,7 @@ $wgSpecialPageGroups = array(
'Newimages' => 'changes',
'Newpages' => 'changes',
'Log' => 'changes',
+ 'Tags' => 'changes',
'Upload' => 'media',
'Listfiles' => 'media',
@@ -3073,6 +3134,19 @@ $wgNoFollowLinks = true;
$wgNoFollowNsExceptions = array();
/**
+ * If this is set to an array of domains, external links to these domain names
+ * (or any subdomains) will not be set to rel="nofollow" regardless of the
+ * value of $wgNoFollowLinks. For instance:
+ *
+ * $wgNoFollowDomainExceptions = array( 'en.wikipedia.org', 'wiktionary.org' );
+ *
+ * This would add rel="nofollow" to links to de.wikipedia.org, but not
+ * en.wikipedia.org, wiktionary.org, en.wiktionary.org, us.en.wikipedia.org,
+ * etc.
+ */
+$wgNoFollowDomainExceptions = array();
+
+/**
* Default robot policy. The default policy is to encourage indexing and fol-
* lowing of links. It may be overridden on a per-namespace and/or per-page
* basis.
@@ -3216,6 +3290,12 @@ $wgRateLimitLog = null;
$wgRateLimitsExcludedGroups = array();
/**
+ * Array of IPs which should be excluded from rate limits.
+ * This may be useful for whitelisting NAT gateways for conferences, etc.
+ */
+$wgRateLimitsExcludedIPs = array();
+
+/**
* On Special:Unusedimages, consider images "used", if they are put
* into a category. Default (false) is not to count those as used.
*/
@@ -3490,6 +3570,18 @@ $wgAPIListModules = array();
$wgAPIMaxDBRows = 5000;
/**
+ * The maximum size (in bytes) of an API result.
+ * Don't set this lower than $wgMaxArticleSize*1024
+ */
+$wgAPIMaxResultSize = 8388608;
+
+/**
+ * The maximum number of uncached diffs that can be retrieved in one API
+ * request. Set this to 0 to disable API diffs altogether
+ */
+$wgAPIMaxUncachedDiffs = 1;
+
+/**
* Parser test suite files to be run by parserTests.php when no specific
* filename is passed to it.
*
@@ -3600,6 +3692,25 @@ $wgMaximumMovedPages = 100;
$wgFixDoubleRedirects = false;
/**
+ * Max number of redirects to follow when resolving redirects.
+ * 1 means only the first redirect is followed (default behavior).
+ * 0 or less means no redirects are followed.
+ */
+$wgMaxRedirects = 1;
+
+/**
+ * Array of invalid page redirect targets.
+ * Attempting to create a redirect to any of the pages in this array
+ * will make the redirect fail.
+ * Userlogout is hard-coded, so it does not need to be listed here.
+ * (bug 10569) Disallow Mypage and Mytalk as well.
+ *
+ * As of now, this only checks special pages. Redirects to pages in
+ * other namespaces cannot be invalidated by this variable.
+ */
+$wgInvalidRedirectTargets = array( 'Filepath', 'Mypage', 'Mytalk' );
+
+/**
* Array of namespaces to generate a sitemap for when the
* maintenance/generateSitemap.php script is run, or false if one is to be ge-
* nerated for all namespaces.
@@ -3626,10 +3737,10 @@ $wgPasswordAttemptThrottle = array( 'count' => 5, 'seconds' => 300 );
$wgEdititis = false;
/**
-* Enable the UniversalEditButton for browsers that support it
-* (currently only Firefox with an extension)
-* See http://universaleditbutton.org for more background information
-*/
+ * Enable the UniversalEditButton for browsers that support it
+ * (currently only Firefox with an extension)
+ * See http://universaleditbutton.org for more background information
+ */
$wgUniversalEditButton = true;
/**
@@ -3638,3 +3749,45 @@ $wgUniversalEditButton = true;
* and the functionality will be enabled universally.
*/
$wgEnforceHtmlIds = true;
+
+/**
+ * Search form behavior
+ * true = use Go & Search buttons
+ * false = use Go button & Advanced search link
+ */
+$wgUseTwoButtonsSearchForm = true;
+
+/**
+ * Preprocessor caching threshold
+ */
+$wgPreprocessorCacheThreshold = 1000;
+
+/**
+ * Allow filtering by change tag in recentchanges, history, etc
+ * Has no effect if no tags are defined in valid_tag.
+ */
+$wgUseTagFilter = true;
+
+/**
+ * Allow redirection to another page when a user logs in.
+ * To enable, set to a string like 'Main Page'
+ */
+$wgRedirectOnLogin = null;
+
+/**
+ * Characters to prevent during new account creations.
+ * This is used in a regular expression character class during
+ * registration (regex metacharacters like / are escaped).
+ */
+$wgInvalidUsernameCharacters = '@';
+
+/**
+ * Character used as a delimiter when testing for interwiki userrights
+ * (In Special:UserRights, it is possible to modify users on different
+ * databases if the delimiter is used, e.g. Someuser@enwiki).
+ *
+ * It is recommended that you have this delimiter in
+ * $wgInvalidUsernameCharacters above, or you will not be able to
+ * modify the user rights of those users via Special:UserRights
+ */
+$wgUserrightsInterwikiDelimiter = '@';
diff --git a/includes/EditPage.php b/includes/EditPage.php
index 0193dc38..3589b52d 100644
--- a/includes/EditPage.php
+++ b/includes/EditPage.php
@@ -84,6 +84,7 @@ class EditPage {
/* $didSave should be set to true whenever an article was succesfully altered. */
public $didSave = false;
+ public $undidRev = 0;
public $suppressIntro = false;
@@ -164,35 +165,28 @@ class EditPage {
$undorev->getPage() == $this->mArticle->getID() &&
!$undorev->isDeleted( Revision::DELETED_TEXT ) &&
!$oldrev->isDeleted( Revision::DELETED_TEXT ) ) {
- $undorev_text = $undorev->getText();
- $oldrev_text = $oldrev->getText();
- $currev_text = $text;
-
- if ( $currev_text != $undorev_text ) {
- $result = wfMerge( $undorev_text, $oldrev_text, $currev_text, $text );
+
+ $undotext = $this->mArticle->getUndoText( $undorev, $oldrev );
+ if ( $undotext === false ) {
+ # Warn the user that something went wrong
+ $this->editFormPageTop .= $wgOut->parse( '<div class="error mw-undo-failure">' . wfMsgNoTrans( 'undo-failure' ) . '</div>' );
} else {
- # No use doing a merge if it's just a straight revert.
- $text = $oldrev_text;
- $result = true;
- }
- if ( $result ) {
+ $text = $undotext;
# Inform the user of our success and set an automatic edit summary
- $this->editFormPageTop .= $wgOut->parse( wfMsgNoTrans( 'undo-success' ) );
+ $this->editFormPageTop .= $wgOut->parse( '<div class="mw-undo-success">' . wfMsgNoTrans( 'undo-success' ) . '</div>' );
$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->summary = wfMsgForContent( 'undo-summary', $undo, $undorev->getUserText() );
+ $this->undidRev = $undo;
}
$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.
- $this->editFormPageTop .= $wgOut->parse( wfMsgNoTrans( 'undo-norev' ) );
+ $this->editFormPageTop .= $wgOut->parse( '<div class="error mw-undo-norev">' . wfMsgNoTrans( 'undo-norev' ) . '</div>' );
}
} else if ( $section != '' ) {
if ( $section == 'new' ) {
@@ -330,7 +324,7 @@ class EditPage {
protected function wasDeletedSinceLastEdit() {
if ( $this->deletedSinceEdit )
return true;
- if ( $this->mTitle->isDeleted() ) {
+ if ( $this->mTitle->isDeletedQuick() ) {
$this->lastDelete = $this->getLastDelete();
if ( $this->lastDelete ) {
$deleteTime = wfTimestamp( TS_MW, $this->lastDelete->log_timestamp );
@@ -409,6 +403,11 @@ class EditPage {
}
}
}
+
+ // If they used redlink=1 and the page exists, redirect to the main article
+ if ( $wgRequest->getBool( 'redlink' ) && $this->mTitle->exists() ) {
+ $wgOut->redirect( $this->mTitle->getFullURL() );
+ }
wfProfileIn( __METHOD__."-business-end" );
@@ -427,7 +426,6 @@ class EditPage {
# Optional notices on a per-namespace and per-page basis
$editnotice_ns = 'editnotice-'.$this->mTitle->getNamespace();
- $editnotice_page = $editnotice_ns.'-'.$this->mTitle->getDBkey();
if ( !wfEmptyMsg( $editnotice_ns, wfMsgForContent( $editnotice_ns ) ) ) {
$wgOut->addWikiText( wfMsgForContent( $editnotice_ns ) );
}
@@ -440,8 +438,6 @@ class EditPage {
$wgOut->addWikiText( wfMsgForContent( $editnotice_base ) );
}
}
- } else if ( !wfEmptyMsg( $editnotice_page, wfMsgForContent( $editnotice_page ) ) ) {
- $wgOut->addWikiText( wfMsgForContent( $editnotice_page ) );
}
# Attempt submission here. This will check for edit conflicts,
@@ -529,7 +525,7 @@ class EditPage {
} elseif ( $this->section == 'new' ) {
// Nothing *to* preview for new sections
return false;
- } elseif ( ( $wgRequest->getVal( 'preload' ) !== '' || $this->mTitle->exists() ) && $wgUser->getOption( 'previewonfirst' ) ) {
+ } elseif ( ( $wgRequest->getVal( 'preload' ) !== null || $this->mTitle->exists() ) && $wgUser->getOption( 'previewonfirst' ) ) {
// Standard preference behaviour
return true;
} elseif ( !$this->mTitle->exists() && $this->mTitle->getNamespace() == NS_CATEGORY ) {
@@ -560,7 +556,7 @@ class EditPage {
$this->textbox2 = $this->safeUnicodeInput( $request, 'wpTextbox2' );
$this->mMetaData = rtrim( $request->getText( 'metadata' ) );
# Truncate for whole multibyte characters. +5 bytes for ellipsis
- $this->summary = $wgLang->truncate( $request->getText( 'wpSummary' ), 250 );
+ $this->summary = $wgLang->truncate( $request->getText( 'wpSummary' ), 250, '' );
# Remove extra headings from summaries and new sections.
$this->summary = preg_replace('/^\s*=+\s*(.*?)\s*=+\s*$/', '$1', $this->summary);
@@ -574,7 +570,7 @@ class EditPage {
# If the form is incomplete, force to preview.
wfDebug( "$fname: Form data appears to be incomplete\n" );
wfDebug( "POST DATA: " . var_export( $_POST, true ) . "\n" );
- $this->preview = true;
+ $this->preview = true;
} else {
/* Fallback for live preview */
$this->preview = $request->getCheck( 'wpPreview' ) || $request->getCheck( 'wpLivePreview' );
@@ -644,6 +640,13 @@ class EditPage {
if ( $this->section == 'new' && $request->getVal( 'preloadtitle' ) ) {
$this->summary = $request->getVal( 'preloadtitle' );
}
+ elseif ( $this->section != 'new' && $request->getVal( 'summary' ) ) {
+ $this->summary = $request->getText( 'summary' );
+ }
+
+ if ( $request->getVal( 'minor' ) ) {
+ $this->minoredit = true;
+ }
}
$this->oldid = $request->getInt( 'oldid' );
@@ -677,8 +680,16 @@ class EditPage {
if ( $this->suppressIntro ) {
return;
}
+
+ $namespace = $this->mTitle->getNamespace();
+
+ if ( $namespace == NS_MEDIAWIKI ) {
+ # Show a warning if editing an interface message
+ $wgOut->wrapWikiMsg( "<div class='mw-editinginterface'>\n$1</div>", 'editinginterface' );
+ }
+
# Show a warning message when someone creates/edits a user (talk) page but the user does not exists
- if ( $this->mTitle->getNamespace() == NS_USER || $this->mTitle->getNamespace() == NS_USER_TALK ) {
+ if ( $namespace == NS_USER || $namespace == NS_USER_TALK ) {
$parts = explode( '/', $this->mTitle->getText(), 2 );
$username = $parts[0];
$id = User::idFromName( $username );
@@ -737,7 +748,7 @@ class EditPage {
if ( !wfRunHooks( 'EditPage::attemptSave', array( &$this ) ) )
{
- wfDebug( "Hook 'EditPage::attemptSave' aborted article saving" );
+ wfDebug( "Hook 'EditPage::attemptSave' aborted article saving\n" );
return self::AS_HOOK_ERROR;
}
@@ -757,7 +768,7 @@ class EditPage {
$this->mMetaData = '' ;
# Check for spam
- $match = self::matchSpamRegex( $this->summary );
+ $match = self::matchSummarySpamRegex( $this->summary );
if ( $match === false ) {
$match = self::matchSpamRegex( $this->textbox1 );
}
@@ -859,11 +870,20 @@ class EditPage {
wfProfileOut( $fname );
return self::AS_HOOK_ERROR;
}
+
+ # Handle the user preference to force summaries here. Check if it's not a redirect.
+ if ( !$this->allowBlankSummary && !Title::newFromRedirect( $this->textbox1 ) ) {
+ if ( md5( $this->summary ) == $this->autoSumm ) {
+ $this->missingSummary = true;
+ wfProfileOut( $fname );
+ return self::AS_SUMMARY_NEEDED;
+ }
+ }
$isComment = ( $this->section == 'new' );
$this->mArticle->insertNewArticle( $this->textbox1, $this->summary,
- $this->minoredit, $this->watchthis, false, $isComment, $bot);
+ $this->minoredit, $this->watchthis, false, $isComment, $bot );
wfProfileOut( $fname );
return self::AS_SUCCESS_NEW_ARTICLE;
@@ -893,39 +913,35 @@ class EditPage {
}
}
$userid = $wgUser->getId();
+
+ # Suppress edit conflict with self, except for section edits where merging is required.
+ if ( $this->isConflict && $this->section == '' && $this->userWasLastToEdit($userid,$this->edittime) ) {
+ wfDebug( "EditPage::editForm Suppressing edit conflict, same user.\n" );
+ $this->isConflict = false;
+ }
if ( $this->isConflict ) {
wfDebug( "EditPage::editForm conflict! getting section '$this->section' for time '$this->edittime' (article time '" .
$this->mArticle->getTimestamp() . "')\n" );
- $text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $this->summary, $this->edittime);
- }
- else {
+ $text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $this->summary, $this->edittime );
+ } else {
wfDebug( "EditPage::editForm getting section '$this->section'\n" );
- $text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $this->summary);
+ $text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $this->summary );
}
if ( is_null( $text ) ) {
wfDebug( "EditPage::editForm activating conflict; section replace failed.\n" );
$this->isConflict = true;
- $text = $this->textbox1;
- }
-
- # Suppress edit conflict with self, except for section edits where merging is required.
- if ( $this->section == '' && $userid && $this->userWasLastToEdit($userid,$this->edittime) ) {
- wfDebug( "EditPage::editForm Suppressing edit conflict, same user.\n" );
- $this->isConflict = false;
- } else {
- # switch from section editing to normal editing in edit conflict
- if ( $this->isConflict ) {
- # Attempt merge
- if ( $this->mergeChangesInto( $text ) ) {
- // Successful merge! Maybe we should tell the user the good news?
- $this->isConflict = false;
- wfDebug( "EditPage::editForm Suppressing edit conflict, successful merge.\n" );
- } else {
- $this->section = '';
- $this->textbox1 = $text;
- wfDebug( "EditPage::editForm Keeping edit conflict, failed merge.\n" );
- }
+ $text = $this->textbox1; // do not try to merge here!
+ } else if ( $this->isConflict ) {
+ # Attempt merge
+ if ( $this->mergeChangesInto( $text ) ) {
+ // Successful merge! Maybe we should tell the user the good news?
+ $this->isConflict = false;
+ wfDebug( "EditPage::editForm Suppressing edit conflict, successful merge.\n" );
+ } else {
+ $this->section = '';
+ $this->textbox1 = $text;
+ wfDebug( "EditPage::editForm Keeping edit conflict, failed merge.\n" );
}
}
@@ -944,9 +960,9 @@ class EditPage {
}
# Handle the user preference to force summaries here, but not for null edits
- if ( $this->section != 'new' && !$this->allowBlankSummary && 0 != strcmp($oldtext, $text) &&
- !is_object( Title::newFromRedirect( $text ) ) # check if it's not a redirect
- ) {
+ if ( $this->section != 'new' && !$this->allowBlankSummary && 0 != strcmp($oldtext,$text)
+ && !Title::newFromRedirect( $text ) ) # check if it's not a redirect
+ {
if ( md5( $this->summary ) == $this->autoSumm ) {
$this->missingSummary = true;
wfProfileOut( $fname );
@@ -1008,7 +1024,8 @@ class EditPage {
# update the article here
if ( $this->mArticle->updateArticle( $text, $this->summary, $this->minoredit,
- $this->watchthis, $bot, $sectionanchor ) ) {
+ $this->watchthis, $bot, $sectionanchor ) )
+ {
wfProfileOut( $fname );
return self::AS_SUCCESS_UPDATE;
} else {
@@ -1024,6 +1041,7 @@ class EditPage {
* 50 revisions for the sake of performance.
*/
protected function userWasLastToEdit( $id, $edittime ) {
+ if( !$id ) return false;
$dbw = wfGetDB( DB_MASTER );
$res = $dbw->select( 'revision',
'rev_user',
@@ -1047,14 +1065,26 @@ class EditPage {
*/
public static function matchSpamRegex( $text ) {
global $wgSpamRegex;
- if ( $wgSpamRegex ) {
- // For back compatibility, $wgSpamRegex may be a single string or an array of regexes.
- $regexes = (array)$wgSpamRegex;
- foreach( $regexes as $regex ) {
- $matches = array();
- if ( preg_match( $regex, $text, $matches ) ) {
- return $matches[0];
- }
+ // For back compatibility, $wgSpamRegex may be a single string or an array of regexes.
+ $regexes = (array)$wgSpamRegex;
+ return self::matchSpamRegexInternal( $text, $regexes );
+ }
+
+ /**
+ * Check given input text against $wgSpamRegex, and return the text of the first match.
+ * @return mixed -- matching string or false
+ */
+ public static function matchSummarySpamRegex( $text ) {
+ global $wgSummarySpamRegex;
+ $regexes = (array)$wgSummarySpamRegex;
+ return self::matchSpamRegexInternal( $text, $regexes );
+ }
+
+ protected static function matchSpamRegexInternal( $text, $regexes ) {
+ foreach( $regexes as $regex ) {
+ $matches = array();
+ if( preg_match( $regex, $text, $matches ) ) {
+ return $matches[0];
}
}
return false;
@@ -1133,7 +1163,7 @@ class EditPage {
$wgOut->setArticleRelated( true );
if ( $this->isConflict ) {
- $wgOut->addWikiMsg( 'explainconflict' );
+ $wgOut->wrapWikiMsg( "<div class='mw-explainconflict'>\n$1</div>", 'explainconflict' );
$this->textbox2 = $this->textbox1;
$this->textbox1 = $this->getContent();
@@ -1142,9 +1172,7 @@ class EditPage {
if ( $this->section != '' && $this->section != 'new' ) {
$matches = array();
if ( !$this->summary && !$this->preview && !$this->diff ) {
- preg_match( "/^(=+)(.+)\\1/mi",
- $this->textbox1,
- $matches );
+ preg_match( "/^(=+)(.+)\\1/mi", $this->textbox1, $matches );
if ( !empty( $matches[2] ) ) {
global $wgParser;
$this->summary = "/* " .
@@ -1155,7 +1183,7 @@ class EditPage {
}
if ( $this->missingComment ) {
- $wgOut->wrapWikiMsg( '<div id="mw-missingcommenttext">$1</div>', 'missingcommenttext' );
+ $wgOut->wrapWikiMsg( '<div id="mw-missingcommenttext">$1</div>', 'missingcommenttext' );
}
if ( $this->missingSummary && $this->section != 'new' ) {
@@ -1177,9 +1205,9 @@ class EditPage {
// Let sysop know that this will make private content public if saved
if ( !$this->mArticle->mRevision->userCan( Revision::DELETED_TEXT ) ) {
- $wgOut->addWikiMsg( 'rev-deleted-text-permission' );
+ $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-permission' );
} else if ( $this->mArticle->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
- $wgOut->addWikiMsg( 'rev-deleted-text-view' );
+ $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-view' );
}
if ( !$this->mArticle->mRevision->isCurrent() ) {
@@ -1208,8 +1236,6 @@ class EditPage {
$classes = array(); // Textarea CSS
if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
- # Show a warning if editing an interface message
- $wgOut->addWikiMsg( 'editinginterface' );
} elseif ( $this->mTitle->isProtected( 'edit' ) ) {
# Is the title semi-protected?
if ( $this->mTitle->isSemiProtected() ) {
@@ -1228,17 +1254,19 @@ class EditPage {
if ( $this->mTitle->isCascadeProtected() ) {
# Is this page under cascading protection from some source pages?
list($cascadeSources, /* $restrictions */) = $this->mTitle->getCascadeProtectionSources();
- $notice = "$1\n";
- if ( count($cascadeSources) > 0 ) {
+ $notice = "<div class='mw-cascadeprotectedwarning'>$1\n";
+ $cascadeSourcesCount = count( $cascadeSources );
+ if ( $cascadeSourcesCount > 0 ) {
# Explain, and list the titles responsible
foreach( $cascadeSources as $page ) {
$notice .= '* [[:' . $page->getPrefixedText() . "]]\n";
}
}
- $wgOut->wrapWikiMsg( $notice, array( 'cascadeprotectedwarning', count($cascadeSources) ) );
+ $notice .= '</div>';
+ $wgOut->wrapWikiMsg( $notice, array( 'cascadeprotectedwarning', $cascadeSourcesCount ) );
}
if ( !$this->mTitle->exists() && $this->mTitle->getRestrictions( 'create' ) ) {
- $wgOut->addWikiMsg( 'titleprotectedwarning' );
+ $wgOut->wrapWikiMsg( '<div class="mw-titleprotectedwarning">$1</div>', 'titleprotectedwarning' );
}
if ( $this->kblength === false ) {
@@ -1263,6 +1291,7 @@ class EditPage {
$cancel = $sk->makeKnownLink( $wgTitle->getPrefixedText(),
wfMsgExt('cancel', array('parseinline')) );
+ $separator = wfMsgExt( 'pipe-separator' , 'escapenoentities' );
$edithelpurl = Skin::makeInternalOrExternalUrl( wfMsgForContent( 'edithelppage' ));
$edithelp = '<a target="helpwindow" href="'.$edithelpurl.'">'.
htmlspecialchars( wfMsg( 'edithelp' ) ).'</a> '.
@@ -1318,7 +1347,7 @@ class EditPage {
# if this is a comment, show a subject line at the top, which is also the edit summary.
# Otherwise, show a summary field at the bottom
- $summarytext = htmlspecialchars( $wgContLang->recodeForEdit( $this->summary ) ); # FIXME
+ $summarytext = $wgContLang->recodeForEdit( $this->summary );
# If a blank edit summary was previously provided, and the appropriate
# user preference is active, pass a hidden tag as wpIgnoreBlankSummary. This will stop the
@@ -1332,7 +1361,26 @@ class EditPage {
$autosumm = $this->autoSumm ? $this->autoSumm : md5( $this->summary );
$summaryhiddens .= Xml::hidden( 'wpAutoSummary', $autosumm );
if ( $this->section == 'new' ) {
- $commentsubject="<span id='wpSummaryLabel'><label for='wpSummary'>{$subject}</label></span>\n<input tabindex='1' type='text' value=\"$summarytext\" name='wpSummary' id='wpSummary' maxlength='200' size='60' />{$summaryhiddens}<br />";
+ $commentsubject = '';
+ if ( !$wgRequest->getBool( 'nosummary' ) ) {
+ # Add a class if 'missingsummary' is triggered to allow styling of the summary line
+ $summaryClass = $this->missingSummary ? 'mw-summarymissed' : 'mw-summary';
+
+ $commentsubject =
+ Xml::tags( 'label', array( 'for' => 'wpSummary' ), $subject );
+ $commentsubject =
+ Xml::tags( 'span', array( 'class' => $summaryClass, 'id' => "wpSummaryLabel" ),
+ $commentsubject );
+ $commentsubject .= '&nbsp;';
+ $commentsubject .= Xml::input( 'wpSummary',
+ 60,
+ $summarytext,
+ array(
+ 'id' => 'wpSummary',
+ 'maxlength' => '200',
+ 'tabindex' => '1'
+ ) );
+ }
$editsummary = "<div class='editOptions'>\n";
global $wgParser;
$formattedSummary = wfMsgForContent( 'newsectionsummary', $wgParser->stripSectionName( $this->summary ) );
@@ -1340,10 +1388,39 @@ class EditPage {
$summarypreview = '';
} else {
$commentsubject = '';
- $editsummary="<div class='editOptions'>\n<span id='wpSummaryLabel'><label for='wpSummary'>{$summary}</label></span>\n<input tabindex='2' type='text' value=\"$summarytext\" name='wpSummary' id='wpSummary' maxlength='200' size='60' />{$summaryhiddens}<br />";
- $summarypreview = $summarytext && $this->preview ? "<div class=\"mw-summary-preview\">". wfMsg('summary-preview') .$sk->commentBlock( $this->summary, $this->mTitle )."</div>\n" : '';
+
+ # Add a class if 'missingsummary' is triggered to allow styling of the summary line
+ $summaryClass = $this->missingSummary ? 'mw-summarymissed' : 'mw-summary';
+
+ $editsummary = Xml::tags( 'label', array( 'for' => 'wpSummary' ), $summary );
+ $editsummary = Xml::tags( 'span', array( 'class' => $summaryClass, 'id' => "wpSummaryLabel" ),
+ $editsummary ) . ' ';
+
+ $editsummary .= Xml::input( 'wpSummary',
+ 60,
+ $summarytext,
+ array(
+ 'id' => 'wpSummary',
+ 'maxlength' => '200',
+ 'tabindex' => '1'
+ ) );
+
+ // No idea where this is closed.
+ $editsummary = Xml::openElement( 'div', array( 'class' => 'editOptions' ) )
+ . $editsummary . '<br/>';
+
+ $summarypreview = '';
+ if ( $summarytext && $this->preview ) {
+ $summarypreview =
+ Xml::tags( 'div',
+ array( 'class' => 'mw-summary-preview' ),
+ wfMsg( 'summary-preview' ) .
+ $sk->commentBlock( $this->summary, $this->mTitle )
+ );
+ }
$subjectpreview = '';
}
+ $commentsubject .= $summaryhiddens;
# Set focus to the edit box on load, except on preview or diff, where it would interfere with the display
if ( !$this->preview && !$this->diff ) {
@@ -1373,15 +1450,18 @@ class EditPage {
$recreate = '';
if ( $this->wasDeletedSinceLastEdit() ) {
if ( 'save' != $this->formtype ) {
- $wgOut->addWikiMsg('deletedwhileediting');
+ $wgOut->wrapWikiMsg(
+ "<div class='error mw-deleted-while-editing'>\n$1</div>",
+ 'deletedwhileediting' );
} else {
- // Hide the toolbar and edit area, use can click preview to get it back
+ // Hide the toolbar and edit area, user can click preview to get it back
// Add an confirmation checkbox and explanation.
$toolbar = '';
- $recreate = $wgOut->parse( wfMsg( 'confirmrecreate', $this->lastDelete->user_name , $this->lastDelete->log_comment ));
- $recreate .=
- "<br /><input tabindex='1' type='checkbox' value='1' name='wpRecreate' id='wpRecreate' />".
- "<label for='wpRecreate' title='".wfMsg('tooltip-recreate')."'>". wfMsg('recreate')."</label>";
+ $recreate = '<div class="mw-confirm-recreate">' .
+ $wgOut->parse( wfMsg( 'confirmrecreate', $this->lastDelete->user_name , $this->lastDelete->log_comment ) ) .
+ Xml::checkLabel( wfMsg( 'recreate' ), 'wpRecreate', 'wpRecreate', false,
+ array( 'title' => $sk->titleAttrib( 'recreate' ), 'tabindex' => 1, 'id' => 'wpRecreate' )
+ ) . '</div>';
}
}
@@ -1436,7 +1516,7 @@ END
$wgOut->addHTML(
"<div class='editButtons'>
{$buttonshtml}
- <span class='editHelp'>{$cancel} | {$edithelp}</span>
+ <span class='editHelp'>{$cancel}{$separator}{$edithelp}</span>
</div><!-- editButtons -->
</div><!-- editOptions -->");
@@ -1606,7 +1686,7 @@ END
$wgOut->addHTML( '</div>' );
}
- function getLastDelete() {
+ protected function getLastDelete() {
$dbr = wfGetDB( DB_SLAVE );
$data = $dbr->selectRow(
array( 'logging', 'user' ),
@@ -1618,15 +1698,23 @@ END
'log_title',
'log_comment',
'log_params',
- 'user_name', ),
+ 'log_deleted',
+ 'user_name' ),
array( 'log_namespace' => $this->mTitle->getNamespace(),
'log_title' => $this->mTitle->getDBkey(),
'log_type' => 'delete',
'log_action' => 'delete',
'user_id=log_user' ),
__METHOD__,
- array( 'LIMIT' => 1, 'ORDER BY' => 'log_timestamp DESC' ) );
-
+ array( 'LIMIT' => 1, 'ORDER BY' => 'log_timestamp DESC' )
+ );
+ // Quick paranoid permission checks...
+ if( is_object($data) ) {
+ if( $data->log_deleted & LogPage::DELETED_USER )
+ $data->user_name = wfMsgHtml('rev-deleted-user');
+ if( $data->log_deleted & LogPage::DELETED_COMMENT )
+ $data->log_comment = wfMsgHtml('rev-deleted-comment');
+ }
return $data;
}
@@ -1651,6 +1739,8 @@ END
$parserOptions = ParserOptions::newFromUser( $wgUser );
$parserOptions->setEditSection( false );
+ $parserOptions->setIsPreview( true );
+ $parserOptions->setIsSectionPreview( !is_null($this->section) && $this->section !== '' );
global $wgRawHtml;
if ( $wgRawHtml && !$this->mTokenOk ) {
@@ -1672,7 +1762,7 @@ END
$parserOptions->setTidy(true);
$parserOutput = $wgParser->parse( $previewtext, $this->mTitle, $parserOptions );
$previewHTML = $parserOutput->mText;
- } elseif ( $rt = Title::newFromRedirect( $this->textbox1 ) ) {
+ } elseif ( $rt = Title::newFromRedirectArray( $this->textbox1 ) ) {
$previewHTML = $this->mArticle->viewRedirect( $rt, false );
} else {
$toparse = $this->textbox1;
@@ -1834,8 +1924,7 @@ END
$baseText = $baseRevision->getText();
// The current state, we want to merge updates into it
- $currentRevision = Revision::loadFromTitle(
- $db, $this->mTitle );
+ $currentRevision = Revision::loadFromTitle( $db, $this->mTitle );
if ( is_null( $currentRevision ) ) {
wfProfileOut( $fname );
return false;
@@ -2389,7 +2478,9 @@ END
global $wgUser, $wgOut, $wgTitle, $wgRequest;
$resultDetails = false;
- $value = $this->internalAttemptSave( $resultDetails, $wgUser->isAllowed('bot') && $wgRequest->getBool('bot', true) );
+ # Allow bots to exempt some edits from bot flagging
+ $bot = $wgUser->isAllowed('bot') && $wgRequest->getBool('bot',true);
+ $value = $this->internalAttemptSave( $resultDetails, $bot );
if ( $value == self::AS_SUCCESS_UPDATE || $value == self::AS_SUCCESS_NEW_ARTICLE ) {
$this->didSave = true;
diff --git a/includes/EnotifNotifyJob.php b/includes/EnotifNotifyJob.php
index 31fcb0d5..f7178d0f 100644
--- a/includes/EnotifNotifyJob.php
+++ b/includes/EnotifNotifyJob.php
@@ -26,7 +26,8 @@ class EnotifNotifyJob extends Job {
$this->params['timestamp'],
$this->params['summary'],
$this->params['minorEdit'],
- $this->params['oldid']
+ $this->params['oldid'],
+ $this->params['watchers']
);
return true;
}
diff --git a/includes/Exception.php b/includes/Exception.php
index eb715986..5f808b20 100644
--- a/includes/Exception.php
+++ b/includes/Exception.php
@@ -161,23 +161,26 @@ class MWException extends Exception {
if( $hookResult = $this->runHooks( get_class( $this ) . "Raw" ) ) {
die( $hookResult );
}
- echo $this->htmlHeader();
- echo $this->getHTML();
- echo $this->htmlFooter();
+ if ( defined( 'MEDIAWIKI_INSTALL' ) || $this->htmlBodyOnly() ) {
+ echo $this->getHTML();
+ } else {
+ echo $this->htmlHeader();
+ echo $this->getHTML();
+ echo $this->htmlFooter();
+ }
}
}
/**
* Output a report about the exception and takes care of formatting.
- * It will be either HTML or plain text based on $wgCommandLineMode.
+ * It will be either HTML or plain text based on isCommandLine().
*/
function report() {
- global $wgCommandLineMode;
$log = $this->getLogMessage();
if ( $log ) {
wfDebugLog( 'exception', $log );
}
- if ( $wgCommandLineMode ) {
+ if ( self::isCommandLine() ) {
wfPrintError( $this->getText() );
} else {
$this->reportHTML();
@@ -204,7 +207,7 @@ class MWException extends Exception {
<title>$title</title>
</head>
<body>
- <h1><img src='$wgLogo' style='float:left;margin-right:1em' alt=''>$title</h1>
+ <h1><img src='$wgLogo' style='float:left;margin-right:1em' alt=''/>$title</h1>
";
}
@@ -214,6 +217,17 @@ class MWException extends Exception {
function htmlFooter() {
echo "</body></html>";
}
+
+ /**
+ * headers handled by subclass?
+ */
+ function htmlBodyOnly() {
+ return false;
+ }
+
+ static function isCommandLine() {
+ return !empty( $GLOBALS['wgCommandLineMode'] ) && !defined( 'MEDIAWIKI_INSTALL' );
+ }
}
/**
@@ -264,41 +278,44 @@ function wfInstallExceptionHandler() {
* Report an exception to the user
*/
function wfReportException( Exception $e ) {
- if ( $e instanceof MWException ) {
- try {
- $e->report();
- } catch ( Exception $e2 ) {
- // Exception occurred from within exception handler
- // Show a simpler error message for the original exception,
- // don't try to invoke report()
- $message = "MediaWiki internal error.\n\n" .
- "Original exception: " . $e->__toString() .
- "\n\nException caught inside exception handler: " .
- $e2->__toString() . "\n";
-
- if ( !empty( $GLOBALS['wgCommandLineMode'] ) ) {
- wfPrintError( $message );
- } else {
- echo nl2br( htmlspecialchars( $message ) ). "\n";
- }
- }
- } else {
- $message = "Unexpected non-MediaWiki exception encountered, of type \"" . get_class( $e ) . "\"\n" .
- $e->__toString() . "\n";
- if ( $GLOBALS['wgShowExceptionDetails'] ) {
- $message .= "\n" . $e->getTraceAsString() ."\n";
- }
- if ( !empty( $GLOBALS['wgCommandLineMode'] ) ) {
- wfPrintError( $message );
- } else {
- echo nl2br( htmlspecialchars( $message ) ). "\n";
- }
- }
+ $cmdLine = MWException::isCommandLine();
+ if ( $e instanceof MWException ) {
+ try {
+ $e->report();
+ } catch ( Exception $e2 ) {
+ // Exception occurred from within exception handler
+ // Show a simpler error message for the original exception,
+ // don't try to invoke report()
+ $message = "MediaWiki internal error.\n\n";
+ if ( $GLOBALS['wgShowExceptionDetails'] )
+ $message .= "Original exception: " . $e->__toString();
+ $message .= "\n\nException caught inside exception handler";
+ if ( $GLOBALS['wgShowExceptionDetails'] )
+ $message .= ": " . $e2->__toString();
+ $message .= "\n";
+ if ( $cmdLine ) {
+ wfPrintError( $message );
+ } else {
+ echo nl2br( htmlspecialchars( $message ) ). "\n";
+ }
+ }
+ } else {
+ $message = "Unexpected non-MediaWiki exception encountered, of type \"" . get_class( $e ) . "\"\n" .
+ $e->__toString() . "\n";
+ if ( $GLOBALS['wgShowExceptionDetails'] ) {
+ $message .= "\n" . $e->getTraceAsString() ."\n";
+ }
+ if ( $cmdLine ) {
+ wfPrintError( $message );
+ } else {
+ echo nl2br( htmlspecialchars( $message ) ). "\n";
+ }
+ }
}
/**
* Print a message, if possible to STDERR.
- * Use this in command line mode only (see wgCommandLineMode)
+ * Use this in command line mode only (see isCommandLine)
*/
function wfPrintError( $message ) {
#NOTE: STDERR may not be available, especially if php-cgi is used from the command line (bug #15602).
diff --git a/includes/Exif.php b/includes/Exif.php
index d5cf09cf..9e54bd55 100644
--- a/includes/Exif.php
+++ b/includes/Exif.php
@@ -1,10 +1,5 @@
<?php
/**
- * @ingroup Media
- * @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
- *
* 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
@@ -20,7 +15,12 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @ingroup Media
+ * @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
* @see http://exif.org/Exif2-2.PDF The Exif 2.2 specification
+ * @file
*/
/**
@@ -28,23 +28,21 @@
* @ingroup Media
*/
class Exif {
+
+ const BYTE = 1; //!< An 8-bit (1-byte) unsigned integer.
+ const ASCII = 2; //!< An 8-bit byte containing one 7-bit ASCII code. The final byte is terminated with NULL.
+ const SHORT = 3; //!< A 16-bit (2-byte) unsigned integer.
+ const LONG = 4; //!< A 32-bit (4-byte) unsigned integer.
+ const RATIONAL = 5; //!< Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator
+ const UNDEFINED = 7; //!< An 8-bit byte that can take any value depending on the field definition
+ const SLONG = 9; //!< A 32-bit (4-byte) signed integer (2's complement notation),
+ const SRATIONAL = 10; //!< Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator.
+
//@{
/* @var array
* @private
*/
- /**#@+
- * Exif tag type definition
- */
- const BYTE = 1; # An 8-bit (1-byte) unsigned integer.
- const ASCII = 2; # An 8-bit byte containing one 7-bit ASCII code. The final byte is terminated with NULL.
- const SHORT = 3; # A 16-bit (2-byte) unsigned integer.
- const LONG = 4; # A 32-bit (4-byte) unsigned integer.
- const RATIONAL = 5; # Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator
- const UNDEFINED = 7; # An 8-bit byte that can take any value depending on the field definition
- const SLONG = 9; # A 32-bit (4-byte) signed integer (2's complement notation),
- const SRATIONAL = 10; # Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator.
-
/**
* Exif tags grouped by category, the tagname itself is the key and the type
* is the value, in the case of more than one possible value type they are
diff --git a/includes/Export.php b/includes/Export.php
index 5f040b13..909804cf 100644
--- a/includes/Export.php
+++ b/includes/Export.php
@@ -30,9 +30,10 @@ class WikiExporter {
var $dumpUploads = false;
- const FULL = 0;
- const CURRENT = 1;
- const LOGS = 2;
+ const FULL = 1;
+ const CURRENT = 2;
+ const STABLE = 4; // extension defined
+ const LOGS = 8;
const BUFFER = 0;
const STREAM = 1;
@@ -175,93 +176,103 @@ class WikiExporter {
}
protected function dumpFrom( $cond = '' ) {
- $fname = 'WikiExporter::dumpFrom';
- wfProfileIn( $fname );
-
- # For logs dumps...
+ wfProfileIn( __METHOD__ );
+ # For logging dumps...
if( $this->history & self::LOGS ) {
+ if( $this->buffer == WikiExporter::STREAM ) {
+ $prev = $this->db->bufferResults( false );
+ }
$where = array( 'user_id = log_user' );
# Hide private logs
- $where[] = LogEventsList::getExcludeClause( $this->db );
+ $hideLogs = LogEventsList::getExcludeClause( $this->db );
+ if( $hideLogs ) $where[] = $hideLogs;
+ # Add on any caller specified conditions
if( $cond ) $where[] = $cond;
+ # Get logging table name for logging.* clause
+ $logging = $this->db->tableName('logging');
$result = $this->db->select( array('logging','user'),
- '*',
+ array( "{$logging}.*", 'user_name' ), // grab the user name
$where,
- $fname,
+ __METHOD__,
array( 'ORDER BY' => 'log_id', 'USE INDEX' => array('logging' => 'PRIMARY') )
);
$wrapper = $this->db->resultObject( $result );
+ if( $this->buffer == WikiExporter::STREAM ) {
+ $this->db->bufferResults( $prev );
+ }
$this->outputLogStream( $wrapper );
# For page dumps...
} else {
- list($page,$revision,$text) = $this->db->tableNamesN('page','revision','text');
-
- $order = 'ORDER BY page_id';
- $limit = '';
-
- if( $this->history == WikiExporter::FULL ) {
- $join = 'page_id=rev_page';
- } elseif( $this->history == WikiExporter::CURRENT ) {
- if ( $this->list_authors && $cond != '' ) { // List authors, if so desired
- $this->do_list_authors ( $page , $revision , $cond );
+ $tables = array( 'page', 'revision' );
+ $opts = array( 'ORDER BY' => 'page_id ASC' );
+ $opts['USE INDEX'] = array();
+ $join = array();
+ # Full history dumps...
+ if( $this->history & WikiExporter::FULL ) {
+ $join['revision'] = array('INNER JOIN','page_id=rev_page');
+ # Latest revision dumps...
+ } elseif( $this->history & WikiExporter::CURRENT ) {
+ if( $this->list_authors && $cond != '' ) { // List authors, if so desired
+ list($page,$revision) = $this->db->tableNamesN('page','revision');
+ $this->do_list_authors( $page, $revision, $cond );
+ }
+ $join['revision'] = array('INNER JOIN','page_id=rev_page AND page_latest=rev_id');
+ # "Stable" revision dumps...
+ } elseif( $this->history & WikiExporter::STABLE ) {
+ # Default JOIN, to be overridden...
+ $join['revision'] = array('INNER JOIN','page_id=rev_page AND page_latest=rev_id');
+ # One, and only one hook should set this, and return false
+ if( wfRunHooks( 'WikiExporter::dumpStableQuery', array(&$tables,&$opts,&$join) ) ) {
+ wfProfileOut( __METHOD__ );
+ return new WikiError( __METHOD__." given invalid history dump type." );
}
- $join = 'page_id=rev_page AND page_latest=rev_id';
- } elseif ( is_array( $this->history ) ) {
- $join = 'page_id=rev_page';
- if ( $this->history['dir'] == 'asc' ) {
+ # Time offset/limit for all pages/history...
+ } elseif( is_array( $this->history ) ) {
+ $revJoin = 'page_id=rev_page';
+ # Set time order
+ if( $this->history['dir'] == 'asc' ) {
$op = '>';
- $order .= ', rev_timestamp';
+ $opts['ORDER BY'] = 'rev_timestamp ASC';
} else {
$op = '<';
- $order .= ', rev_timestamp DESC';
+ $opts['ORDER BY'] = 'rev_timestamp DESC';
}
- if ( !empty( $this->history['offset'] ) ) {
- $join .= " AND rev_timestamp $op " . $this->db->addQuotes(
- $this->db->timestamp( $this->history['offset'] ) );
+ # Set offset
+ if( !empty( $this->history['offset'] ) ) {
+ $revJoin .= " AND rev_timestamp $op " .
+ $this->db->addQuotes( $this->db->timestamp( $this->history['offset'] ) );
}
- if ( !empty( $this->history['limit'] ) ) {
- $limitNum = intval( $this->history['limit'] );
- if ( $limitNum > 0 ) {
- $limit = "LIMIT $limitNum";
- }
+ $join['revision'] = array('INNER JOIN',$revJoin);
+ # Set query limit
+ if( !empty( $this->history['limit'] ) ) {
+ $opts['LIMIT'] = intval( $this->history['limit'] );
}
+ # Uknown history specification parameter?
} else {
- wfProfileOut( $fname );
- return new WikiError( "$fname given invalid history dump type." );
+ wfProfileOut( __METHOD__ );
+ return new WikiError( __METHOD__." given invalid history dump type." );
+ }
+ # Query optimization hacks
+ if( $cond == '' ) {
+ $opts[] = 'STRAIGHT_JOIN';
+ $opts['USE INDEX']['page'] = 'PRIMARY';
+ }
+ # Build text join options
+ if( $this->text != WikiExporter::STUB ) { // 1-pass
+ $tables[] = 'text';
+ $join['text'] = array('INNER JOIN','rev_text_id=old_id');
}
- $where = ( $cond == '' ) ? '' : "$cond AND";
if( $this->buffer == WikiExporter::STREAM ) {
$prev = $this->db->bufferResults( false );
}
- if( $cond == '' ) {
- // Optimization hack for full-database dump
- $revindex = $pageindex = $this->db->useIndexClause("PRIMARY");
- $straight = ' /*! STRAIGHT_JOIN */ ';
- } else {
- $pageindex = '';
- $revindex = '';
- $straight = '';
- }
- if( $this->text == WikiExporter::STUB ) {
- $sql = "SELECT $straight * FROM
- $page $pageindex,
- $revision $revindex
- WHERE $where $join
- $order $limit";
- } else {
- $sql = "SELECT $straight * FROM
- $page $pageindex,
- $revision $revindex,
- $text
- WHERE $where $join AND rev_text_id=old_id
- $order $limit";
- }
- $result = $this->db->query( $sql, $fname );
+
+ # Do the query!
+ $result = $this->db->select( $tables, '*', $cond, __METHOD__, $opts, $join );
$wrapper = $this->db->resultObject( $result );
+ # Output dump results
$this->outputPageStream( $wrapper );
-
- if ( $this->list_authors ) {
+ if( $this->list_authors ) {
$this->outputPageStream( $wrapper );
}
@@ -269,7 +280,7 @@ class WikiExporter {
$this->db->bufferResults( $prev );
}
}
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
}
/**
@@ -399,7 +410,7 @@ class XmlDumpWriter {
function namespaces() {
global $wgContLang;
- $spaces = " <namespaces>\n";
+ $spaces = "<namespaces>\n";
foreach( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
$spaces .= ' ' . Xml::element( 'namespace', array( 'key' => $ns ), $title ) . "\n";
}
diff --git a/includes/ExternalStore.php b/includes/ExternalStore.php
index d095aba0..1e750bb5 100644
--- a/includes/ExternalStore.php
+++ b/includes/ExternalStore.php
@@ -92,6 +92,8 @@ class ExternalStore {
$url = $store->store( $params, $data ); // Try to save the object
} catch ( DBConnectionError $error ) {
$url = false;
+ } catch( DBQueryError $error ) {
+ $url = false;
}
if ( $url ) {
return $url; // Done!
diff --git a/includes/FileDeleteForm.php b/includes/FileDeleteForm.php
index 66086b0f..5177d35f 100644
--- a/includes/FileDeleteForm.php
+++ b/includes/FileDeleteForm.php
@@ -67,7 +67,7 @@ class FileDeleteForm {
$reason = $this->DeleteReasonList;
if ( $reason != 'other' && $this->DeleteReason != '') {
// Entry from drop down menu + additional comment
- $reason .= ': ' . $this->DeleteReason;
+ $reason .= wfMsgForContent( 'colon-separator' ) . $this->DeleteReason;
} elseif ( $reason == 'other' ) {
$reason = $this->DeleteReason;
}
@@ -108,7 +108,8 @@ class FileDeleteForm {
$id = $title->getArticleID( GAID_FOR_UPDATE );
// Need to delete the associated article
$article = new Article( $title );
- if( wfRunHooks('ArticleDelete', array(&$article, &$wgUser, &$reason)) ) {
+ $error = '';
+ if( wfRunHooks('ArticleDelete', array(&$article, &$wgUser, &$reason, &$error)) ) {
if( $article->doDeleteArticle( $reason, $suppress, $id ) ) {
global $wgRequest;
if( $wgRequest->getCheck( 'wpWatch' ) ) {
diff --git a/includes/ForkController.php b/includes/ForkController.php
new file mode 100644
index 00000000..09e1788b
--- /dev/null
+++ b/includes/ForkController.php
@@ -0,0 +1,160 @@
+<?php
+
+/**
+ * Class for managing forking command line scripts.
+ * Currently just does forking and process control, but it could easily be extended
+ * to provide IPC and job dispatch.
+ *
+ * This class requires the posix and pcntl extensions.
+ */
+class ForkController {
+ var $children = array();
+ var $termReceived = false;
+ var $flags = 0, $procsToStart = 0;
+
+ static $restartableSignals = array(
+ SIGFPE,
+ SIGILL,
+ SIGSEGV,
+ SIGBUS,
+ SIGABRT,
+ SIGSYS,
+ SIGPIPE,
+ SIGXCPU,
+ SIGXFSZ,
+ );
+
+ /**
+ * Pass this flag to __construct() to cause the class to automatically restart
+ * workers that exit with non-zero exit status or a signal such as SIGSEGV.
+ */
+ const RESTART_ON_ERROR = 1;
+
+ public function __construct( $numProcs, $flags = 0 ) {
+ if ( php_sapi_name() != 'cli' ) {
+ throw new MWException( "MultiProcess cannot be used from the web." );
+ }
+ $this->procsToStart = $numProcs;
+ $this->flags = $flags;
+ }
+
+ /**
+ * Start the child processes.
+ *
+ * This should only be called from the command line. It should be called
+ * as early as possible during execution.
+ *
+ * This will return 'child' in the child processes. In the parent process,
+ * it will run until all the child processes exit or a TERM signal is
+ * received. It will then return 'done'.
+ */
+ public function start() {
+ // Trap SIGTERM
+ pcntl_signal( SIGTERM, array( $this, 'handleTermSignal' ), false );
+
+ do {
+ // Start child processes
+ if ( $this->procsToStart ) {
+ if ( $this->forkWorkers( $this->procsToStart ) == 'child' ) {
+ return 'child';
+ }
+ $this->procsToStart = 0;
+ }
+
+ // Check child status
+ $status = false;
+ $deadPid = pcntl_wait( $status );
+
+ if ( $deadPid > 0 ) {
+ // Respond to child process termination
+ unset( $this->children[$deadPid] );
+ if ( $this->flags & self::RESTART_ON_ERROR ) {
+ if ( pcntl_wifsignaled( $status ) ) {
+ // Restart if the signal was abnormal termination
+ // Don't restart if it was deliberately killed
+ $signal = pcntl_wtermsig( $status );
+ if ( in_array( $signal, self::$restartableSignals ) ) {
+ echo "Worker exited with signal $signal, restarting\n";
+ $this->procsToStart++;
+ }
+ } elseif ( pcntl_wifexited( $status ) ) {
+ // Restart on non-zero exit status
+ $exitStatus = pcntl_wexitstatus( $status );
+ if ( $exitStatus > 0 ) {
+ echo "Worker exited with status $exitStatus, restarting\n";
+ $this->procsToStart++;
+ }
+ }
+ }
+ // Throttle restarts
+ if ( $this->procsToStart ) {
+ usleep( 500000 );
+ }
+ }
+
+ // Run signal handlers
+ if ( function_exists( 'pcntl_signal_dispatch' ) ) {
+ pcntl_signal_dispatch();
+ } else {
+ declare (ticks=1) { $status = $status; }
+ }
+ // Respond to TERM signal
+ if ( $this->termReceived ) {
+ foreach ( $this->children as $childPid => $unused ) {
+ posix_kill( $childPid, SIGTERM );
+ }
+ $this->termReceived = false;
+ }
+ } while ( count( $this->children ) );
+ pcntl_signal( SIGTERM, SIG_DFL );
+ return 'done';
+ }
+
+ protected function prepareEnvironment() {
+ global $wgCaches, $wgMemc;
+ // Don't share DB or memcached connections
+ wfGetLBFactory()->destroyInstance();
+ $wgCaches = array();
+ unset( $wgMemc );
+ }
+
+ /**
+ * Fork a number of worker processes.
+ */
+ protected function forkWorkers( $numProcs ) {
+ global $wgMemc, $wgCaches, $wgMainCacheType;
+
+ $this->prepareEnvironment();
+
+ // Create the child processes
+ for ( $i = 0; $i < $numProcs; $i++ ) {
+ // Do the fork
+ $pid = pcntl_fork();
+ if ( $pid === -1 || $pid === false ) {
+ echo "Error creating child processes\n";
+ exit( 1 );
+ }
+
+ if ( !$pid ) {
+ $this->initChild();
+ return 'child';
+ } else {
+ // This is the parent process
+ $this->children[$pid] = true;
+ }
+ }
+
+ return 'parent';
+ }
+
+ protected function initChild() {
+ global $wgMemc, $wgMainCacheType;
+ $wgMemc = wfGetCache( $wgMainCacheType );
+ $this->children = null;
+ pcntl_signal( SIGTERM, SIG_DFL );
+ }
+
+ protected function handleTermSignal( $signal ) {
+ $this->termReceived = true;
+ }
+}
diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php
index 33f5831d..0807f0be 100644
--- a/includes/GlobalFunctions.php
+++ b/includes/GlobalFunctions.php
@@ -89,6 +89,19 @@ if ( !function_exists( 'array_diff_key' ) ) {
}
}
+// Support for Wietse Venema's taint feature
+if ( !function_exists( 'istainted' ) ) {
+ function istainted( $var ) {
+ return 0;
+ }
+ function taint( $var, $level = 0 ) {}
+ function untaint( $var, $level = 0 ) {}
+ define( 'TC_HTML', 1 );
+ define( 'TC_SHELL', 1 );
+ define( 'TC_MYSQL', 1 );
+ define( 'TC_PCRE', 1 );
+ define( 'TC_SELF', 1 );
+}
/// @endcond
@@ -337,12 +350,14 @@ function wfErrorLog( $text, $file ) {
*/
function wfLogProfilingData() {
global $wgRequestTime, $wgDebugLogFile, $wgDebugRawPage, $wgRequest;
- global $wgProfiler, $wgUser;
- if ( !isset( $wgProfiler ) )
- return;
-
+ global $wgProfiler, $wgProfileLimit, $wgUser;
+ # Profiling must actually be enabled...
+ if( !isset( $wgProfiler ) ) return;
+ # Get total page request time
$now = wfTime();
$elapsed = $now - $wgRequestTime;
+ # Only show pages that longer than $wgProfileLimit time (default is 0)
+ if( $elapsed <= $wgProfileLimit ) return;
$prof = wfGetProfilingOutput( $wgRequestTime, $elapsed );
$forward = '';
if( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) )
@@ -431,7 +446,7 @@ function wfGetLangObj( $langcode = false ){
return Language::factory( $langcode );
# $langcode is a string, but not a valid language code; use content language.
- wfDebug( 'Invalid language code passed to wfGetLangObj, falling back to content language.' );
+ wfDebug( "Invalid language code passed to wfGetLangObj, falling back to content language.\n" );
return $wgContLang;
}
@@ -771,7 +786,7 @@ function wfAbruptExit( $error = false ){
wfDebug("WARNING: Abrupt exit in $file at line $line\n");
}
} else {
- wfDebug('WARNING: Abrupt exit\n');
+ wfDebug("WARNING: Abrupt exit\n");
}
wfLogProfilingData();
@@ -860,18 +875,35 @@ function wfHostname() {
* murky circumstances, which may be triggered in part by stub objects
* or other fancy talkin'.
*
- * Will return an empty array if Zend Optimizer is detected, otherwise
- * the output from debug_backtrace() (trimmed).
+ * Will return an empty array if Zend Optimizer is detected or if
+ * debug_backtrace is disabled, otherwise the output from
+ * debug_backtrace() (trimmed).
*
* @return array of backtrace information
*/
function wfDebugBacktrace() {
+ static $disabled = null;
+
if( extension_loaded( 'Zend Optimizer' ) ) {
wfDebug( "Zend Optimizer detected; skipping debug_backtrace for safety.\n" );
return array();
- } else {
- return array_slice( debug_backtrace(), 1 );
}
+
+ if ( is_null( $disabled ) ) {
+ $disabled = false;
+ $functions = explode( ',', ini_get( 'disable_functions' ) );
+ $functions = array_map( 'trim', $functions );
+ $functions = array_map( 'strtolower', $functions );
+ if ( in_array( 'debug_backtrace', $functions ) ) {
+ wfDebug( "debug_backtrace is in disabled_functions\n" );
+ $disabled = true;
+ }
+ }
+ if ( $disabled ) {
+ return array();
+ }
+
+ return array_slice( debug_backtrace(), 1 );
}
function wfBacktrace() {
@@ -927,7 +959,8 @@ function wfBacktrace() {
*/
function wfShowingResults( $offset, $limit ) {
global $wgLang;
- return wfMsgExt( 'showingresults', array( 'parseinline' ), $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ) );
+ return wfMsgExt( 'showingresults', array( 'parseinline' ), $wgLang->formatNum( $limit ),
+ $wgLang->formatNum( $offset+1 ) );
}
/**
@@ -935,18 +968,28 @@ function wfShowingResults( $offset, $limit ) {
*/
function wfShowingResultsNum( $offset, $limit, $num ) {
global $wgLang;
- return wfMsgExt( 'showingresultsnum', array( 'parseinline' ), $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 ) );
}
/**
- * @todo document
+ * Generate (prev x| next x) (20|50|100...) type links for paging
+ * @param $offset string
+ * @param $limit int
+ * @param $link string
+ * @param $query string, optional URL query parameter string
+ * @param $atend bool, optional param for specified if this is the last page
*/
function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) {
global $wgLang;
$fmtLimit = $wgLang->formatNum( $limit );
- $prev = wfMsg( 'prevn', $fmtLimit );
- $next = wfMsg( 'nextn', $fmtLimit );
-
+ # Get prev/next link display text
+ $prev = wfMsgHtml( 'prevn', $fmtLimit );
+ $next = wfMsgHtml( 'nextn', $fmtLimit );
+ # Get prev/next link title text
+ $pTitle = wfMsgExt( 'prevn-title', array('parsemag','escape'), $fmtLimit );
+ $nTitle = wfMsgExt( 'nextn-title', array('parsemag','escape'), $fmtLimit );
+ # Fetch the title object
if( is_object( $link ) ) {
$title =& $link;
} else {
@@ -955,44 +998,58 @@ function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) {
return false;
}
}
-
- if ( 0 != $offset ) {
+ # Make 'previous' link
+ if( 0 != $offset ) {
$po = $offset - $limit;
- if ( $po < 0 ) { $po = 0; }
+ $po = max($po,0);
$q = "limit={$limit}&offset={$po}";
- if ( '' != $query ) { $q .= '&'.$query; }
- $plink = '<a href="' . $title->escapeLocalUrl( $q ) . "\" class=\"mw-prevlink\">{$prev}</a>";
- } else { $plink = $prev; }
-
+ if( $query != '' ) {
+ $q .= '&'.$query;
+ }
+ $plink = '<a href="' . $title->escapeLocalUrl( $q ) . "\" title=\"{$pTitle}\" class=\"mw-prevlink\">{$prev}</a>";
+ } else {
+ $plink = $prev;
+ }
+ # Make 'next' link
$no = $offset + $limit;
- $q = 'limit='.$limit.'&offset='.$no;
- if ( '' != $query ) { $q .= '&'.$query; }
-
- if ( $atend ) {
+ $q = "limit={$limit}&offset={$no}";
+ if( $query != '' ) {
+ $q .= '&'.$query;
+ }
+ if( $atend ) {
$nlink = $next;
} else {
- $nlink = '<a href="' . $title->escapeLocalUrl( $q ) . "\" class=\"mw-nextlink\">{$next}</a>";
- }
- $nums = wfNumLink( $offset, 20, $title, $query ) . ' | ' .
- wfNumLink( $offset, 50, $title, $query ) . ' | ' .
- wfNumLink( $offset, 100, $title, $query ) . ' | ' .
- wfNumLink( $offset, 250, $title, $query ) . ' | ' .
- wfNumLink( $offset, 500, $title, $query );
-
+ $nlink = '<a href="' . $title->escapeLocalUrl( $q ) . "\" title=\"{$nTitle}\" class=\"mw-nextlink\">{$next}</a>";
+ }
+ # Make links to set number of items per page
+ $nums = $wgLang->pipeList( array(
+ wfNumLink( $offset, 20, $title, $query ),
+ wfNumLink( $offset, 50, $title, $query ),
+ wfNumLink( $offset, 100, $title, $query ),
+ wfNumLink( $offset, 250, $title, $query ),
+ wfNumLink( $offset, 500, $title, $query )
+ ) );
return wfMsg( 'viewprevnext', $plink, $nlink, $nums );
}
/**
- * @todo document
+ * Generate links for (20|50|100...) items-per-page links
+ * @param $offset string
+ * @param $limit int
+ * @param $title Title
+ * @param $query string, optional URL query parameter string
*/
-function wfNumLink( $offset, $limit, &$title, $query = '' ) {
+function wfNumLink( $offset, $limit, $title, $query = '' ) {
global $wgLang;
- if ( '' == $query ) { $q = ''; }
- else { $q = $query.'&'; }
- $q .= 'limit='.$limit.'&offset='.$offset;
-
+ if( $query == '' ) {
+ $q = '';
+ } else {
+ $q = $query.'&';
+ }
+ $q .= "limit={$limit}&offset={$offset}";
$fmtLimit = $wgLang->formatNum( $limit );
- $s = '<a href="' . $title->escapeLocalUrl( $q ) . "\" class=\"mw-numlink\">{$fmtLimit}</a>";
+ $lTitle = wfMsgExt('shown-title',array('parsemag','escape'),$limit);
+ $s = '<a href="' . $title->escapeLocalUrl( $q ) . "\" title=\"{$lTitle}\" class=\"mw-numlink\">{$fmtLimit}</a>";
return $s;
}
@@ -1693,6 +1750,11 @@ define('TS_ORACLE', 6);
define('TS_POSTGRES', 7);
/**
+ * DB2 format time
+ */
+define('TS_DB2', 8);
+
+/**
* @param mixed $outputtype A timestamp in one of the supported formats, the
* function will autodetect which format is supplied
* and act accordingly.
@@ -1753,6 +1815,8 @@ function wfTimestamp($outputtype=TS_UNIX,$ts=0) {
return gmdate( 'd-M-y h.i.s A', $uts) . ' +00:00';
case TS_POSTGRES:
return gmdate( 'Y-m-d H:i:s', $uts) . ' GMT';
+ case TS_DB2:
+ return gmdate( 'Y-m-d H:i:s', $uts);
default:
throw new MWException( 'wfTimestamp() called with illegal output type.');
}
@@ -1837,7 +1901,7 @@ function wfGetCachedNotice( $name ) {
$parserMemc->set( $key, array( 'html' => $parsed, 'hash' => md5( $notice ) ), 600 );
$notice = $parsed;
} else {
- wfDebug( 'wfGetCachedNotice called for ' . $name . ' with no $wgOut available' );
+ wfDebug( 'wfGetCachedNotice called for ' . $name . ' with no $wgOut available'."\n" );
$notice = '';
}
}
@@ -1929,11 +1993,16 @@ function wfTempDir() {
*
* @param string $dir Full path to directory to create
* @param int $mode Chmod value to use, default is $wgDirectoryMode
+ * @param string $caller Optional caller param for debugging.
* @return bool
*/
-function wfMkdirParents( $dir, $mode = null ) {
+function wfMkdirParents( $dir, $mode = null, $caller = null ) {
global $wgDirectoryMode;
+ if ( !is_null( $caller ) ) {
+ wfDebug( "$caller: called wfMkdirParents($dir)" );
+ }
+
if( strval( $dir ) === '' || file_exists( $dir ) )
return true;
@@ -2101,11 +2170,26 @@ function wfIniGetBool( $setting ) {
function wfShellExec( $cmd, &$retval=null ) {
global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime;
- if( wfIniGetBool( 'safe_mode' ) ) {
- wfDebug( "wfShellExec can't run in safe_mode, PHP's exec functions are too broken.\n" );
+ static $disabled;
+ if ( is_null( $disabled ) ) {
+ $disabled = false;
+ if( wfIniGetBool( 'safe_mode' ) ) {
+ wfDebug( "wfShellExec can't run in safe_mode, PHP's exec functions are too broken.\n" );
+ $disabled = true;
+ }
+ $functions = explode( ',', ini_get( 'disable_functions' ) );
+ $functions = array_map( 'trim', $functions );
+ $functions = array_map( 'strtolower', $functions );
+ if ( in_array( 'passthru', $functions ) ) {
+ wfDebug( "passthru is in disabled_functions\n" );
+ $disabled = true;
+ }
+ }
+ if ( $disabled ) {
$retval = 1;
return "Unable to run external programs in safe mode.";
}
+
wfInitShellLocale();
if ( php_uname( 's' ) == 'Linux' ) {
@@ -2317,9 +2401,16 @@ function wfMergeErrorArrays(/*...*/) {
}
/**
- * Make a URL index, appropriate for the el_index field of externallinks.
+ * parse_url() work-alike, but non-broken. Differences:
+ *
+ * 1) Does not raise warnings on bad URLs (just returns false)
+ * 2) Handles protocols that don't use :// (e.g., mailto: and news:) correctly
+ * 3) Adds a "delimiter" element to the array, either '://' or ':' (see (2))
+ *
+ * @param string $url A URL to parse
+ * @return array Bits of the URL in an associative array, per PHP docs
*/
-function wfMakeUrlIndex( $url ) {
+function wfParseUrl( $url ) {
global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
wfSuppressWarnings();
$bits = parse_url( $url );
@@ -2327,12 +2418,12 @@ function wfMakeUrlIndex( $url ) {
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 = ':';
+ if ( in_array( $bits['scheme'] . '://', $wgUrlProtocols ) ) {
+ $bits['delimiter'] = '://';
+ } elseif ( in_array( $bits['scheme'] . ':', $wgUrlProtocols ) ) {
+ $bits['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'] ) ) {
@@ -2343,6 +2434,15 @@ function wfMakeUrlIndex( $url ) {
return false;
}
+ return $bits;
+}
+
+/**
+ * Make a URL index, appropriate for the el_index field of externallinks.
+ */
+function wfMakeUrlIndex( $url ) {
+ $bits = wfParseUrl( $url );
+
// Reverse the labels in the hostname, convert to lower case
// For emails reverse domainpart only
if ( $bits['scheme'] == 'mailto' ) {
@@ -2364,7 +2464,7 @@ function wfMakeUrlIndex( $url ) {
}
// Reconstruct the pseudo-URL
$prot = $bits['scheme'];
- $index = "$prot$delimiter$reversedHost";
+ $index = $prot . $bits['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'] ) ) {
diff --git a/includes/HTMLCacheUpdate.php b/includes/HTMLCacheUpdate.php
index 402102ea..bd63c072 100644
--- a/includes/HTMLCacheUpdate.php
+++ b/includes/HTMLCacheUpdate.php
@@ -35,146 +35,76 @@ class HTMLCacheUpdate
$this->mTable = $table;
$this->mRowsPerJob = $wgUpdateRowsPerJob;
$this->mRowsPerQuery = $wgUpdateRowsPerQuery;
+ $this->mCache = $this->mTitle->getBacklinkCache();
}
public function doUpdate() {
# Fetch the IDs
- $cond = $this->getToCondition();
- $dbr = wfGetDB( DB_SLAVE );
- $res = $dbr->select( $this->mTable, $this->getFromField(), $cond, __METHOD__ );
+ $numRows = $this->mCache->getNumLinks( $this->mTable );
- if ( $dbr->numRows( $res ) != 0 ) {
- if ( $dbr->numRows( $res ) > $this->mRowsPerJob ) {
- $this->insertJobs( $res );
+ if ( $numRows != 0 ) {
+ if ( $numRows > $this->mRowsPerJob ) {
+ $this->insertJobs();
} else {
- $this->invalidateIDs( $res );
+ $this->invalidate();
}
}
wfRunHooks( 'HTMLCacheUpdate::doUpdate', array($this->mTitle) );
}
- protected function insertJobs( ResultWrapper $res ) {
- $numRows = $res->numRows();
- $numBatches = ceil( $numRows / $this->mRowsPerJob );
- $realBatchSize = $numRows / $numBatches;
- $start = false;
- $jobs = array();
- do {
- for ( $i = 0; $i <= $realBatchSize - 1; $i++ ) {
- $row = $res->fetchRow();
- if ( $row ) {
- $id = $row[0];
- } else {
- $id = false;
- break;
- }
- }
-
+ protected function insertJobs() {
+ $batches = $this->mCache->partition( $this->mTable, $this->mRowsPerJob );
+ if ( !$batches ) {
+ return;
+ }
+ foreach ( $batches as $batch ) {
$params = array(
'table' => $this->mTable,
- 'start' => $start,
- 'end' => ( $id !== false ? $id - 1 : false ),
+ 'start' => $batch[0],
+ 'end' => $batch[1],
);
$jobs[] = new HTMLCacheUpdateJob( $this->mTitle, $params );
-
- $start = $id;
- } while ( $start );
-
- Job::batchInsert( $jobs );
- }
-
- protected function getPrefix() {
- static $prefixes = array(
- 'pagelinks' => 'pl',
- 'imagelinks' => 'il',
- 'categorylinks' => 'cl',
- 'templatelinks' => 'tl',
- 'redirect' => 'rd',
- );
-
- if ( is_null( $this->mPrefix ) ) {
- $this->mPrefix = $prefixes[$this->mTable];
- if ( is_null( $this->mPrefix ) ) {
- throw new MWException( "Invalid table type \"{$this->mTable}\" in " . __CLASS__ );
- }
}
- return $this->mPrefix;
- }
-
- public function getFromField() {
- return $this->getPrefix() . '_from';
+ Job::batchInsert( $jobs );
}
- public function getToCondition() {
- $prefix = $this->getPrefix();
- switch ( $this->mTable ) {
- case 'pagelinks':
- case 'templatelinks':
- case 'redirect':
- return array(
- "{$prefix}_namespace" => $this->mTitle->getNamespace(),
- "{$prefix}_title" => $this->mTitle->getDBkey()
- );
- case 'imagelinks':
- return array( 'il_to' => $this->mTitle->getDBkey() );
- case 'categorylinks':
- return array( 'cl_to' => $this->mTitle->getDBkey() );
- }
- throw new MWException( 'Invalid table type in ' . __CLASS__ );
- }
/**
- * Invalidate a set of IDs, right now
+ * Invalidate a set of pages, right now
*/
- public function invalidateIDs( ResultWrapper $res ) {
+ public function invalidate( $startId = false, $endId = false ) {
global $wgUseFileCache, $wgUseSquid;
- if ( $res->numRows() == 0 ) {
+ $titleArray = $this->mCache->getLinks( $this->mTable, $startId, $endId );
+ if ( $titleArray->count() == 0 ) {
return;
}
$dbw = wfGetDB( DB_MASTER );
$timestamp = $dbw->timestamp();
- $done = false;
-
- while ( !$done ) {
- # Get all IDs in this query into an array
- $ids = array();
- for ( $i = 0; $i < $this->mRowsPerQuery; $i++ ) {
- $row = $res->fetchRow();
- if ( $row ) {
- $ids[] = $row[0];
- } else {
- $done = true;
- break;
- }
- }
- if ( !count( $ids ) ) {
- break;
- }
+ # Get all IDs in this query into an array
+ $ids = array();
+ foreach ( $titleArray as $title ) {
+ $ids[] = $title->getArticleID();
+ }
+ # Update page_touched
+ $dbw->update( 'page',
+ array( 'page_touched' => $timestamp ),
+ array( 'page_id IN (' . $dbw->makeList( $ids ) . ')' ),
+ __METHOD__
+ );
- # Update page_touched
- $dbw->update( 'page',
- array( 'page_touched' => $timestamp ),
- array( 'page_id IN (' . $dbw->makeList( $ids ) . ')' ),
- __METHOD__
- );
+ # Update squid
+ if ( $wgUseSquid ) {
+ $u = SquidUpdate::newFromTitles( $titleArray );
+ $u->doUpdate();
+ }
- # Update squid
- if ( $wgUseSquid || $wgUseFileCache ) {
- $titles = Title::newFromIDs( $ids );
- if ( $wgUseSquid ) {
- $u = SquidUpdate::newFromTitles( $titles );
- $u->doUpdate();
- }
-
- # Update file cache
- if ( $wgUseFileCache ) {
- foreach ( $titles as $title ) {
- HTMLFileCache::clearFileCache( $title );
- }
- }
+ # Update file cache
+ if ( $wgUseFileCache ) {
+ foreach ( $titleArray as $title ) {
+ HTMLFileCache::clearFileCache( $title );
}
}
}
@@ -204,20 +134,7 @@ class HTMLCacheUpdateJob extends Job {
public function run() {
$update = new HTMLCacheUpdate( $this->title, $this->table );
-
- $fromField = $update->getFromField();
- $conds = $update->getToCondition();
- if ( $this->start ) {
- $conds[] = "$fromField >= {$this->start}";
- }
- if ( $this->end ) {
- $conds[] = "$fromField <= {$this->end}";
- }
-
- $dbr = wfGetDB( DB_SLAVE );
- $res = $dbr->select( $this->table, $fromField, $conds, __METHOD__ );
- $update->invalidateIDs( $res );
-
+ $update->invalidate( $this->start, $this->end );
return true;
}
}
diff --git a/includes/HTMLFileCache.php b/includes/HTMLFileCache.php
index e267962c..68cafa24 100644
--- a/includes/HTMLFileCache.php
+++ b/includes/HTMLFileCache.php
@@ -128,7 +128,6 @@ class HTMLFileCache {
public function loadFromFileCache() {
global $wgOut, $wgMimeType, $wgOutputEncoding, $wgContLanguageCode;
wfDebug(" loadFromFileCache()\n");
-
$filename = $this->fileCacheName();
// Raw pages should handle cache control on their own,
// even when using file cache. This reduces hits from clients.
@@ -148,6 +147,7 @@ class HTMLFileCache {
}
}
readfile( $filename );
+ $wgOut->disable(); // tell $wgOut that output is taken care of
}
protected function checkCacheDirs() {
@@ -159,13 +159,12 @@ class HTMLFileCache {
wfMkdirParents( $mydir2 );
}
- public function saveToFileCache( $origtext ) {
+ public function saveToFileCache( $text ) {
global $wgUseFileCache;
- if( !$wgUseFileCache ) {
- return $origtext; // return to output
+ if( !$wgUseFileCache || strlen( $text ) < 512 ) {
+ // Disabled or empty/broken output (OOM and PHP errors)
+ return $text;
}
- $text = $origtext;
- if( strcmp($text,'') == 0 ) return '';
wfDebug(" saveToFileCache()\n", false);
diff --git a/includes/ImageGallery.php b/includes/ImageGallery.php
index f3f525c1..8a38bed7 100644
--- a/includes/ImageGallery.php
+++ b/includes/ImageGallery.php
@@ -289,7 +289,7 @@ class ImageGallery
}
$textlink = $this->mShowFilename ?
- $sk->makeKnownLinkObj( $nt, htmlspecialchars( $wgLang->truncate( $nt->getText(), 20, '...' ) ) ) . "<br />\n" :
+ $sk->makeKnownLinkObj( $nt, htmlspecialchars( $wgLang->truncate( $nt->getText(), 20 ) ) ) . "<br />\n" :
'' ;
# ATTENTION: The newline after <div class="gallerytext"> is needed to accommodate htmltidy which
diff --git a/includes/ImagePage.php b/includes/ImagePage.php
index 314d478e..4f3b859a 100644
--- a/includes/ImagePage.php
+++ b/includes/ImagePage.php
@@ -105,7 +105,6 @@ class ImagePage extends Article {
} else {
# Just need to set the right headers
$wgOut->setArticleFlag( true );
- $wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
$this->viewUpdates();
}
@@ -117,8 +116,6 @@ class ImagePage extends Article {
$wgOut->addWikiText( $fol );
}
$wgOut->addHTML( '<div id="shared-image-desc">' . $this->mExtraDescription . '</div>' );
- } else {
- $this->checkSharedConflict();
}
$this->closeShowImage();
@@ -129,11 +126,9 @@ class ImagePage extends Article {
array( 'id' => 'filelinks' ),
wfMsg( 'imagelinks' ) ) . "\n" );
$this->imageDupes();
- // TODO: We may want to find local images redirecting to a foreign
- // file: "The following local files redirect to this file"
- if( $this->img->isLocal() ) {
- $this->imageRedirects();
- }
+ # TODO! FIXME! For some freaky reason, we can't redirect to foreign images.
+ # Yet we return metadata about the target. Definitely an issue in the FileRepo
+ $this->imageRedirects();
$this->imageLinks();
if( $showmeta ) {
@@ -473,6 +468,7 @@ EOT
$title = SpecialPage::getTitleFor( 'Upload' );
$link = $sk->makeKnownLinkObj($title, wfMsgHtml('noimage-linktext'),
'wpDestFile=' . urlencode( $this->displayImg->getName() ) );
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->addHTML( wfMsgWikiHtml( 'noimage', $link ) );
}
}
@@ -487,17 +483,18 @@ EOT
$descUrl = $this->img->getDescriptionUrl();
$descText = $this->img->getDescriptionText();
- $s = "<div class='sharedUploadNotice'>" . wfMsgWikiHtml( 'sharedupload' );
+ $msg = '';
if( $descUrl ) {
$sk = $wgUser->getSkin();
$link = $sk->makeExternalLink( $descUrl, wfMsg( 'shareduploadwiki-linktext' ) );
$msg = ( $descText ) ? 'shareduploadwiki-desc' : 'shareduploadwiki';
$msg = wfMsgExt( $msg, array( 'parseinline', 'replaceafter' ), $link );
- if( $msg != '-' ) {
- # Show message only if not voided by local sysops
- $s .= $msg;
+ if( $msg == '-' ) {
+ $msg = '';
}
}
+ $s = "<div class='sharedUploadNotice'>";
+ $s .= wfMsgWikiHtml( 'sharedupload', $this->img->getRepo()->getDisplayName(), $msg );
$s .= "</div>";
$wgOut->addHTML( $s );
@@ -506,58 +503,10 @@ EOT
}
}
- /*
- * Check for files with the same name on the foreign repos.
- */
- protected function checkSharedConflict() {
- global $wgOut, $wgUser;
-
- $repoGroup = RepoGroup::singleton();
- if( !$repoGroup->hasForeignRepos() ) {
- return;
- }
-
- $this->loadFile();
- if( !$this->img->isLocal() ) {
- return;
- }
-
- $this->dupFile = null;
- $repoGroup->forEachForeignRepo( array( $this, 'checkSharedConflictCallback' ) );
-
- if( !$this->dupFile )
- return;
- $dupfile = $this->dupFile;
- $same = (
- ($this->img->getSha1() == $dupfile->getSha1()) &&
- ($this->img->getSize() == $dupfile->getSize())
- );
-
- $sk = $wgUser->getSkin();
- $descUrl = $dupfile->getDescriptionUrl();
- if( $same ) {
- $link = $sk->makeExternalLink( $descUrl, wfMsg( 'shareduploadduplicate-linktext' ) );
- $wgOut->addHTML( '<div id="shared-image-dup">' . wfMsgWikiHtml( 'shareduploadduplicate', $link ) . '</div>' );
- } else {
- $link = $sk->makeExternalLink( $descUrl, wfMsg( 'shareduploadconflict-linktext' ) );
- $wgOut->addHTML( '<div id="shared-image-conflict">' . wfMsgWikiHtml( 'shareduploadconflict', $link ) . '</div>' );
- }
- }
-
- public function checkSharedConflictCallback( $repo ) {
- $this->loadFile();
- $dupfile = $repo->newFile( $this->img->getTitle() );
- if( $dupfile && $dupfile->exists() ) {
- $this->dupFile = $dupfile;
- return $dupfile->exists();
- }
- return false;
- }
-
public function getUploadUrl() {
$this->loadFile();
$uploadTitle = SpecialPage::getTitleFor( 'Upload' );
- return $uploadTitle->getFullUrl( 'wpDestFile=' . urlencode( $this->img->getName() ) );
+ return $uploadTitle->getFullUrl( 'wpDestFile=' . urlencode( $this->img->getName() ) . '&wpForReUpload=1' );
}
/**
@@ -581,10 +530,6 @@ EOT
$wgOut->addHTML( "<li><div class='plainlinks'>{$ulink}</div></li>" );
}
- # Link to Special:FileDuplicateSearch
- $dupeLink = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'FileDuplicateSearch', $this->mTitle->getDBkey() ), wfMsgHtml( 'imagepage-searchdupe' ) );
- $wgOut->addHTML( "<li>{$dupeLink}</li>" );
-
# External editing link
$elink = $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml( 'edit-externally' ), 'action=edit&externaledit=true&mode=file' );
$wgOut->addHTML( '<li>' . $elink . ' <small>' . wfMsgExt( 'edit-externally-help', array( 'parseinline' ) ) . '</small></li>' );
@@ -698,19 +643,21 @@ EOT
$wgOut->addHTML( "<div id='mw-imagepage-section-duplicates'>\n" );
$wgOut->addWikiMsg( 'duplicatesoffile',
- $wgLang->formatNum( count( $dupes ) )
+ $wgLang->formatNum( count( $dupes ) ), $this->mTitle->getDBkey()
);
$wgOut->addHTML( "<ul class='mw-imagepage-duplicates'>\n" );
$sk = $wgUser->getSkin();
foreach ( $dupes as $file ) {
+ $fromSrc = '';
if( $file->isLocal() )
$link = $sk->makeKnownLinkObj( $file->getTitle(), "" );
else {
$link = $sk->makeExternalLink( $file->getDescriptionUrl(),
$file->getTitle()->getPrefixedText() );
+ $fromSrc = wfMsg( 'shared-repo-from', $file->getRepo()->getDisplayName() );
}
- $wgOut->addHTML( "<li>{$link}</li>\n" );
+ $wgOut->addHTML( "<li>{$link} {$fromSrc}</li>\n" );
}
$wgOut->addHTML( "</ul></div>\n" );
}
@@ -922,7 +869,8 @@ class ImageHistoryList {
'alt' => wfMsg( 'filehist-thumbtext', $wgLang->timeAndDate( $timestamp, true ) ),
'file-link' => true,
);
- $row .= '</td><td>' . $thumbnail->toHtml( $options );
+ $row .= '</td><td>' . ( $thumbnail ? $thumbnail->toHtml( $options ) :
+ wfMsgHtml( 'filehist-nothumb' ) );
} else {
$row .= '</td><td>' . wfMsgHtml( 'filehist-nothumb' );
}
diff --git a/includes/Import.php b/includes/Import.php
index 56e7a7fb..973866df 100644
--- a/includes/Import.php
+++ b/includes/Import.php
@@ -223,7 +223,7 @@ class WikiRevision {
} elseif( $changed ) {
wfDebug( __METHOD__ . ": running onArticleEdit\n" );
- Article::onArticleEdit( $this->title, 'skiptransclusions' ); // leave templatelinks for editUpdates()
+ Article::onArticleEdit( $this->title );
wfDebug( __METHOD__ . ": running edit updates\n" );
$article->editUpdates(
@@ -1116,7 +1116,7 @@ class ImportStreamSource {
}
}
- public static function newFromInterwiki( $interwiki, $page, $history=false ) {
+ public static function newFromInterwiki( $interwiki, $page, $history = false, $templates = false, $pageLinkDepth = 0 ) {
if( $page == '' ) {
return new WikiErrorMsg( 'import-noarticle' );
}
@@ -1124,7 +1124,10 @@ class ImportStreamSource {
if( is_null( $link ) || $link->getInterwiki() == '' ) {
return new WikiErrorMsg( 'importbadinterwiki' );
} else {
- $params = $history ? 'history=1' : '';
+ $params = array();
+ if ( $history ) $params['history'] = 1;
+ if ( $templates ) $params['templates'] = 1;
+ if ( $pageLinkDepth ) $params['pagelink-depth'] = $pageLinkDepth;
$url = $link->getFullUrl( $params );
# For interwikis, use POST to avoid redirects.
return ImportStreamSource::newFromURL( $url, "POST" );
diff --git a/includes/Linker.php b/includes/Linker.php
index f116fb4a..b739244b 100644
--- a/includes/Linker.php
+++ b/includes/Linker.php
@@ -618,7 +618,7 @@ class Linker {
$img = '';
$success = wfRunHooks('LinkerMakeExternalImage', array( &$url, &$alt, &$img ) );
if(!$success) {
- wfDebug("Hook LinkerMakeExternalImage changed the output of external image with url {$url} and alt text {$alt} to {$img}", true);
+ wfDebug("Hook LinkerMakeExternalImage changed the output of external image with url {$url} and alt text {$alt} to {$img}\n", true);
return $img;
}
return Xml::element( 'img',
@@ -882,10 +882,13 @@ class Linker {
}
}
- if( $page ) {
- $query = $query ? '&page=' . urlencode( $page ) : 'page=' . urlencode( $page );
- }
+ # ThumbnailImage::toHtml() already adds page= onto the end of DjVu URLs
+ # So we don't need to pass it here in $query. However, the URL for the
+ # zoom icon still needs it, so we make a unique query for it. See bug 14771
$url = $title->getLocalURL( $query );
+ if( $page ) {
+ $url = wfAppendQuery( $url, 'page=' . urlencode( $page ) );
+ }
$more = htmlspecialchars( wfMsg( 'thumbnail-more' ) );
@@ -1007,22 +1010,37 @@ class Linker {
wfMsg( $key ) );
}
- /** @todo document */
+ /**
+ * Make an external link
+ * @param String $url URL to link to
+ * @param String $text text of link
+ * @param boolean $escape Do we escape the link text?
+ * @param String $linktype Type of external link. Gets added to the classes
+ * @param array $attribs Array of extra attributes to <a>
+ *
+ * @TODO! @FIXME! This is a really crappy implementation. $linktype and
+ * 'external' are mashed into the class attrib for the link (which is made
+ * into a string). Then, if we've got additional params in $attribs, we
+ * add to it. People using this might want to change the classes (or other
+ * default link attributes), but passing $attribsText is just messy. Would
+ * make a lot more sense to make put the classes into $attribs, let the
+ * hook play with them, *then* expand it all at once.
+ */
function makeExternalLink( $url, $text, $escape = true, $linktype = '', $attribs = array() ) {
$attribsText = $this->getExternalLinkAttributes( $url, $text, 'external ' . $linktype );
- if ( $attribs ) {
- $attribsText .= Xml::expandAttributes( $attribs );
- }
$url = htmlspecialchars( $url );
if( $escape ) {
$text = htmlspecialchars( $text );
}
$link = '';
- $success = wfRunHooks('LinkerMakeExternalLink', array( &$url, &$text, &$link ) );
+ $success = wfRunHooks('LinkerMakeExternalLink', array( &$url, &$text, &$link, &$attribs, $linktype ) );
if(!$success) {
- wfDebug("Hook LinkerMakeExternalLink changed the output of link with url {$url} and text {$text} to {$link}", true);
+ wfDebug("Hook LinkerMakeExternalLink changed the output of link with url {$url} and text {$text} to {$link}\n", true);
return $link;
}
+ if ( $attribs ) {
+ $attribsText .= Xml::expandAttributes( $attribs );
+ }
return '<a href="'.$url.'"'.$attribsText.'>'.$text.'</a>';
}
@@ -1053,7 +1071,7 @@ class Linker {
* @return string
*/
public function userToolLinks( $userId, $userText, $redContribsWhenNoEdits = false, $flags = 0, $edits=null ) {
- global $wgUser, $wgDisableAnonTalk, $wgSysopUserBans;
+ global $wgUser, $wgDisableAnonTalk, $wgSysopUserBans, $wgLang;
$talkable = !( $wgDisableAnonTalk && 0 == $userId );
$blockable = ( $wgSysopUserBans || 0 == $userId ) && !$flags & self::TOOL_LINKS_NOBLOCK;
@@ -1079,7 +1097,7 @@ class Linker {
}
if( $items ) {
- return ' <span class="mw-usertoollinks">(' . implode( ' | ', $items ) . ')</span>';
+ return ' <span class="mw-usertoollinks">(' . $wgLang->pipeList( $items ) . ')</span>';
} else {
return '';
}
@@ -1783,9 +1801,7 @@ class Linker {
# FIXME: Per standard MW behavior, a value of '-' means to suppress the
# attribute, but this is broken for accesskey: that might be a useful
# value.
- if( $accesskey != ''
- && $accesskey != '-'
- && !wfEmptyMsg( "accesskey-$name", $accesskey ) ) {
+ if( $accesskey != '' && $accesskey != '-' && !wfEmptyMsg( "accesskey-$name", $accesskey ) ) {
wfProfileOut( __METHOD__ );
return $accesskey;
}
@@ -1793,4 +1809,21 @@ class Linker {
wfProfileOut( __METHOD__ );
return false;
}
+
+ /**
+ * Creates a (show/hide) link for deleting revisions/log entries
+ *
+ * @param array $query Query parameters to be passed to link()
+ * @param bool $restricted Set to true to use a <strong> instead of a <span>
+ *
+ * @return string HTML <a> link to Special:Revisiondelete, wrapped in a
+ * span to allow for customization of appearance with CSS
+ */
+ public function revDeleteLink( $query = array(), $restricted = false ) {
+ $sp = SpecialPage::getTitleFor( 'Revisiondelete' );
+ $text = wfMsgHtml( 'rev-delundel' );
+ $tag = $restricted ? 'strong' : 'span';
+ $link = $this->link( $sp, $text, array(), $query, array( 'known', 'noclasses' ) );
+ return Xml::tags( $tag, array( 'class' => 'mw-revdelundel-link' ), "($link)" );
+ }
}
diff --git a/includes/LinksUpdate.php b/includes/LinksUpdate.php
index 13f35b5a..caacb49c 100644
--- a/includes/LinksUpdate.php
+++ b/includes/LinksUpdate.php
@@ -20,8 +20,7 @@ class LinksUpdate {
$mProperties, //!< Map of arbitrary name to value
$mDb, //!< Database connection reference
$mOptions, //!< SELECT options to be used (array)
- $mRecursive, //!< Whether to queue jobs for recursive updates
- $mTouchTmplLinks; //!< Whether to queue HTMLCacheUpdate jobs IF recursive
+ $mRecursive; //!< Whether to queue jobs for recursive updates
/**@}}*/
/**
@@ -72,15 +71,6 @@ class LinksUpdate {
wfRunHooks( 'LinksUpdateConstructed', array( &$this ) );
}
-
- /**
- * Invalidate HTML cache of pages that include this page?
- */
- public function setRecursiveTouch( $val ) {
- $this->mTouchTmplLinks = (bool)$val;
- if( $val ) // Cannot invalidate without queueRecursiveJobs()
- $this->mRecursive = true;
- }
/**
* Update link tables with outgoing links from an updated article
@@ -95,7 +85,6 @@ class LinksUpdate {
$this->doIncrementalUpdate();
}
wfRunHooks( 'LinksUpdateComplete', array( &$this ) );
-
}
protected function doIncrementalUpdate() {
@@ -207,49 +196,21 @@ class LinksUpdate {
global $wgUpdateRowsPerJob;
wfProfileIn( __METHOD__ );
- $dbr = wfGetDB( DB_SLAVE );
- $res = $dbr->select( 'templatelinks',
- array( 'tl_from' ),
- array(
- 'tl_namespace' => $this->mTitle->getNamespace(),
- 'tl_title' => $this->mTitle->getDBkey()
- ), __METHOD__
- );
-
- $numRows = $res->numRows();
- if( !$numRows ) {
+ $cache = $this->mTitle->getBacklinkCache();
+ $batches = $cache->partition( 'templatelinks', $wgUpdateRowsPerJob );
+ if ( !$batches ) {
wfProfileOut( __METHOD__ );
- return; // nothing to do
+ return;
}
- $numBatches = ceil( $numRows / $wgUpdateRowsPerJob );
- $realBatchSize = $numRows / $numBatches;
- $start = false;
$jobs = array();
- do {
- for( $i = 0; $i <= $realBatchSize - 1; $i++ ) {
- $row = $res->fetchRow();
- if( $row ) {
- $id = $row[0];
- } else {
- $id = false;
- break;
- }
- }
+ foreach ( $batches as $batch ) {
+ list( $start, $end ) = $batch;
$params = array(
'start' => $start,
- 'end' => ( $id !== false ? $id - 1 : false ),
+ 'end' => $end,
);
$jobs[] = new RefreshLinksJob2( $this->mTitle, $params );
- # Hit page caches while we're at it if set to do so...
- if( $this->mTouchTmplLinks ) {
- $params['table'] = 'templatelinks';
- $jobs[] = new HTMLCacheUpdateJob( $this->mTitle, $params );
- }
- $start = $id;
- } while ( $start );
-
- $dbr->freeResult( $res );
-
+ }
Job::batchInsert( $jobs );
wfProfileOut( __METHOD__ );
@@ -465,9 +426,12 @@ class LinksUpdate {
* @private
*/
function getCategoryInsertions( $existing = array() ) {
+ global $wgContLang;
$diffs = array_diff_assoc( $this->mCategories, $existing );
$arr = array();
foreach ( $diffs as $name => $sortkey ) {
+ $nt = Title::makeTitleSafe( NS_CATEGORY, $name );
+ $wgContLang->findVariantLink( $name, $nt, true );
$arr[] = array(
'cl_from' => $this->mId,
'cl_to' => $name,
diff --git a/includes/LogEventsList.php b/includes/LogEventsList.php
index 528bd3aa..95109eb5 100644
--- a/includes/LogEventsList.php
+++ b/includes/LogEventsList.php
@@ -39,9 +39,10 @@ class LogEventsList {
// Precache various messages
if( !isset( $this->message ) ) {
$messages = array( 'revertmerge', 'protect_change', 'unblocklink', 'change-blocklink',
- 'revertmove', 'undeletelink', 'revdel-restore', 'rev-delundel', 'hist', 'pipe-separator' );
+ 'revertmove', 'undeletelink', 'revdel-restore', 'rev-delundel', 'hist', 'diff',
+ 'pipe-separator' );
foreach( $messages as $msg ) {
- $this->message[$msg] = wfMsgExt( $msg, array( 'escape' ) );
+ $this->message[$msg] = wfMsgExt( $msg, array( 'escapenoentities' ) );
}
}
}
@@ -65,16 +66,19 @@ class LogEventsList {
* @param $pattern String
* @param $year Integer: year
* @param $month Integer: month
- * @param $filter Boolean
+ * @param $filter: array
+ * @param $tagFilter: array?
*/
public function showOptions( $type = '', $user = '', $page = '', $pattern = '', $year = '',
- $month = '', $filter = null )
+ $month = '', $filter = null, $tagFilter='' )
{
global $wgScript, $wgMiserMode;
$action = htmlspecialchars( $wgScript );
$title = SpecialPage::getTitleFor( 'Log' );
$special = htmlspecialchars( $title->getPrefixedDBkey() );
+ $tagSelector = ChangeTags::buildTagFilterSelector( $tagFilter );
+
$this->out->addHTML( "<form action=\"$action\" method=\"get\"><fieldset>" .
Xml::element( 'legend', array(), wfMsg( 'log' ) ) .
Xml::hidden( 'title', $special ) . "\n" .
@@ -82,28 +86,31 @@ class LogEventsList {
$this->getUserInput( $user ) . "\n" .
$this->getTitleInput( $page ) . "\n" .
( !$wgMiserMode ? ($this->getTitlePattern( $pattern )."\n") : "" ) .
- "<p>" . $this->getDateMenu( $year, $month ) . "\n" .
- ( $filter ? "</p><p>".$this->getFilterLinks( $type, $filter )."\n" : "" ) .
+ "<p>" . Xml::dateMenu( $year, $month ) . "\n" .
+ ( $tagSelector ? Xml::tags( 'p', null, implode( '&nbsp;', $tagSelector ) ) :'' ). "\n" .
+ ( $filter ? "</p><p>".$this->getFilterLinks( $type, $filter )."\n" : "" ) . "\n" .
Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . "</p>\n" .
"</fieldset></form>"
);
}
private function getFilterLinks( $logType, $filter ) {
- global $wgTitle;
+ global $wgTitle, $wgLang;
// show/hide links
$messages = array( wfMsgHtml( 'show' ), wfMsgHtml( 'hide' ) );
// Option value -> message mapping
$links = array();
+ $hiddens = ''; // keep track for "go" button
foreach( $filter as $type => $val ) {
$hideVal = 1 - intval($val);
$link = $this->skin->makeKnownLinkObj( $wgTitle, $messages[$hideVal],
wfArrayToCGI( array( "hide_{$type}_log" => $hideVal ), $this->getDefaultQuery() )
);
$links[$type] = wfMsgHtml( "log-show-hide-{$type}", $link );
+ $hiddens .= Xml::hidden( "hide_{$type}_log", $val ) . "\n";
}
// Build links
- return implode( ' | ', $links );
+ return '<small>'.$wgLang->pipeList( $links ) . '</small>' . $hiddens;
}
private function getDefaultQuery() {
@@ -163,7 +170,7 @@ class LogEventsList {
* @return String: Formatted HTML
*/
private function getUserInput( $user ) {
- return Xml::inputLabel( wfMsg( 'specialloguserlabel' ), 'user', 'user', 15, $user );
+ return Xml::inputLabel( wfMsg( 'specialloguserlabel' ), 'user', 'mw-log-user', 15, $user );
}
/**
@@ -171,38 +178,7 @@ class LogEventsList {
* @return String: Formatted HTML
*/
private function getTitleInput( $title ) {
- return Xml::inputLabel( wfMsg( 'speciallogtitlelabel' ), 'page', 'page', 20, $title );
- }
-
- /**
- * @param $year Integer
- * @param $month Integer
- * @return string Formatted HTML
- */
- private function getDateMenu( $year, $month ) {
- # Offset overrides year/month selection
- if( $month && $month !== -1 ) {
- $encMonth = intval( $month );
- } else {
- $encMonth = '';
- }
- if ( $year ) {
- $encYear = intval( $year );
- } else if( $encMonth ) {
- $thisMonth = intval( gmdate( 'n' ) );
- $thisYear = intval( gmdate( 'Y' ) );
- if( intval($encMonth) > $thisMonth ) {
- $thisYear--;
- }
- $encYear = $thisYear;
- } else {
- $encYear = '';
- }
- return Xml::label( wfMsg( 'year' ), 'year' ) . ' '.
- Xml::input( 'year', 4, $encYear, array('id' => 'year', 'maxlength' => 4) ) .
- ' '.
- Xml::label( wfMsg( 'month' ), 'month' ) . ' '.
- Xml::monthSelector( $encMonth, -1 );
+ return Xml::inputLabel( wfMsg( 'speciallogtitlelabel' ), 'page', 'mw-log-page', 20, $title );
}
/**
@@ -230,6 +206,7 @@ class LogEventsList {
global $wgLang, $wgUser, $wgContLang;
$title = Title::makeTitle( $row->log_namespace, $row->log_title );
+ $classes = array( "mw-logline-{$row->log_type}" );
$time = $wgLang->timeanddate( wfTimestamp(TS_MW, $row->log_timestamp), true );
// User links
if( self::isDeleted($row,LogPage::DELETED_USER) ) {
@@ -276,7 +253,7 @@ class LogEventsList {
array(),
array( 'action' => 'unblock', 'ip' => $row->log_title ),
'known' )
- . ' ' . $this->message['pipe-separator'] . ' ' .
+ . $this->message['pipe-separator'] .
$this->skin->link( SpecialPage::getTitleFor( 'Blockip', $row->log_title ),
$this->message['change-blocklink'],
array(), array(), 'known' ) .
@@ -289,7 +266,7 @@ class LogEventsList {
array(),
array( 'action' => 'history', 'offset' => $row->log_timestamp ) );
if( $wgUser->isAllowed( 'protect' ) ) {
- $revert .= ' ' . $this->message['pipe-separator'] . ' ' .
+ $revert .= $this->message['pipe-separator'] .
$this->skin->link( $title,
$this->message['protect_change'],
array(),
@@ -315,8 +292,17 @@ class LogEventsList {
foreach( $Ids as $n => $id ) {
$revParams .= '&' . urlencode($key) . '[]=' . urlencode($id);
}
- $revert = '(' . $this->skin->makeKnownLinkObj( $revdel, $this->message['revdel-restore'],
- 'target=' . $title->getPrefixedUrl() . $revParams ) . ')';
+ $revert = array();
+ // Diff link for single rev deletions
+ if( $key === 'oldid' && count($Ids) == 1 ) {
+ $token = urlencode( $wgUser->editToken( intval($Ids[0]) ) );
+ $revert[] = $this->skin->makeKnownLinkObj( $title, $this->message['diff'],
+ 'diff='.intval($Ids[0])."&unhide=1&token=$token" );
+ }
+ // View/modify link...
+ $revert[] = $this->skin->makeKnownLinkObj( $revdel, $this->message['revdel-restore'],
+ 'target=' . $title->getPrefixedUrl() . $revParams );
+ $revert = '(' . implode(' | ',$revert) . ')';
}
// Hidden log items, give review link
} else if( self::typeAction($row,array('delete','suppress'),'event','deleterevision') ) {
@@ -357,12 +343,16 @@ class LogEventsList {
$this->skin, $paramArray, true );
}
+ // Any tags...
+ list($tagDisplay, $newClasses) = ChangeTags::formatSummaryRow( $row->ts_tags, 'logevent' );
+ $classes = array_merge( $classes, $newClasses );
+
if( $revert != '' ) {
$revert = '<span class="mw-logevent-actionlink">' . $revert . '</span>';
}
- return Xml::tags( 'li', array( "class" => "mw-logline-$row->log_type" ),
- $del . $time . ' ' . $userLink . ' ' . $action . ' ' . $comment . ' ' . $revert );
+ return Xml::tags( 'li', array( "class" => implode( ' ', $classes ) ),
+ $del . $time . ' ' . $userLink . ' ' . $action . ' ' . $comment . ' ' . $revert . " $tagDisplay" ) . "\n";
}
/**
@@ -373,19 +363,18 @@ class LogEventsList {
$revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
// If event was hidden from sysops
if( !self::userCan( $row, LogPage::DELETED_RESTRICTED ) ) {
- $del = $this->message['rev-delundel'];
+ $del = Xml::tags( 'span', array( 'class'=>'mw-revdelundel-link' ), '('.$this->message['rev-delundel'].')' );
} else if( $row->log_type == 'suppress' ) {
// No one should be hiding from the oversight log
- $del = $this->message['rev-delundel'];
+ $del = Xml::tags( 'span', array( 'class'=>'mw-revdelundel-link' ), '('.$this->message['rev-delundel'].')' );
} else {
$target = SpecialPage::getTitleFor( 'Log', $row->log_type );
- $del = $this->skin->makeKnownLinkObj( $revdel, $this->message['rev-delundel'],
- 'target=' . $target->getPrefixedUrl() . '&logid='.$row->log_id );
- // Bolden oversighted content
- if( self::isDeleted( $row, LogPage::DELETED_RESTRICTED ) )
- $del = "<strong>$del</strong>";
+ $query = array( 'target' => $target->getPrefixedDBkey(),
+ 'logid[]' => $row->log_id
+ );
+ $del = $this->skin->revDeleteLink( $query, self::isDeleted( $row, LogPage::DELETED_RESTRICTED ) );
}
- return "<tt>(<small>$del</small>)</tt>";
+ return $del;
}
/**
@@ -468,15 +457,16 @@ class LogEventsList {
/**
* SQL clause to skip forbidden log types for this user
* @param $db Database
+ * @param $audience string, public/user
* @return mixed (string or false)
*/
- public static function getExcludeClause( $db ) {
+ public static function getExcludeClause( $db, $audience = 'public' ) {
global $wgLogRestrictions, $wgUser;
// Reset the array, clears extra "where" clauses when $par is used
$hiddenLogs = array();
// Don't show private logs to unprivileged users
foreach( $wgLogRestrictions as $logType => $right ) {
- if( !$wgUser->isAllowed($right) ) {
+ if( $audience == 'public' || !$wgUser->isAllowed($right) ) {
$safeType = $db->strencode( $logType );
$hiddenLogs[] = $safeType;
}
@@ -509,17 +499,18 @@ class LogPager extends ReverseChronologicalPager {
* @param $month Integer
*/
public function __construct( $list, $type = '', $user = '', $title = '', $pattern = '',
- $conds = array(), $year = false, $month = false )
+ $conds = array(), $year = false, $month = false, $tagFilter = '' )
{
parent::__construct();
$this->mConds = $conds;
$this->mLogEventsList = $list;
- $this->limitType( $type );
+ $this->limitType( $type ); // also excludes hidden types
$this->limitUser( $user );
$this->limitTitle( $title, $pattern );
$this->getDateCond( $year, $month );
+ $this->mTagFilter = $tagFilter;
}
public function getDefaultQuery() {
@@ -560,16 +551,17 @@ class LogPager extends ReverseChronologicalPager {
if( isset($wgLogRestrictions[$type]) && !$wgUser->isAllowed($wgLogRestrictions[$type]) ) {
$type = '';
}
- // Don't show private logs to unpriviledged users
- $hideLogs = LogEventsList::getExcludeClause( $this->mDb );
+ // Don't show private logs to unpriviledged users.
+ // Also, only show them upon specific request to avoid suprises.
+ $audience = $type ? 'user' : 'public';
+ $hideLogs = LogEventsList::getExcludeClause( $this->mDb, $audience );
if( $hideLogs !== false ) {
$this->mConds[] = $hideLogs;
}
- if( !$type ) {
- return false;
+ if( $type ) {
+ $this->type = $type;
+ $this->mConds['log_type'] = $type;
}
- $this->type = $type;
- $this->mConds['log_type'] = $type;
}
/**
@@ -591,7 +583,12 @@ class LogPager extends ReverseChronologicalPager {
but for now it won't pass anywhere behind the optimizer */
$this->mConds[] = "NULL";
} else {
+ global $wgUser;
$this->mConds['log_user'] = $userid;
+ // Paranoia: avoid brute force searches (bug 17342)
+ if( !$wgUser->isAllowed( 'suppressrevision' ) ) {
+ $this->mConds[] = 'log_deleted & ' . LogPage::DELETED_USER . ' = 0';
+ }
$this->user = $usertitle->getText();
}
}
@@ -603,7 +600,7 @@ class LogPager extends ReverseChronologicalPager {
* @param $pattern String
*/
private function limitTitle( $page, $pattern ) {
- global $wgMiserMode;
+ global $wgMiserMode, $wgUser;
$title = Title::newFromText( $page );
if( strlen($page) == 0 || !$title instanceof Title )
@@ -632,6 +629,10 @@ class LogPager extends ReverseChronologicalPager {
$this->mConds['log_namespace'] = $ns;
$this->mConds['log_title'] = $title->getDBkey();
}
+ // Paranoia: avoid brute force searches (bug 17342)
+ if( !$wgUser->isAllowed( 'suppressrevision' ) ) {
+ $this->mConds[] = 'log_deleted & ' . LogPage::DELETED_ACTION . ' = 0';
+ }
}
public function getQueryInfo() {
@@ -644,13 +645,19 @@ class LogPager extends ReverseChronologicalPager {
} else {
$index = array( 'USE INDEX' => array( 'logging' => 'times' ) );
}
- return array(
+ $info = array(
'tables' => array( 'logging', 'user' ),
'fields' => array( 'log_type', 'log_action', 'log_user', 'log_namespace', 'log_title', 'log_params',
'log_comment', 'log_id', 'log_deleted', 'log_timestamp', 'user_name', 'user_editcount' ),
'conds' => $this->mConds,
- 'options' => $index
+ 'options' => $index,
+ 'join_conds' => array( 'user' => array( 'INNER JOIN', 'user_id=log_user' ) ),
);
+
+ ChangeTags::modifyDisplayQuery( $info['tables'], $info['fields'], $info['conds'],
+ $info['join_conds'], $info['options'], $this->mTagFilter );
+
+ return $info;
}
function getIndexField() {
@@ -701,6 +708,10 @@ class LogPager extends ReverseChronologicalPager {
public function getMonth() {
return $this->mMonth;
}
+
+ public function getTagFilter() {
+ return $this->mTagFilter;
+ }
}
/**
@@ -722,6 +733,7 @@ class LogReader {
$pattern = $request->getBool( 'pattern' );
$year = $request->getIntOrNull( 'year' );
$month = $request->getIntOrNull( 'month' );
+ $tagFilter = $request->getVal( 'tagfilter' );
# Don't let the user get stuck with a certain date
$skip = $request->getText( 'offset' ) || $request->getText( 'dir' ) == 'prev';
if( $skip ) {
@@ -730,7 +742,7 @@ class LogReader {
}
# Use new list class to output results
$loglist = new LogEventsList( $wgUser->getSkin(), $wgOut, 0 );
- $this->pager = new LogPager( $loglist, $type, $user, $title, $pattern, $year, $month );
+ $this->pager = new LogPager( $loglist, $type, $user, $title, $pattern, $year, $month, $tagFilter );
}
/**
diff --git a/includes/LogPage.php b/includes/LogPage.php
index 50a9a232..0d572385 100644
--- a/includes/LogPage.php
+++ b/includes/LogPage.php
@@ -37,7 +37,7 @@ class LogPage {
/* @access private */
var $type, $action, $comment, $params, $target, $doer;
/* @acess public */
- var $updateRecentChanges;
+ var $updateRecentChanges, $sendToUDP;
/**
* Constructor
@@ -45,15 +45,16 @@ class LogPage {
* @param string $type One of '', 'block', 'protect', 'rights', 'delete',
* 'upload', 'move'
* @param bool $rc Whether to update recent changes as well as the logging table
+ * @param bool $udp Whether to send to the UDP feed if NOT sent to RC
*/
- function __construct( $type, $rc = true ) {
+ public function __construct( $type, $rc = true, $udp = 'skipUDP' ) {
$this->type = $type;
$this->updateRecentChanges = $rc;
+ $this->sendToUDP = ($udp == 'UDP');
}
protected function saveContent() {
- global $wgUser, $wgLogRestrictions;
- $fname = 'LogPage::saveContent';
+ global $wgLogRestrictions;
$dbw = wfGetDB( DB_MASTER );
$log_id = $dbw->nextSequenceValue( 'log_log_id_seq' );
@@ -70,21 +71,25 @@ class LogPage {
'log_comment' => $this->comment,
'log_params' => $this->params
);
- $dbw->insert( 'logging', $data, $fname );
+ $dbw->insert( 'logging', $data, __METHOD__ );
$newId = !is_null($log_id) ? $log_id : $dbw->insertId();
- if( !($dbw->affectedRows() > 0) ) {
- wfDebugLog( "logging", "LogPage::saveContent failed to insert row - Error {$dbw->lastErrno()}: {$dbw->lastError()}" );
- }
# And update recentchanges
if( $this->updateRecentChanges ) {
- # Don't add private logs to RC!
- if( !isset($wgLogRestrictions[$this->type]) || $wgLogRestrictions[$this->type]=='*' ) {
- $titleObj = SpecialPage::getTitleFor( 'Log', $this->type );
- $rcComment = $this->getRcComment();
- RecentChange::notifyLog( $now, $titleObj, $this->doer, $rcComment, '',
- $this->type, $this->action, $this->target, $this->comment, $this->params, $newId );
+ $titleObj = SpecialPage::getTitleFor( 'Log', $this->type );
+ RecentChange::notifyLog( $now, $titleObj, $this->doer, $this->getRcComment(), '', $this->type,
+ $this->action, $this->target, $this->comment, $this->params, $newId );
+ } else if( $this->sendToUDP ) {
+ # Don't send private logs to UDP
+ if( isset($wgLogRestrictions[$this->type]) && $wgLogRestrictions[$this->type] !='*' ) {
+ return true;
}
+ # Notify external application via UDP.
+ # We send this to IRC but do not want to add it the RC table.
+ $titleObj = SpecialPage::getTitleFor( 'Log', $this->type );
+ $rc = RecentChange::newLogEntry( $now, $titleObj, $this->doer, $this->getRcComment(), '',
+ $this->type, $this->action, $this->target, $this->comment, $this->params, $newId );
+ $rc->notifyRC2UDP();
}
return true;
}
@@ -98,7 +103,7 @@ class LogPage {
if ($rcComment == '')
$rcComment = $this->comment;
else
- $rcComment .= ': ' . $this->comment;
+ $rcComment .= wfMsgForContent( 'colon-separator' ) . $this->comment;
}
return $rcComment;
}
@@ -145,7 +150,7 @@ class LogPage {
* @param string $type logtype
* @return string Headertext of this logtype
*/
- static function logHeader( $type ) {
+ public static function logHeader( $type ) {
global $wgLogHeaders, $wgMessageCache;
$wgMessageCache->loadAllMessages();
return wfMsgExt($wgLogHeaders[$type],array('parseinline'));
@@ -155,7 +160,7 @@ class LogPage {
* @static
* @return HTML string
*/
- static function actionText( $type, $action, $title = NULL, $skin = NULL,
+ public static function actionText( $type, $action, $title = NULL, $skin = NULL,
$params = array(), $filterWikilinks = false )
{
global $wgLang, $wgContLang, $wgLogActions, $wgMessageCache;
@@ -196,7 +201,7 @@ class LogPage {
} else {
$details = '';
array_unshift( $params, $titleLink );
- if ( $key == 'block/block' || $key == 'suppress/block' || $key == 'block/reblock' ) {
+ if ( preg_match( '/^(block|suppress)\/(block|reblock)$/', $key ) ) {
if ( $skin ) {
$params[1] = '<span title="' . htmlspecialchars( $params[1] ). '">' .
$wgLang->translateBlockExpiry( $params[1] ) . '</span>';
@@ -208,11 +213,19 @@ class LogPage {
} else if ( $type == 'protect' && count($params) == 3 ) {
$details .= " {$params[1]}"; // restrictions and expiries
if( $params[2] ) {
- $details .= ' ['.wfMsg('protect-summary-cascade').']';
+ if ( $skin ) {
+ $details .= ' ['.wfMsg('protect-summary-cascade').']';
+ } else {
+ $details .= ' ['.wfMsgForContent('protect-summary-cascade').']';
+ }
}
} else if ( $type == 'move' && count( $params ) == 3 ) {
if( $params[2] ) {
- $details .= ' [' . wfMsg( 'move-redirect-suppressed' ) . ']';
+ if ( $skin ) {
+ $details .= ' [' . wfMsg( 'move-redirect-suppressed' ) . ']';
+ } else {
+ $details .= ' [' . wfMsgForContent( 'move-redirect-suppressed' ) . ']';
+ }
}
}
$rv = wfMsgReal( $wgLogActions[$key], $params, true, !$skin ) . $details;
@@ -228,6 +241,17 @@ class LogPage {
$rv = "$action";
}
}
+
+ // For the perplexed, this feature was added in r7855 by Erik.
+ // The feature was added because we liked adding [[$1]] in our log entries
+ // but the log entries are parsed as Wikitext on RecentChanges but as HTML
+ // on Special:Log. The hack is essentially that [[$1]] represented a link
+ // to the title in question. The first parameter to the HTML version (Special:Log)
+ // is that link in HTML form, and so this just gets rid of the ugly [[]].
+ // However, this is a horrible hack and it doesn't work like you expect if, say,
+ // you want to link to something OTHER than the title of the log entry.
+ // The real problem, which Erik was trying to fix (and it sort-of works now) is
+ // that the same messages are being treated as both wikitext *and* HTML.
if( $filterWikilinks ) {
$rv = str_replace( "[[", "", $rv );
$rv = str_replace( "]]", "", $rv );
@@ -296,7 +320,7 @@ class LogPage {
* @param array $params Parameters passed later to wfMsg.* functions
* @param User $doer The user doing the action
*/
- function addEntry( $action, $target, $comment, $params = array(), $doer = null ) {
+ public function addEntry( $action, $target, $comment, $params = array(), $doer = null ) {
if ( !is_array( $params ) ) {
$params = array( $params );
}
@@ -324,7 +348,7 @@ class LogPage {
* Create a blob from a parameter array
* @static
*/
- static function makeParamBlob( $params ) {
+ public static function makeParamBlob( $params ) {
return implode( "\n", $params );
}
@@ -332,7 +356,7 @@ class LogPage {
* Extract a parameter array from a blob
* @static
*/
- static function extractParams( $blob ) {
+ public static function extractParams( $blob ) {
if ( $blob === '' ) {
return array();
} else {
@@ -350,11 +374,13 @@ class LogPage {
* @return string
*/
public static function formatBlockFlags( $flags, $forContent = false ) {
+ global $wgLang;
+
$flags = explode( ',', trim( $flags ) );
if( count( $flags ) > 0 ) {
for( $i = 0; $i < count( $flags ); $i++ )
$flags[$i] = self::formatBlockFlag( $flags[$i], $forContent );
- return '(' . implode( ', ', $flags ) . ')';
+ return '(' . $wgLang->commaList( $flags ) . ')';
} else {
return '';
}
diff --git a/includes/MagicWord.php b/includes/MagicWord.php
index 5b5b77f0..4e97016d 100644
--- a/includes/MagicWord.php
+++ b/includes/MagicWord.php
@@ -78,6 +78,7 @@ class MagicWord {
'revisionmonth',
'revisionyear',
'revisiontimestamp',
+ 'revisionuser',
'subpagename',
'subpagenamee',
'displaytitle',
@@ -90,7 +91,9 @@ class MagicWord {
'subjectpagename',
'subjectpagenamee',
'numberofusers',
+ 'numberofactiveusers',
'newsectionlink',
+ 'nonewsectionlink',
'numberofpages',
'currentversion',
'basepagename',
@@ -141,6 +144,7 @@ class MagicWord {
'localweek' => 3600,
'localdow' => 3600,
'numberofusers' => 3600,
+ 'numberofactiveusers' => 3600,
'numberofpages' => 3600,
'currentversion' => 86400,
'currenttimestamp' => 3600,
@@ -158,6 +162,7 @@ class MagicWord {
'toc',
'noeditsection',
'newsectionlink',
+ 'nonewsectionlink',
'hiddencat',
'index',
'noindex',
diff --git a/includes/MessageCache.php b/includes/MessageCache.php
index a06b0cb9..2236bdd7 100644
--- a/includes/MessageCache.php
+++ b/includes/MessageCache.php
@@ -702,7 +702,10 @@ class MessageCache {
* @param string $lang The messages language, English by default
*/
function addMessage( $key, $value, $lang = 'en' ) {
- $this->mExtensionMessages[$lang][$key] = $value;
+ global $wgContLang;
+ # Normalise title-case input
+ $lckey = str_replace( ' ', '_', $wgContLang->lcfirst( $key ) );
+ $this->mExtensionMessages[$lang][$lckey] = $value;
}
/**
@@ -800,6 +803,7 @@ class MessageCache {
*/
function loadMessagesFile( $filename, $langcode = false ) {
global $wgLang, $wgContLang;
+ wfProfileIn( __METHOD__ );
$messages = $magicWords = false;
require( $filename );
@@ -822,6 +826,7 @@ class MessageCache {
global $wgContLang;
$wgContLang->addMagicWordsByLang( $magicWords );
}
+ wfProfileOut( __METHOD__ );
}
/**
@@ -831,6 +836,7 @@ class MessageCache {
* @param string $langcode Language code to process.
*/
function processMessagesArray( $messages, $langcode ) {
+ wfProfileIn( __METHOD__ );
$fallbackCode = $langcode;
$mergedMessages = array();
do {
@@ -842,6 +848,7 @@ class MessageCache {
if ( !empty($mergedMessages) )
$this->addMessages( $mergedMessages, $langcode );
+ wfProfileOut( __METHOD__ );
}
public function figureMessage( $key ) {
diff --git a/includes/MimeMagic.php b/includes/MimeMagic.php
index 4797752d..d52de994 100644
--- a/includes/MimeMagic.php
+++ b/includes/MimeMagic.php
@@ -579,22 +579,22 @@ class MimeMagic {
*/
function detectZipType( $header ) {
$opendocTypes = array(
- 'chart',
'chart-template',
- 'formula',
+ 'chart',
'formula-template',
- 'graphics',
+ 'formula',
'graphics-template',
- 'image',
+ 'graphics',
'image-template',
- 'presentation',
+ 'image',
'presentation-template',
- 'spreadsheet',
+ 'presentation',
'spreadsheet-template',
- 'text',
+ 'spreadsheet',
'text-template',
'text-master',
- 'text-web' );
+ 'text-web',
+ 'text' );
// http://lists.oasis-open.org/archives/office/200505/msg00006.html
$types = '(?:' . implode( '|', $opendocTypes ) . ')';
diff --git a/includes/OutputHandler.php b/includes/OutputHandler.php
index 2b3e9fae..1f4798b7 100644
--- a/includes/OutputHandler.php
+++ b/includes/OutputHandler.php
@@ -10,7 +10,7 @@ function wfOutputHandler( $s ) {
$headers = apache_response_headers();
$isHTML = true;
foreach ( $headers as $name => $value ) {
- if ( strtolower( $name ) == 'content-type' && strpos( $value, 'text/html' ) === false ) {
+ if ( strtolower( $name ) == 'content-type' && strpos( $value, 'text/html' ) === false && strpos( $value, 'application/xhtml+xml' ) === false ) {
$isHTML = false;
break;
}
@@ -123,10 +123,9 @@ function wfDoContentLength( $length ) {
* Replace the output with an error if the HTML is not valid
*/
function wfHtmlValidationHandler( $s ) {
- global $IP;
- $tidy = new tidy;
- $tidy->parseString( $s, "$IP/includes/tidy.conf", 'utf8' );
- if ( $tidy->getStatus() == 0 ) {
+
+ $errors = '';
+ if ( MWTidy::checkErrors( $s, $errors ) ) {
return $s;
}
@@ -134,7 +133,7 @@ function wfHtmlValidationHandler( $s ) {
$out = <<<EOT
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
<head>
<title>HTML validation error</title>
<style>
@@ -147,7 +146,7 @@ li { white-space: pre }
<ul>
EOT;
- $error = strtok( $tidy->errorBuffer, "\n" );
+ $error = strtok( $errors, "\n" );
$badLines = array();
while ( $error !== false ) {
if ( preg_match( '/^line (\d+)/', $error, $m ) ) {
@@ -158,8 +157,9 @@ EOT;
$error = strtok( "\n" );
}
- $out .= '<pre>' . htmlspecialchars( $tidy->errorBuffer ) . '</pre>';
- $out .= '<ol>';
+ $out .= '</ul>';
+ $out .= '<pre>' . htmlspecialchars( $errors ) . '</pre>';
+ $out .= "<ol>\n";
$line = strtok( $s, "\n" );
$i = 1;
while ( $line !== false ) {
@@ -168,7 +168,7 @@ EOT;
} else {
$out .= '<li>';
}
- $out .= htmlspecialchars( $line ) . '</li>';
+ $out .= htmlspecialchars( $line ) . "</li>\n";
$line = strtok( "\n" );
$i++;
}
diff --git a/includes/OutputPage.php b/includes/OutputPage.php
index f8dba714..ed9a43d3 100644
--- a/includes/OutputPage.php
+++ b/includes/OutputPage.php
@@ -29,6 +29,7 @@ class OutputPage {
var $mArticleBodyOnly = false;
var $mNewSectionLink = false;
+ var $mHideNewSectionLink = false;
var $mNoGallery = false;
var $mPageTitleActionText = '';
var $mParseWarnings = array();
@@ -165,7 +166,7 @@ class OutputPage {
*
* @return bool True iff cache-ok headers was sent.
*/
- function checkLastModified ( $timestamp ) {
+ function checkLastModified( $timestamp ) {
global $wgCachePages, $wgCacheEpoch, $wgUser, $wgRequest;
if ( !$timestamp || $timestamp == '19700101000000' ) {
@@ -232,6 +233,7 @@ class OutputPage {
# Not modified
# Give a 304 response code and disable body output
wfDebug( __METHOD__ . ": NOT MODIFIED, $info\n", false );
+ ini_set('zlib.output_compression', 0);
$wgRequest->response()->header( "HTTP/1.1 304 Not Modified" );
$this->sendCacheControl();
$this->disable();
@@ -309,20 +311,20 @@ class OutputPage {
}
}
- public function setHTMLTitle( $name ) {$this->mHTMLtitle = $name; }
+ public function setHTMLTitle( $name ) { $this->mHTMLtitle = $name; }
public function setPageTitle( $name ) {
- global $action, $wgContLang;
- $name = $wgContLang->convert($name, true);
+ global $wgContLang;
+ $name = $wgContLang->convert( $name, true );
$this->mPagetitle = $name;
- if(!empty($action)) {
- $taction = $this->getPageTitleActionText();
- if( !empty( $taction ) ) {
- $name .= ' - '.$taction;
- }
+
+ $taction = $this->getPageTitleActionText();
+ if( !empty( $taction ) ) {
+ $name .= ' - '.$taction;
}
$this->setHTMLTitle( wfMsg( 'pagetitle', $name ) );
}
+
public function getHTMLTitle() { return $this->mHTMLtitle; }
public function getPageTitle() { return $this->mPagetitle; }
public function setSubtitle( $str ) { $this->mSubtitle = /*$this->parse(*/$str/*)*/; } // @bug 2514
@@ -338,6 +340,7 @@ class OutputPage {
public function setOnloadHandler( $js ) { $this->mOnloadHandler = $js; }
public function getOnloadHandler() { return $this->mOnloadHandler; }
public function disable() { $this->mDoNothing = true; }
+ public function isDisabled() { return $this->mDoNothing; }
public function setArticleRelated( $v ) {
$this->mIsArticleRelated = $v;
@@ -408,7 +411,12 @@ class OutputPage {
if ( wfRunHooks( 'OutputPageMakeCategoryLinks', array( &$this, $categories, &$this->mCategoryLinks ) ) ) {
$sk = $wgUser->getSkin();
foreach ( $categories as $category => $type ) {
+ $origcategory = $category;
$title = Title::makeTitleSafe( NS_CATEGORY, $category );
+ $wgContLang->findVariantLink( $category, $title, true );
+ if ( $category != $origcategory )
+ if ( array_key_exists( $category, $categories ) )
+ continue;
$text = $wgContLang->convertHtml( $title->getText() );
$this->mCategoryLinks[$type][] = $sk->makeLinkObj( $title, $text );
}
@@ -511,6 +519,7 @@ class OutputPage {
$this->mLanguageLinks += $parserOutput->getLanguageLinks();
$this->addCategoryLinks( $parserOutput->getCategories() );
$this->mNewSectionLink = $parserOutput->getNewSection();
+ $this->mHideNewSectionLink = $parserOutput->getHideNewSection();
if( is_null( $wgExemptFromUserRobotsControl ) ) {
$bannedNamespaces = $wgContentNamespaces;
@@ -538,9 +547,11 @@ class OutputPage {
$this->mTemplateIds[$ns] = $dbks;
}
}
- // Display title
+ // Page title
if( ( $dt = $parserOutput->getDisplayTitle() ) !== false )
$this->setPageTitle( $dt );
+ else if ( ( $title = $parserOutput->getTitleText() ) != '' )
+ $this->setPageTitle( $title );
// Hooks registered in the object
global $wgParserOutputHooks;
@@ -586,7 +597,7 @@ class OutputPage {
$popts->setTidy(false);
if ( $cache && $article && $parserOutput->getCacheTime() != -1 ) {
$parserCache = ParserCache::singleton();
- $parserCache->save( $parserOutput, $article, $wgUser );
+ $parserCache->save( $parserOutput, $article, $popts);
}
$this->addParserOutput( $parserOutput );
@@ -642,15 +653,27 @@ class OutputPage {
return $parserOutput->getText();
}
+ /** Parse wikitext, strip paragraphs, and return the HTML. */
+ public function parseInline( $text, $linestart = true, $interface = false ) {
+ $parsed = $this->parse( $text, $linestart, $interface );
+
+ $m = array();
+ if ( preg_match( '/^<p>(.*)\n?<\/p>\n?/sU', $parsed, $m ) ) {
+ $parsed = $m[1];
+ }
+
+ return $parsed;
+ }
+
/**
* @param Article $article
* @param User $user
*
* @return bool True if successful, else false.
*/
- public function tryParserCache( &$article, $user ) {
+ public function tryParserCache( &$article ) {
$parserCache = ParserCache::singleton();
- $parserOutput = $parserCache->get( $article, $user );
+ $parserOutput = $parserCache->get( $article, $this->parserOptions() );
if ( $parserOutput !== false ) {
$this->addParserOutput( $parserOutput );
return true;
@@ -917,13 +940,13 @@ class OutputPage {
'rel' => 'alternate',
'type' => 'application/x-wiki',
'title' => wfMsg( 'edit' ),
- 'href' => $wgTitle->getFullURL( 'action=edit' )
+ 'href' => $wgTitle->getLocalURL( 'action=edit' )
) );
// Alternate edit link
$this->addLink( array(
'rel' => 'edit',
'title' => wfMsg( 'edit' ),
- 'href' => $wgTitle->getFullURL( 'action=edit' )
+ 'href' => $wgTitle->getLocalURL( 'action=edit' )
) );
}
}
@@ -1132,7 +1155,7 @@ class OutputPage {
* @param string $permission key required
*/
public function permissionRequired( $permission ) {
- global $wgUser;
+ global $wgUser, $wgLang;
$this->setPageTitle( wfMsg( 'badaccess' ) );
$this->setHTMLTitle( wfMsg( 'errorpagetitle' ) );
@@ -1144,7 +1167,7 @@ class OutputPage {
User::getGroupsWithPermission( $permission ) );
if( $groups ) {
$this->addWikiMsg( 'badaccess-groups',
- implode( ', ', $groups ),
+ $wgLang->commaList( $groups ),
count( $groups) );
} else {
$this->addWikiMsg( 'badaccess-group0' );
@@ -1457,7 +1480,7 @@ class OutputPage {
$ret = '';
if( $wgMimeType == 'text/xml' || $wgMimeType == 'application/xhtml+xml' || $wgMimeType == 'application/xml' ) {
- $ret .= "<?xml version=\"1.0\" encoding=\"$wgOutputEncoding\" ?>\n";
+ $ret .= "<?xml version=\"1.0\" encoding=\"$wgOutputEncoding\" ?" . ">\n";
}
$ret .= "<!DOCTYPE html PUBLIC \"$wgDocType\"\n \"$wgDTD\">\n";
@@ -1505,7 +1528,7 @@ class OutputPage {
if ( count( $this->mKeywords ) > 0 ) {
$strip = array(
- "/<.*?>/" => '',
+ "/<.*?" . ">/" => '',
"/_/" => ' '
);
$this->addMeta( 'keywords', preg_replace(array_keys($strip), array_values($strip),implode( ",", $this->mKeywords ) ) );
@@ -1576,7 +1599,7 @@ class OutputPage {
foreach( $wgFeedClasses as $format => $class ) {
$tags[] = $this->feedLink(
$format,
- $rctitle->getFullURL( "feed={$format}" ),
+ $rctitle->getLocalURL( "feed={$format}" ),
wfMsg( "site-{$format}-feed", $wgSitename ) ); # For grep: 'site-rss-feed', 'site-atom-feed'.
}
}
@@ -1759,6 +1782,15 @@ class OutputPage {
}
/**
+ * Forcibly hide the new section link?
+ *
+ * @return bool
+ */
+ public function forceHideNewSectionLink() {
+ return $this->mHideNewSectionLink;
+ }
+
+ /**
* Show a warning about slave lag
*
* If the lag is higher than $wgSlaveLagCritical seconds,
@@ -1843,7 +1875,7 @@ class OutputPage {
$args = array();
$name = $spec;
}
- $s = str_replace( '$' . ($n+1), wfMsgExt( $name, $options, $args ), $s );
+ $s = str_replace( '$' . ( $n + 1 ), wfMsgExt( $name, $options, $args ), $s );
}
$this->addHTML( $this->parse( $s, /*linestart*/true, /*uilang*/true ) );
}
diff --git a/includes/PageHistory.php b/includes/PageHistory.php
index b01b485e..9477981f 100644
--- a/includes/PageHistory.php
+++ b/includes/PageHistory.php
@@ -23,6 +23,8 @@ class PageHistory {
var $lastdate;
var $linesonpage;
var $mLatestId = null;
+
+ private $mOldIdChecked = 0;
/**
* Construct a new PageHistory.
@@ -112,6 +114,8 @@ class PageHistory {
*/
$year = $wgRequest->getInt( 'year' );
$month = $wgRequest->getInt( 'month' );
+ $tagFilter = $wgRequest->getVal( 'tagfilter' );
+ $tagSelector = ChangeTags::buildTagFilterSelector( $tagFilter );
$action = htmlspecialchars( $wgScript );
$wgOut->addHTML(
@@ -119,7 +123,8 @@ class PageHistory {
Xml::fieldset( wfMsg( 'history-fieldset-title' ), false, array( 'id' => 'mw-history-search' ) ) .
Xml::hidden( 'title', $this->mTitle->getPrefixedDBKey() ) . "\n" .
Xml::hidden( 'action', 'history' ) . "\n" .
- $this->getDateMenu( $year, $month ) . '&nbsp;' .
+ xml::dateMenu( $year, $month ) . '&nbsp;' .
+ ( $tagSelector ? ( implode( '&nbsp;', $tagSelector ) . '&nbsp;' ) : '' ) .
Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . "\n" .
'</fieldset></form>'
);
@@ -129,7 +134,7 @@ class PageHistory {
/**
* Do the list
*/
- $pager = new PageHistoryPager( $this, $year, $month );
+ $pager = new PageHistoryPager( $this, $year, $month, $tagFilter );
$this->linesonpage = $pager->getNumRows();
$wgOut->addHTML(
$pager->getNavigationBar() .
@@ -143,37 +148,6 @@ class PageHistory {
}
/**
- * @return string Formatted HTML
- * @param int $year
- * @param int $month
- */
- private function getDateMenu( $year, $month ) {
- # Offset overrides year/month selection
- if( $month && $month !== -1 ) {
- $encMonth = intval( $month );
- } else {
- $encMonth = '';
- }
- if( $year ) {
- $encYear = intval( $year );
- } else if( $encMonth ) {
- $thisMonth = intval( gmdate( 'n' ) );
- $thisYear = intval( gmdate( 'Y' ) );
- if( intval($encMonth) > $thisMonth ) {
- $thisYear--;
- }
- $encYear = $thisYear;
- } else {
- $encYear = '';
- }
- return Xml::label( wfMsg( 'year' ), 'year' ) . ' '.
- Xml::input( 'year', 4, $encYear, array('id' => 'year', 'maxlength' => 4) ) .
- ' '.
- Xml::label( wfMsg( 'month' ), 'month' ) . ' '.
- Xml::monthSelector( $encMonth, -1 );
- }
-
- /**
* Creates begin of history list with a submit button
*
* @return string HTML output
@@ -287,37 +261,34 @@ class PageHistory {
$lastlink = $this->lastLink( $rev, $next, $counter );
$arbitrary = $this->diffButtons( $rev, $firstInList, $counter );
$link = $this->revLink( $rev );
+ $classes = array();
$s = "($curlink) ($lastlink) $arbitrary";
if( $wgUser->isAllowed( 'deleterevision' ) ) {
- $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
- if( $firstInList ) {
+ if( $latest ) {
// We don't currently handle well changing the top revision's settings
- $del = $this->message['rev-delundel'];
+ $del = Xml::tags( 'span', array( 'class'=>'mw-revdelundel-link' ), '('.$this->message['rev-delundel'].')' );
} else if( !$rev->userCan( Revision::DELETED_RESTRICTED ) ) {
// If revision was hidden from sysops
- $del = $this->message['rev-delundel'];
+ $del = Xml::tags( 'span', array( 'class'=>'mw-revdelundel-link' ), '('.$this->message['rev-delundel'].')' );
} else {
- $del = $this->mSkin->makeKnownLinkObj( $revdel,
- $this->message['rev-delundel'],
- 'target=' . urlencode( $this->mTitle->getPrefixedDbkey() ) .
- '&oldid=' . urlencode( $rev->getId() ) );
- // Bolden oversighted content
- if( $rev->isDeleted( Revision::DELETED_RESTRICTED ) )
- $del = "<strong>$del</strong>";
+ $query = array( 'target' => $this->mTitle->getPrefixedDbkey(),
+ 'oldid' => $rev->getId()
+ );
+ $del = $this->mSkin->revDeleteLink( $query, $rev->isDeleted( Revision::DELETED_RESTRICTED ) );
}
- $s .= " <tt>(<small>$del</small>)</tt> ";
+ $s .= " $del ";
}
$s .= " $link";
$s .= " <span class='history-user'>" . $this->mSkin->revUserTools( $rev, true ) . "</span>";
- if( $row->rev_minor_edit ) {
+ if( $rev->isMinor() ) {
$s .= ' ' . Xml::element( 'span', array( 'class' => 'minor' ), wfMsg( 'minoreditletter') );
}
- if( !is_null( $size = $rev->getSize() ) && $rev->userCan( Revision::DELETED_TEXT ) ) {
+ if( !is_null( $size = $rev->getSize() ) && !$rev->isDeleted( Revision::DELETED_TEXT ) ) {
$s .= ' ' . $this->mSkin->formatRevisionSize( $size );
}
@@ -356,12 +327,19 @@ class PageHistory {
}
if( $tools ) {
- $s .= ' (' . implode( ' | ', $tools ) . ')';
+ $s .= ' (' . $wgLang->pipeList( $tools ) . ')';
}
+ # Tags
+ list($tagSummary, $newClasses) = ChangeTags::formatSummaryRow( $row->ts_tags, 'history' );
+ $classes = array_merge( $classes, $newClasses );
+ $s .= " $tagSummary";
+
wfRunHooks( 'PageHistoryLineEnding', array( $this, &$row , &$s ) );
- return "<li>$s</li>\n";
+ $classes = implode( ' ', $classes );
+
+ return "<li class=\"$classes\">$s</li>\n";
}
/**
@@ -372,14 +350,10 @@ class PageHistory {
function revLink( $rev ) {
global $wgLang;
$date = $wgLang->timeanddate( wfTimestamp(TS_MW, $rev->getTimestamp()), true );
- if( $rev->userCan( Revision::DELETED_TEXT ) ) {
- $link = $this->mSkin->makeKnownLinkObj(
- $this->mTitle, $date, "oldid=" . $rev->getId() );
+ if( !$rev->isDeleted( Revision::DELETED_TEXT ) ) {
+ $link = $this->mSkin->makeKnownLinkObj( $this->mTitle, $date, "oldid=" . $rev->getId() );
} else {
- $link = $date;
- }
- if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
- return '<span class="history-deleted">' . $link . '</span>';
+ $link = '<span class="history-deleted">' . $date . '</span>';
}
return $link;
}
@@ -392,7 +366,7 @@ class PageHistory {
*/
function curLink( $rev, $latest ) {
$cur = $this->message['cur'];
- if( $latest || !$rev->userCan( Revision::DELETED_TEXT ) ) {
+ if( $latest || $rev->isDeleted( Revision::DELETED_TEXT ) ) {
return $cur;
} else {
return $this->mSkin->makeKnownLinkObj( $this->mTitle, $cur,
@@ -418,7 +392,7 @@ class PageHistory {
# Next row probably exists but is unknown, use an oldid=prev link
return $this->mSkin->makeKnownLinkObj( $this->mTitle, $last,
"diff=" . $prevRev->getId() . "&oldid=prev" );
- } elseif( !$prevRev->userCan(Revision::DELETED_TEXT) || !$nextRev->userCan(Revision::DELETED_TEXT) ) {
+ } elseif( $prevRev->isDeleted(Revision::DELETED_TEXT) || $nextRev->isDeleted(Revision::DELETED_TEXT) ) {
return $last;
} else {
return $this->mSkin->makeKnownLinkObj( $this->mTitle, $last,
@@ -435,40 +409,29 @@ class PageHistory {
* @return string HTML output for the radio buttons
*/
function diffButtons( $rev, $firstInList, $counter ) {
- if( $this->linesonpage > 1) {
- $radio = array(
- 'type' => 'radio',
- 'value' => $rev->getId(),
- );
-
- if( !$rev->userCan( Revision::DELETED_TEXT ) ) {
- $radio['disabled'] = 'disabled';
- }
-
+ if( $this->linesonpage > 1 ) {
+ $radio = array( 'type' => 'radio', 'value' => $rev->getId() );
/** @todo: move title texts to javascript */
if( $firstInList ) {
- $first = Xml::element( 'input', array_merge(
- $radio,
- array(
- 'style' => 'visibility:hidden',
- 'name' => 'oldid' ) ) );
+ $first = Xml::element( 'input',
+ array_merge( $radio, array( 'style' => 'visibility:hidden', 'name' => 'oldid' ) )
+ );
$checkmark = array( 'checked' => 'checked' );
} else {
- if( $counter == 2 ) {
+ # Check visibility of old revisions
+ if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
+ $radio['disabled'] = 'disabled';
+ $checkmark = array(); // We will check the next possible one
+ } else if( $counter == 2 || !$this->mOldIdChecked ) {
$checkmark = array( 'checked' => 'checked' );
+ $this->mOldIdChecked = $rev->getId();
} else {
$checkmark = array();
}
- $first = Xml::element( 'input', array_merge(
- $radio,
- $checkmark,
- array( 'name' => 'oldid' ) ) );
+ $first = Xml::element( 'input', array_merge( $radio, $checkmark, array( 'name' => 'oldid' ) ) );
$checkmark = array();
}
- $second = Xml::element( 'input', array_merge(
- $radio,
- $checkmark,
- array( 'name' => 'diff' ) ) );
+ $second = Xml::element( 'input', array_merge( $radio, $checkmark, array( 'name' => 'diff' ) ) );
return $first . $second;
} else {
return '';
@@ -573,7 +536,7 @@ class PageHistory {
$rev->getUserText(),
$wgContLang->timeanddate( $rev->getTimestamp() ) );
} else {
- $title = $rev->getUserText() . ": " . FeedItem::stripComment( $rev->getComment() );
+ $title = $rev->getUserText() . wfMsgForContent( 'colon-separator' ) . FeedItem::stripComment( $rev->getComment() );
}
return new FeedItem(
@@ -593,20 +556,28 @@ class PageHistory {
class PageHistoryPager extends ReverseChronologicalPager {
public $mLastRow = false, $mPageHistory, $mTitle;
- function __construct( $pageHistory, $year='', $month='' ) {
+ function __construct( $pageHistory, $year='', $month='', $tagFilter = '' ) {
parent::__construct();
$this->mPageHistory = $pageHistory;
$this->mTitle =& $this->mPageHistory->mTitle;
+ $this->tagFilter = $tagFilter;
$this->getDateCond( $year, $month );
}
function getQueryInfo() {
$queryInfo = array(
'tables' => array('revision'),
- 'fields' => Revision::selectFields(),
+ 'fields' => array_merge( Revision::selectFields(), array('ts_tags') ),
'conds' => array('rev_page' => $this->mPageHistory->mTitle->getArticleID() ),
- 'options' => array( 'USE INDEX' => array('revision' => 'page_timestamp') )
+ 'options' => array( 'USE INDEX' => array('revision' => 'page_timestamp') ),
+ 'join_conds' => array( 'tag_summary' => array( 'LEFT JOIN', 'ts_rev_id=rev_id' ) ),
);
+ ChangeTags::modifyDisplayQuery( $queryInfo['tables'],
+ $queryInfo['fields'],
+ $queryInfo['conds'],
+ $queryInfo['join_conds'],
+ $queryInfo['options'],
+ $this->tagFilter );
wfRunHooks( 'PageHistoryPager::getQueryInfo', array( &$this, &$queryInfo ) );
return $queryInfo;
}
diff --git a/includes/Pager.php b/includes/Pager.php
index 8ec32ff4..8faec533 100644
--- a/includes/Pager.php
+++ b/includes/Pager.php
@@ -304,20 +304,18 @@ abstract class IndexPager implements Pager {
if ( $query === null ) {
return $text;
}
- if( $type == 'prev' || $type == 'next' ) {
- $attrs = "rel=\"$type\"";
- } elseif( $type == 'first' ) {
- $attrs = "rel=\"start\"";
- } else {
- # HTML 4 has no rel="end" . . .
- $attrs = '';
+
+ $attrs = array();
+ if( in_array( $type, array( 'first', 'prev', 'next', 'last' ) ) ) {
+ # HTML5 rel attributes
+ $attrs['rel'] = $type;
}
if( $type ) {
- $attrs .= " class=\"mw-{$type}link\"" ;
+ $attrs['class'] = "mw-{$type}link";
}
- return $this->getSkin()->makeKnownLinkObj( $this->getTitle(), $text,
- wfArrayToCGI( $query, $this->getDefaultQuery() ), '', '', $attrs );
+ return $this->getSkin()->link( $this->getTitle(), $text,
+ $attrs, $query + $this->getDefaultQuery(), array('noclasses','known') );
}
/**
@@ -532,10 +530,10 @@ abstract class AlphabeticPager extends IndexPager {
$pagingLinks = $this->getPagingLinks( $linkTexts );
$limitLinks = $this->getLimitLinks();
- $limits = implode( ' | ', $limitLinks );
+ $limits = $wgLang->pipeList( $limitLinks );
$this->mNavigationBar =
- "({$pagingLinks['first']} | {$pagingLinks['last']}) " .
+ "(" . $wgLang->pipeList( array( $pagingLinks['first'], $pagingLinks['last'] ) ) . ") " .
wfMsgHtml( 'viewprevnext', $pagingLinks['prev'],
$pagingLinks['next'], $limits );
@@ -551,7 +549,7 @@ abstract class AlphabeticPager extends IndexPager {
if( $first ) {
$first = false;
} else {
- $extra .= ' | ';
+ $extra .= wfMsgExt( 'pipe-separator' , 'escapenoentities' );
}
if( $order == $this->mOrderType ) {
@@ -612,9 +610,9 @@ abstract class ReverseChronologicalPager extends IndexPager {
$pagingLinks = $this->getPagingLinks( $linkTexts );
$limitLinks = $this->getLimitLinks();
- $limits = implode( ' | ', $limitLinks );
+ $limits = $wgLang->pipeList( $limitLinks );
- $this->mNavigationBar = "({$pagingLinks['first']} | {$pagingLinks['last']}) " .
+ $this->mNavigationBar = "({$pagingLinks['first']}" . wfMsgExt( 'pipe-separator' , 'escapenoentities' ) . "{$pagingLinks['last']}) " .
wfMsgHtml("viewprevnext", $pagingLinks['prev'], $pagingLinks['next'], $limits);
return $this->mNavigationBar;
}
@@ -747,7 +745,8 @@ abstract class TablePager extends IndexPager {
}
function formatRow( $row ) {
- $s = "<tr>\n";
+ $rowClass = $this->getRowClass( $row );
+ $s = "<tr class=\"$rowClass\">\n";
$fieldNames = $this->getFieldNames();
$this->mCurrentRow = $row; # In case formatValue needs to know
foreach ( $fieldNames as $field => $name ) {
@@ -763,6 +762,10 @@ abstract class TablePager extends IndexPager {
return $s;
}
+ function getRowClass($row) {
+ return '';
+ }
+
function getIndexField() {
return $this->mSort;
}
diff --git a/includes/PatrolLog.php b/includes/PatrolLog.php
index 5f305c10..978821c1 100644
--- a/includes/PatrolLog.php
+++ b/includes/PatrolLog.php
@@ -14,22 +14,20 @@ class PatrolLog {
* @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 ) )
+ public static function record( $rc, $auto = false ) {
+ if( !( $rc instanceof RecentChange ) ) {
+ $rc = RecentChange::newFromId( $rc );
+ if( !is_object( $rc ) )
return false;
}
- $title = Title::makeTitleSafe( $change->getAttribute( 'rc_namespace' ),
- $change->getAttribute( 'rc_title' ) );
+ $title = Title::makeTitleSafe( $rc->getAttribute( 'rc_namespace' ), $rc->getAttribute( 'rc_title' ) );
if( is_object( $title ) ) {
- $params = self::buildParams( $change, $auto );
- $log = new LogPage( 'patrol', false ); # False suppresses RC entries
+ $params = self::buildParams( $rc, $auto );
+ $log = new LogPage( 'patrol', false, $auto ? "skipUDP" : "UDP" ); # False suppresses RC entries
$log->addEntry( 'patrol', $title, '', $params );
return true;
- } else {
- return false;
}
+ return false;
}
/**
@@ -41,12 +39,8 @@ class PatrolLog {
* @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
+ list( $cur, /* $prev */, $auto ) = $params;
if( is_object( $skin ) ) {
- list( $cur, /* $prev */, $auto ) = $params;
# Standard link to the page in question
$link = $skin->makeLinkObj( $title );
if( $title->exists() ) {
@@ -64,7 +58,8 @@ class PatrolLog {
# Put it all together
return wfMsgHtml( 'patrol-log-line', $diff, $link, $auto );
} else {
- return '';
+ $text = $title->getPrefixedText();
+ return wfMsgForContent( 'patrol-log-line', wfMsgHtml('patrol-log-diff',$cur), "[[$text]]", '' );
}
}
diff --git a/includes/PrefixSearch.php b/includes/PrefixSearch.php
index af569112..10c85930 100644
--- a/includes/PrefixSearch.php
+++ b/includes/PrefixSearch.php
@@ -135,7 +135,7 @@ class PrefixSearch {
// Reformat useful data for future printing by JSON engine
$srchres = array ();
- foreach ($data['query']['allpages'] as & $pageinfo) {
+ foreach ((array)$data['query']['allpages'] as $pageinfo) {
// Note: this data will no be printable by the xml engine
// because it does not support lists of unnamed items
$srchres[] = $pageinfo['title'];
diff --git a/includes/Profiler.php b/includes/Profiler.php
index ffb48978..80a6a68a 100644
--- a/includes/Profiler.php
+++ b/includes/Profiler.php
@@ -78,8 +78,8 @@ class Profiler {
* @param $functionname string
*/
function profileIn( $functionname ) {
- global $wgDebugFunctionEntry;
-
+ global $wgDebugFunctionEntry, $wgProfiling;
+ if( !$wgProfiling ) return;
if( $wgDebugFunctionEntry ){
$this->debug( str_repeat( ' ', count( $this->mWorkStack ) ) . 'Entering ' . $functionname . "\n" );
}
@@ -92,8 +92,8 @@ class Profiler {
* @param $functionname string
*/
function profileOut($functionname) {
- global $wgDebugFunctionEntry;
-
+ global $wgDebugFunctionEntry, $wgProfiling;
+ if( !$wgProfiling ) return;
$memory = memory_get_usage();
$time = $this->getTime();
@@ -145,7 +145,12 @@ class Profiler {
}
$this->close();
- if( $wgProfileCallTree ){
+ if( $wgProfileCallTree ) {
+ global $wgProfileToDatabase;
+ # XXX: We must call $this->getFunctionReport() to log to the DB
+ if( $wgProfileToDatabase ) {
+ $this->getFunctionReport();
+ }
return $this->getCallTree();
} else {
return $this->getFunctionReport();
@@ -202,16 +207,13 @@ class Profiler {
/**
* Callback to get a formatted line for the call tree
*/
- function getCallTreeLine($entry) {
+ function getCallTreeLine( $entry ) {
list( $fname, $level, $start, /* $x */, $end) = $entry;
$delta = $end - $start;
$space = str_repeat(' ', $level);
-
# The ugly double sprintf is to work around a PHP bug,
# which has been fixed in recent releases.
- return sprintf( "%10s %s %s\n",
- trim( sprintf( "%7.3f", $delta * 1000.0 ) ),
- $space, $fname );
+ return sprintf( "%10s %s %s\n", trim( sprintf( "%7.3f", $delta * 1000.0 ) ), $space, $fname );
}
function getTime() {
@@ -316,8 +318,8 @@ class Profiler {
$percent = $total ? 100. * $elapsed / $total : 0;
$memory = $this->mMemory[$fname];
$prof .= sprintf($format, substr($fname, 0, $nameWidth), $calls, (float) ($elapsed * 1000), (float) ($elapsed * 1000) / $calls, $percent, $memory, ($this->mMin[$fname] * 1000.0), ($this->mMax[$fname] * 1000.0), $this->mOverhead[$fname]);
-
- if( $wgProfileToDatabase ){
+ # Log to the DB
+ if( $wgProfileToDatabase ) {
self::logToDB($fname, (float) ($elapsed * 1000), $calls, (float) ($memory) );
}
}
diff --git a/includes/ProfilerSimple.php b/includes/ProfilerSimple.php
index 349a7cac..5989061d 100644
--- a/includes/ProfilerSimple.php
+++ b/includes/ProfilerSimple.php
@@ -4,7 +4,9 @@
* @ingroup Profiler
*/
-require_once(dirname(__FILE__).'/Profiler.php');
+if ( !class_exists( 'Profiler' ) ) {
+ require_once(dirname(__FILE__).'/Profiler.php');
+}
/**
* Simple profiler base class.
diff --git a/includes/ProfilerSimpleTrace.php b/includes/ProfilerSimpleTrace.php
new file mode 100644
index 00000000..63119228
--- /dev/null
+++ b/includes/ProfilerSimpleTrace.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * @file
+ * @ingroup Profiler
+ */
+
+if ( !class_exists( 'ProfilerSimple' ) ) {
+ require_once(dirname(__FILE__).'/ProfilerSimple.php');
+}
+
+/**
+ * Execution trace
+ * @todo document methods (?)
+ * @ingroup Profiler
+ */
+class ProfilerSimpleTrace extends ProfilerSimple {
+ var $mMinimumTime = 0;
+ var $mProfileID = false;
+ var $trace = "";
+ var $memory = 0;
+
+ function __construct() {
+ global $wgRequestTime, $wgRUstart;
+ if (!empty($wgRequestTime) && !empty($wgRUstart)) {
+ $this->mWorkStack[] = array( '-total', 0, $wgRequestTime,$this->getCpuTime($wgRUstart));
+ $elapsedcpu = $this->getCpuTime() - $this->getCpuTime($wgRUstart);
+ $elapsedreal = microtime(true) - $wgRequestTime;
+ }
+ $this->trace .= "Beginning trace: \n";
+ }
+
+ function profileIn($functionname) {
+ global $wgDebugFunctionEntry;
+ $this->mWorkStack[] = array($functionname, count( $this->mWorkStack ), microtime(true), $this->getCpuTime());
+ $this->trace .= " " . sprintf("%6.1f",$this->memoryDiff()) . str_repeat( " ", count($this->mWorkStack)) . " > " . $functionname . "\n";
+ }
+
+ function profileOut($functionname) {
+ global $wgDebugFunctionEntry;
+
+ if ($wgDebugFunctionEntry) {
+ $this->debug(str_repeat(' ', count($this->mWorkStack) - 1).'Exiting '.$functionname."\n");
+ }
+
+ list($ofname, /* $ocount */ ,$ortime,$octime) = array_pop($this->mWorkStack);
+
+ if (!$ofname) {
+ $this->trace .= "Profiling error: $functionname\n";
+ } else {
+ if ($functionname == 'close') {
+ $message = "Profile section ended by close(): {$ofname}";
+ $functionname = $ofname;
+ $this->trace .= $message . "\n";
+ }
+ elseif ($ofname != $functionname) {
+ $self->trace .= "Profiling error: in({$ofname}), out($functionname)";
+ }
+ $elapsedcpu = $this->getCpuTime() - $octime;
+ $elapsedreal = microtime(true) - $ortime;
+ $this->trace .= sprintf("%03.6f %6.1f",$elapsedreal,$this->memoryDiff()) . str_repeat(" ",count($this->mWorkStack)+1) . " < " . $functionname . "\n";
+ }
+ }
+
+ function memoryDiff() {
+ $diff = memory_get_usage() - $this->memory;
+ $this->memory = memory_get_usage();
+ return $diff/1024;
+ }
+
+ function getOutput() {
+ print "<!-- \n {$this->trace} \n -->";
+ }
+}
diff --git a/includes/ProtectionForm.php b/includes/ProtectionForm.php
index 372edfcd..5fe3cbc7 100644
--- a/includes/ProtectionForm.php
+++ b/includes/ProtectionForm.php
@@ -235,7 +235,7 @@ class ProtectionForm {
$reasonstr = $this->mReasonSelection;
if ( $reasonstr != 'other' && $this->mReason != '' ) {
// Entry from drop down menu + additional comment
- $reasonstr .= ': ' . $this->mReason;
+ $reasonstr .= wfMsgForContent( 'colon-separator' ) . $this->mReason;
} elseif ( $reasonstr == 'other' ) {
$reasonstr = $this->mReason;
}
@@ -374,7 +374,8 @@ class ProtectionForm {
</tr></table>";
}
# Add custom expiry field
- $attribs = array( 'id' => "mwProtect-$action-expires", 'onkeyup' => 'ProtectionForm.updateExpiry(this)' ) + $this->disabledAttrib;
+ $attribs = array( 'id' => "mwProtect-$action-expires",
+ 'onkeyup' => 'ProtectionForm.updateExpiry(this)' ) + $this->disabledAttrib;
$out .= "<table><tr>
<td class='mw-label'>" .
$mProtectother .
diff --git a/includes/QueryPage.php b/includes/QueryPage.php
index 0b587508..1cef31ea 100644
--- a/includes/QueryPage.php
+++ b/includes/QueryPage.php
@@ -204,7 +204,7 @@ class QueryPage {
* Clear the cache and save new results
*/
function recache( $limit, $ignoreErrors = true ) {
- $fname = get_class($this) . '::recache';
+ $fname = get_class( $this ) . '::recache';
$dbw = wfGetDB( DB_MASTER );
$dbr = wfGetDB( DB_SLAVE, array( $this->getName(), 'QueryPage::recache', 'vslow' ) );
if ( !$dbw || !$dbr ) {
@@ -222,9 +222,9 @@ class QueryPage {
$dbw->delete( 'querycache', array( 'qc_type' => $this->getName() ), $fname );
# Do query
$sql = $this->getSQL() . $this->getOrder();
- if ($limit !== false)
- $sql = $dbr->limitResult($sql, $limit, 0);
- $res = $dbr->query($sql, $fname);
+ if ( $limit !== false )
+ $sql = $dbr->limitResult( $sql, $limit, 0 );
+ $res = $dbr->query( $sql, $fname );
$num = false;
if ( $res ) {
$num = $dbr->numRows( $res );
@@ -238,7 +238,7 @@ class QueryPage {
$insertSql .= ',';
}
if ( isset( $row->value ) ) {
- $value = $row->value;
+ $value = intval( $row->value ); // @bug 14414
} else {
$value = 0;
}
diff --git a/includes/RawPage.php b/includes/RawPage.php
index 7093367f..b422d49e 100644
--- a/includes/RawPage.php
+++ b/includes/RawPage.php
@@ -127,6 +127,15 @@ class RawPage {
$url = $_SERVER['PHP_SELF'];
}
+ if( $url == '' ) {
+ # This will make the next check fail with a confusing error
+ # message, so we should mention it separately.
+ wfHttpError( 500, 'Internal Server Error',
+ "\$_SERVER['PHP_SELF'] is not set. Perhaps you're using CGI" .
+ " and haven't set cgi.fix_pathinfo = 1 in php.ini?" );
+ return;
+ }
+
if( strcmp( $wgScript, $url ) ) {
# Internet Explorer will ignore the Content-Type header if it
# thinks it sees a file extension it recognizes. Make sure that
@@ -164,7 +173,7 @@ class RawPage {
$text = $this->getRawText();
if( !wfRunHooks( 'RawPageViewBeforeOutput', array( &$this, &$text ) ) ) {
- wfDebug( __METHOD__ . ': RawPageViewBeforeOutput hook broke raw page output.' );
+ wfDebug( __METHOD__ . ": RawPageViewBeforeOutput hook broke raw page output.\n" );
}
echo $text;
diff --git a/includes/RecentChange.php b/includes/RecentChange.php
index f03fbcbb..8e3f1107 100644
--- a/includes/RecentChange.php
+++ b/includes/RecentChange.php
@@ -166,6 +166,9 @@ class RecentChange
# Set the ID
$this->mAttribs['rc_id'] = $dbw->insertId();
+
+ # Notify extensions
+ wfRunHooks( 'RecentChange_save', array( &$this ) );
# Notify external application via UDP
if( $wgRC2UDPAddress && ( !$this->mAttribs['rc_bot'] || !$wgRC2UDPOmitBots ) ) {
@@ -193,9 +196,14 @@ class RecentChange
$this->mAttribs['rc_minor'],
$this->mAttribs['rc_last_oldid'] );
}
-
- # Notify extensions
- wfRunHooks( 'RecentChange_save', array( &$this ) );
+ }
+
+ public function notifyRC2UDP() {
+ global $wgRC2UDPAddress, $wgRC2UDPOmitBots;
+ # Notify external application via UDP
+ if( $wgRC2UDPAddress && ( !$this->mAttribs['rc_bot'] || !$wgRC2UDPOmitBots ) ) {
+ self::sendToUDP( $this->getIRCLine() );
+ }
}
/**
@@ -227,12 +235,12 @@ class RecentChange
}
/**
- * Remove newlines and carriage returns
+ * Remove newlines, carriage returns and decode html entites
* @param string $line
* @return string
*/
public static function cleanupForIRC( $text ) {
- return str_replace(array("\n", "\r"), array("", ""), $text);
+ return Sanitizer::decodeCharReferences( str_replace( array( "\n", "\r" ), array( "", "" ), $text ) );
}
/**
@@ -318,9 +326,7 @@ class RecentChange
{
if( !$ip ) {
$ip = wfGetIP();
- if( !$ip ) {
- $ip = '';
- }
+ if( !$ip ) $ip = '';
}
$rc = new RecentChange;
@@ -372,9 +378,7 @@ class RecentChange
{
if( !$ip ) {
$ip = wfGetIP();
- if( !$ip ) {
- $ip = '';
- }
+ if( !$ip ) $ip = '';
}
$rc = new RecentChange;
@@ -420,12 +424,9 @@ class RecentChange
public static function notifyMove( $timestamp, &$oldTitle, &$newTitle, &$user, $comment, $ip='', $overRedir = false )
{
global $wgRequest;
-
if( !$ip ) {
$ip = wfGetIP();
- if( !$ip ) {
- $ip = '';
- }
+ if( !$ip ) $ip = '';
}
$rc = new RecentChange;
@@ -473,16 +474,27 @@ class RecentChange
RecentChange::notifyMove( $timestamp, $oldTitle, $newTitle, $user, $comment, $ip, true );
}
- public static function notifyLog( $timestamp, &$title, &$user, $actionComment, $ip='',
- $type, $action, $target, $logComment, $params, $newId=0 )
+ public static function notifyLog( $timestamp, &$title, &$user, $actionComment, $ip='', $type,
+ $action, $target, $logComment, $params, $newId=0 )
{
- global $wgRequest;
+ global $wgLogRestrictions;
+ # Don't add private logs to RC!
+ if( isset($wgLogRestrictions[$type]) && $wgLogRestrictions[$type] != '*' ) {
+ return false;
+ }
+ $rc = self::newLogEntry( $timestamp, $title, $user, $actionComment, $ip, $type, $action,
+ $target, $logComment, $params, $newId );
+ $rc->save();
+ return true;
+ }
+ public static function newLogEntry( $timestamp, &$title, &$user, $actionComment, $ip='',
+ $type, $action, $target, $logComment, $params, $newId=0 )
+ {
+ global $wgRequest;
if( !$ip ) {
$ip = wfGetIP();
- if( !$ip ) {
- $ip = '';
- }
+ if( !$ip ) $ip = '';
}
$rc = new RecentChange;
@@ -518,7 +530,7 @@ class RecentChange
'lastTimestamp' => 0,
'actionComment' => $actionComment, // the comment appended to the action, passed from LogPage
);
- $rc->save();
+ return $rc;
}
# Initialises the members of this object from a mysql row object
@@ -589,7 +601,7 @@ class RecentChange
return $trail;
}
- protected function getIRCLine() {
+ public function getIRCLine() {
global $wgUseRCPatrol, $wgUseNPPatrol, $wgRC2UDPInterwikiPrefix, $wgLocalInterwiki;
// FIXME: Would be good to replace these 2 extract() calls with something more explicit
@@ -643,7 +655,11 @@ class RecentChange
$flag = $rc_log_action;
} else {
$comment = self::cleanupForIRC( $rc_comment );
- $flag = ($rc_new ? "N" : "") . ($rc_minor ? "M" : "") . ($rc_bot ? "B" : "");
+ $flag = '';
+ if( !$rc_patrolled && ($wgUseRCPatrol || $rc_new && $wgUseNPPatrol) ) {
+ $flag .= '!';
+ }
+ $flag .= ($rc_new ? "N" : "") . ($rc_minor ? "M" : "") . ($rc_bot ? "B" : "");
}
if ( $wgRC2UDPInterwikiPrefix === true ) {
diff --git a/includes/RefreshLinksJob.php b/includes/RefreshLinksJob.php
index 1c119a8d..91cff40b 100644
--- a/includes/RefreshLinksJob.php
+++ b/includes/RefreshLinksJob.php
@@ -82,35 +82,21 @@ class RefreshLinksJob2 extends Job {
wfProfileOut( __METHOD__ );
return false;
}
- $start = intval($this->params['start']);
- $end = intval($this->params['end']);
-
- $dbr = wfGetDB( DB_SLAVE );
- $res = $dbr->select( array( 'templatelinks', 'page' ),
- array( 'page_namespace', 'page_title' ),
- array(
- 'page_id=tl_from',
- "tl_from >= '$start'",
- "tl_from <= '$end'",
- 'tl_namespace' => $this->title->getNamespace(),
- 'tl_title' => $this->title->getDBkey()
- ), __METHOD__
- );
+ $titles = $this->title->getBacklinkCache()->getLinks(
+ 'templatelinks', $this->params['start'], $this->params['end']);
# Not suitable for page load triggered job running!
# Gracefully switch to refreshLinks jobs if this happens.
if( php_sapi_name() != 'cli' ) {
$jobs = array();
- while( $row = $dbr->fetchObject( $res ) ) {
- $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+ foreach ( $titles as $title ) {
$jobs[] = new RefreshLinksJob( $title, '' );
}
Job::batchInsert( $jobs );
return true;
}
# Re-parse each page that transcludes this page and update their tracking links...
- while( $row = $dbr->fetchObject( $res ) ) {
- $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+ foreach ( $titles as $title ) {
$revision = Revision::newFromTitle( $title );
if ( !$revision ) {
$this->error = 'refreshLinks: Article not found "' . $title->getPrefixedDBkey() . '"';
diff --git a/includes/Revision.php b/includes/Revision.php
index 7938d88a..8a2149c0 100644
--- a/includes/Revision.php
+++ b/includes/Revision.php
@@ -53,6 +53,10 @@ class Revision {
// Get the latest revision ID from the master
$dbw = wfGetDB( DB_MASTER );
$latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ );
+ if ( $latest === false ) {
+ // Page does not exist
+ return null;
+ }
$conds['rev_id'] = $latest;
} else {
// Use a join to get the latest revision
@@ -363,6 +367,7 @@ class Revision {
} else {
throw new MWException( 'Revision constructor passed invalid row format.' );
}
+ $this->mUnpatrolled = NULL;
}
/**#@+
@@ -536,6 +541,27 @@ class Revision {
public function isMinor() {
return (bool)$this->mMinorEdit;
}
+
+ /**
+ * @return int rcid of the unpatrolled row, zero if there isn't one
+ */
+ public function isUnpatrolled() {
+ if( $this->mUnpatrolled !== NULL ) {
+ return $this->mUnpatrolled;
+ }
+ $dbr = wfGetDB( DB_SLAVE );
+ $this->mUnpatrolled = $dbr->selectField( 'recentchanges',
+ 'rc_id',
+ array( // Add redundant user,timestamp condition so we can use the existing index
+ 'rc_user_text' => $this->getRawUserText(),
+ 'rc_timestamp' => $dbr->timestamp( $this->getTimestamp() ),
+ 'rc_this_oldid' => $this->getId(),
+ 'rc_patrolled' => 0
+ ),
+ __METHOD__
+ );
+ return (int)$this->mUnpatrolled;
+ }
/**
* int $field one of DELETED_* bitfield constants
@@ -819,7 +845,8 @@ class Revision {
'rev_timestamp' => $dbw->timestamp( $this->mTimestamp ),
'rev_deleted' => $this->mDeleted,
'rev_len' => $this->mSize,
- 'rev_parent_id' => $this->mParentId ? $this->mParentId : $this->getPreviousRevisionId( $dbw )
+ 'rev_parent_id' => is_null($this->mParentId) ?
+ $this->getPreviousRevisionId( $dbw ) : $this->mParentId
), __METHOD__
);
@@ -961,6 +988,10 @@ class Revision {
*/
static function getTimestampFromId( $title, $id ) {
$dbr = wfGetDB( DB_SLAVE );
+ // Casting fix for DB2
+ if ($id == '') {
+ $id = 0;
+ }
$conds = array( 'rev_id' => $id );
$conds['rev_page'] = $title->getArticleId();
$timestamp = $dbr->selectField( 'revision', 'rev_timestamp', $conds, __METHOD__ );
diff --git a/includes/SearchEngine.php b/includes/SearchEngine.php
index 3ea0341d..e5392f7c 100644
--- a/includes/SearchEngine.php
+++ b/includes/SearchEngine.php
@@ -80,8 +80,13 @@ class SearchEngine {
if (is_null($title))
return NULL;
- if ( $title->getNamespace() == NS_SPECIAL || $title->isExternal()
- || $title->exists() ) {
+ if ( $title->getNamespace() == NS_SPECIAL || $title->isExternal() || $title->exists() ) {
+ return $title;
+ }
+
+ # See if it still otherwise has content is some sane sense
+ $article = MediaWiki::articleFromTitle( $title );
+ if( $article->hasViewableContent() ) {
return $title;
}
@@ -403,7 +408,7 @@ class SearchEngine {
if($wgMWSuggestTemplate)
return $wgMWSuggestTemplate;
else
- return $wgServer . $wgScriptPath . '/api.php?action=opensearch&search={searchTerms}&namespace={namespaces}';
+ return $wgServer . $wgScriptPath . '/api.php?action=opensearch&search={searchTerms}&namespace={namespaces}&suggest';
}
}
@@ -1165,12 +1170,12 @@ class SearchHighlighter {
continue;
}
--$contextlines;
- $pre = $wgContLang->truncate( $m[1], -$contextchars, ' ... ' );
+ $pre = $wgContLang->truncate( $m[1], -$contextchars );
if ( count( $m ) < 3 ) {
$post = '';
} else {
- $post = $wgContLang->truncate( $m[3], $contextchars, ' ... ' );
+ $post = $wgContLang->truncate( $m[3], $contextchars );
}
$found = $m[2];
diff --git a/includes/SearchIBM_DB2.php b/includes/SearchIBM_DB2.php
new file mode 100644
index 00000000..57813a73
--- /dev/null
+++ b/includes/SearchIBM_DB2.php
@@ -0,0 +1,247 @@
+<?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
+
+/**
+ * @file
+ * @ingroup Search
+ */
+
+/**
+ * Search engine hook base class for IBM DB2
+ * @ingroup Search
+ */
+class SearchIBM_DB2 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 IBM_DB2SearchResultSet
+ * @access public
+ */
+ function searchText( $term ) {
+ $resultSet = $this->db->resultObject($this->db->query($this->getQuery($this->filter($term), true)));
+ return new IBM_DB2SearchResultSet($resultSet, $this->searchTerms);
+ }
+
+ /**
+ * Perform a title-only search query and return a result set.
+ *
+ * @param string $term - Raw search term
+ * @return IBM_DB2SearchResultSet
+ * @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() {
+ if( is_null($this->namespaces) )
+ return '';
+ $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) {
+ // requires Net Search Extender or equivalent
+ // return ' ORDER BY score(1)';
+ return '';
+ }
+
+ /**
+ * 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);
+
+ // requires Net Search Extender or equivalent
+ //return " CONTAINS($field, '$searchon') > 0 ";
+
+ return " lcase($field) LIKE lcase('%$searchon%')";
+ }
+
+ /**
+ * 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
+ ), 'SearchIBM_DB2::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),
+ 'SearchIBM_DB2::updateTitle',
+ array());
+ }
+}
+
+/**
+ * @ingroup Search
+ */
+class IBM_DB2SearchResultSet 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 4862a44e..fa9d8420 100644
--- a/includes/SearchPostgres.php
+++ b/includes/SearchPostgres.php
@@ -70,7 +70,7 @@ class SearchPostgres extends SearchEngine {
*/
function parseQuery( $term ) {
- wfDebug( "parseQuery received: $term" );
+ wfDebug( "parseQuery received: $term \n" );
## No backslashes allowed
$term = preg_replace('/\\\/', '', $term);
@@ -122,7 +122,7 @@ class SearchPostgres extends SearchEngine {
## Quote the whole thing
$searchstring = $this->db->addQuotes($searchstring);
- wfDebug( "parseQuery returned: $searchstring" );
+ wfDebug( "parseQuery returned: $searchstring \n" );
return $searchstring;
@@ -194,7 +194,7 @@ class SearchPostgres extends SearchEngine {
$query .= $this->db->limitResult( '', $this->limit, $this->offset );
- wfDebug( "searchQuery returned: $query" );
+ wfDebug( "searchQuery returned: $query \n" );
return $query;
}
diff --git a/includes/Setup.php b/includes/Setup.php
index 859ad008..d450dfdb 100644
--- a/includes/Setup.php
+++ b/includes/Setup.php
@@ -197,6 +197,10 @@ if($wgMetaNamespace === FALSE) {
# To determine the user language, use $wgLang->getCode()
$wgContLanguageCode = $wgLanguageCode;
+# Easy to forget to falsify $wgShowIPinHeader for static caches.
+# If file cache or squid cache is on, just disable this (DWIMD).
+if( $wgUseFileCache || $wgUseSquid ) $wgShowIPinHeader = false;
+
wfProfileOut( $fname.'-misc1' );
wfProfileIn( $fname.'-memcached' );
diff --git a/includes/SiteStats.php b/includes/SiteStats.php
index ab0caa7e..9427536f 100644
--- a/includes/SiteStats.php
+++ b/includes/SiteStats.php
@@ -222,7 +222,7 @@ class SiteStatsUpdate {
if ( $updates ) {
$site_stats = $dbw->tableName( 'site_stats' );
- $sql = $dbw->limitResultForUpdate("UPDATE $site_stats SET $updates", 1);
+ $sql = "UPDATE $site_stats SET $updates";
# Need a separate transaction because this a global lock
$dbw->begin();
@@ -240,7 +240,7 @@ class SiteStatsUpdate {
__METHOD__ );
$dbw->update( 'site_stats',
array( 'ss_active_users' => intval($activeUsers) ),
- array( 'ss_row_id' => 1 ), __METHOD__, array( 'LIMIT' => 1 )
+ array( 'ss_row_id' => 1 ), __METHOD__
);
}
}
diff --git a/includes/Skin.php b/includes/Skin.php
index 636b96bf..47285acc 100644
--- a/includes/Skin.php
+++ b/includes/Skin.php
@@ -401,7 +401,7 @@ class Skin extends Linker {
$vars['wgLivepreviewMessageError'] = wfMsg( 'livepreview-error' );
}
- if($wgUseAjax && $wgAjaxWatch && $wgUser->isLoggedIn() ) {
+ if ( $wgOut->isArticleRelated() && $wgUseAjax && $wgAjaxWatch && $wgUser->isLoggedIn() ) {
$msgs = (object)array();
foreach ( array( 'watch', 'unwatch', 'watching', 'unwatching' ) as $msgName ) {
$msgs->{$msgName . 'Msg'} = wfMsg( $msgName );
@@ -758,7 +758,7 @@ END;
if( count( $wgOut->mCategoryLinks ) == 0 ) return '';
# Separator
- $sep = wfMsgHtml( 'catseparator' );
+ $sep = wfMsgExt( 'catseparator', array( 'parsemag', 'escapenoentities' ) );
// Use Unicode bidi embedding override characters,
// to make sure links don't smash each other up in ugly ways.
@@ -890,12 +890,27 @@ END;
}
/**
+ * Generate debug data HTML for displaying at the bottom of the main content
+ * area.
+ * @return String HTML containing debug data, if enabled (otherwise empty).
+ */
+ protected function generateDebugHTML() {
+ global $wgShowDebug, $wgOut;
+ if ( $wgShowDebug ) {
+ $listInternals = str_replace( "\n", "</li>\n<li>", htmlspecialchars( $wgOut->mDebugtext ) );
+ return "\n<hr>\n<strong>Debug data:</strong><ul style=\"font-family:monospace;\"><li>" .
+ $listInternals . "</li></ul>\n";
+ }
+ return '';
+ }
+
+ /**
* This gets called shortly before the </body> tag.
* @return String HTML to be put before </body>
*/
function afterContent() {
$printfooter = "<div class=\"printfooter\">\n" . $this->printFooter() . "</div>\n";
- return $printfooter . $this->doAfterContent();
+ return $printfooter . $this->generateDebugHTML() . $this->doAfterContent();
}
/**
@@ -925,20 +940,20 @@ END;
function doAfterContent() { return "</div></div>"; }
function pageTitleLinks() {
- global $wgOut, $wgTitle, $wgUser, $wgRequest;
+ global $wgOut, $wgTitle, $wgUser, $wgRequest, $wgLang;
$oldid = $wgRequest->getVal( 'oldid' );
$diff = $wgRequest->getVal( 'diff' );
$action = $wgRequest->getText( 'action' );
- $s = $this->printableLink();
+ $s[] = $this->printableLink();
$disclaimer = $this->disclaimerLink(); # may be empty
if( $disclaimer ) {
- $s .= ' | ' . $disclaimer;
+ $s[] = $disclaimer;
}
$privacy = $this->privacyLink(); # may be empty too
if( $privacy ) {
- $s .= ' | ' . $privacy;
+ $s[] = $privacy;
}
if ( $wgOut->isArticleRelated() ) {
@@ -948,12 +963,12 @@ END;
if( $image ) {
$link = htmlspecialchars( $image->getURL() );
$style = $this->getInternalLinkAttributes( $link, $name );
- $s .= " | <a href=\"{$link}\"{$style}>{$name}</a>";
+ $s[] = "<a href=\"{$link}\"{$style}>{$name}</a>";
}
}
}
if ( 'history' == $action || isset( $diff ) || isset( $oldid ) ) {
- $s .= ' | ' . $this->makeKnownLinkObj( $wgTitle,
+ $s[] .= $this->makeKnownLinkObj( $wgTitle,
wfMsg( 'currentrev' ) );
}
@@ -963,7 +978,7 @@ END;
if( !$wgTitle->equals( $wgUser->getTalkPage() ) ) {
$tl = $this->makeKnownLinkObj( $wgUser->getTalkPage(), wfMsgHtml( 'newmessageslink' ), 'redirect=no' );
$dl = $this->makeKnownLinkObj( $wgUser->getTalkPage(), wfMsgHtml( 'newmessagesdifflink' ), 'diff=cur' );
- $s.= ' | <strong>'. wfMsg( 'youhavenewmessages', $tl, $dl ) . '</strong>';
+ $s[] = '<strong>'. wfMsg( 'youhavenewmessages', $tl, $dl ) . '</strong>';
# disable caching
$wgOut->setSquidMaxage(0);
$wgOut->enableClientCache(false);
@@ -972,9 +987,9 @@ END;
$undelete = $this->getUndeleteLink();
if( !empty( $undelete ) ) {
- $s .= ' | '.$undelete;
+ $s[] = $undelete;
}
- return $s;
+ return $wgLang->pipeList( $s );
}
function getUndeleteLink() {
@@ -997,18 +1012,19 @@ END;
}
function printableLink() {
- global $wgOut, $wgFeedClasses, $wgRequest;
+ global $wgOut, $wgFeedClasses, $wgRequest, $wgLang;
$printurl = $wgRequest->escapeAppendQuery( 'printable=yes' );
- $s = "<a href=\"$printurl\">" . wfMsg( 'printableversion' ) . '</a>';
+ $s[] = "<a href=\"$printurl\" rel=\"alternate\">" . wfMsg( 'printableversion' ) . '</a>';
if( $wgOut->isSyndicated() ) {
foreach( $wgFeedClasses as $format => $class ) {
$feedurl = $wgRequest->escapeAppendQuery( "feed=$format" );
- $s .= " | <a href=\"$feedurl\">{$format}</a>";
+ $s[] = "<a href=\"$feedurl\" rel=\"alternate\" type=\"application/{$format}+xml\""
+ . " class=\"feedlink\">" . wfMsgHtml( "feed-$format" ) . "</a>";
}
}
- return $s;
+ return $wgLang->pipeList( $s );
}
function pageTitle() {
@@ -1053,7 +1069,7 @@ END;
$getlink = $this->makeKnownLinkObj( $linkObj, htmlspecialchars( $display ) );
$c++;
if ($c>1) {
- $subpages .= ' | ';
+ $subpages .= wfMsgExt( 'pipe-separator' , 'escapenoentities' );
} else {
$subpages .= '&lt; ';
}
@@ -1116,16 +1132,21 @@ END;
$ret .= $this->link( $wgUser->getUserPage(),
htmlspecialchars( $wgUser->getName() ) );
$ret .= " ($talkLink)<br />";
- $ret .= $this->link(
- SpecialPage::getTitleFor( 'Userlogout' ), wfMsg( 'logout' ),
- array(), array( 'returnto' => $returnTo )
- );
- $ret .= ' | ' . $this->specialLink( 'preferences' );
- }
- $ret .= ' | ' . $this->link(
- Title::newFromText( wfMsgForContent( 'helppage' ) ),
- wfMsg( 'help' )
- );
+ $ret .= $wgLang->pipeList( array(
+ $this->link(
+ SpecialPage::getTitleFor( 'Userlogout' ), wfMsg( 'logout' ),
+ array(), array( 'returnto' => $returnTo )
+ ),
+ $this->specialLink( 'preferences' ),
+ ) );
+ }
+ $ret = $wgLang->pipeList( array(
+ $ret,
+ $this->link(
+ Title::newFromText( wfMsgForContent( 'helppage' ) ),
+ wfMsg( 'help' )
+ ),
+ ) );
return $ret;
}
@@ -1140,16 +1161,22 @@ END;
}
function searchForm() {
- global $wgRequest;
+ global $wgRequest, $wgUseTwoButtonsSearchForm;
$search = $wgRequest->getText( 'search' );
$s = '<form id="searchform'.$this->searchboxes.'" name="search" class="inline" method="post" action="'
. $this->escapeSearchLink() . "\">\n"
. '<input type="text" id="searchInput'.$this->searchboxes.'" name="search" size="19" value="'
. htmlspecialchars(substr($search,0,256)) . "\" />\n"
- . '<input type="submit" name="go" value="' . wfMsg ('searcharticle') . '" />&nbsp;'
- . '<input type="submit" name="fulltext" value="' . wfMsg ('searchbutton') . "\" />\n</form>";
-
+ . '<input type="submit" name="go" value="' . wfMsg ('searcharticle') . '" />';
+
+ if ($wgUseTwoButtonsSearchForm)
+ $s .= '&nbsp;<input type="submit" name="fulltext" value="' . wfMsg ('searchbutton') . "\" />\n";
+ else
+ $s .= ' <a href="' . $this->escapeSearchLink() . '" rel="search">' . wfMsg ('powersearch-legend') . "</a>\n";
+
+ $s .= '</form>';
+
// Ensure unique id's for search boxes made after the first
$this->searchboxes = $this->searchboxes == '' ? 2 : $this->searchboxes + 1;
@@ -1158,23 +1185,29 @@ END;
function topLinks() {
global $wgOut;
- $sep = " |\n";
- $s = $this->mainPageLink() . $sep
- . $this->specialLink( 'recentchanges' );
+ $s = array(
+ $this->mainPageLink(),
+ $this->specialLink( 'recentchanges' )
+ );
if ( $wgOut->isArticleRelated() ) {
- $s .= $sep . $this->editThisPage()
- . $sep . $this->historyLink();
+ $s[] = $this->editThisPage();
+ $s[] = $this->historyLink();
}
# Many people don't like this dropdown box
- #$s .= $sep . $this->specialPagesList();
+ #$s[] = $this->specialPagesList();
- $s .= $this->variantLinks();
+ if( $this->variantLinks() ) {
+ $s[] = $this->variantLinks();
+ }
- $s .= $this->extensionTabLinks();
+ if( $this->extensionTabLinks() ) {
+ $s[] = $this->extensionTabLinks();
+ }
- return $s;
+ // FIXME: Is using Language::pipeList impossible here? Do not quite understand the use of the newline
+ return implode( $s, wfMsgExt( 'pipe-separator' , 'escapenoentities' ) . "\n" );
}
/**
@@ -1185,14 +1218,23 @@ END;
*/
function extensionTabLinks() {
$tabs = array();
- $s = '';
+ $out = '';
+ $s = array();
wfRunHooks( 'SkinTemplateTabs', array( $this, &$tabs ) );
foreach( $tabs as $tab ) {
- $s .= ' | ' . Xml::element( 'a',
+ $s[] = Xml::element( 'a',
array( 'href' => $tab['href'] ),
$tab['text'] );
}
- return $s;
+
+ if( count( $s ) ) {
+ global $wgLang;
+
+ $out = wfMsgExt( 'pipe-separator' , 'escapenoentities' );
+ $out .= $wgLang->pipeList( $s );
+ }
+
+ return $out;
}
/**
@@ -1202,14 +1244,14 @@ END;
function variantLinks() {
$s = '';
/* show links to different language variants */
- global $wgDisableLangConversion, $wgContLang, $wgTitle;
+ global $wgDisableLangConversion, $wgLang, $wgContLang, $wgTitle;
$variants = $wgContLang->getVariants();
if( !$wgDisableLangConversion && sizeof( $variants ) > 1 ) {
foreach( $variants as $code ) {
$varname = $wgContLang->getVariantname( $code );
if( $varname == 'disable' )
continue;
- $s .= ' | <a href="' . $wgTitle->escapeLocalUrl( 'variant=' . $code ) . '">' . htmlspecialchars( $varname ) . '</a>';
+ $s = $wgLang->pipeList( array( $s, '<a href="' . $wgTitle->escapeLocalUrl( 'variant=' . $code ) . '">' . htmlspecialchars( $varname ) . '</a>' ) );
}
}
return $s;
@@ -1217,21 +1259,21 @@ END;
function bottomLinks() {
global $wgOut, $wgUser, $wgTitle, $wgUseTrackbacks;
- $sep = " |\n";
+ $sep = wfMsgExt( 'pipe-separator' , 'escapenoentities' ) . "\n";
$s = '';
if ( $wgOut->isArticleRelated() ) {
- $s .= '<strong>' . $this->editThisPage() . '</strong>';
+ $element[] = '<strong>' . $this->editThisPage() . '</strong>';
if ( $wgUser->isLoggedIn() ) {
- $s .= $sep . $this->watchThisPage();
+ $element[] = $this->watchThisPage();
}
- $s .= $sep . $this->talkLink()
- . $sep . $this->historyLink()
- . $sep . $this->whatLinksHere()
- . $sep . $this->watchPageLinksLink();
+ $element[] = $this->talkLink();
+ $element[] = $this->historyLink();
+ $element[] = $this->whatLinksHere();
+ $element[] = $this->watchPageLinksLink();
if ($wgUseTrackbacks)
- $s .= $sep . $this->trackbackLink();
+ $element[] = $this->trackbackLink();
if ( $wgTitle->getNamespace() == NS_USER
|| $wgTitle->getNamespace() == NS_USER_TALK )
@@ -1241,12 +1283,15 @@ END;
$ip=User::isIP($wgTitle->getText());
if($id || $ip) { # both anons and non-anons have contri list
- $s .= $sep . $this->userContribsLink();
+ $element[] = $this->userContribsLink();
}
if( $this->showEmailUser( $id ) ) {
- $s .= $sep . $this->emailUserLink();
+ $element[] = $this->emailUserLink();
}
}
+
+ $s = implode( $element, $sep );
+
if ( $wgTitle->getArticleId() ) {
$s .= "\n<br />";
if($wgUser->isAllowed('delete')) { $s .= $this->deleteThisPage(); }
@@ -1255,6 +1300,7 @@ END;
}
$s .= "<br />\n" . $this->otherLanguages();
}
+
return $s;
}
@@ -1566,8 +1612,8 @@ END;
function historyLink() {
global $wgTitle;
- return $this->makeKnownLinkObj( $wgTitle,
- wfMsg( 'history' ), 'action=history' );
+ return $this->link( $wgTitle, wfMsg( 'history' ),
+ array( 'rel' => 'archives' ), array( 'action' => 'history' ) );
}
function whatLinksHere() {
@@ -1632,11 +1678,11 @@ END;
return '';
}
- $s = wfMsg( 'otherlanguages' ) . ': ';
+ $s = wfMsg( 'otherlanguages' ) . wfMsg( 'colon-separator' );
$first = true;
if($wgContLang->isRTL()) $s .= '<span dir="LTR">';
foreach( $a as $l ) {
- if ( ! $first ) { $s .= ' | '; }
+ if ( ! $first ) { $s .= wfMsgExt( 'pipe-separator' , 'escapenoentities' ); }
$first = false;
$nt = Title::newFromText( $l );
diff --git a/includes/SkinTemplate.php b/includes/SkinTemplate.php
index 4f13571a..4317a93e 100644
--- a/includes/SkinTemplate.php
+++ b/includes/SkinTemplate.php
@@ -138,7 +138,7 @@ class SkinTemplate extends Skin {
global $wgScript, $wgStylePath, $wgContLanguageCode;
global $wgMimeType, $wgJsMimeType, $wgOutputEncoding, $wgRequest;
global $wgXhtmlDefaultNamespace, $wgXhtmlNamespaces;
- global $wgDisableCounters, $wgLogo, $action, $wgFeedClasses, $wgHideInterlanguageLinks;
+ global $wgDisableCounters, $wgLogo, $wgHideInterlanguageLinks;
global $wgMaxCredits, $wgShowCreditsIfMax;
global $wgPageShowWatchingUsers;
global $wgUseTrackbacks, $wgUseSiteJs;
@@ -148,6 +148,7 @@ class SkinTemplate extends Skin {
$oldid = $wgRequest->getVal( 'oldid' );
$diff = $wgRequest->getVal( 'diff' );
+ $action = $wgRequest->getVal( 'action', 'view' );
wfProfileIn( __METHOD__."-init" );
$this->initPage( $out );
@@ -258,6 +259,7 @@ class SkinTemplate extends Skin {
$tpl->set( "helppage", wfMsg('helppage'));
*/
$tpl->set( 'searchaction', $this->escapeSearchLink() );
+ $tpl->set( 'searchtitle', SpecialPage::getTitleFor('search')->getPrefixedDBKey() );
$tpl->set( 'search', trim( $wgRequest->getVal( 'search' ) ) );
$tpl->setRef( 'stylepath', $wgStylePath );
$tpl->setRef( 'articlepath', $wgArticlePath );
@@ -401,7 +403,7 @@ class SkinTemplate extends Skin {
$tpl->set( 'bottomscripts', $this->bottomScripts() );
$printfooter = "<div class=\"printfooter\">\n" . $this->printSource() . "</div>\n";
- $out->mBodytext .= $printfooter ;
+ $out->mBodytext .= $printfooter . $this->generateDebugHTML();
$tpl->setRef( 'bodytext', $out->mBodytext );
# Language links
@@ -449,7 +451,7 @@ class SkinTemplate extends Skin {
// original version by hansm
if( !wfRunHooks( 'SkinTemplateOutputPageBeforeExec', array( &$this, &$tpl ) ) ) {
- wfDebug( __METHOD__ . ': Hook SkinTemplateOutputPageBeforeExec broke outputPage execution!' );
+ wfDebug( __METHOD__ . ": Hook SkinTemplateOutputPageBeforeExec broke outputPage execution!\n" );
}
// allow extensions adding stuff after the page content.
@@ -647,12 +649,12 @@ class SkinTemplate extends Skin {
* @private
*/
function buildContentActionUrls() {
- global $wgContLang, $wgLang, $wgOut;
+ global $wgContLang, $wgLang, $wgOut, $wgUser, $wgRequest;
+
wfProfileIn( __METHOD__ );
- global $wgUser, $wgRequest;
- $action = $wgRequest->getText( 'action' );
- $section = $wgRequest->getText( 'section' );
+ $action = $wgRequest->getVal( 'action', 'view' );
+ $section = $wgRequest->getVal( 'section' );
$content_actions = array();
$prevent_active_tabs = false ;
@@ -689,11 +691,13 @@ class SkinTemplate extends Skin {
);
if ( $istalk || $wgOut->showNewSectionLink() ) {
- $content_actions['addsection'] = array(
- 'class' => $section == 'new'?'selected':false,
- 'text' => wfMsg('addsection'),
- 'href' => $this->mTitle->getLocalUrl( 'action=edit&section=new' )
- );
+ if ( !$wgOut->forceHideNewSectionLink() ) {
+ $content_actions['addsection'] = array(
+ 'class' => $section == 'new' ? 'selected' : false,
+ 'text' => wfMsg('addsection'),
+ 'href' => $this->mTitle->getLocalUrl( 'action=edit&section=new' )
+ );
+ }
}
} elseif ( $this->mTitle->isKnown() ) {
$content_actions['viewsource'] = array(
@@ -710,7 +714,8 @@ class SkinTemplate extends Skin {
$content_actions['history'] = array(
'class' => ($action == 'history') ? 'selected' : false,
'text' => wfMsg('history_short'),
- 'href' => $this->mTitle->getLocalUrl( 'action=history')
+ 'href' => $this->mTitle->getLocalUrl( 'action=history' ),
+ 'rel' => 'archives',
);
if( $wgUser->isAllowed('delete') ) {
@@ -848,7 +853,7 @@ class SkinTemplate extends Skin {
wfProfileIn( __METHOD__ );
- $action = $wgRequest->getText( 'action' );
+ $action = $wgRequest->getVal( 'action', 'view' );
$nav_urls = array();
$nav_urls['mainpage'] = array( 'href' => self::makeMainPageUrl() );
@@ -871,7 +876,7 @@ class SkinTemplate extends Skin {
// 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' ) ) {
+ if( $this->iscontent && ( $action == 'view' || $action == 'purge' ) ) {
$nav_urls['print'] = array(
'text' => wfMsg( 'printableversion' ),
'href' => $wgRequest->appendQuery( 'printable=yes' )
@@ -965,10 +970,11 @@ class SkinTemplate extends Skin {
* @private
*/
function setupUserJs( $allowUserJs ) {
+ global $wgRequest, $wgJsMimeType;
+
wfProfileIn( __METHOD__ );
- global $wgRequest, $wgJsMimeType;
- $action = $wgRequest->getText('action');
+ $action = $wgRequest->getVal( 'action', 'view' );
if( $allowUserJs && $this->loggedin ) {
if( $this->mTitle->isJsSubpage() and $this->userCanPreview( $action ) ) {
diff --git a/includes/SpecialPage.php b/includes/SpecialPage.php
index 00eacd1e..31b43839 100644
--- a/includes/SpecialPage.php
+++ b/includes/SpecialPage.php
@@ -80,92 +80,112 @@ class SpecialPage
** array( 'SpecialRedirectToSpecial', name, page to redirect to, special page param, ... )
*/
static public $mList = array(
- 'DoubleRedirects' => array( 'SpecialPage', 'DoubleRedirects' ),
+ # Maintenance Reports
'BrokenRedirects' => array( 'SpecialPage', 'BrokenRedirects' ),
+ 'Deadendpages' => array( 'SpecialPage', 'Deadendpages' ),
+ 'DoubleRedirects' => array( 'SpecialPage', 'DoubleRedirects' ),
+ 'Longpages' => array( 'SpecialPage', 'Longpages' ),
+ 'Ancientpages' => array( 'SpecialPage', 'Ancientpages' ),
+ 'Lonelypages' => array( 'SpecialPage', 'Lonelypages' ),
+ 'Fewestrevisions' => array( 'SpecialPage', 'Fewestrevisions' ),
+ 'Withoutinterwiki' => array( 'SpecialPage', 'Withoutinterwiki' ),
+ 'Protectedpages' => array( 'SpecialPage', 'Protectedpages' ),
+ 'Protectedtitles' => array( 'SpecialPage', 'Protectedtitles' ),
+ 'Shortpages' => array( 'SpecialPage', 'Shortpages' ),
+ 'Uncategorizedcategories' => array( 'SpecialPage', 'Uncategorizedcategories' ),
+ 'Uncategorizedimages' => array( 'SpecialPage', 'Uncategorizedimages' ),
+ 'Uncategorizedpages' => array( 'SpecialPage', 'Uncategorizedpages' ),
+ 'Uncategorizedtemplates' => array( 'SpecialPage', 'Uncategorizedtemplates' ),
+ 'Unusedcategories' => array( 'SpecialPage', 'Unusedcategories' ),
+ 'Unusedimages' => array( 'SpecialPage', 'Unusedimages' ),
+ 'Unusedtemplates' => array( 'SpecialPage', 'Unusedtemplates' ),
+ 'Unwatchedpages' => array( 'SpecialPage', 'Unwatchedpages', 'unwatchedpages' ),
+ 'Wantedcategories' => array( 'SpecialPage', 'Wantedcategories' ),
+ 'Wantedfiles' => array( 'SpecialPage', 'Wantedfiles' ),
+ 'Wantedpages' => array( 'IncludableSpecialPage', 'Wantedpages' ),
+ 'Wantedtemplates' => array( 'SpecialPage', 'Wantedtemplates' ),
+
+ # List of pages
+ 'Allpages' => 'SpecialAllpages',
+ 'Prefixindex' => 'SpecialPrefixindex',
+ 'Categories' => array( 'SpecialPage', 'Categories' ),
'Disambiguations' => array( 'SpecialPage', 'Disambiguations' ),
+ 'Listredirects' => array( 'SpecialPage', 'Listredirects' ),
- 'Userlogin' => array( 'SpecialPage', 'Userlogin' ),
- 'Userlogout' => array( 'UnlistedSpecialPage', 'Userlogout' ),
+ # Login/create account
+ 'Userlogin' => array( 'SpecialPage', 'Userlogin' ),
'CreateAccount' => array( 'SpecialRedirectToSpecial', 'CreateAccount', 'Userlogin', 'signup', array( 'uselang' ) ),
- 'Preferences' => array( 'SpecialPage', 'Preferences' ),
- 'Watchlist' => array( 'SpecialPage', 'Watchlist' ),
- 'Resetpass' => 'SpecialResetpass',
+ # Users and rights
+ 'Blockip' => array( 'SpecialPage', 'Blockip', 'block' ),
+ 'Ipblocklist' => array( 'SpecialPage', 'Ipblocklist' ),
+ 'Resetpass' => 'SpecialResetpass',
+ 'DeletedContributions' => 'DeletedContributionsPage',
+ 'Preferences' => array( 'SpecialPage', 'Preferences' ),
+ 'Contributions' => 'SpecialContributions',
+ 'Listgrouprights' => 'SpecialListGroupRights',
+ 'Listusers' => array( 'SpecialPage', 'Listusers' ),
+ 'Userrights' => 'UserrightsPage',
+ # Recent changes and logs
+ 'Newimages' => array( 'IncludableSpecialPage', 'Newimages' ),
+ 'Log' => array( 'SpecialPage', 'Log' ),
+ 'Watchlist' => array( 'SpecialPage', 'Watchlist' ),
+ 'Newpages' => 'SpecialNewpages',
'Recentchanges' => 'SpecialRecentchanges',
- 'Upload' => array( 'SpecialPage', 'Upload' ),
+ 'Recentchangeslinked' => 'SpecialRecentchangeslinked',
+ 'Tags' => 'SpecialTags',
+
+ # Media reports and uploads
'Listfiles' => array( 'SpecialPage', 'Listfiles' ),
- 'Newimages' => array( 'IncludableSpecialPage', 'Newimages' ),
- 'Listusers' => array( 'SpecialPage', 'Listusers' ),
- 'Listgrouprights' => 'SpecialListGroupRights',
- 'DeletedContributions' => 'DeletedContributionsPage',
+ 'Filepath' => array( 'SpecialPage', 'Filepath' ),
+ 'MIMEsearch' => array( 'SpecialPage', 'MIMEsearch' ),
+ 'FileDuplicateSearch' => array( 'SpecialPage', 'FileDuplicateSearch' ),
+ 'Upload' => array( 'SpecialPage', 'Upload' ),
+
+ # Wiki data and tools
'Statistics' => 'SpecialStatistics',
+ 'Allmessages' => array( 'SpecialPage', 'Allmessages' ),
+ 'Version' => 'SpecialVersion',
+ 'Lockdb' => array( 'SpecialPage', 'Lockdb', 'siteadmin' ),
+ 'Unlockdb' => array( 'SpecialPage', 'Unlockdb', 'siteadmin' ),
+
+ # Redirecting special pages
+ 'LinkSearch' => array( 'SpecialPage', 'LinkSearch' ),
'Randompage' => 'Randompage',
- 'Lonelypages' => array( 'SpecialPage', 'Lonelypages' ),
- 'Uncategorizedpages' => array( 'SpecialPage', 'Uncategorizedpages' ),
- 'Uncategorizedcategories' => array( 'SpecialPage', 'Uncategorizedcategories' ),
- 'Uncategorizedimages' => array( 'SpecialPage', 'Uncategorizedimages' ),
- 'Uncategorizedtemplates' => array( 'SpecialPage', 'Uncategorizedtemplates' ),
- 'Unusedcategories' => array( 'SpecialPage', 'Unusedcategories' ),
- 'Unusedimages' => array( 'SpecialPage', 'Unusedimages' ),
- 'Wantedpages' => array( 'IncludableSpecialPage', 'Wantedpages' ),
- 'Wantedcategories' => array( 'SpecialPage', 'Wantedcategories' ),
- 'Wantedfiles' => array( 'SpecialPage', 'Wantedfiles' ),
- 'Wantedtemplates' => array( 'SpecialPage', 'Wantedtemplates' ),
- 'Mostlinked' => array( 'SpecialPage', 'Mostlinked' ),
+ 'Randomredirect' => 'SpecialRandomredirect',
+
+ # High use pages
'Mostlinkedcategories' => array( 'SpecialPage', 'Mostlinkedcategories' ),
+ 'Mostimages' => array( 'SpecialPage', 'Mostimages' ),
+ 'Mostlinked' => array( 'SpecialPage', 'Mostlinked' ),
'Mostlinkedtemplates' => array( 'SpecialPage', 'Mostlinkedtemplates' ),
'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' => 'SpecialNewpages',
- 'Ancientpages' => array( 'SpecialPage', 'Ancientpages' ),
- 'Deadendpages' => array( 'SpecialPage', 'Deadendpages' ),
- 'Protectedpages' => array( 'SpecialPage', 'Protectedpages' ),
- 'Protectedtitles' => array( 'SpecialPage', 'Protectedtitles' ),
- 'Allpages' => 'SpecialAllpages',
- 'Prefixindex' => 'SpecialPrefixindex',
- 'Ipblocklist' => array( 'SpecialPage', 'Ipblocklist' ),
- 'Specialpages' => array( 'UnlistedSpecialPage', 'Specialpages' ),
- 'Contributions' => 'SpecialContributions',
- 'Emailuser' => array( 'UnlistedSpecialPage', 'Emailuser' ),
+
+ # Page tools
+ 'Export' => 'SpecialExport',
+ 'Import' => 'SpecialImport',
+ 'Undelete' => array( 'SpecialPage', 'Undelete', 'deletedhistory' ),
'Whatlinkshere' => array( 'SpecialPage', 'Whatlinkshere' ),
- 'LinkSearch' => array( 'SpecialPage', 'LinkSearch' ),
- 'Recentchangeslinked' => 'SpecialRecentchangeslinked',
- 'Movepage' => array( 'UnlistedSpecialPage', 'Movepage' ),
- 'Blockme' => array( 'UnlistedSpecialPage', 'Blockme' ),
+ 'MergeHistory' => array( 'SpecialPage', 'MergeHistory', 'mergehistory' ),
+
+ # Other
'Booksources' => 'SpecialBookSources',
- 'Categories' => array( 'SpecialPage', 'Categories' ),
- 'Export' => array( 'SpecialPage', 'Export' ),
- 'Version' => 'SpecialVersion',
+
+ # Unlisted / redirects
'Blankpage' => array( 'UnlistedSpecialPage', 'Blankpage' ),
- 'Allmessages' => array( 'SpecialPage', 'Allmessages' ),
- 'Log' => array( 'SpecialPage', 'Log' ),
- 'Blockip' => array( 'SpecialPage', 'Blockip', 'block' ),
- 'Undelete' => array( 'SpecialPage', 'Undelete', 'deletedhistory' ),
- 'Import' => 'SpecialImport',
- 'Lockdb' => array( 'SpecialPage', 'Lockdb', 'siteadmin' ),
- 'Unlockdb' => array( 'SpecialPage', 'Unlockdb', 'siteadmin' ),
- 'Userrights' => 'UserrightsPage',
- 'MIMEsearch' => array( 'SpecialPage', 'MIMEsearch' ),
- 'FileDuplicateSearch' => array( 'SpecialPage', 'FileDuplicateSearch' ),
- 'Unwatchedpages' => array( 'SpecialPage', 'Unwatchedpages', 'unwatchedpages' ),
- 'Listredirects' => array( 'SpecialPage', 'Listredirects' ),
- 'Revisiondelete' => array( 'UnlistedSpecialPage', 'Revisiondelete', 'deleterevision' ),
- 'Unusedtemplates' => array( 'SpecialPage', 'Unusedtemplates' ),
- 'Randomredirect' => 'SpecialRandomredirect',
- 'Withoutinterwiki' => array( 'SpecialPage', 'Withoutinterwiki' ),
- 'Filepath' => array( 'SpecialPage', 'Filepath' ),
-
- 'Mypage' => array( 'SpecialMypage' ),
- 'Mytalk' => array( 'SpecialMytalk' ),
- 'Mycontributions' => array( 'SpecialMycontributions' ),
+ 'Blockme' => array( 'UnlistedSpecialPage', 'Blockme' ),
+ 'Emailuser' => array( 'UnlistedSpecialPage', 'Emailuser' ),
'Listadmins' => array( 'SpecialRedirectToSpecial', 'Listadmins', 'Listusers', 'sysop' ),
- 'MergeHistory' => array( 'SpecialPage', 'MergeHistory', 'mergehistory' ),
'Listbots' => array( 'SpecialRedirectToSpecial', 'Listbots', 'Listusers', 'bot' ),
+ 'Movepage' => array( 'UnlistedSpecialPage', 'Movepage' ),
+ 'Mycontributions' => array( 'SpecialMycontributions' ),
+ 'Mypage' => array( 'SpecialMypage' ),
+ 'Mytalk' => array( 'SpecialMytalk' ),
+ 'Revisiondelete' => 'SpecialRevisionDelete',
+ 'Specialpages' => array( 'UnlistedSpecialPage', 'Specialpages' ),
+ 'Userlogout' => array( 'UnlistedSpecialPage', 'Userlogout' ),
);
static public $mAliases;
@@ -695,7 +715,9 @@ class SpecialPage
* pages?
*/
public function isRestricted() {
- return $this->mRestriction != '';
+ global $wgGroupPermissions;
+ // DWIM: If all anons can do something, then it is not restricted
+ return $this->mRestriction != '' && empty($wgGroupPermissions['*'][$this->mRestriction]);
}
/**
@@ -752,13 +774,25 @@ class SpecialPage
}
}
- function outputHeader() {
+ /**
+ * Outputs a summary message on top of special pages
+ * Per default the message key is the canonical name of the special page
+ * May be overriden, i.e. by extensions to stick with the naming conventions
+ * for message keys: 'extensionname-xxx'
+ *
+ * @param string message key of the summary
+ */
+ function outputHeader( $summaryMessageKey = '' ) {
global $wgOut, $wgContLang;
- $msg = $wgContLang->lc( $this->name() ) . '-summary';
+ if( $summaryMessageKey == '' ) {
+ $msg = $wgContLang->lc( $this->name() ) . '-summary';
+ } else {
+ $msg = $summaryMessageKey;
+ }
$out = wfMsgNoTrans( $msg );
if ( ! wfEmptyMsg( $msg, $out ) and $out !== '' and ! $this->including() ) {
- $wgOut->addWikiMsg( $msg );
+ $wgOut->wrapWikiMsg( "<div class='mw-specialpage-summary'>\n$1</div>", $msg );
}
}
diff --git a/includes/SquidUpdate.php b/includes/SquidUpdate.php
index c8497a83..b1f01924 100644
--- a/includes/SquidUpdate.php
+++ b/includes/SquidUpdate.php
@@ -52,13 +52,17 @@ class SquidUpdate {
return new SquidUpdate( $blurlArr );
}
- static function newFromTitles( &$titles, $urlArr = array() ) {
+ /**
+ * Create a SquidUpdate from an array of Title objects, or a TitleArray object
+ */
+ static function newFromTitles( $titles, $urlArr = array() ) {
global $wgMaxSquidPurgeTitles;
- if ( count( $titles ) > $wgMaxSquidPurgeTitles ) {
- $titles = array_slice( $titles, 0, $wgMaxSquidPurgeTitles );
- }
+ $i = 0;
foreach ( $titles as $title ) {
$urlArr[] = $title->getInternalURL();
+ if ( $i++ > $wgMaxSquidPurgeTitles ) {
+ break;
+ }
}
return new SquidUpdate( $urlArr );
}
diff --git a/includes/StreamFile.php b/includes/StreamFile.php
index 4abd7364..bdd2a2e5 100644
--- a/includes/StreamFile.php
+++ b/includes/StreamFile.php
@@ -48,6 +48,7 @@ function wfStreamFile( $fname, $headers = array() ) {
$modsince = preg_replace( '/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE'] );
$sinceTime = strtotime( $modsince );
if ( $stat['mtime'] <= $sinceTime ) {
+ ini_set('zlib.output_compression', 0);
header( "HTTP/1.0 304 Not Modified" );
return;
}
diff --git a/includes/StubObject.php b/includes/StubObject.php
index e27f0b25..f1847a39 100644
--- a/includes/StubObject.php
+++ b/includes/StubObject.php
@@ -154,7 +154,7 @@ class StubUserLang extends StubObject {
}
# Validate $code
- if( empty( $code ) || !preg_match( '/^[a-z-]+$/', $code ) ) {
+ if( empty( $code ) || !preg_match( '/^[a-z-]+$/', $code ) || ( $code === 'qqq' ) ) {
wfDebug( "Invalid user language code\n" );
$code = $wgContLanguageCode;
}
diff --git a/includes/Title.php b/includes/Title.php
index 515a3b65..f6c0d5de 100644
--- a/includes/Title.php
+++ b/includes/Title.php
@@ -69,6 +69,7 @@ class Title {
var $mLength = -1; ///< The page length, 0 for special pages
var $mRedirect = null; ///< Is the article at this title a redirect?
var $mNotificationTimestamp = array(); ///< Associative array of user ID -> timestamp/false
+ var $mBacklinkCache = null; ///< Cache of links to this title
//@}
@@ -294,11 +295,77 @@ class Title {
/**
* Extract a redirect destination from a string and return the
* Title, or null if the text doesn't contain a valid redirect
+ * This will only return the very next target, useful for
+ * the redirect table and other checks that don't need full recursion
*
- * @param $text \type{String} Text with possible redirect
+ * @param $text \type{\string} Text with possible redirect
* @return \type{Title} The corresponding Title
*/
public static function newFromRedirect( $text ) {
+ return self::newFromRedirectInternal( $text );
+ }
+
+ /**
+ * Extract a redirect destination from a string and return the
+ * Title, or null if the text doesn't contain a valid redirect
+ * This will recurse down $wgMaxRedirects times or until a non-redirect target is hit
+ * in order to provide (hopefully) the Title of the final destination instead of another redirect
+ *
+ * @param $text \type{\string} Text with possible redirect
+ * @return \type{Title} The corresponding Title
+ */
+ public static function newFromRedirectRecurse( $text ) {
+ $titles = self::newFromRedirectArray( $text );
+ return $titles ? array_pop( $titles ) : null;
+ }
+
+ /**
+ * Extract a redirect destination from a string and return an
+ * array of Titles, or null if the text doesn't contain a valid redirect
+ * The last element in the array is the final destination after all redirects
+ * have been resolved (up to $wgMaxRedirects times)
+ *
+ * @param $text \type{\string} Text with possible redirect
+ * @return \type{\array} Array of Titles, with the destination last
+ */
+ public static function newFromRedirectArray( $text ) {
+ global $wgMaxRedirects;
+ // are redirects disabled?
+ if( $wgMaxRedirects < 1 )
+ return null;
+ $title = self::newFromRedirectInternal( $text );
+ if( is_null( $title ) )
+ return null;
+ // recursive check to follow double redirects
+ $recurse = $wgMaxRedirects;
+ $titles = array( $title );
+ while( --$recurse > 0 ) {
+ if( $title->isRedirect() ) {
+ $article = new Article( $title, 0 );
+ $newtitle = $article->getRedirectTarget();
+ } else {
+ break;
+ }
+ // Redirects to some special pages are not permitted
+ if( $newtitle instanceOf Title && $newtitle->isValidRedirectTarget() ) {
+ // the new title passes the checks, so make that our current title so that further recursion can be checked
+ $title = $newtitle;
+ $titles[] = $newtitle;
+ } else {
+ break;
+ }
+ }
+ return $titles;
+ }
+
+ /**
+ * Really extract the redirect destination
+ * Do not call this function directly, use one of the newFromRedirect* functions above
+ *
+ * @param $text \type{\string} Text with possible redirect
+ * @return \type{Title} The corresponding Title
+ */
+ protected static function newFromRedirectInternal( $text ) {
$redir = MagicWord::get( 'redirect' );
$text = trim($text);
if( $redir->matchStartAndRemove( $text ) ) {
@@ -316,13 +383,11 @@ class Title {
$m[1] = urldecode( ltrim( $m[1], ':' ) );
}
$title = Title::newFromText( $m[1] );
- // Redirects to some special pages are not permitted
- if( $title instanceof Title
- && !$title->isSpecial( 'Userlogout' )
- && !$title->isSpecial( 'Filepath' ) )
- {
- return $title;
+ // If the title is a redirect to bad special pages or is invalid, return null
+ if( !$title instanceof Title || !$title->isValidRedirectTarget() ) {
+ return null;
}
+ return $title;
}
}
return null;
@@ -802,19 +867,21 @@ class Title {
* @return \type{\string} the URL
*/
public function getLinkUrl( $query = array(), $variant = false ) {
+ wfProfileIn( __METHOD__ );
if( !is_array( $query ) ) {
+ wfProfileOut( __METHOD__ );
throw new MWException( 'Title::getLinkUrl passed a non-array for '.
'$query' );
}
if( $this->isExternal() ) {
- return $this->getFullURL( $query );
- } elseif( $this->getPrefixedText() === ''
- and $this->getFragment() !== '' ) {
- return $this->getFragmentForURL();
+ $ret = $this->getFullURL( $query );
+ } elseif( $this->getPrefixedText() === '' && $this->getFragment() !== '' ) {
+ $ret = $this->getFragmentForURL();
} else {
- return $this->getLocalURL( $query, $variant )
- . $this->getFragmentForURL();
+ $ret = $this->getLocalURL( $query, $variant ) . $this->getFragmentForURL();
}
+ wfProfileOut( __METHOD__ );
+ return $ret;
}
/**
@@ -992,7 +1059,7 @@ class Title {
*/
public function userCan( $action, $doExpensiveQueries = true ) {
global $wgUser;
- return ( $this->getUserPermissionsErrorsInternal( $action, $wgUser, $doExpensiveQueries ) === array());
+ return ($this->getUserPermissionsErrorsInternal( $action, $wgUser, $doExpensiveQueries, true ) === array());
}
/**
@@ -1024,7 +1091,7 @@ class Title {
}
// Edit blocks should not affect reading. Account creation blocks handled at userlogin.
- if ( $user->isBlockedFrom( $this ) && $action != 'read' && $action != 'createaccount' ) {
+ if ( $action != 'read' && $action != 'createaccount' && $user->isBlockedFrom( $this ) ) {
$block = $user->mBlock;
// This is from OutputPage::blockedPage
@@ -1094,19 +1161,73 @@ class Title {
* @param $action \type{\string} action that permission needs to be checked for
* @param $user \type{User} user to check
* @param $doExpensiveQueries \type{\bool} Set this to false to avoid doing unnecessary queries.
+ * @param $short \type{\bool} Set this to true to stop after the first permission error.
* @return \type{\array} Array of arrays of the arguments to wfMsg to explain permissions problems.
*/
- private function getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries = true ) {
+ private function getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries=true, $short=false ) {
wfProfileIn( __METHOD__ );
$errors = array();
+ // First stop is permissions checks, which fail most often, and which are easiest to test.
+ if ( $action == 'move' ) {
+ if( !$user->isAllowed( 'move-rootuserpages' )
+ && $this->getNamespace() == NS_USER && !$this->isSubpage() )
+ {
+ // Show user page-specific message only if the user can move other pages
+ $errors[] = array( 'cant-move-user-page' );
+ }
+
+ // Check if user is allowed to move files if it's a file
+ if( $this->getNamespace() == NS_FILE && !$user->isAllowed( 'movefile' ) ) {
+ $errors[] = array( 'movenotallowedfile' );
+ }
+
+ if( !$user->isAllowed( 'move' ) ) {
+ // User can't move anything
+ $errors[] = $user->isAnon() ? array ( 'movenologintext' ) : array ('movenotallowed');
+ }
+ } elseif ( $action == 'create' ) {
+ if( ( $this->isTalkPage() && !$user->isAllowed( 'createtalk' ) ) ||
+ ( !$this->isTalkPage() && !$user->isAllowed( 'createpage' ) ) )
+ {
+ $errors[] = $user->isAnon() ? array ('nocreatetext') : array ('nocreate-loggedin');
+ }
+ } elseif( $action == 'move-target' ) {
+ if( !$user->isAllowed( 'move' ) ) {
+ // User can't move anything
+ $errors[] = $user->isAnon() ? array ( 'movenologintext' ) : array ('movenotallowed');
+ } elseif( !$user->isAllowed( 'move-rootuserpages' )
+ && $this->getNamespace() == NS_USER && !$this->isSubpage() )
+ {
+ // Show user page-specific message only if the user can move other pages
+ $errors[] = array( 'cant-move-to-user-page' );
+ }
+ } elseif( !$user->isAllowed( $action ) ) {
+ $return = null;
+ $groups = array_map( array( 'User', 'makeGroupLinkWiki' ),
+ User::getGroupsWithPermission( $action ) );
+ if( $groups ) {
+ $return = array( 'badaccess-groups',
+ array( implode( ', ', $groups ), count( $groups ) ) );
+ } else {
+ $return = array( "badaccess-group0" );
+ }
+ $errors[] = $return;
+ }
+
+ # Short-circuit point
+ if( $short && count($errors) > 0 ) {
+ wfProfileOut( __METHOD__ );
+ return $errors;
+ }
+
// Use getUserPermissionsErrors instead
if( !wfRunHooks( 'userCan', array( &$this, &$user, $action, &$result ) ) ) {
wfProfileOut( __METHOD__ );
return $result ? array() : array( array( 'badaccess-group0' ) );
}
-
+ // Check getUserPermissionsErrors hook
if( !wfRunHooks( 'getUserPermissionsErrors', array(&$this,&$user,$action,&$result) ) ) {
if( is_array($result) && count($result) && !is_array($result[0]) )
$errors[] = $result; # A single array representing an error
@@ -1117,6 +1238,12 @@ class Title {
else if( $result === false )
$errors[] = array('badaccess-group0'); # a generic "We don't want them to do that"
}
+ # Short-circuit point
+ if( $short && count($errors) > 0 ) {
+ wfProfileOut( __METHOD__ );
+ return $errors;
+ }
+ // Check getUserPermissionsErrorsExpensive hook
if( $doExpensiveQueries && !wfRunHooks( 'getUserPermissionsErrorsExpensive', array(&$this,&$user,$action,&$result) ) ) {
if( is_array($result) && count($result) && !is_array($result[0]) )
$errors[] = $result; # A single array representing an error
@@ -1127,13 +1254,20 @@ class Title {
else if( $result === false )
$errors[] = array('badaccess-group0'); # a generic "We don't want them to do that"
}
+ # Short-circuit point
+ if( $short && count($errors) > 0 ) {
+ wfProfileOut( __METHOD__ );
+ return $errors;
+ }
- // TODO: document
+ # Only 'createaccount' and 'execute' can be performed on
+ # special pages, which don't actually exist in the DB.
$specialOKActions = array( 'createaccount', 'execute' );
if( NS_SPECIAL == $this->mNamespace && !in_array( $action, $specialOKActions) ) {
$errors[] = array('ns-specialprotected');
}
+ # Check $wgNamespaceProtection for restricted namespaces
if( $this->isNamespaceProtected() ) {
$ns = $this->getNamespace() == NS_MAIN ?
wfMsg( 'nstab-main' ) : $this->getNsText();
@@ -1141,7 +1275,7 @@ class Title {
array('protectedinterface') : array( 'namespaceprotected', $ns );
}
- # protect css/js subpages of user pages
+ # Protect css/js subpages of user pages
# XXX: this might be better using restrictions
# XXX: Find a way to work around the php bug that prevents using $this->userCanEditCssJsSubpage() from working
if( $this->isCssJsSubpage() && !$user->isAllowed('editusercssjs')
@@ -1150,6 +1284,32 @@ class Title {
$errors[] = array('customcssjsprotected');
}
+ # Check against page_restrictions table requirements on this
+ # page. The user must possess all required rights for this action.
+ foreach( $this->getRestrictions($action) as $right ) {
+ // Backwards compatibility, rewrite sysop -> protect
+ if( $right == 'sysop' ) {
+ $right = 'protect';
+ }
+ if( '' != $right && !$user->isAllowed( $right ) ) {
+ // Users with 'editprotected' permission can edit protected pages
+ if( $action=='edit' && $user->isAllowed( 'editprotected' ) ) {
+ // Users with 'editprotected' permission cannot edit protected pages
+ // with cascading option turned on.
+ if( $this->mCascadeRestriction ) {
+ $errors[] = array( 'protectedpagetext', $right );
+ }
+ } else {
+ $errors[] = array( 'protectedpagetext', $right );
+ }
+ }
+ }
+ # Short-circuit point
+ if( $short && count($errors) > 0 ) {
+ wfProfileOut( __METHOD__ );
+ return $errors;
+ }
+
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
@@ -1172,26 +1332,10 @@ class Title {
}
}
}
-
- foreach( $this->getRestrictions($action) as $right ) {
- // Backwards compatibility, rewrite sysop -> protect
- if( $right == 'sysop' ) {
- $right = 'protect';
- }
- if( '' != $right && !$user->isAllowed( $right ) ) {
- // Users with 'editprotected' permission can edit protected pages
- if( $action=='edit' && $user->isAllowed( 'editprotected' ) ) {
- // Users with 'editprotected' permission cannot edit protected pages
- // with cascading option turned on.
- if( $this->mCascadeRestriction ) {
- $errors[] = array( 'protectedpagetext', $right );
- } else {
- // Nothing, user can edit!
- }
- } else {
- $errors[] = array( 'protectedpagetext', $right );
- }
- }
+ # Short-circuit point
+ if( $short && count($errors) > 0 ) {
+ wfProfileOut( __METHOD__ );
+ return $errors;
}
if( $action == 'protect' ) {
@@ -1212,26 +1356,7 @@ class Title {
$errors[] = array( 'titleprotected', User::whoIs($pt_user), $pt_reason );
}
}
-
- if( ( $this->isTalkPage() && !$user->isAllowed( 'createtalk' ) ) ||
- ( !$this->isTalkPage() && !$user->isAllowed( 'createpage' ) ) )
- {
- $errors[] = $user->isAnon() ? array ('nocreatetext') : array ('nocreate-loggedin');
- }
} elseif( $action == 'move' ) {
- if( !$user->isAllowed( 'move' ) ) {
- // User can't move anything
- $errors[] = $user->isAnon() ? array ( 'movenologintext' ) : array ('movenotallowed');
- } elseif( !$user->isAllowed( 'move-rootuserpages' )
- && $this->getNamespace() == NS_USER && !$this->isSubpage() )
- {
- // Show user page-specific message only if the user can move other pages
- $errors[] = array( 'cant-move-user-page' );
- }
- // Check if user is allowed to move files if it's a file
- if( $this->getNamespace() == NS_FILE && !$user->isAllowed( 'movefile' ) ) {
- $errors[] = array( 'movenotallowedfile' );
- }
// Check for immobile pages
if( !MWNamespace::isMovable( $this->getNamespace() ) ) {
// Specific message for this case
@@ -1241,31 +1366,11 @@ class Title {
$errors[] = array( 'immobile-page' );
}
} elseif( $action == 'move-target' ) {
- if( !$user->isAllowed( 'move' ) ) {
- // User can't move anything
- $errors[] = $user->isAnon() ? array ( 'movenologintext' ) : array ('movenotallowed');
- } elseif( !$user->isAllowed( 'move-rootuserpages' )
- && $this->getNamespace() == NS_USER && !$this->isSubpage() )
- {
- // Show user page-specific message only if the user can move other pages
- $errors[] = array( 'cant-move-to-user-page' );
- }
if( !MWNamespace::isMovable( $this->getNamespace() ) ) {
$errors[] = array( 'immobile-target-namespace', $this->getNsText() );
} elseif( !$this->isMovable() ) {
$errors[] = array( 'immobile-target-page' );
}
- } elseif( !$user->isAllowed( $action ) ) {
- $return = null;
- $groups = array_map( array( 'User', 'makeGroupLinkWiki' ),
- User::getGroupsWithPermission( $action ) );
- if( $groups ) {
- $return = array( 'badaccess-groups',
- array( implode( ', ', $groups ), count( $groups ) ) );
- } else {
- $return = array( "badaccess-group0" );
- }
- $errors[] = $return;
}
wfProfileOut( __METHOD__ );
@@ -1412,7 +1517,7 @@ class Title {
}
# Shortcut for public wikis, allows skipping quite a bit of code
- if ($wgGroupPermissions['*']['read'])
+ if ( !empty( $wgGroupPermissions['*']['read'] ) )
return true;
if( $wgUser->isAllowed( 'read' ) ) {
@@ -1510,11 +1615,36 @@ class Title {
return $this->mHasSubpages;
}
- $db = wfGetDB( DB_SLAVE );
- return $this->mHasSubpages = (bool)$db->selectField( 'page', '1',
- "page_namespace = {$this->mNamespace} AND page_title LIKE '"
- . $db->escapeLike( $this->mDbkeyform ) . "/%'",
- __METHOD__
+ $subpages = $this->getSubpages( 1 );
+ if( $subpages instanceof TitleArray )
+ return $this->mHasSubpages = (bool)$subpages->count();
+ return $this->mHasSubpages = false;
+ }
+
+ /**
+ * Get all subpages of this page.
+ * @param $limit Maximum number of subpages to fetch; -1 for no limit
+ * @return mixed TitleArray, or empty array if this page's namespace
+ * doesn't allow subpages
+ */
+ public function getSubpages( $limit = -1 ) {
+ if( !MWNamespace::hasSubpages( $this->getNamespace() ) )
+ return array();
+
+ $dbr = wfGetDB( DB_SLAVE );
+ $conds['page_namespace'] = $this->getNamespace();
+ $conds[] = 'page_title LIKE ' . $dbr->addQuotes(
+ $dbr->escapeLike( $this->getDBkey() ) . '/%' );
+ $options = array();
+ if( $limit > -1 )
+ $options['LIMIT'] = $limit;
+ return $this->mSubpages = TitleArray::newFromResult(
+ $dbr->select( 'page',
+ array( 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' ),
+ $conds,
+ __METHOD__,
+ $options
+ )
);
}
@@ -1849,20 +1979,45 @@ class Title {
* @return \type{\int} the number of archived revisions
*/
public function isDeleted() {
- $fname = 'Title::isDeleted';
- if ( $this->getNamespace() < 0 ) {
+ if( $this->getNamespace() < 0 ) {
$n = 0;
} else {
$dbr = wfGetDB( DB_SLAVE );
- $n = $dbr->selectField( 'archive', 'COUNT(*)', array( 'ar_namespace' => $this->getNamespace(),
- 'ar_title' => $this->getDBkey() ), $fname );
+ $n = $dbr->selectField( 'archive', 'COUNT(*)',
+ array( 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ),
+ __METHOD__
+ );
if( $this->getNamespace() == NS_FILE ) {
$n += $dbr->selectField( 'filearchive', 'COUNT(*)',
- array( 'fa_name' => $this->getDBkey() ), $fname );
+ array( 'fa_name' => $this->getDBkey() ),
+ __METHOD__
+ );
}
}
return (int)$n;
}
+
+ /**
+ * Is there a version of this page in the deletion archive?
+ * @return bool
+ */
+ public function isDeletedQuick() {
+ if( $this->getNamespace() < 0 ) {
+ return false;
+ }
+ $dbr = wfGetDB( DB_SLAVE );
+ $deleted = (bool)$dbr->selectField( 'archive', '1',
+ array( 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ),
+ __METHOD__
+ );
+ if( !$deleted && $this->getNamespace() == NS_FILE ) {
+ $deleted = (bool)$dbr->selectField( 'filearchive', '1',
+ array( 'fa_name' => $this->getDBkey() ),
+ __METHOD__
+ );
+ }
+ return $deleted;
+ }
/**
* Get the article ID for this Title from the link cache,
@@ -1955,7 +2110,7 @@ class Title {
$linkCache = LinkCache::singleton();
$linkCache->clearBadLink( $this->getPrefixedDBkey() );
- if ( 0 == $newid ) { $this->mArticleID = -1; }
+ if ( $newid === false ) { $this->mArticleID = -1; }
else { $this->mArticleID = $newid; }
$this->mRestrictionsLoaded = false;
$this->mRestrictions = array();
@@ -2064,14 +2219,22 @@ class Title {
# Namespace or interwiki prefix
$firstPass = true;
+ $prefixRegexp = "/^(.+?)_*:_*(.*)$/S";
do {
$m = array();
- if ( preg_match( "/^(.+?)_*:_*(.*)$/S", $dbkey, $m ) ) {
+ if ( preg_match( $prefixRegexp, $dbkey, $m ) ) {
$p = $m[1];
- if ( $ns = $wgContLang->getNsIndex( $p )) {
+ if ( $ns = $wgContLang->getNsIndex( $p ) ) {
# Ordinary namespace
$dbkey = $m[2];
$this->mNamespace = $ns;
+ # For Talk:X pages, check if X has a "namespace" prefix
+ if( $ns == NS_TALK && preg_match( $prefixRegexp, $dbkey, $x ) ) {
+ if( $wgContLang->getNsIndex( $x[1] ) )
+ return false; # Disallow Talk:File:x type titles...
+ else if( Interwiki::isValidInterwiki( $x[1] ) )
+ return false; # Disallow Talk:Interwiki:x type titles...
+ }
} elseif( Interwiki::isValidInterwiki( $p ) ) {
if( !$firstPass ) {
# Can't make a local interwiki link to an interwiki link.
@@ -2254,13 +2417,13 @@ class Title {
* WARNING: do not use this function on arbitrary user-supplied titles!
* On heavily-used templates it will max out the memory.
*
- * @param $options \type{\string} may be FOR UPDATE
+ * @param array $options may be FOR UPDATE
* @return \type{\arrayof{Title}} the Title objects linking here
*/
- public function getLinksTo( $options = '', $table = 'pagelinks', $prefix = 'pl' ) {
+ public function getLinksTo( $options = array(), $table = 'pagelinks', $prefix = 'pl' ) {
$linkCache = LinkCache::singleton();
- if ( $options ) {
+ if ( count( $options ) > 0 ) {
$db = wfGetDB( DB_MASTER );
} else {
$db = wfGetDB( DB_SLAVE );
@@ -2295,10 +2458,10 @@ class Title {
* WARNING: do not use this function on arbitrary user-supplied titles!
* On heavily-used templates it will max out the memory.
*
- * @param $options \type{\string} may be FOR UPDATE
+ * @param array $options may be FOR UPDATE
* @return \type{\arrayof{Title}} the Title objects linking here
*/
- public function getTemplateLinksTo( $options = '' ) {
+ public function getTemplateLinksTo( $options = array() ) {
return $this->getLinksTo( $options, 'templatelinks', 'tl' );
}
@@ -2306,42 +2469,35 @@ class Title {
* Get an array of Title objects referring to non-existent articles linked from this page
*
* @todo check if needed (used only in SpecialBrokenRedirects.php, and should use redirect table in this case)
- * @param $options \type{\string} may be FOR UPDATE
* @return \type{\arrayof{Title}} the Title objects
*/
- public function getBrokenLinksFrom( $options = '' ) {
+ public function getBrokenLinksFrom() {
if ( $this->getArticleId() == 0 ) {
# All links from article ID 0 are false positives
return array();
}
- if ( $options ) {
- $db = wfGetDB( DB_MASTER );
- } else {
- $db = wfGetDB( DB_SLAVE );
- }
-
- $res = $db->safeQuery(
- "SELECT pl_namespace, pl_title
- FROM !
- LEFT JOIN !
- ON pl_namespace=page_namespace
- AND pl_title=page_title
- WHERE pl_from=?
- AND page_namespace IS NULL
- !",
- $db->tableName( 'pagelinks' ),
- $db->tableName( 'page' ),
- $this->getArticleId(),
- $options );
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select(
+ array( 'page', 'pagelinks' ),
+ array( 'pl_namespace', 'pl_title' ),
+ array(
+ 'pl_from' => $this->getArticleId(),
+ 'page_namespace IS NULL'
+ ),
+ __METHOD__, array(),
+ array(
+ 'page' => array(
+ 'LEFT JOIN',
+ array( 'pl_namespace=page_namespace', 'pl_title=page_title' )
+ )
+ )
+ );
$retVal = array();
- if ( $db->numRows( $res ) ) {
- foreach( $res as $row ) {
- $retVal[] = Title::makeTitle( $row->pl_namespace, $row->pl_title );
- }
+ foreach( $res as $row ) {
+ $retVal[] = Title::makeTitle( $row->pl_namespace, $row->pl_title );
}
- $db->freeResult( $res );
return $retVal;
}
@@ -2459,7 +2615,7 @@ class Title {
$nt->getUserPermissionsErrors('edit', $wgUser) );
}
- $match = EditPage::matchSpamRegex( $reason );
+ $match = EditPage::matchSummarySpamRegex( $reason );
if( $match !== false ) {
// This is kind of lame, won't display nice
$errors[] = array('spamprotectiontext');
@@ -2559,8 +2715,8 @@ class Title {
);
# Update the protection log
$log = new LogPage( 'protect' );
- $comment = wfMsgForContent('prot_1movedto2',$this->getPrefixedText(), $nt->getPrefixedText() );
- if( $reason ) $comment .= ': ' . $reason;
+ $comment = wfMsgForContent( 'prot_1movedto2', $this->getPrefixedText(), $nt->getPrefixedText() );
+ if( $reason ) $comment .= wfMsgForContent( 'colon-separator' ) . $reason;
$log->addEntry( 'move_prot', $nt, $comment, array($this->getPrefixedText()) ); // FIXME: $params?
}
@@ -2601,8 +2757,16 @@ class Title {
# Update message cache for interface messages
if( $nt->getNamespace() == NS_MEDIAWIKI ) {
global $wgMessageCache;
- $oldarticle = new Article( $this );
- $wgMessageCache->replace( $this->getDBkey(), $oldarticle->getContent() );
+
+ # @bug 17860: old article can be deleted, if this the case,
+ # delete it from message cache
+ if ( $this->getArticleID === 0 ) {
+ $wgMessageCache->replace( $this->getDBkey(), false );
+ } else {
+ $oldarticle = new Article( $this );
+ $wgMessageCache->replace( $this->getDBkey(), $oldarticle->getContent() );
+ }
+
$newarticle = new Article( $nt );
$wgMessageCache->replace( $nt->getDBkey(), $newarticle->getContent() );
}
@@ -2830,6 +2994,67 @@ class Title {
}
/**
+ * Move this page's subpages to be subpages of $nt
+ * @param $nt Title Move target
+ * @param $auth bool Whether $wgUser's permissions should be checked
+ * @param $reason string The reason for the move
+ * @param $createRedirect bool Whether to create redirects from the old subpages to the new ones
+ * Ignored if the user doesn't have the 'suppressredirect' right
+ * @return mixed array with old page titles as keys, and strings (new page titles) or
+ * arrays (errors) as values, or an error array with numeric indices if no pages were moved
+ */
+ public function moveSubpages( $nt, $auth = true, $reason = '', $createRedirect = true ) {
+ global $wgUser, $wgMaximumMovedPages;
+ // Check permissions
+ if( !$this->userCan( 'move-subpages' ) )
+ return array( 'cant-move-subpages' );
+ // Do the source and target namespaces support subpages?
+ if( !MWNamespace::hasSubpages( $this->getNamespace() ) )
+ return array( 'namespace-nosubpages',
+ MWNamespace::getCanonicalName( $this->getNamespace() ) );
+ if( !MWNamespace::hasSubpages( $nt->getNamespace() ) )
+ return array( 'namespace-nosubpages',
+ MWNamespace::getCanonicalName( $nt->getNamespace() ) );
+
+ $subpages = $this->getSubpages($wgMaximumMovedPages + 1);
+ $retval = array();
+ $count = 0;
+ foreach( $subpages as $oldSubpage ) {
+ $count++;
+ if( $count > $wgMaximumMovedPages ) {
+ $retval[$oldSubpage->getPrefixedTitle()] =
+ array( 'movepage-max-pages',
+ $wgMaximumMovedPages );
+ break;
+ }
+
+ if( $oldSubpage->getArticleId() == $this->getArticleId() )
+ // When moving a page to a subpage of itself,
+ // don't move it twice
+ continue;
+ $newPageName = preg_replace(
+ '#^'.preg_quote( $this->getDBKey(), '#' ).'#',
+ $nt->getDBKey(), $oldSubpage->getDBKey() );
+ if( $oldSubpage->isTalkPage() ) {
+ $newNs = $nt->getTalkPage()->getNamespace();
+ } else {
+ $newNs = $nt->getSubjectPage()->getNamespace();
+ }
+ # Bug 14385: we need makeTitleSafe because the new page names may
+ # be longer than 255 characters.
+ $newSubpage = Title::makeTitleSafe( $newNs, $newPageName );
+
+ $success = $oldSubpage->moveTo( $newSubpage, $auth, $reason, $createRedirect );
+ if( $success === true ) {
+ $retval[$oldSubpage->getPrefixedText()] = $newSubpage->getPrefixedText();
+ } else {
+ $retval[$oldSubpage->getPrefixedText()] = $success;
+ }
+ }
+ return $retval;
+ }
+
+ /**
* Checks if this page is just a one-rev redirect.
* Adds lock, so don't use just for light purposes.
*
@@ -2842,7 +3067,7 @@ class Title {
array( 'page_is_redirect', 'page_latest', 'page_id' ),
$this->pageCond(),
__METHOD__,
- 'FOR UPDATE'
+ array( 'FOR UPDATE' )
);
# Cache some fields we may want
$this->mArticleID = $row ? intval($row->page_id) : 0;
@@ -2860,7 +3085,7 @@ class Title {
'page_latest != rev_id'
),
__METHOD__,
- 'FOR UPDATE'
+ array( 'FOR UPDATE' )
);
# Return true if there was no history
return ($row === false);
@@ -3034,6 +3259,28 @@ class Title {
}
/**
+ * Get the first revision of the page
+ *
+ * @param $flags \type{\int} GAID_FOR_UPDATE
+ * @return Revision (or NULL if page doesn't exist)
+ */
+ public function getFirstRevision( $flags=0 ) {
+ $db = ($flags & GAID_FOR_UPDATE) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
+ $pageId = $this->getArticleId($flags);
+ if( !$pageId ) return NULL;
+ $row = $db->selectRow( 'revision', '*',
+ array( 'rev_page' => $pageId ),
+ __METHOD__,
+ array( 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 1 )
+ );
+ if( !$row ) {
+ return NULL;
+ } else {
+ return new Revision( $row );
+ }
+ }
+
+ /**
* Check if this is a new page
*
* @return bool
@@ -3074,8 +3321,8 @@ class Title {
'rev_page = ' . intval( $this->getArticleId() ) .
' AND rev_id > ' . intval( $old ) .
' AND rev_id < ' . intval( $new ),
- __METHOD__,
- array( 'USE INDEX' => 'PRIMARY' ) );
+ __METHOD__
+ );
}
/**
@@ -3094,7 +3341,7 @@ class Title {
/**
* Callback for usort() to do title sorts by (namespace, title)
*/
- static function compare( $a, $b ) {
+ public static function compare( $a, $b ) {
if( $a->getNamespace() == $b->getNamespace() ) {
return strcmp( $a->getText(), $b->getText() );
} else {
@@ -3144,7 +3391,7 @@ class Title {
if( $this->mInterwiki != '' ) {
return true; // any interwiki link might be viewable, for all we know
}
- switch( $this->mNamespace ) {
+ switch( $this->mNamespace ) {
case NS_MEDIA:
case NS_FILE:
return wfFindFile( $this ); // file exists, possibly in a foreign repo
@@ -3250,9 +3497,9 @@ class Title {
* @return \type{\string} Trackback URL
*/
public function trackbackURL() {
- global $wgScriptPath, $wgServer;
+ global $wgScriptPath, $wgServer, $wgScriptExtension;
- return "$wgServer$wgScriptPath/trackback.php?article="
+ return "$wgServer$wgScriptPath/trackback$wgScriptExtension?article="
. htmlspecialchars(urlencode($this->getPrefixedDBkey()));
}
@@ -3396,4 +3643,36 @@ class Title {
}
return $redirs;
}
+
+ /**
+ * Check if this Title is a valid redirect target
+ *
+ * @return \type{\bool} TRUE or FALSE
+ */
+ public function isValidRedirectTarget() {
+ global $wgInvalidRedirectTargets;
+
+ // invalid redirect targets are stored in a global array, but explicity disallow Userlogout here
+ if( $this->isSpecial( 'Userlogout' ) ) {
+ return false;
+ }
+
+ foreach( $wgInvalidRedirectTargets as $target ) {
+ if( $this->isSpecial( $target ) ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Get a backlink cache object
+ */
+ function getBacklinkCache() {
+ if ( is_null( $this->mBacklinkCache ) ) {
+ $this->mBacklinkCache = new BacklinkCache( $this );
+ }
+ return $this->mBacklinkCache;
+ }
}
diff --git a/includes/UploadBase.php b/includes/UploadBase.php
deleted file mode 100644
index 91155a1b..00000000
--- a/includes/UploadBase.php
+++ /dev/null
@@ -1,867 +0,0 @@
-<?php
-
-class UploadBase {
- var $mTempPath;
- var $mDesiredDestName, $mDestName, $mRemoveTempFile, $mSourceType;
- var $mTitle = false, $mTitleError = 0;
- var $mFilteredName, $mFinalExtension;
-
- const SUCCESS = 0;
- const OK = 0;
- const BEFORE_PROCESSING = 1;
- const LARGE_FILE_SERVER = 2;
- const EMPTY_FILE = 3;
- const MIN_LENGTH_PARTNAME = 4;
- const ILLEGAL_FILENAME = 5;
- const PROTECTED_PAGE = 6;
- const OVERWRITE_EXISTING_FILE = 7;
- const FILETYPE_MISSING = 8;
- const FILETYPE_BADTYPE = 9;
- const VERIFICATION_ERROR = 10;
- const UPLOAD_VERIFICATION_ERROR = 11;
- const UPLOAD_WARNING = 12;
- const INTERNAL_ERROR = 13;
-
- const SESSION_VERSION = 2;
-
- /**
- * Returns true if uploads are enabled.
- * Can be overriden by subclasses.
- */
- static function isEnabled() {
- global $wgEnableUploads;
- return $wgEnableUploads;
- }
- /**
- * Returns true if the user can use this upload module or else a string
- * identifying the missing permission.
- * Can be overriden by subclasses.
- */
- static function isAllowed( $user ) {
- if( !$user->isAllowed( 'upload' ) )
- return 'upload';
- return true;
- }
-
- // Upload handlers. Should probably just be a global
- static $uploadHandlers = array( 'Stash', 'Upload', 'Url' );
- /**
- * Create a form of UploadBase depending on wpSourceType and initializes it
- */
- static function createFromRequest( &$request, $type = null ) {
- $type = $type ? $type : $request->getVal( 'wpSourceType' );
- if( !$type )
- return null;
- $type = ucfirst($type);
- $className = 'UploadFrom'.$type;
- if( !in_array( $type, self::$uploadHandlers ) )
- return null;
- if( !call_user_func( array( $className, 'isEnabled' ) ) )
- return null;
- if( !call_user_func( array( $className, 'isValidRequest' ), $request ) )
- return null;
-
- $handler = new $className;
- $handler->initializeFromRequest( $request );
- return $handler;
- }
-
- /**
- * Check whether a request if valid for this handler
- */
- static function isValidRequest( $request ) {
- return false;
- }
-
- function __construct() {}
-
- /**
- * Do the real variable initialization
- */
- function initialize( $name, $tempPath, $fileSize, $removeTempFile = false ) {
- $this->mDesiredDestName = $name;
- $this->mTempPath = $tempPath;
- $this->mFileSize = $fileSize;
- $this->mRemoveTempFile = $removeTempFile;
- }
-
- /**
- * Fetch the file. Usually a no-op
- */
- function fetchFile() {
- return self::OK;
- }
-
- /**
- * Verify whether the upload is sane.
- * Returns self::OK or else an array with error information
- */
- function verifyUpload() {
- global $wgUser;
-
- /**
- * If there was no filename or a zero size given, give up quick.
- */
- if( empty( $this->mFileSize ) )
- return array( 'status' => self::EMPTY_FILE );
-
- $nt = $this->getTitle();
- if( is_null( $nt ) ) {
- $result = array( 'status' => $this->mTitleError );
- if( $this->mTitleError == self::ILLEGAL_FILENAME )
- $resul['filtered'] = $this->mFilteredName;
- if ( $this->mTitleError == self::FILETYPE_BADTYPE )
- $result['finalExt'] = $this->mFinalExtension;
- return $result;
- }
- $this->mLocalFile = wfLocalFile( $nt );
- $this->mDestName = $this->mLocalFile->getName();
-
- /**
- * In some cases we may forbid overwriting of existing files.
- */
- $overwrite = $this->checkOverwrite( $this->mDestName );
- if( $overwrite !== true )
- return array( 'status' => self::OVERWRITE_EXISTING_FILE, 'overwrite' => $overwrite );
-
- /**
- * Look at the contents of the file; if we can recognize the
- * type but it's corrupt or data of the wrong type, we should
- * probably not accept it.
- */
- $verification = $this->verifyFile( $this->mTempPath );
-
- if( $verification !== true ) {
- if( !is_array( $verification ) )
- $verification = array( $verification );
- $verification['status'] = self::VERIFICATION_ERROR;
- return $verification;
- }
-
- $error = '';
- if( !wfRunHooks( 'UploadVerification',
- array( $this->mDestName, $this->mTempPath, &$error ) ) ) {
- return array( 'status' => self::UPLOAD_VERIFICATION_ERROR, 'error' => $error );
- }
-
- return self::OK;
- }
-
- /**
- * Verifies that it's ok to include the uploaded file
- *
- * @param string $tmpfile the full path of the temporary file to verify
- * @return mixed true of the file is verified, a string or array otherwise.
- */
- protected function verifyFile( $tmpfile ) {
- $this->mFileProps = File::getPropsFromPath( $this->mTempPath,
- $this->mFinalExtension );
- $this->checkMacBinary();
-
- #magically determine mime type
- $magic = MimeMagic::singleton();
- $mime = $magic->guessMimeType( $tmpfile, false );
-
- #check mime type, if desired
- global $wgVerifyMimeType;
- if ( $wgVerifyMimeType ) {
-
- wfDebug ( "\n\nmime: <$mime> extension: <{$this->mFinalExtension}>\n\n");
- #check mime type against file extension
- if( !self::verifyExtension( $mime, $this->mFinalExtension ) ) {
- return 'uploadcorrupt';
- }
-
- #check mime type blacklist
- global $wgMimeTypeBlacklist;
- if( isset($wgMimeTypeBlacklist) && !is_null($wgMimeTypeBlacklist)
- && $this->checkFileExtension( $mime, $wgMimeTypeBlacklist ) ) {
- return array( 'filetype-badmime', $mime );
- }
- }
-
- #check for htmlish code and javascript
- if( $this->detectScript ( $tmpfile, $mime, $this->mFinalExtension ) ) {
- return 'uploadscripted';
- }
-
- /**
- * Scan the uploaded file for viruses
- */
- $virus = $this->detectVirus($tmpfile);
- if ( $virus ) {
- return array( 'uploadvirus', $virus );
- }
-
- wfDebug( __METHOD__.": all clear; passing.\n" );
- return true;
- }
-
- /**
- * Check whether the user can edit, upload and create the image
- */
- function verifyPermissions( $user ) {
- /**
- * If the image is protected, non-sysop users won't be able
- * to modify it by uploading a new revision.
- */
- $nt = $this->getTitle();
- if( is_null( $nt ) )
- return true;
- $permErrors = $nt->getUserPermissionsErrors( 'edit', $user );
- $permErrorsUpload = $nt->getUserPermissionsErrors( 'upload', $user );
- $permErrorsCreate = ( $nt->exists() ? array() : $nt->getUserPermissionsErrors( 'create', $user ) );
- if( $permErrors || $permErrorsUpload || $permErrorsCreate ) {
- $permErrors = array_merge( $permErrors, wfArrayDiff2( $permErrorsUpload, $permErrors ) );
- $permErrors = array_merge( $permErrors, wfArrayDiff2( $permErrorsCreate, $permErrors ) );
- return $permErrors;
- }
- return true;
- }
-
- /**
- * Check for non fatal problems with the file
- */
- function checkWarnings() {
- $warning = array();
-
- $filename = $this->mLocalFile->getName();
- $n = strrpos( $filename, '.' );
- $partname = $n ? substr( $filename, 0, $n ) : $filename;
-
- // Check whether the resulting filename is different from the desired one
- if( $this->mDesiredDestName != $filename )
- $warning['badfilename'] = $filename;
-
- // Check whether the file extension is on the unwanted list
- global $wgCheckFileExtensions, $wgFileExtensions;
- if ( $wgCheckFileExtensions ) {
- if ( !$this->checkFileExtension( $this->mFinalExtension, $wgFileExtensions ) )
- $warning['filetype-unwanted-type'] = $this->mFinalExtension;
- }
-
- global $wgUploadSizeWarning;
- if ( $wgUploadSizeWarning && ( $this->mFileSize > $wgUploadSizeWarning ) )
- $warning['large-file'] = $wgUploadSizeWarning;
-
- if ( $this->mFileSize == 0 )
- $warning['emptyfile'] = true;
-
- $exists = self::getExistsWarning( $this->mLocalFile );
- if( $exists !== false )
- $warning['exists'] = $exists;
-
- // Check whether this may be a thumbnail
- if( $exists !== false && $exists[0] != 'thumb'
- && self::isThumbName( $this->mLocalFile->getName() ) )
- $warning['file-thumbnail-no'] = substr( $filename , 0,
- strpos( $nt->getText() , '-' ) +1 );
-
- $hash = File::sha1Base36( $this->mTempPath );
- $dupes = RepoGroup::singleton()->findBySha1( $hash );
- if( $dupes )
- $warning['duplicate'] = $dupes;
-
- $filenamePrefixBlacklist = self::getFilenamePrefixBlacklist();
- foreach( $filenamePrefixBlacklist as $prefix ) {
- if ( substr( $partname, 0, strlen( $prefix ) ) == $prefix ) {
- $warning['filename-bad-prefix'] = $prefix;
- break;
- }
- }
-
- # If the file existed before and was deleted, warn the user of this
- # Don't bother doing so if the file exists now, however
- if( $this->mLocalFile->wasDeleted() && !$this->mLocalFile->exists() )
- $warning['filewasdeleted'] = $this->mLocalFile->getTitle();
-
- return $warning;
- }
-
- /**
- * Really perform the upload.
- */
- function performUpload( $comment, $pageText, $watch, $user ) {
- $status = $this->mLocalFile->upload( $this->mTempPath, $comment, $pageText,
- File::DELETE_SOURCE, $this->mFileProps, false, $user );
-
- if( $status->isGood() && $watch ) {
- $user->addWatch( $this->mLocalFile->getTitle() );
- }
-
- if( $status->isGood() )
- wfRunHooks( 'UploadComplete', array( &$this ) );
-
- return $status;
- }
-
- /**
- * Returns a title or null
- */
- function getTitle() {
- if ( $this->mTitle !== false )
- return $this->mTitle;
-
- /**
- * Chop off any directories in the given filename. Then
- * filter out illegal characters, and try to make a legible name
- * out of it. We'll strip some silently that Title would die on.
- */
-
- $basename = $this->mDesiredDestName;
-
- $this->mFilteredName = wfStripIllegalFilenameChars( $basename );
-
- /**
- * We'll want to blacklist against *any* 'extension', and use
- * only the final one for the whitelist.
- */
- list( $partname, $ext ) = $this->splitExtensions( $this->mFilteredName );
-
- if( count( $ext ) ) {
- $this->mFinalExtension = $ext[count( $ext ) - 1];
- } else {
- $this->mFinalExtension = '';
- }
-
- /* Don't allow users to override the blacklist (check file extension) */
- global $wgCheckFileExtensions, $wgStrictFileExtensions;
- global $wgFileExtensions, $wgFileBlacklist;
- if ( $this->mFinalExtension == '' ) {
- $this->mTitleError = self::FILETYPE_MISSING;
- return $this->mTitle = null;
- } elseif ( $this->checkFileExtensionList( $ext, $wgFileBlacklist ) ||
- ( $wgCheckFileExtensions && $wgStrictFileExtensions &&
- !$this->checkFileExtension( $this->mFinalExtension, $wgFileExtensions ) ) ) {
- $this->mTitleError = self::FILETYPE_BADTYPE;
- return $this->mTitle = null;
- }
-
- # If there was more than one "extension", reassemble the base
- # filename to prevent bogus complaints about length
- if( count( $ext ) > 1 ) {
- for( $i = 0; $i < count( $ext ) - 1; $i++ )
- $partname .= '.' . $ext[$i];
- }
-
- if( strlen( $partname ) < 1 ) {
- $this->mTitleError = self::MIN_LENGTH_PARTNAME;
- return $this->mTitle = null;
- }
-
- $nt = Title::makeTitleSafe( NS_FILE, $this->mFilteredName );
- if( is_null( $nt ) ) {
- $this->mTitleError = self::ILLEGAL_FILENAME;
- return $this->mTitle = null;
- }
- return $this->mTitle = $nt;
- }
-
- function getLocalFile() {
- if( is_null( $this->mLocalFile ) ) {
- $nt = $this->getTitle();
- $this->mLocalFile = is_null( $nt ) ? null : wfLocalFile( $nt );
- }
- return $this->mLocalFile;
- }
-
- /**
- * Stash a file in a temporary directory for later processing
- * after the user has confirmed it.
- *
- * If the user doesn't explicitly cancel or accept, these files
- * can accumulate in the temp directory.
- *
- * @param string $saveName - the destination filename
- * @param string $tempName - the source temporary file to save
- * @return string - full path the stashed file, or false on failure
- * @access private
- */
- function saveTempUploadedFile( $saveName, $tempName ) {
- global $wgOut;
- $repo = RepoGroup::singleton()->getLocalRepo();
- $status = $repo->storeTemp( $saveName, $tempName );
- return $status;
- }
-
- /**
- * Stash a file in a temporary directory for later processing,
- * and save the necessary descriptive info into the session.
- * Returns a key value which will be passed through a form
- * to pick up the path info on a later invocation.
- *
- * @return int
- * @access private
- */
- function stashSession() {
- $status = $this->saveTempUploadedFile( $this->mDestName, $this->mTempPath );
-
- if( !$status->isGood() ) {
- # Couldn't save the file.
- return false;
- }
-
- return array(
- 'mTempPath' => $status->value,
- 'mFileSize' => $this->mFileSize,
- 'mFileProps' => $this->mFileProps,
- 'version' => self::SESSION_VERSION,
- );
- }
-
- /**
- * Remove a temporarily kept file stashed by saveTempUploadedFile().
- * @return success
- */
- function unsaveUploadedFile() {
- $repo = RepoGroup::singleton()->getLocalRepo();
- $success = $repo->freeTemp( $this->mTempPath );
- return $success;
- }
-
- /**
- * If we've modified the upload file we need to manually remove it
- * on exit to clean up.
- * @access private
- */
- function cleanupTempFile() {
- if ( $this->mRemoveTempFile && file_exists( $this->mTempPath ) ) {
- wfDebug( __METHOD__.": Removing temporary file {$this->mTempPath}\n" );
- unlink( $this->mTempPath );
- }
- }
-
- function getTempPath() {
- return $this->mTempPath;
- }
-
-
- /**
- * Split a file into a base name and all dot-delimited 'extensions'
- * on the end. Some web server configurations will fall back to
- * earlier pseudo-'extensions' to determine type and execute
- * scripts, so the blacklist needs to check them all.
- *
- * @return array
- */
- function splitExtensions( $filename ) {
- $bits = explode( '.', $filename );
- $basename = array_shift( $bits );
- return array( $basename, $bits );
- }
-
- /**
- * Perform case-insensitive match against a list of file extensions.
- * Returns true if the extension is in the list.
- *
- * @param string $ext
- * @param array $list
- * @return bool
- */
- function checkFileExtension( $ext, $list ) {
- return in_array( strtolower( $ext ), $list );
- }
-
- /**
- * Perform case-insensitive match against a list of file extensions.
- * Returns true if any of the extensions are in the list.
- *
- * @param array $ext
- * @param array $list
- * @return bool
- */
- function checkFileExtensionList( $ext, $list ) {
- foreach( $ext as $e ) {
- if( in_array( strtolower( $e ), $list ) ) {
- return true;
- }
- }
- return false;
- }
-
-
- /**
- * Checks if the mime type of the uploaded file matches the file extension.
- *
- * @param string $mime the mime type of the uploaded file
- * @param string $extension The filename extension that the file is to be served with
- * @return bool
- */
- public static function verifyExtension( $mime, $extension ) {
- $magic = MimeMagic::singleton();
-
- if ( ! $mime || $mime == 'unknown' || $mime == 'unknown/unknown' )
- if ( ! $magic->isRecognizableExtension( $extension ) ) {
- wfDebug( __METHOD__.": passing file with unknown detected mime type; " .
- "unrecognized extension '$extension', can't verify\n" );
- return true;
- } else {
- wfDebug( __METHOD__.": rejecting file with unknown detected mime type; ".
- "recognized extension '$extension', so probably invalid file\n" );
- return false;
- }
-
- $match= $magic->isMatchingExtension($extension,$mime);
-
- if ($match===NULL) {
- wfDebug( __METHOD__.": no file extension known for mime type $mime, passing file\n" );
- return true;
- } elseif ($match===true) {
- wfDebug( __METHOD__.": mime type $mime matches extension $extension, passing file\n" );
-
- #TODO: if it's a bitmap, make sure PHP or ImageMagic resp. can handle it!
- return true;
-
- } else {
- wfDebug( __METHOD__.": mime type $mime mismatches file extension $extension, rejecting file\n" );
- return false;
- }
- }
-
- /**
- * Heuristic for detecting files that *could* contain JavaScript instructions or
- * things that may look like HTML to a browser and are thus
- * potentially harmful. The present implementation will produce false positives in some situations.
- *
- * @param string $file Pathname to the temporary upload file
- * @param string $mime The mime type of the file
- * @param string $extension The extension of the file
- * @return bool true if the file contains something looking like embedded scripts
- */
- function detectScript($file, $mime, $extension) {
- global $wgAllowTitlesInSVG;
-
- #ugly hack: for text files, always look at the entire file.
- #For binary field, just check the first K.
-
- if (strpos($mime,'text/')===0) $chunk = file_get_contents( $file );
- else {
- $fp = fopen( $file, 'rb' );
- $chunk = fread( $fp, 1024 );
- fclose( $fp );
- }
-
- $chunk= strtolower( $chunk );
-
- if (!$chunk) return false;
-
- #decode from UTF-16 if needed (could be used for obfuscation).
- if (substr($chunk,0,2)=="\xfe\xff") $enc= "UTF-16BE";
- elseif (substr($chunk,0,2)=="\xff\xfe") $enc= "UTF-16LE";
- else $enc= NULL;
-
- if ($enc) $chunk= iconv($enc,"ASCII//IGNORE",$chunk);
-
- $chunk= trim($chunk);
-
- #FIXME: convert from UTF-16 if necessarry!
-
- wfDebug("SpecialUpload::detectScript: checking for embedded scripts and HTML stuff\n");
-
- #check for HTML doctype
- if (eregi("<!DOCTYPE *X?HTML",$chunk)) return true;
-
- /**
- * Internet Explorer for Windows performs some really stupid file type
- * autodetection which can cause it to interpret valid image files as HTML
- * and potentially execute JavaScript, creating a cross-site scripting
- * attack vectors.
- *
- * Apple's Safari browser also performs some unsafe file type autodetection
- * which can cause legitimate files to be interpreted as HTML if the
- * web server is not correctly configured to send the right content-type
- * (or if you're really uploading plain text and octet streams!)
- *
- * Returns true if IE is likely to mistake the given file for HTML.
- * Also returns true if Safari would mistake the given file for HTML
- * when served with a generic content-type.
- */
-
- $tags = array(
- '<body',
- '<head',
- '<html', #also in safari
- '<img',
- '<pre',
- '<script', #also in safari
- '<table'
- );
- if( ! $wgAllowTitlesInSVG && $extension !== 'svg' && $mime !== 'image/svg' ) {
- $tags[] = '<title';
- }
-
- foreach( $tags as $tag ) {
- if( false !== strpos( $chunk, $tag ) ) {
- return true;
- }
- }
-
- /*
- * look for javascript
- */
-
- #resolve entity-refs to look at attributes. may be harsh on big files... cache result?
- $chunk = Sanitizer::decodeCharReferences( $chunk );
-
- #look for script-types
- 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;
-
- #look for css-style script-urls
- if (preg_match('!url\s*\(\s*[\'"]?\s*(?:ecma|java)script:!sim',$chunk)) return true;
-
- wfDebug("SpecialUpload::detectScript: no scripts found\n");
- return false;
- }
-
- /**
- * Generic wrapper function for a virus scanner program.
- * This relies on the $wgAntivirus and $wgAntivirusSetup variables.
- * $wgAntivirusRequired may be used to deny upload if the scan fails.
- *
- * @param string $file Pathname to the temporary upload file
- * @return mixed false if not virus is found, NULL if the scan fails or is disabled,
- * or a string containing feedback from the virus scanner if a virus was found.
- * If textual feedback is missing but a virus was found, this function returns true.
- */
- function detectVirus($file) {
- global $wgAntivirus, $wgAntivirusSetup, $wgAntivirusRequired, $wgOut;
-
- if ( !$wgAntivirus ) {
- wfDebug( __METHOD__.": virus scanner disabled\n");
- return NULL;
- }
-
- if ( !$wgAntivirusSetup[$wgAntivirus] ) {
- wfDebug( __METHOD__.": unknown virus scanner: $wgAntivirus\n" );
- $wgOut->wrapWikiMsg( '<div class="error">$1</div>', array( 'virus-badscanner', $wgAntivirus ) );
- return wfMsg('virus-unknownscanner') . " $wgAntivirus";
- }
-
- # look up scanner configuration
- $command = $wgAntivirusSetup[$wgAntivirus]["command"];
- $exitCodeMap = $wgAntivirusSetup[$wgAntivirus]["codemap"];
- $msgPattern = isset( $wgAntivirusSetup[$wgAntivirus]["messagepattern"] ) ?
- $wgAntivirusSetup[$wgAntivirus]["messagepattern"] : null;
-
- if ( strpos( $command,"%f" ) === false ) {
- # simple pattern: append file to scan
- $command .= " " . wfEscapeShellArg( $file );
- } else {
- # complex pattern: replace "%f" with file to scan
- $command = str_replace( "%f", wfEscapeShellArg( $file ), $command );
- }
-
- wfDebug( __METHOD__.": running virus scan: $command \n" );
-
- # execute virus scanner
- $exitCode = false;
-
- #NOTE: there's a 50 line workaround to make stderr redirection work on windows, too.
- # that does not seem to be worth the pain.
- # Ask me (Duesentrieb) about it if it's ever needed.
- $output = array();
- if ( wfIsWindows() ) {
- exec( "$command", $output, $exitCode );
- } else {
- exec( "$command 2>&1", $output, $exitCode );
- }
-
- # map exit code to AV_xxx constants.
- $mappedCode = $exitCode;
- if ( $exitCodeMap ) {
- if ( isset( $exitCodeMap[$exitCode] ) ) {
- $mappedCode = $exitCodeMap[$exitCode];
- } elseif ( isset( $exitCodeMap["*"] ) ) {
- $mappedCode = $exitCodeMap["*"];
- }
- }
-
- if ( $mappedCode === AV_SCAN_FAILED ) {
- # scan failed (code was mapped to false by $exitCodeMap)
- wfDebug( __METHOD__.": failed to scan $file (code $exitCode).\n" );
-
- if ( $wgAntivirusRequired ) {
- return wfMsg('virus-scanfailed', array( $exitCode ) );
- } else {
- return NULL;
- }
- } else if ( $mappedCode === AV_SCAN_ABORTED ) {
- # scan failed because filetype is unknown (probably imune)
- wfDebug( __METHOD__.": unsupported file type $file (code $exitCode).\n" );
- return NULL;
- } else if ( $mappedCode === AV_NO_VIRUS ) {
- # no virus found
- wfDebug( __METHOD__.": file passed virus scan.\n" );
- return false;
- } else {
- $output = join( "\n", $output );
- $output = trim( $output );
-
- if ( !$output ) {
- $output = true; #if there's no output, return true
- } elseif ( $msgPattern ) {
- $groups = array();
- if ( preg_match( $msgPattern, $output, $groups ) ) {
- if ( $groups[1] ) {
- $output = $groups[1];
- }
- }
- }
-
- wfDebug( __METHOD__.": FOUND VIRUS! scanner feedback: $output" );
- return $output;
- }
- }
-
- /**
- * Check if the temporary file is MacBinary-encoded, as some uploads
- * from Internet Explorer on Mac OS Classic and Mac OS X will be.
- * If so, the data fork will be extracted to a second temporary file,
- * which will then be checked for validity and either kept or discarded.
- *
- * @access private
- */
- function checkMacBinary() {
- $macbin = new MacBinary( $this->mTempPath );
- if( $macbin->isValid() ) {
- $dataFile = tempnam( wfTempDir(), "WikiMacBinary" );
- $dataHandle = fopen( $dataFile, 'wb' );
-
- wfDebug( "SpecialUpload::checkMacBinary: Extracting MacBinary data fork to $dataFile\n" );
- $macbin->extractData( $dataHandle );
-
- $this->mTempPath = $dataFile;
- $this->mFileSize = $macbin->dataForkLength();
-
- // We'll have to manually remove the new file if it's not kept.
- $this->mRemoveTempFile = true;
- }
- $macbin->close();
- }
-
- /**
- * Check if there's an overwrite conflict and, if so, if restrictions
- * forbid this user from performing the upload.
- *
- * @return mixed true on success, WikiError on failure
- * @access private
- */
- function checkOverwrite() {
- global $wgUser;
- // First check whether the local file can be overwritten
- if( $this->mLocalFile->exists() )
- if( !self::userCanReUpload( $wgUser, $this->mLocalFile ) )
- return 'fileexists-forbidden';
-
- // Check shared conflicts
- $file = wfFindFile( $this->mLocalFile->getName() );
- if ( $file && ( !$wgUser->isAllowed( 'reupload' ) ||
- !$wgUser->isAllowed( 'reupload-shared' ) ) )
- return 'fileexists-shared-forbidden';
-
- return true;
-
- }
-
- /**
- * Check if a user is the last uploader
- *
- * @param User $user
- * @param string $img, image name
- * @return bool
- */
- public static function userCanReUpload( User $user, $img ) {
- if( $user->isAllowed( 'reupload' ) )
- return true; // non-conditional
- if( !$user->isAllowed( 'reupload-own' ) )
- return false;
- if( is_string( $img ) )
- $img = wfLocalFile( $img );
- if ( !( $img instanceof LocalFile ) )
- return false;
-
- return $user->getId() == $img->getUser( 'id' );
- }
-
- public static function getExistsWarning( $file ) {
- if( $file->exists() )
- return array( 'exists', $file );
-
- if( $file->getTitle()->getArticleID() )
- return array( 'page-exists', $file );
-
- if( strpos( $file->getName(), '.' ) == false ) {
- $partname = $file->getName();
- $rawExtension = '';
- } else {
- $n = strrpos( $file->getName(), '.' );
- $rawExtension = substr( $file->getName(), $n + 1 );
- $partname = substr( $file->getName(), 0, $n );
- }
-
- if ( $rawExtension != $file->getExtension() ) {
- // We're not using the normalized form of the extension.
- // Normal form is lowercase, using most common of alternate
- // extensions (eg 'jpg' rather than 'JPEG').
- //
- // Check for another file using the normalized form...
- $nt_lc = Title::makeTitle( NS_FILE, $partname . '.' . $file->getExtension() );
- $file_lc = wfLocalFile( $nt_lc );
-
- if( $file_lc->exists() )
- return array( 'exists-normalized', $file_lc );
- }
-
- if ( self::isThumbName( $file->getName() ) ) {
- # Check for filenames like 50px- or 180px-, these are mostly thumbnails
- $nt_thb = Title::newFromText( substr( $partname , strpos( $partname , '-' ) +1 ) . '.' . $rawExtension );
- $file_thb = wfLocalFile( $nt_thb );
- if( $file_thb->exists() )
- return array( 'thumb', $file_thb );
- }
-
- return false;
- }
-
- public static function isThumbName( $filename ) {
- $n = strrpos( $filename, '.' );
- $partname = $n ? substr( $filename, 0, $n ) : $filename;
- return (
- substr( $partname , 3, 3 ) == 'px-' ||
- substr( $partname , 2, 3 ) == 'px-'
- ) &&
- ereg( "[0-9]{2}" , substr( $partname , 0, 2) );
- }
-
- /**
- * Get a list of blacklisted filename prefixes from [[MediaWiki:filename-prefix-blacklist]]
- *
- * @return array list of prefixes
- */
- public static function getFilenamePrefixBlacklist() {
- $blacklist = array();
- $message = wfMsgForContent( 'filename-prefix-blacklist' );
- if( $message && !( wfEmptyMsg( 'filename-prefix-blacklist', $message ) || $message == '-' ) ) {
- $lines = explode( "\n", $message );
- foreach( $lines as $line ) {
- // Remove comment lines
- $comment = substr( trim( $line ), 0, 1 );
- if ( $comment == '#' || $comment == '' ) {
- continue;
- }
- // Remove additional comments after a prefix
- $comment = strpos( $line, '#' );
- if ( $comment > 0 ) {
- $line = substr( $line, 0, $comment-1 );
- }
- $blacklist[] = trim( $line );
- }
- }
- return $blacklist;
- }
-
-
-}
diff --git a/includes/UploadFromStash.php b/includes/UploadFromStash.php
deleted file mode 100644
index 8bff3b49..00000000
--- a/includes/UploadFromStash.php
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-
-class UploadFromStash extends UploadBase {
- static function isValidSessionKey( $key, $sessionData ) {
- return !empty( $key ) &&
- is_array( $sessionData ) &&
- isset( $sessionData[$key] ) &&
- isset( $sessionData[$key]['version'] ) &&
- $sessionData[$key]['version'] == self::SESSION_VERSION
- ;
- }
- static function isValidRequest( $request ) {
- $sessionData = $request->getSessionData('wsUploadData');
- return self::isValidSessionKey(
- $request->getInt( 'wpSessionKey' ),
- $sessionData
- );
- }
-
- function initialize( $name, $sessionData ) {
- /**
- * Confirming a temporarily stashed upload.
- * We don't want path names to be forged, so we keep
- * them in the session on the server and just give
- * an opaque key to the user agent.
- */
- $this->initialize( $name,
- $sessionData['mTempPath'],
- $sessionData['mFileSize'],
- false
- );
-
- $this->mFileProps = $sessionData['mFileProps'];
- }
- function initializeFromRequest( &$request ) {
- $sessionKey = $request->getInt( 'wpSessionKey' );
- $sessionData = $request->getSessionData('wsUploadData');
-
- $desiredDestName = $request->getText( 'wpDestFile' );
- if( !$desiredDestName )
- $desiredDestName = $request->getText( 'wpUploadFile' );
-
- return $this->initialize( $desiredDestName, $sessionData[$sessionKey] );
- }
-
- /**
- * File has been previously verified so no need to do so again.
- */
- protected function verifyFile( $tmpfile ) {
- return true;
- }
- /**
- * We're here from "ignore warnings anyway" so return just OK
- */
- function checkWarnings() {
- return array();
- }
-}
diff --git a/includes/UploadFromUpload.php b/includes/UploadFromUpload.php
deleted file mode 100644
index 1b6762c6..00000000
--- a/includes/UploadFromUpload.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-class UploadFromUpload extends UploadBase {
-
- function initializeFromRequest( &$request ) {
- $desiredDestName = $request->getText( 'wpDestFile' );
- if( !$desiredDestName )
- $desiredDestName = $request->getText( 'wpUploadFile' );
-
- return $this->initialize(
- $desiredDestName,
- $request->getFileTempName( 'wpUploadFile' ),
- $request->getFileSize( 'wpUploadFile' )
- );
- }
-
- static function isValidRequest( $request ) {
- return (bool)$request->getFileTempName( 'wpUploadFile' );
- }
-}
diff --git a/includes/UploadFromUrl.php b/includes/UploadFromUrl.php
deleted file mode 100644
index 7e23b8cd..00000000
--- a/includes/UploadFromUrl.php
+++ /dev/null
@@ -1,92 +0,0 @@
-<?php
-
-
-class UploadFromUrl extends UploadBase {
- static function isAllowed( $user ) {
- if( !$user->isAllowed( 'upload_by_url' ) )
- return 'upload_by_url';
- return parent::isAllowed( $user );
- }
- static function isEnabled() {
- global $wgAllowCopyUploads;
- return $wgAllowCopyUploads && parent::isEnabled();
- }
-
- function initialize( $name, $url ) {
- global $wgTmpDirectory;
- $local_file = tempnam( $wgTmpDirectory, 'WEBUPLOAD' );
- $this-initialize( $name, $local_file, 0, true );
-
- $this->mUrl = trim( $url );
- }
-
- /**
- * Do the real fetching stuff
- */
- function fetchFile() {
- if( stripos($this->mUrl, 'http://') !== 0 && stripos($this->mUrl, 'ftp://') !== 0 ) {
- return array(
- 'status' => self::BEFORE_PROCESSING,
- 'error' => 'upload-proto-error',
- );
- }
- $res = $this->curlCopy();
- if( $res !== true ) {
- return array(
- 'status' => self::BEFORE_PROCESSING,
- 'error' => $res,
- );
- }
- return self::OK;
- }
-
- /**
- * Safe copy from URL
- * Returns true if there was an error, false otherwise
- */
- private function curlCopy() {
- global $wgUser, $wgOut;
-
- # Open temporary file
- $this->mCurlDestHandle = @fopen( $this->mTempPath, "wb" );
- if( $this->mCurlDestHandle === false ) {
- # Could not open temporary file to write in
- return 'upload-file-error';
- }
-
- $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, $this->mUrl);
- curl_setopt( $ch, CURLOPT_WRITEFUNCTION, array( $this, 'uploadCurlCallback' ) );
- curl_exec( $ch );
- $error = curl_errno( $ch );
- curl_close( $ch );
-
- fclose( $this->mCurlDestHandle );
- unset( $this->mCurlDestHandle );
-
- if( $error )
- return "upload-curl-error$errornum";
-
- return true;
- }
-
- /**
- * Callback function for CURL-based web transfer
- * Write data to file unless we've passed the length limit;
- * if so, abort immediately.
- * @access private
- */
- function uploadCurlCallback( $ch, $data ) {
- global $wgMaxUploadSize;
- $length = strlen( $data );
- $this->mFileSize += $length;
- if( $this->mFileSize > $wgMaxUploadSize ) {
- return 0;
- }
- fwrite( $this->mCurlDestHandle, $data );
- return $length;
- }
-}
diff --git a/includes/User.php b/includes/User.php
index 9fee089c..cc861ad4 100644
--- a/includes/User.php
+++ b/includes/User.php
@@ -141,9 +141,11 @@ class User {
'createtalk',
'delete',
'deletedhistory',
+ 'deleterevision',
'edit',
'editinterface',
'editusercssjs',
+ 'hideuser',
'import',
'importupload',
'ipblock-exempt',
@@ -155,6 +157,7 @@ class User {
'move-subpages',
'nominornewtalk',
'noratelimit',
+ 'override-export-depth',
'patrol',
'protect',
'proxyunbannable',
@@ -164,13 +167,17 @@ class User {
'reupload-shared',
'rollback',
'siteadmin',
+ 'suppressionlog',
'suppressredirect',
+ 'suppressrevision',
'trackback',
'undelete',
'unwatchedpages',
'upload',
'upload_by_url',
'userrights',
+ 'userrights-interwiki',
+ 'writeapi',
);
/**
* \string Cached results of getAllRights()
@@ -581,11 +588,12 @@ class User {
* @return \bool True or false
*/
static function isCreatableName( $name ) {
+ global $wgInvalidUsernameCharacters;
return
self::isUsableName( $name ) &&
// Registration-time character blacklisting...
- strpos( $name, '@' ) === false;
+ !preg_match( '/[' . preg_quote( $wgInvalidUsernameCharacters, '/' ) . ']/', $name );
}
/**
@@ -886,6 +894,8 @@ class User {
$dbr = wfGetDB( DB_MASTER );
$s = $dbr->selectRow( 'user', '*', array( 'user_id' => $this->mId ), __METHOD__ );
+ wfRunHooks( 'UserLoadFromDatabase', array( $this, &$s ) );
+
if ( $s !== false ) {
# Initialise user table data
$this->loadFromRow( $s );
@@ -909,7 +919,7 @@ class User {
$this->mDataLoaded = true;
if ( isset( $row->user_id ) ) {
- $this->mId = $row->user_id;
+ $this->mId = intval( $row->user_id );
}
$this->mName = $row->user_name;
$this->mRealName = $row->user_real_name;
@@ -1013,9 +1023,14 @@ class User {
* @return \type{\arrayof{\string}} Array of user toggle names
*/
static function getToggles() {
- global $wgContLang;
+ global $wgContLang, $wgUseRCPatrol;
$extraToggles = array();
wfRunHooks( 'UserToggles', array( &$extraToggles ) );
+ if( $wgUseRCPatrol ) {
+ $extraToggles[] = 'hidepatrolled';
+ $extraToggles[] = 'newpageshidepatrolled';
+ $extraToggles[] = 'watchlisthidepatrolled';
+ }
return array_merge( self::$mToggles, $extraToggles, $wgContLang->getExtraUserToggles() );
}
@@ -1149,10 +1164,17 @@ class User {
*/
public function isPingLimitable() {
global $wgRateLimitsExcludedGroups;
+ global $wgRateLimitsExcludedIPs;
if( array_intersect( $this->getEffectiveGroups(), $wgRateLimitsExcludedGroups ) ) {
// Deprecated, but kept for backwards-compatibility config
return false;
}
+ if( in_array( wfGetIP(), $wgRateLimitsExcludedIPs ) ) {
+ // No other good way currently to disable rate limits
+ // for specific IPs. :P
+ // But this is a crappy hack and should die.
+ return false;
+ }
return !$this->isAllowed('noratelimit');
}
@@ -1309,6 +1331,15 @@ class User {
}
/**
+ * If user is blocked, return the ID for the block
+ * @return \int Block ID
+ */
+ function getBlockId() {
+ $this->getBlockedStatus();
+ return ($this->mBlock ? $this->mBlock->mId : false);
+ }
+
+ /**
* Check if user is blocked on all wikis.
* Do not use for actual edit permission checks!
* This is intented for quick UI checks.
@@ -1909,6 +1940,13 @@ class User {
}
$this->mOptions[$oname] = $val;
}
+
+ /**
+ * Reset all options to the site defaults
+ */
+ function restoreOptions() {
+ $this->mOptions = User::getDefaultOptions();
+ }
/**
* Get the user's preferred date format.
@@ -1983,7 +2021,7 @@ class User {
* @return \int User'e edit count
*/
function getEditCount() {
- if ($this->mId) {
+ if ($this->getId()) {
if ( !isset( $this->mEditCount ) ) {
/* Populate the count, if it has not been populated yet */
$this->mEditCount = User::edits($this->mId);
@@ -2073,11 +2111,15 @@ class User {
* @param $action \string action to be checked
* @return \bool True if action is allowed, else false
*/
- function isAllowed($action='') {
+ function isAllowed( $action = '' ) {
if ( $action === '' )
- // In the spirit of DWIM
- return true;
-
+ return true; // In the spirit of DWIM
+ # Patrolling may not be enabled
+ if( $action === 'patrol' || $action === 'autopatrol' ) {
+ global $wgUseRCPatrol, $wgUseNPPatrol;
+ if( !$wgUseRCPatrol && !$wgUseNPPatrol )
+ return false;
+ }
# Use strict parameter to avoid matching numeric 0 accidentally inserted
# by misconfiguration: 0 == 'foo'
return in_array( $action, $this->getRights(), true );
@@ -2705,7 +2747,14 @@ class User {
* @return \bool True if matches, false otherwise
*/
function checkTemporaryPassword( $plaintext ) {
- return self::comparePasswords( $this->mNewpassword, $plaintext, $this->getId() );
+ global $wgNewPasswordExpiry;
+ if( self::comparePasswords( $this->mNewpassword, $plaintext, $this->getId() ) ) {
+ $this->load();
+ $expiry = wfTimestamp( TS_UNIX, $this->mNewpassTime ) + $wgNewPasswordExpiry;
+ return ( time() < $expiry );
+ } else {
+ return false;
+ }
}
/**
diff --git a/includes/UserArray.php b/includes/UserArray.php
index a2f54b7f..d48a4440 100644
--- a/includes/UserArray.php
+++ b/includes/UserArray.php
@@ -12,6 +12,17 @@ abstract class UserArray implements Iterator {
return $userArray;
}
+ static function newFromIDs( $ids ) {
+ $ids = array_map( 'intval', (array)$ids ); // paranoia
+ if ( !$ids )
+ // Database::select() doesn't like empty arrays
+ return new ArrayIterator(array());
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select( 'user', '*', array( 'user_id' => $ids ),
+ __METHOD__ );
+ return self::newFromResult( $res );
+ }
+
protected static function newFromResult_internal( $res ) {
$userArray = new UserArrayFromResult( $res );
return $userArray;
diff --git a/includes/UserMailer.php b/includes/UserMailer.php
index ab1a740b..b6484935 100644
--- a/includes/UserMailer.php
+++ b/includes/UserMailer.php
@@ -40,7 +40,7 @@ class MailAddress {
} else {
$this->address = strval( $address );
$this->name = strval( $name );
- $this->reaName = strval( $realName );
+ $this->realName = strval( $realName );
}
}
@@ -101,7 +101,7 @@ class UserMailer {
* @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: null).
+ * @param $replyto MailAddress: optional reply-to email (default: null).
* @param $contentType String: optional custom Content-Type
* @return mixed True on success, a WikiError object on failure.
*/
@@ -281,11 +281,44 @@ class EmailNotification {
* @param $oldid (default: false)
*/
function notifyOnPageChange($editor, $title, $timestamp, $summary, $minorEdit, $oldid = false) {
- global $wgEnotifUseJobQ;
+ global $wgEnotifUseJobQ, $wgEnotifWatchlist, $wgShowUpdatedMarker;
- if( $title->getNamespace() < 0 )
+ if ($title->getNamespace() < 0)
return;
+ // Build a list of users to notfiy
+ $watchers = array();
+ if ($wgEnotifWatchlist || $wgShowUpdatedMarker) {
+ $dbw = wfGetDB( DB_MASTER );
+ $res = $dbw->select( array( 'watchlist' ),
+ array( 'wl_user' ),
+ array(
+ 'wl_title' => $title->getDBkey(),
+ 'wl_namespace' => $title->getNamespace(),
+ 'wl_user != ' . intval( $editor->getID() ),
+ 'wl_notificationtimestamp IS NULL',
+ ), __METHOD__
+ );
+ while ($row = $dbw->fetchObject( $res ) ) {
+ $watchers[] = intval( $row->wl_user );
+ }
+ if ($watchers) {
+ // Update wl_notificationtimestamp for all watching users except
+ // the editor
+ $dbw->begin();
+ $dbw->update( 'watchlist',
+ array( /* SET */
+ 'wl_notificationtimestamp' => $dbw->timestamp( $timestamp )
+ ), array( /* WHERE */
+ 'wl_title' => $title->getDBkey(),
+ 'wl_namespace' => $title->getNamespace(),
+ 'wl_user' => $watchers
+ ), __METHOD__
+ );
+ $dbw->commit();
+ }
+ }
+
if ($wgEnotifUseJobQ) {
$params = array(
"editor" => $editor->getName(),
@@ -293,11 +326,12 @@ class EmailNotification {
"timestamp" => $timestamp,
"summary" => $summary,
"minorEdit" => $minorEdit,
- "oldid" => $oldid);
+ "oldid" => $oldid,
+ "watchers" => $watchers);
$job = new EnotifNotifyJob( $title, $params );
$job->insert();
} else {
- $this->actuallyNotifyOnPageChange($editor, $title, $timestamp, $summary, $minorEdit, $oldid);
+ $this->actuallyNotifyOnPageChange( $editor, $title, $timestamp, $summary, $minorEdit, $oldid, $watchers );
}
}
@@ -310,16 +344,16 @@ class EmailNotification {
*
* @param $editor User object
* @param $title Title object
- * @param $timestamp
- * @param $summary
- * @param $minorEdit
- * @param $oldid (default: false)
+ * @param $timestamp string Edit timestamp
+ * @param $summary string Edit summary
+ * @param $minorEdit bool
+ * @param $oldid int Revision ID
+ * @param $watchers array of user IDs
*/
- function actuallyNotifyOnPageChange($editor, $title, $timestamp, $summary, $minorEdit, $oldid=false) {
-
+ function actuallyNotifyOnPageChange($editor, $title, $timestamp, $summary, $minorEdit, $oldid, $watchers) {
# we use $wgPasswordSender as sender's address
global $wgEnotifWatchlist;
- global $wgEnotifMinorEdits, $wgEnotifUserTalk, $wgShowUpdatedMarker;
+ global $wgEnotifMinorEdits, $wgEnotifUserTalk;
global $wgEnotifImpersonal;
wfProfileIn( __METHOD__ );
@@ -364,30 +398,12 @@ class EmailNotification {
if ( $wgEnotifWatchlist ) {
// Send updates to watchers other than the current editor
- $userCondition = 'wl_user != ' . $editor->getID();
- if ( $userTalkId !== false ) {
- // Already sent an email to this person
- $userCondition .= ' AND wl_user != ' . intval( $userTalkId );
- }
- $dbr = wfGetDB( DB_SLAVE );
-
- list( $user ) = $dbr->tableNamesN( 'user' );
-
- $res = $dbr->select( array( 'watchlist', 'user' ),
- array( "$user.*" ),
- array(
- 'wl_user=user_id',
- 'wl_title' => $title->getDBkey(),
- 'wl_namespace' => $title->getNamespace(),
- $userCondition,
- 'wl_notificationtimestamp IS NULL',
- ), __METHOD__ );
- $userArray = UserArray::newFromResult( $res );
-
+ $userArray = UserArray::newFromIDs( $watchers );
foreach ( $userArray as $watchingUser ) {
if ( $watchingUser->getOption( 'enotifwatchlistpages' ) &&
( !$minorEdit || $watchingUser->getOption('enotifminoredits') ) &&
- $watchingUser->isEmailConfirmed() )
+ $watchingUser->isEmailConfirmed() &&
+ $watchingUser->getID() != $userTalkId )
{
$this->compose( $watchingUser );
}
@@ -402,28 +418,8 @@ class EmailNotification {
}
$this->sendMails();
-
- $latestTimestamp = Revision::getTimestampFromId( $title, $title->getLatestRevID() );
- // Do not update watchlists if something else already did.
- if ( $timestamp >= $latestTimestamp && ($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. Do
- # not do this to users for their own edits.
- $dbw = wfGetDB( DB_MASTER );
- $dbw->update( 'watchlist',
- array( /* SET */
- 'wl_notificationtimestamp' => $dbw->timestamp($timestamp)
- ), array( /* WHERE */
- 'wl_title' => $title->getDBkey(),
- 'wl_namespace' => $title->getNamespace(),
- 'wl_notificationtimestamp IS NULL',
- 'wl_user != ' . $editor->getID()
- ), __METHOD__
- );
- }
-
wfProfileOut( __METHOD__ );
- } # function NotifyOnChange
+ }
/**
* @private
@@ -563,7 +559,7 @@ class EmailNotification {
* @private
*/
function sendPersonalised( $watchingUser ) {
- global $wgLang, $wgEnotifUseRealName;
+ global $wgContLang, $wgEnotifUseRealName;
// From the PHP manual:
// Note: The to parameter cannot be an address in the form of "Something <someone@example.com>".
// The mail command will not parse this properly while talking with the MTA.
@@ -577,7 +573,7 @@ class EmailNotification {
# expressed in terms of individual local time of the notification
# recipient, i.e. watching user
$body = str_replace('$PAGEEDITDATE',
- $wgLang->timeanddate( $this->timestamp, true, false, $timecorrection ),
+ $wgContLang->timeanddate( $this->timestamp, true, false, $timecorrection ),
$body);
return UserMailer::send($to, $this->from, $this->subject, $body, $this->replyto);
@@ -588,7 +584,7 @@ class EmailNotification {
* mailing. Takes an array of MailAddress objects.
*/
function sendImpersonal( $addresses ) {
- global $wgLang;
+ global $wgContLang;
if (empty($addresses))
return;
@@ -597,7 +593,7 @@ class EmailNotification {
array( '$WATCHINGUSERNAME',
'$PAGEEDITDATE'),
array( wfMsgForContent('enotif_impersonal_salutation'),
- $wgLang->timeanddate($this->timestamp, true, false, false)),
+ $wgContLang->timeanddate($this->timestamp, true, false, false)),
$this->body);
return UserMailer::send($addresses, $this->from, $this->subject, $body, $this->replyto);
diff --git a/includes/WatchedItem.php b/includes/WatchedItem.php
index 2d2d34f1..a2c1f036 100644
--- a/includes/WatchedItem.php
+++ b/includes/WatchedItem.php
@@ -38,11 +38,10 @@ class WatchedItem {
public function isWatched() {
# Pages and their talk pages are considered equivalent for watching;
# remember that talk namespaces are numbered as page namespace+1.
- $fname = 'WatchedItem::isWatched';
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'watchlist', 1, array( 'wl_user' => $this->id, 'wl_namespace' => $this->ns,
- 'wl_title' => $this->ti ), $fname );
+ 'wl_title' => $this->ti ), __METHOD__ );
$iswatched = ($dbr->numRows( $res ) > 0) ? 1 : 0;
return $iswatched;
}
@@ -53,31 +52,30 @@ class WatchedItem {
* @return bool (always true)
*/
public function addWatch() {
- $fname = 'WatchedItem::addWatch';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
// Use INSERT IGNORE to avoid overwriting the notification timestamp
// if there's already an entry for this page
$dbw = wfGetDB( DB_MASTER );
$dbw->insert( 'watchlist',
array(
- 'wl_user' => $this->id,
- 'wl_namespace' => ($this->ns & ~1),
+ 'wl_user' => $this->id,
+ 'wl_namespace' => MWNamespace::getSubject($this->ns),
'wl_title' => $this->ti,
'wl_notificationtimestamp' => NULL
- ), $fname, 'IGNORE' );
+ ), __METHOD__, 'IGNORE' );
// Every single watched page needs now to be listed in watchlist;
// namespace:page and namespace_talk:page need separate entries:
$dbw->insert( 'watchlist',
array(
'wl_user' => $this->id,
- 'wl_namespace' => ($this->ns | 1 ),
+ 'wl_namespace' => MWNamespace::getTalk($this->ns),
'wl_title' => $this->ti,
'wl_notificationtimestamp' => NULL
- ), $fname, 'IGNORE' );
+ ), __METHOD__, 'IGNORE' );
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return true;
}
@@ -86,16 +84,14 @@ class WatchedItem {
* @return bool
*/
public function removeWatch() {
- $fname = 'WatchedItem::removeWatch';
-
$success = false;
$dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'watchlist',
array(
'wl_user' => $this->id,
- 'wl_namespace' => ($this->ns & ~1),
+ 'wl_namespace' => MWNamespace::getSubject($this->ns),
'wl_title' => $this->ti
- ), $fname
+ ), __METHOD__
);
if ( $dbw->affectedRows() ) {
$success = true;
@@ -108,9 +104,9 @@ class WatchedItem {
$dbw->delete( 'watchlist',
array(
'wl_user' => $this->id,
- 'wl_namespace' => ($this->ns | 1),
+ 'wl_namespace' => MWNamespace::getTalk($this->ns),
'wl_title' => $this->ti
- ), $fname
+ ), __METHOD__
);
if ( $dbw->affectedRows() ) {
@@ -134,8 +130,7 @@ class WatchedItem {
/**
* Handle duplicate entries. Backend for duplicateEntries().
*/
- private static function doDuplicateEntries( $ot, $nt ) {
- $fname = "WatchedItem::duplicateEntries";
+ private static function doDuplicateEntries( $ot, $nt ) {
$oldnamespace = $ot->getNamespace();
$newnamespace = $nt->getNamespace();
$oldtitle = $ot->getDBkey();
@@ -144,7 +139,7 @@ class WatchedItem {
$dbw = wfGetDB( DB_MASTER );
$res = $dbw->select( 'watchlist', 'wl_user',
array( 'wl_namespace' => $oldnamespace, 'wl_title' => $oldtitle ),
- $fname, 'FOR UPDATE'
+ __METHOD__, 'FOR UPDATE'
);
# Construct array to replace into the watchlist
$values = array();
@@ -165,7 +160,7 @@ class WatchedItem {
# Perform replace
# Note that multi-row replace is very efficient for MySQL but may be inefficient for
# some other DBMSes, mostly due to poor simulation by us
- $dbw->replace( 'watchlist', array(array( 'wl_user', 'wl_namespace', 'wl_title')), $values, $fname );
+ $dbw->replace( 'watchlist', array( array( 'wl_user', 'wl_namespace', 'wl_title' ) ), $values, __METHOD__ );
return true;
}
}
diff --git a/includes/WatchlistEditor.php b/includes/WatchlistEditor.php
index e49851bd..82f62f6a 100644
--- a/includes/WatchlistEditor.php
+++ b/includes/WatchlistEditor.php
@@ -407,6 +407,8 @@ class WatchlistEditor {
* @return string
*/
private function buildRemoveLine( $title, $redirect, $skin ) {
+ global $wgLang;
+
$link = $skin->makeLinkObj( $title );
if( $redirect )
$link = '<span class="watchlistredir">' . $link . '</span>';
@@ -419,7 +421,7 @@ class WatchlistEditor {
}
return "<li>"
. Xml::check( 'titles[]', false, array( 'value' => $title->getPrefixedText() ) )
- . $link . " (" . implode( ' | ', $tools ) . ")" . "</li>\n";
+ . $link . " (" . $wgLang->pipeList( $tools ) . ")" . "</li>\n";
}
/**
@@ -480,11 +482,13 @@ class WatchlistEditor {
* @return string
*/
public static function buildTools( $skin ) {
+ global $wgLang;
+
$tools = array();
$modes = array( 'view' => false, 'edit' => 'edit', 'raw' => 'raw' );
foreach( $modes as $mode => $subpage ) {
$tools[] = $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Watchlist', $subpage ), wfMsgHtml( "watchlisttools-{$mode}" ) );
}
- return implode( ' | ', $tools );
+ return $wgLang->pipeList( $tools );
}
}
diff --git a/includes/WebRequest.php b/includes/WebRequest.php
index 46747125..0928e4d5 100644
--- a/includes/WebRequest.php
+++ b/includes/WebRequest.php
@@ -231,6 +231,7 @@ class WebRequest {
$data = $this->normalizeUnicode( $data );
return $data;
} else {
+ taint( $default );
return $default;
}
}
@@ -251,7 +252,7 @@ class WebRequest {
$val = $default;
}
if( is_null( $val ) ) {
- return null;
+ return $val;
} else {
return (string)$val;
}
diff --git a/includes/Wiki.php b/includes/Wiki.php
index ce4ce67e..38f19c96 100644
--- a/includes/Wiki.php
+++ b/includes/Wiki.php
@@ -115,12 +115,15 @@ class MediaWiki {
if( count( $wgContLang->getVariants() ) > 1 && !is_null( $ret ) && $ret->getArticleID() == 0 )
$wgContLang->findVariantLink( $title, $ret );
}
- if( ( $oldid = $wgRequest->getInt( 'oldid' ) )
- && ( is_null( $ret ) || $ret->getNamespace() != NS_SPECIAL ) ) {
- // Allow oldid to override a changed or missing title.
- $rev = Revision::newFromId( $oldid );
- if( $rev ) {
- $ret = $rev->getTitle();
+ # For non-special titles, check for implicit titles
+ if( is_null( $ret ) || $ret->getNamespace() != NS_SPECIAL ) {
+ // We can have urls with just ?diff=,?oldid= or even just ?diff=
+ $oldid = $wgRequest->getInt( 'oldid' );
+ $oldid = $oldid ? $oldid : $wgRequest->getInt( 'diff' );
+ // Allow oldid to override a changed or missing title
+ if( $oldid ) {
+ $rev = Revision::newFromId( $oldid );
+ $ret = $rev ? $rev->getTitle() : $ret;
}
}
return $ret;
@@ -447,8 +450,10 @@ class MediaWiki {
$article->view();
break;
case 'raw': // includes JS/CSS
+ wfProfileIn( __METHOD__.'-raw' );
$raw = new RawPage( $article );
$raw->view();
+ wfProfileOut( __METHOD__.'-raw' );
break;
case 'watch':
case 'unwatch':
diff --git a/includes/WikiError.php b/includes/WikiError.php
index 41edb2f3..251c1742 100644
--- a/includes/WikiError.php
+++ b/includes/WikiError.php
@@ -75,6 +75,16 @@ class WikiErrorMsg extends WikiError {
$args = func_get_args();
array_shift( $args );
$this->mMessage = wfMsgReal( $message, $args, true );
+ $this->mMsgKey = $message;
+ $this->mMsgArgs = $args;
+ }
+
+ function getMessageKey() {
+ return $this->mMsgKey;
+ }
+
+ function getMessageArgs() {
+ return $this->mMsgArgs;
}
}
diff --git a/includes/Xml.php b/includes/Xml.php
index 68990d86..bbe0717c 100644
--- a/includes/Xml.php
+++ b/includes/Xml.php
@@ -172,6 +172,36 @@ class Xml {
. implode( "\n", $options )
. self::closeElement( 'select' );
}
+
+ /**
+ * @param $year Integer
+ * @param $month Integer
+ * @return string Formatted HTML
+ */
+ public static function dateMenu( $year, $month ) {
+ # Offset overrides year/month selection
+ if( $month && $month !== -1 ) {
+ $encMonth = intval( $month );
+ } else {
+ $encMonth = '';
+ }
+ if( $year ) {
+ $encYear = intval( $year );
+ } else if( $encMonth ) {
+ $thisMonth = intval( gmdate( 'n' ) );
+ $thisYear = intval( gmdate( 'Y' ) );
+ if( intval($encMonth) > $thisMonth ) {
+ $thisYear--;
+ }
+ $encYear = $thisYear;
+ } else {
+ $encYear = '';
+ }
+ return Xml::label( wfMsg( 'year' ), 'year' ) . ' '.
+ Xml::input( 'year', 4, $encYear, array('id' => 'year', 'maxlength' => 4) ) . ' '.
+ Xml::label( wfMsg( 'month' ), 'month' ) . ' '.
+ Xml::monthSelector( $encMonth, -1 );
+ }
/**
*
@@ -641,7 +671,6 @@ class Xml {
foreach( $fields as $labelmsg => $input ) {
$id = "mw-$labelmsg";
-
$form .= Xml::openElement( 'tr', array( 'id' => $id ) );
$form .= Xml::tags( 'td', array('class' => 'mw-label'), wfMsgExt( $labelmsg, array('parseinline') ) );
$form .= Xml::openElement( 'td', array( 'class' => 'mw-input' ) ) . $input . Xml::closeElement( 'td' );
@@ -649,7 +678,7 @@ class Xml {
}
if( $submitLabel ) {
- $form .= Xml::openElement( 'tr', array( 'id' => $id ) );
+ $form .= Xml::openElement( 'tr' );
$form .= Xml::tags( 'td', array(), '' );
$form .= Xml::openElement( 'td', array( 'class' => 'mw-submit' ) ) . Xml::submitButton( wfMsg( $submitLabel ) ) . Xml::closeElement( 'td' );
$form .= Xml::closeElement( 'tr' );
diff --git a/includes/ZhConversion.php b/includes/ZhConversion.php
index 4c1e0ae8..d7655df0 100644
--- a/includes/ZhConversion.php
+++ b/includes/ZhConversion.php
@@ -7,11356 +7,16956 @@
*/
$zh2Hant = array(
-"画"=>"畫",
-"丰"=>"豐",
-"帘"=>"簾",
-"愿"=>"願",
-"云"=>"雲",
-"筑"=>"築",
-"厂"=>"廠",
-"广"=>"廣",
-"别"=>"別",
-"冲"=>"沖",
-"种"=>"種",
-"虫"=>"蟲",
-"担"=>"擔",
-"党"=>"黨",
-"儿"=>"兒",
-"柜"=>"櫃",
-"坏"=>"壞",
-"几"=>"幾",
-"价"=>"價",
-"据"=>"據",
-"适"=>"適",
-"蜡"=>"蠟",
-"腊"=>"臘",
-"万"=>"萬",
-"宁"=>"寧",
-"苹"=>"蘋",
-"确"=>"確",
-"胜"=>"勝",
-"术"=>"術",
-"体"=>"體",
-"涂"=>"塗",
-"叶"=>"葉",
-"与"=>"與",
-"恶"=>"惡",
-"发"=>"發",
-"复"=>"復",
-"汇"=>"匯",
-"获"=>"獲",
-"饥"=>"飢",
-"尽"=>"盡",
-"历"=>"歷",
-"卤"=>"鹵",
-"弥"=>"彌",
-"签"=>"簽",
-"纤"=>"纖",
-"苏"=>"蘇",
-"坛"=>"壇",
-"团"=>"團",
-"须"=>"須",
-"脏"=>"臟",
-"钟"=>"鍾",
-"药"=>"葯",
-"当"=>"當",
-"蕴"=>"蘊",
-"线"=>"線",
-"为"=>"為",
-"产"=>"產",
-"众"=>"眾",
-"伪"=>"偽",
-"凫"=>"鳧",
-"厕"=>"廁",
-"启"=>"啟",
-"墙"=>"牆",
-"壳"=>"殼",
-"奖"=>"獎",
-"妫"=>"媯",
-"并"=>"並",
-"录"=>"錄",
-"悫"=>"愨",
-"极"=>"極",
-"沩"=>"溈",
-"瘘"=>"瘺",
-"硷"=>"礆",
-"竖"=>"豎",
-"绝"=>"絕",
-"绣"=>"綉",
-"绦"=>"絛",
-"绱"=>"緔",
-"绷"=>"綳",
-"绿"=>"綠",
-"缰"=>"韁",
-"苧"=>"苎",
-"莼"=>"蒓",
-"说"=>"說",
-"谣"=>"謠",
-"谫"=>"譾",
-"赃"=>"贓",
-"赍"=>"齎",
-"赝"=>"贗",
-"酝"=>"醞",
-"钩"=>"鉤",
-"钵"=>"缽",
-"锈"=>"銹",
-"锐"=>"銳",
-"锨"=>"杴",
-"镌"=>"鐫",
-"镢"=>"钁",
-"阅"=>"閱",
-"颓"=>"頹",
-"颜"=>"顏",
-"骂"=>"罵",
-"鲇"=>"鯰",
-"鲞"=>"鯗",
-"鳄"=>"鱷",
-"鸡"=>"雞",
-"鹚"=>"鶿",
-"仑"=>"侖",
-"赞"=>"贊",
-"荡"=>"盪",
-"锤"=>"錘",
-"㟆"=>"㠏",
-"㛟"=>"𡞵",
-"专"=>"專",
-"业"=>"業",
-"丛"=>"叢",
-"东"=>"東",
-"丝"=>"絲",
-"丢"=>"丟",
-"两"=>"兩",
-"严"=>"嚴",
-"丧"=>"喪",
-"个"=>"個",
-"临"=>"臨",
-"丽"=>"麗",
-"举"=>"舉",
-"义"=>"義",
-"乌"=>"烏",
-"乐"=>"樂",
-"乔"=>"喬",
-"习"=>"習",
-"乡"=>"鄉",
-"书"=>"書",
-"买"=>"買",
-"乱"=>"亂",
-"争"=>"爭",
-"亏"=>"虧",
-"亚"=>"亞",
-"亩"=>"畝",
-"亲"=>"親",
-"亵"=>"褻",
-"亸"=>"嚲",
-"亿"=>"億",
-"仅"=>"僅",
-"从"=>"從",
-"仓"=>"倉",
-"仪"=>"儀",
-"们"=>"們",
-"优"=>"優",
-"会"=>"會",
-"伛"=>"傴",
-"伞"=>"傘",
-"伟"=>"偉",
-"传"=>"傳",
-"伣"=>"俔",
-"伤"=>"傷",
-"伥"=>"倀",
-"伦"=>"倫",
-"伧"=>"傖",
-"伫"=>"佇",
-"佥"=>"僉",
-"侠"=>"俠",
-"侣"=>"侶",
-"侥"=>"僥",
-"侦"=>"偵",
-"侧"=>"側",
-"侨"=>"僑",
-"侩"=>"儈",
-"侪"=>"儕",
-"侬"=>"儂",
-"俣"=>"俁",
-"俦"=>"儔",
-"俨"=>"儼",
-"俩"=>"倆",
-"俪"=>"儷",
-"俫"=>"倈",
-"俭"=>"儉",
-"债"=>"債",
-"倾"=>"傾",
-"偬"=>"傯",
-"偻"=>"僂",
-"偾"=>"僨",
-"偿"=>"償",
-"傥"=>"儻",
-"傧"=>"儐",
-"储"=>"儲",
-"傩"=>"儺",
-"㑩"=>"儸",
-"兑"=>"兌",
-"兖"=>"兗",
-"兰"=>"蘭",
-"关"=>"關",
-"兴"=>"興",
-"兹"=>"茲",
-"养"=>"養",
-"兽"=>"獸",
-"冁"=>"囅",
-"内"=>"內",
-"冈"=>"岡",
-"册"=>"冊",
-"写"=>"寫",
-"军"=>"軍",
-"农"=>"農",
-"冯"=>"馮",
-"决"=>"決",
-"况"=>"況",
-"冻"=>"凍",
-"净"=>"凈",
-"凉"=>"涼",
-"减"=>"減",
-"凑"=>"湊",
-"凛"=>"凜",
-"凤"=>"鳳",
-"凭"=>"憑",
-"凯"=>"凱",
-"击"=>"擊",
-"凿"=>"鑿",
-"刍"=>"芻",
-"刘"=>"劉",
-"则"=>"則",
-"刚"=>"剛",
-"创"=>"創",
-"删"=>"刪",
-"刬"=>"剗",
-"刭"=>"剄",
-"刹"=>"剎",
-"刽"=>"劊",
-"刿"=>"劌",
-"剀"=>"剴",
-"剂"=>"劑",
-"剐"=>"剮",
-"剑"=>"劍",
-"剥"=>"剝",
-"剧"=>"劇",
-"㓥"=>"劏",
-"㔉"=>"劚",
-"劝"=>"勸",
-"办"=>"辦",
-"务"=>"務",
-"劢"=>"勱",
-"动"=>"動",
-"励"=>"勵",
-"劲"=>"勁",
-"劳"=>"勞",
-"势"=>"勢",
-"勋"=>"勛",
-"勚"=>"勩",
-"匀"=>"勻",
-"匦"=>"匭",
-"匮"=>"匱",
-"区"=>"區",
-"医"=>"醫",
-"华"=>"華",
-"协"=>"協",
-"单"=>"單",
-"卖"=>"賣",
-"卢"=>"盧",
-"卫"=>"衛",
-"却"=>"卻",
-"厅"=>"廳",
-"厉"=>"厲",
-"压"=>"壓",
-"厌"=>"厭",
-"厍"=>"厙",
-"厐"=>"龎",
-"厢"=>"廂",
-"厣"=>"厴",
-"厦"=>"廈",
-"厨"=>"廚",
-"厩"=>"廄",
-"厮"=>"廝",
-"县"=>"縣",
-"叁"=>"叄",
-"参"=>"參",
-"双"=>"雙",
-"变"=>"變",
-"叙"=>"敘",
-"叠"=>"疊",
-"号"=>"號",
-"叹"=>"嘆",
-"叽"=>"嘰",
-"吓"=>"嚇",
-"吕"=>"呂",
-"吗"=>"嗎",
-"吣"=>"唚",
-"吨"=>"噸",
-"听"=>"聽",
-"吴"=>"吳",
-"呐"=>"吶",
-"呒"=>"嘸",
-"呓"=>"囈",
-"呕"=>"嘔",
-"呖"=>"嚦",
-"呗"=>"唄",
-"员"=>"員",
-"呙"=>"咼",
-"呛"=>"嗆",
-"呜"=>"嗚",
-"咏"=>"詠",
-"咙"=>"嚨",
-"咛"=>"嚀",
-"咝"=>"噝",
-"咤"=>"吒",
-"响"=>"響",
-"哑"=>"啞",
-"哒"=>"噠",
-"哓"=>"嘵",
-"哔"=>"嗶",
-"哕"=>"噦",
-"哗"=>"嘩",
-"哙"=>"噲",
-"哜"=>"嚌",
-"哝"=>"噥",
-"哟"=>"喲",
-"唛"=>"嘜",
-"唝"=>"嗊",
-"唠"=>"嘮",
-"唡"=>"啢",
-"唢"=>"嗩",
-"唤"=>"喚",
-"啧"=>"嘖",
-"啬"=>"嗇",
-"啭"=>"囀",
-"啮"=>"嚙",
-"啴"=>"嘽",
-"啸"=>"嘯",
-"㖞"=>"喎",
-"喷"=>"噴",
-"喽"=>"嘍",
-"喾"=>"嚳",
-"嗫"=>"囁",
-"嗳"=>"噯",
-"嘘"=>"噓",
-"嘤"=>"嚶",
-"嘱"=>"囑",
-"㖊"=>"噚",
-"噜"=>"嚕",
-"嚣"=>"囂",
-"园"=>"園",
-"囱"=>"囪",
-"围"=>"圍",
-"囵"=>"圇",
-"国"=>"國",
-"图"=>"圖",
-"圆"=>"圓",
-"圣"=>"聖",
-"圹"=>"壙",
-"场"=>"場",
-"块"=>"塊",
-"坚"=>"堅",
-"坜"=>"壢",
-"坝"=>"壩",
-"坞"=>"塢",
-"坟"=>"墳",
-"坠"=>"墜",
-"垄"=>"壟",
-"垅"=>"壠",
-"垆"=>"壚",
-"垒"=>"壘",
-"垦"=>"墾",
-"垩"=>"堊",
-"垫"=>"墊",
-"垭"=>"埡",
-"垱"=>"壋",
-"垲"=>"塏",
-"垴"=>"堖",
-"埘"=>"塒",
-"埙"=>"塤",
-"埚"=>"堝",
-"埯"=>"垵",
-"堑"=>"塹",
-"堕"=>"墮",
-"𡒄"=>"壈",
-"壮"=>"壯",
-"声"=>"聲",
-"壶"=>"壺",
-"壸"=>"壼",
-"处"=>"處",
-"备"=>"備",
-"够"=>"夠",
-"头"=>"頭",
-"夸"=>"誇",
-"夹"=>"夾",
-"夺"=>"奪",
-"奁"=>"奩",
-"奂"=>"奐",
-"奋"=>"奮",
-"奥"=>"奧",
-"妆"=>"妝",
-"妇"=>"婦",
-"妈"=>"媽",
-"妩"=>"嫵",
-"妪"=>"嫗",
-"姗"=>"姍",
-"姹"=>"奼",
-"娄"=>"婁",
-"娅"=>"婭",
-"娆"=>"嬈",
-"娇"=>"嬌",
-"娈"=>"孌",
-"娱"=>"娛",
-"娲"=>"媧",
-"娴"=>"嫻",
-"婳"=>"嫿",
-"婴"=>"嬰",
-"婵"=>"嬋",
-"婶"=>"嬸",
-"媪"=>"媼",
-"嫒"=>"嬡",
-"嫔"=>"嬪",
-"嫱"=>"嬙",
-"嬷"=>"嬤",
-"孙"=>"孫",
-"学"=>"學",
-"孪"=>"孿",
-"宝"=>"寶",
-"实"=>"實",
-"宠"=>"寵",
-"审"=>"審",
-"宪"=>"憲",
-"宫"=>"宮",
-"宽"=>"寬",
-"宾"=>"賓",
-"寝"=>"寢",
-"对"=>"對",
-"寻"=>"尋",
-"导"=>"導",
-"寿"=>"壽",
-"将"=>"將",
-"尔"=>"爾",
-"尘"=>"塵",
-"尝"=>"嘗",
-"尧"=>"堯",
-"尴"=>"尷",
-"尸"=>"屍",
-"层"=>"層",
-"屃"=>"屓",
-"屉"=>"屜",
-"届"=>"屆",
-"属"=>"屬",
-"屡"=>"屢",
-"屦"=>"屨",
-"屿"=>"嶼",
-"岁"=>"歲",
-"岂"=>"豈",
-"岖"=>"嶇",
-"岗"=>"崗",
-"岘"=>"峴",
-"岙"=>"嶴",
-"岚"=>"嵐",
-"岛"=>"島",
-"岭"=>"嶺",
-"岽"=>"崬",
-"岿"=>"巋",
-"峄"=>"嶧",
-"峡"=>"峽",
-"峣"=>"嶢",
-"峤"=>"嶠",
-"峥"=>"崢",
-"峦"=>"巒",
-"崂"=>"嶗",
-"崃"=>"崍",
-"崄"=>"嶮",
-"崭"=>"嶄",
-"嵘"=>"嶸",
-"嵚"=>"嶔",
-"嵝"=>"嶁",
-"巅"=>"巔",
-"巩"=>"鞏",
-"巯"=>"巰",
-"币"=>"幣",
-"帅"=>"帥",
-"师"=>"師",
-"帏"=>"幃",
-"帐"=>"帳",
-"帜"=>"幟",
-"带"=>"帶",
-"帧"=>"幀",
-"帮"=>"幫",
-"帱"=>"幬",
-"帻"=>"幘",
-"帼"=>"幗",
-"幂"=>"冪",
-"庆"=>"慶",
-"庐"=>"廬",
-"庑"=>"廡",
-"库"=>"庫",
-"应"=>"應",
-"庙"=>"廟",
-"庞"=>"龐",
-"废"=>"廢",
-"廪"=>"廩",
-"开"=>"開",
-"异"=>"異",
-"弃"=>"棄",
-"弑"=>"弒",
-"张"=>"張",
-"弪"=>"弳",
-"弯"=>"彎",
-"弹"=>"彈",
-"强"=>"強",
-"归"=>"歸",
-"彦"=>"彥",
-"彻"=>"徹",
-"径"=>"徑",
-"徕"=>"徠",
-"忆"=>"憶",
-"忏"=>"懺",
-"忧"=>"憂",
-"忾"=>"愾",
-"怀"=>"懷",
-"态"=>"態",
-"怂"=>"慫",
-"怃"=>"憮",
-"怄"=>"慪",
-"怅"=>"悵",
-"怆"=>"愴",
-"怜"=>"憐",
-"总"=>"總",
-"怼"=>"懟",
-"怿"=>"懌",
-"恋"=>"戀",
-"恒"=>"恆",
-"恳"=>"懇",
-"恸"=>"慟",
-"恹"=>"懨",
-"恺"=>"愷",
-"恻"=>"惻",
-"恼"=>"惱",
-"恽"=>"惲",
-"悦"=>"悅",
-"悬"=>"懸",
-"悭"=>"慳",
-"悮"=>"悞",
-"悯"=>"憫",
-"惊"=>"驚",
-"惧"=>"懼",
-"惨"=>"慘",
-"惩"=>"懲",
-"惫"=>"憊",
-"惬"=>"愜",
-"惭"=>"慚",
-"惮"=>"憚",
-"惯"=>"慣",
-"愠"=>"慍",
-"愤"=>"憤",
-"愦"=>"憒",
-"慑"=>"懾",
-"懑"=>"懣",
-"懒"=>"懶",
-"懔"=>"懍",
-"戆"=>"戇",
-"戋"=>"戔",
-"戏"=>"戲",
-"戗"=>"戧",
-"战"=>"戰",
-"戬"=>"戩",
-"戯"=>"戱",
-"户"=>"戶",
-"扑"=>"撲",
-"执"=>"執",
-"扩"=>"擴",
-"扪"=>"捫",
-"扫"=>"掃",
-"扬"=>"揚",
-"扰"=>"擾",
-"抚"=>"撫",
-"抛"=>"拋",
-"抟"=>"摶",
-"抠"=>"摳",
-"抡"=>"掄",
-"抢"=>"搶",
-"护"=>"護",
-"报"=>"報",
-"拟"=>"擬",
-"拢"=>"攏",
-"拣"=>"揀",
-"拥"=>"擁",
-"拦"=>"攔",
-"拧"=>"擰",
-"拨"=>"撥",
-"择"=>"擇",
-"挂"=>"掛",
-"挚"=>"摯",
-"挛"=>"攣",
-"挜"=>"掗",
-"挝"=>"撾",
-"挞"=>"撻",
-"挟"=>"挾",
-"挠"=>"撓",
-"挡"=>"擋",
-"挢"=>"撟",
-"挣"=>"掙",
-"挤"=>"擠",
-"挥"=>"揮",
-"挦"=>"撏",
-"捝"=>"挩",
-"捞"=>"撈",
-"损"=>"損",
-"捡"=>"撿",
-"换"=>"換",
-"捣"=>"搗",
-"掳"=>"擄",
-"掴"=>"摑",
-"掷"=>"擲",
-"掸"=>"撣",
-"掺"=>"摻",
-"掼"=>"摜",
-"揽"=>"攬",
-"揾"=>"搵",
-"揿"=>"撳",
-"搀"=>"攙",
-"搁"=>"擱",
-"搂"=>"摟",
-"搅"=>"攪",
-"携"=>"攜",
-"摄"=>"攝",
-"摅"=>"攄",
-"摆"=>"擺",
-"摇"=>"搖",
-"摈"=>"擯",
-"摊"=>"攤",
-"撄"=>"攖",
-"撑"=>"撐",
-"㧑"=>"撝",
-"撵"=>"攆",
-"撷"=>"擷",
-"撸"=>"擼",
-"撺"=>"攛",
-"㧟"=>"擓",
-"擞"=>"擻",
-"攒"=>"攢",
-"敌"=>"敵",
-"敛"=>"斂",
-"数"=>"數",
-"斋"=>"齋",
-"斓"=>"斕",
-"斩"=>"斬",
-"断"=>"斷",
-"无"=>"無",
-"旧"=>"舊",
-"时"=>"時",
-"旷"=>"曠",
-"旸"=>"暘",
-"昙"=>"曇",
-"昼"=>"晝",
-"昽"=>"曨",
-"显"=>"顯",
-"晋"=>"晉",
-"晒"=>"曬",
-"晓"=>"曉",
-"晔"=>"曄",
-"晕"=>"暈",
-"晖"=>"暉",
-"暂"=>"暫",
-"暧"=>"曖",
-"机"=>"機",
-"杀"=>"殺",
-"杂"=>"雜",
-"权"=>"權",
-"杆"=>"桿",
-"条"=>"條",
-"来"=>"來",
-"杨"=>"楊",
-"杩"=>"榪",
-"杰"=>"傑",
-"构"=>"構",
-"枞"=>"樅",
-"枢"=>"樞",
-"枣"=>"棗",
-"枥"=>"櫪",
-"枧"=>"梘",
-"枨"=>"棖",
-"枪"=>"槍",
-"枫"=>"楓",
-"枭"=>"梟",
-"柠"=>"檸",
-"柽"=>"檉",
-"栀"=>"梔",
-"栅"=>"柵",
-"标"=>"標",
-"栈"=>"棧",
-"栉"=>"櫛",
-"栊"=>"櫳",
-"栋"=>"棟",
-"栌"=>"櫨",
-"栎"=>"櫟",
-"栏"=>"欄",
-"树"=>"樹",
-"栖"=>"棲",
-"样"=>"樣",
-"栾"=>"欒",
-"桠"=>"椏",
-"桡"=>"橈",
-"桢"=>"楨",
-"档"=>"檔",
-"桤"=>"榿",
-"桥"=>"橋",
-"桦"=>"樺",
-"桧"=>"檜",
-"桨"=>"槳",
-"桩"=>"樁",
-"梦"=>"夢",
-"梼"=>"檮",
-"梾"=>"棶",
-"梿"=>"槤",
-"检"=>"檢",
-"棁"=>"梲",
-"棂"=>"欞",
-"椁"=>"槨",
-"椟"=>"櫝",
-"椠"=>"槧",
-"椤"=>"欏",
-"椭"=>"橢",
-"楼"=>"樓",
-"榄"=>"欖",
-"榅"=>"榲",
-"榇"=>"櫬",
-"榈"=>"櫚",
-"榉"=>"櫸",
-"槚"=>"檟",
-"槛"=>"檻",
-"槟"=>"檳",
-"槠"=>"櫧",
-"横"=>"橫",
-"樯"=>"檣",
-"樱"=>"櫻",
-"橥"=>"櫫",
-"橱"=>"櫥",
-"橹"=>"櫓",
-"橼"=>"櫞",
-"檩"=>"檁",
-"欢"=>"歡",
-"欤"=>"歟",
-"欧"=>"歐",
-"歼"=>"殲",
-"殁"=>"歿",
-"殇"=>"殤",
-"残"=>"殘",
-"殒"=>"殞",
-"殓"=>"殮",
-"殚"=>"殫",
-"殡"=>"殯",
-"㱮"=>"殨",
-"㱩"=>"殰",
-"殴"=>"毆",
-"毁"=>"毀",
-"毂"=>"轂",
-"毕"=>"畢",
-"毙"=>"斃",
-"毡"=>"氈",
-"毵"=>"毿",
-"氇"=>"氌",
-"气"=>"氣",
-"氢"=>"氫",
-"氩"=>"氬",
-"氲"=>"氳",
-"汉"=>"漢",
-"汤"=>"湯",
-"汹"=>"洶",
-"沟"=>"溝",
-"没"=>"沒",
-"沣"=>"灃",
-"沤"=>"漚",
-"沥"=>"瀝",
-"沦"=>"淪",
-"沧"=>"滄",
-"沪"=>"滬",
-"泞"=>"濘",
-"泪"=>"淚",
-"泶"=>"澩",
-"泷"=>"瀧",
-"泸"=>"瀘",
-"泺"=>"濼",
-"泻"=>"瀉",
-"泼"=>"潑",
-"泽"=>"澤",
-"泾"=>"涇",
-"洁"=>"潔",
-"洒"=>"灑",
-"洼"=>"窪",
-"浃"=>"浹",
-"浅"=>"淺",
-"浆"=>"漿",
-"浇"=>"澆",
-"浈"=>"湞",
-"浊"=>"濁",
-"测"=>"測",
-"浍"=>"澮",
-"济"=>"濟",
-"浏"=>"瀏",
-"浐"=>"滻",
-"浑"=>"渾",
-"浒"=>"滸",
-"浓"=>"濃",
-"浔"=>"潯",
-"涛"=>"濤",
-"涝"=>"澇",
-"涞"=>"淶",
-"涟"=>"漣",
-"涠"=>"潿",
-"涡"=>"渦",
-"涣"=>"渙",
-"涤"=>"滌",
-"润"=>"潤",
-"涧"=>"澗",
-"涨"=>"漲",
-"涩"=>"澀",
-"渊"=>"淵",
-"渌"=>"淥",
-"渍"=>"漬",
-"渎"=>"瀆",
-"渐"=>"漸",
-"渑"=>"澠",
-"渔"=>"漁",
-"渖"=>"瀋",
-"渗"=>"滲",
-"温"=>"溫",
-"湾"=>"灣",
-"湿"=>"濕",
-"溃"=>"潰",
-"溅"=>"濺",
-"溆"=>"漵",
-"滗"=>"潷",
-"滚"=>"滾",
-"滞"=>"滯",
-"滟"=>"灧",
-"滠"=>"灄",
-"满"=>"滿",
-"滢"=>"瀅",
-"滤"=>"濾",
-"滥"=>"濫",
-"滦"=>"灤",
-"滨"=>"濱",
-"滩"=>"灘",
-"滪"=>"澦",
-"漤"=>"灠",
-"潆"=>"瀠",
-"潇"=>"瀟",
-"潋"=>"瀲",
-"潍"=>"濰",
-"潜"=>"潛",
-"潴"=>"瀦",
-"澜"=>"瀾",
-"濑"=>"瀨",
-"濒"=>"瀕",
-"㲿"=>"瀇",
-"灏"=>"灝",
-"灭"=>"滅",
-"灯"=>"燈",
-"灵"=>"靈",
-"灾"=>"災",
-"灿"=>"燦",
-"炀"=>"煬",
-"炉"=>"爐",
-"炖"=>"燉",
-"炜"=>"煒",
-"炝"=>"熗",
-"点"=>"點",
-"炼"=>"煉",
-"炽"=>"熾",
-"烁"=>"爍",
-"烂"=>"爛",
-"烃"=>"烴",
-"烛"=>"燭",
-"烟"=>"煙",
-"烦"=>"煩",
-"烧"=>"燒",
-"烨"=>"燁",
-"烩"=>"燴",
-"烫"=>"燙",
-"烬"=>"燼",
-"热"=>"熱",
-"焕"=>"煥",
-"焖"=>"燜",
-"焘"=>"燾",
-"㶽"=>"煱",
-"煴"=>"熅",
-"㶶"=>"燶",
-"爱"=>"愛",
-"爷"=>"爺",
-"牍"=>"牘",
-"牦"=>"氂",
-"牵"=>"牽",
-"牺"=>"犧",
-"犊"=>"犢",
-"状"=>"狀",
-"犷"=>"獷",
-"犸"=>"獁",
-"犹"=>"猶",
-"狈"=>"狽",
-"狝"=>"獮",
-"狞"=>"獰",
-"独"=>"獨",
-"狭"=>"狹",
-"狮"=>"獅",
-"狯"=>"獪",
-"狰"=>"猙",
-"狱"=>"獄",
-"狲"=>"猻",
-"猃"=>"獫",
-"猎"=>"獵",
-"猕"=>"獼",
-"猡"=>"玀",
-"猪"=>"豬",
-"猫"=>"貓",
-"猬"=>"蝟",
-"献"=>"獻",
-"獭"=>"獺",
-"㺍"=>"獱",
-"玑"=>"璣",
-"玚"=>"瑒",
-"玛"=>"瑪",
-"玮"=>"瑋",
-"环"=>"環",
-"现"=>"現",
-"玱"=>"瑲",
-"玺"=>"璽",
-"珐"=>"琺",
-"珑"=>"瓏",
-"珰"=>"璫",
-"珲"=>"琿",
-"琏"=>"璉",
-"琐"=>"瑣",
-"琼"=>"瓊",
-"瑶"=>"瑤",
-"瑷"=>"璦",
-"璎"=>"瓔",
-"瓒"=>"瓚",
-"瓯"=>"甌",
-"电"=>"電",
-"画"=>"畫",
-"畅"=>"暢",
-"畴"=>"疇",
-"疖"=>"癤",
-"疗"=>"療",
-"疟"=>"瘧",
-"疠"=>"癘",
-"疡"=>"瘍",
-"疬"=>"癧",
-"疭"=>"瘲",
-"疮"=>"瘡",
-"疯"=>"瘋",
-"疱"=>"皰",
-"疴"=>"痾",
-"痈"=>"癰",
-"痉"=>"痙",
-"痒"=>"癢",
-"痖"=>"瘂",
-"痨"=>"癆",
-"痪"=>"瘓",
-"痫"=>"癇",
-"瘅"=>"癉",
-"瘆"=>"瘮",
-"瘗"=>"瘞",
-"瘪"=>"癟",
-"瘫"=>"癱",
-"瘾"=>"癮",
-"瘿"=>"癭",
-"癞"=>"癩",
-"癣"=>"癬",
-"癫"=>"癲",
-"皑"=>"皚",
-"皱"=>"皺",
-"皲"=>"皸",
-"盏"=>"盞",
-"盐"=>"鹽",
-"监"=>"監",
-"盖"=>"蓋",
-"盗"=>"盜",
-"盘"=>"盤",
-"眍"=>"瞘",
-"眦"=>"眥",
-"眬"=>"矓",
-"睁"=>"睜",
-"睐"=>"睞",
-"睑"=>"瞼",
-"瞆"=>"瞶",
-"瞒"=>"瞞",
-"䁖"=>"瞜",
-"瞩"=>"矚",
-"矫"=>"矯",
-"矶"=>"磯",
-"矾"=>"礬",
-"矿"=>"礦",
-"砀"=>"碭",
-"码"=>"碼",
-"砖"=>"磚",
-"砗"=>"硨",
-"砚"=>"硯",
-"砜"=>"碸",
-"砺"=>"礪",
-"砻"=>"礱",
-"砾"=>"礫",
-"础"=>"礎",
-"硁"=>"硜",
-"硕"=>"碩",
-"硖"=>"硤",
-"硗"=>"磽",
-"硙"=>"磑",
-"碍"=>"礙",
-"碛"=>"磧",
-"碜"=>"磣",
-"碱"=>"鹼",
-"礼"=>"禮",
-"祃"=>"禡",
-"祎"=>"禕",
-"祢"=>"禰",
-"祯"=>"禎",
-"祷"=>"禱",
-"祸"=>"禍",
-"禀"=>"稟",
-"禄"=>"祿",
-"禅"=>"禪",
-"离"=>"離",
-"秃"=>"禿",
-"秆"=>"稈",
-"积"=>"積",
-"称"=>"稱",
-"秽"=>"穢",
-"秾"=>"穠",
-"稆"=>"穭",
-"税"=>"稅",
-"䅉"=>"稏",
-"稣"=>"穌",
-"稳"=>"穩",
-"穑"=>"穡",
-"穷"=>"窮",
-"窃"=>"竊",
-"窍"=>"竅",
-"窎"=>"窵",
-"窑"=>"窯",
-"窜"=>"竄",
-"窝"=>"窩",
-"窥"=>"窺",
-"窦"=>"竇",
-"窭"=>"窶",
-"竞"=>"競",
-"笃"=>"篤",
-"笋"=>"筍",
-"笔"=>"筆",
-"笕"=>"筧",
-"笺"=>"箋",
-"笼"=>"籠",
-"笾"=>"籩",
-"筚"=>"篳",
-"筛"=>"篩",
-"筜"=>"簹",
-"筝"=>"箏",
-"䇲"=>"筴",
-"筹"=>"籌",
-"筼"=>"篔",
-"简"=>"簡",
-"箓"=>"籙",
-"箦"=>"簀",
-"箧"=>"篋",
-"箨"=>"籜",
-"箩"=>"籮",
-"箪"=>"簞",
-"箫"=>"簫",
-"篑"=>"簣",
-"篓"=>"簍",
-"篮"=>"籃",
-"篱"=>"籬",
-"簖"=>"籪",
-"籁"=>"籟",
-"籴"=>"糴",
-"类"=>"類",
-"籼"=>"秈",
-"粜"=>"糶",
-"粝"=>"糲",
-"粤"=>"粵",
-"粪"=>"糞",
-"粮"=>"糧",
-"糁"=>"糝",
-"糇"=>"餱",
-"紧"=>"緊",
-"䌷"=>"紬",
-"䌹"=>"絅",
-"絷"=>"縶",
-"䌼"=>"綐",
-"䌽"=>"綵",
-"䌸"=>"縳",
-"䍁"=>"繸",
-"䍀"=>"繿",
-"纟"=>"糹",
-"纠"=>"糾",
-"纡"=>"紆",
-"红"=>"紅",
-"纣"=>"紂",
-"纥"=>"紇",
-"约"=>"約",
-"级"=>"級",
-"纨"=>"紈",
-"纩"=>"纊",
-"纪"=>"紀",
-"纫"=>"紉",
-"纬"=>"緯",
-"纭"=>"紜",
-"纮"=>"紘",
-"纯"=>"純",
-"纰"=>"紕",
-"纱"=>"紗",
-"纲"=>"綱",
-"纳"=>"納",
-"纴"=>"紝",
-"纵"=>"縱",
-"纶"=>"綸",
-"纷"=>"紛",
-"纸"=>"紙",
-"纹"=>"紋",
-"纺"=>"紡",
-"纻"=>"紵",
-"纼"=>"紖",
-"纽"=>"紐",
-"纾"=>"紓",
-"绀"=>"紺",
-"绁"=>"紲",
-"绂"=>"紱",
-"练"=>"練",
-"组"=>"組",
-"绅"=>"紳",
-"细"=>"細",
-"织"=>"織",
-"终"=>"終",
-"绉"=>"縐",
-"绊"=>"絆",
-"绋"=>"紼",
-"绌"=>"絀",
-"绍"=>"紹",
-"绎"=>"繹",
-"经"=>"經",
-"绐"=>"紿",
-"绑"=>"綁",
-"绒"=>"絨",
-"结"=>"結",
-"绔"=>"絝",
-"绕"=>"繞",
-"绖"=>"絰",
-"绗"=>"絎",
-"绘"=>"繪",
-"给"=>"給",
-"绚"=>"絢",
-"绛"=>"絳",
-"络"=>"絡",
-"绞"=>"絞",
-"统"=>"統",
-"绠"=>"綆",
-"绡"=>"綃",
-"绢"=>"絹",
-"绤"=>"綌",
-"绥"=>"綏",
-"继"=>"繼",
-"绨"=>"綈",
-"绩"=>"績",
-"绪"=>"緒",
-"绫"=>"綾",
-"绬"=>"緓",
-"续"=>"續",
-"绮"=>"綺",
-"绯"=>"緋",
-"绰"=>"綽",
-"绲"=>"緄",
-"绳"=>"繩",
-"维"=>"維",
-"绵"=>"綿",
-"绶"=>"綬",
-"绸"=>"綢",
-"绹"=>"綯",
-"绺"=>"綹",
-"绻"=>"綣",
-"综"=>"綜",
-"绽"=>"綻",
-"绾"=>"綰",
-"缀"=>"綴",
-"缁"=>"緇",
-"缂"=>"緙",
-"缃"=>"緗",
-"缄"=>"緘",
-"缅"=>"緬",
-"缆"=>"纜",
-"缇"=>"緹",
-"缈"=>"緲",
-"缉"=>"緝",
-"缊"=>"縕",
-"缋"=>"繢",
-"缌"=>"緦",
-"缍"=>"綞",
-"缎"=>"緞",
-"缏"=>"緶",
-"缑"=>"緱",
-"缒"=>"縋",
-"缓"=>"緩",
-"缔"=>"締",
-"缕"=>"縷",
-"编"=>"編",
-"缗"=>"緡",
-"缘"=>"緣",
-"缙"=>"縉",
-"缚"=>"縛",
-"缛"=>"縟",
-"缜"=>"縝",
-"缝"=>"縫",
-"缞"=>"縗",
-"缟"=>"縞",
-"缠"=>"纏",
-"缡"=>"縭",
-"缢"=>"縊",
-"缣"=>"縑",
-"缤"=>"繽",
-"缥"=>"縹",
-"缦"=>"縵",
-"缧"=>"縲",
-"缨"=>"纓",
-"缩"=>"縮",
-"缪"=>"繆",
-"缫"=>"繅",
-"缬"=>"纈",
-"缭"=>"繚",
-"缮"=>"繕",
-"缯"=>"繒",
-"缱"=>"繾",
-"缲"=>"繰",
-"缳"=>"繯",
-"缴"=>"繳",
-"缵"=>"纘",
-"罂"=>"罌",
-"网"=>"網",
-"罗"=>"羅",
-"罚"=>"罰",
-"罢"=>"罷",
-"罴"=>"羆",
-"羁"=>"羈",
-"羟"=>"羥",
-"翘"=>"翹",
-"耢"=>"耮",
-"耧"=>"耬",
-"耸"=>"聳",
-"耻"=>"恥",
-"聂"=>"聶",
-"聋"=>"聾",
-"职"=>"職",
-"聍"=>"聹",
-"联"=>"聯",
-"聩"=>"聵",
-"聪"=>"聰",
-"肃"=>"肅",
-"肠"=>"腸",
-"肤"=>"膚",
-"肮"=>"骯",
-"肴"=>"餚",
-"肾"=>"腎",
-"肿"=>"腫",
-"胀"=>"脹",
-"胁"=>"脅",
-"胆"=>"膽",
-"胧"=>"朧",
-"胨"=>"腖",
-"胪"=>"臚",
-"胫"=>"脛",
-"胶"=>"膠",
-"脉"=>"脈",
-"脍"=>"膾",
-"脐"=>"臍",
-"脑"=>"腦",
-"脓"=>"膿",
-"脔"=>"臠",
-"脚"=>"腳",
-"脱"=>"脫",
-"脶"=>"腡",
-"脸"=>"臉",
-"腭"=>"齶",
-"腻"=>"膩",
-"腼"=>"靦",
-"腽"=>"膃",
-"腾"=>"騰",
-"膑"=>"臏",
-"臜"=>"臢",
-"舆"=>"輿",
-"舣"=>"艤",
-"舰"=>"艦",
-"舱"=>"艙",
-"舻"=>"艫",
-"艰"=>"艱",
-"艳"=>"艷",
-"艺"=>"藝",
-"节"=>"節",
-"芈"=>"羋",
-"芗"=>"薌",
-"芜"=>"蕪",
-"芦"=>"蘆",
-"苁"=>"蓯",
-"苇"=>"葦",
-"苈"=>"藶",
-"苋"=>"莧",
-"苌"=>"萇",
-"苍"=>"蒼",
-"苎"=>"苧",
-"茎"=>"莖",
-"茏"=>"蘢",
-"茑"=>"蔦",
-"茔"=>"塋",
-"茕"=>"煢",
-"茧"=>"繭",
-"荆"=>"荊",
-"荐"=>"薦",
-"荙"=>"薘",
-"荚"=>"莢",
-"荛"=>"蕘",
-"荜"=>"蓽",
-"荞"=>"蕎",
-"荟"=>"薈",
-"荠"=>"薺",
-"荣"=>"榮",
-"荤"=>"葷",
-"荥"=>"滎",
-"荦"=>"犖",
-"荧"=>"熒",
-"荨"=>"蕁",
-"荩"=>"藎",
-"荪"=>"蓀",
-"荫"=>"蔭",
-"荬"=>"蕒",
-"荭"=>"葒",
-"荮"=>"葤",
-"莅"=>"蒞",
-"莱"=>"萊",
-"莲"=>"蓮",
-"莳"=>"蒔",
-"莴"=>"萵",
-"莶"=>"薟",
-"莸"=>"蕕",
-"莹"=>"瑩",
-"莺"=>"鶯",
-"萝"=>"蘿",
-"萤"=>"螢",
-"营"=>"營",
-"萦"=>"縈",
-"萧"=>"蕭",
-"萨"=>"薩",
-"葱"=>"蔥",
-"蒇"=>"蕆",
-"蒉"=>"蕢",
-"蒋"=>"蔣",
-"蒌"=>"蔞",
-"蓝"=>"藍",
-"蓟"=>"薊",
-"蓠"=>"蘺",
-"蓣"=>"蕷",
-"蓥"=>"鎣",
-"蓦"=>"驀",
-"蔂"=>"虆",
-"蔷"=>"薔",
-"蔹"=>"蘞",
-"蔺"=>"藺",
-"蔼"=>"藹",
-"蕰"=>"薀",
-"蕲"=>"蘄",
-"薮"=>"藪",
-"䓕"=>"薳",
-"藓"=>"蘚",
-"蘖"=>"櫱",
-"虏"=>"虜",
-"虑"=>"慮",
-"虚"=>"虛",
-"虬"=>"虯",
-"虮"=>"蟣",
-"虽"=>"雖",
-"虾"=>"蝦",
-"虿"=>"蠆",
-"蚀"=>"蝕",
-"蚁"=>"蟻",
-"蚂"=>"螞",
-"蚕"=>"蠶",
-"蚬"=>"蜆",
-"蛊"=>"蠱",
-"蛎"=>"蠣",
-"蛏"=>"蟶",
-"蛮"=>"蠻",
-"蛰"=>"蟄",
-"蛱"=>"蛺",
-"蛲"=>"蟯",
-"蛳"=>"螄",
-"蛴"=>"蠐",
-"蜕"=>"蛻",
-"蜗"=>"蝸",
-"蝇"=>"蠅",
-"蝈"=>"蟈",
-"蝉"=>"蟬",
-"蝼"=>"螻",
-"蝾"=>"蠑",
-"螀"=>"螿",
-"螨"=>"蟎",
-"䗖"=>"螮",
-"蟏"=>"蠨",
-"衅"=>"釁",
-"衔"=>"銜",
-"补"=>"補",
-"衬"=>"襯",
-"衮"=>"袞",
-"袄"=>"襖",
-"袅"=>"裊",
-"袆"=>"褘",
-"袜"=>"襪",
-"袭"=>"襲",
-"袯"=>"襏",
-"装"=>"裝",
-"裆"=>"襠",
-"裈"=>"褌",
-"裢"=>"褳",
-"裣"=>"襝",
-"裤"=>"褲",
-"裥"=>"襇",
-"褛"=>"褸",
-"褴"=>"襤",
-"䙓"=>"襬",
-"见"=>"見",
-"观"=>"觀",
-"觃"=>"覎",
-"规"=>"規",
-"觅"=>"覓",
-"视"=>"視",
-"觇"=>"覘",
-"览"=>"覽",
-"觉"=>"覺",
-"觊"=>"覬",
-"觋"=>"覡",
-"觌"=>"覿",
-"觍"=>"覥",
-"觎"=>"覦",
-"觏"=>"覯",
-"觐"=>"覲",
-"觑"=>"覷",
-"觞"=>"觴",
-"触"=>"觸",
-"觯"=>"觶",
-"訚"=>"誾",
-"䜣"=>"訢",
-"誉"=>"譽",
-"誊"=>"謄",
-"䜧"=>"譅",
-"讠"=>"訁",
-"计"=>"計",
-"订"=>"訂",
-"讣"=>"訃",
-"认"=>"認",
-"讥"=>"譏",
-"讦"=>"訐",
-"讧"=>"訌",
-"讨"=>"討",
-"让"=>"讓",
-"讪"=>"訕",
-"讫"=>"訖",
-"讬"=>"託",
-"训"=>"訓",
-"议"=>"議",
-"讯"=>"訊",
-"记"=>"記",
-"讱"=>"訒",
-"讲"=>"講",
-"讳"=>"諱",
-"讴"=>"謳",
-"讵"=>"詎",
-"讶"=>"訝",
-"讷"=>"訥",
-"许"=>"許",
-"讹"=>"訛",
-"论"=>"論",
-"讻"=>"訩",
-"讼"=>"訟",
-"讽"=>"諷",
-"设"=>"設",
-"访"=>"訪",
-"诀"=>"訣",
-"证"=>"證",
-"诂"=>"詁",
-"诃"=>"訶",
-"评"=>"評",
-"诅"=>"詛",
-"识"=>"識",
-"诇"=>"詗",
-"诈"=>"詐",
-"诉"=>"訴",
-"诊"=>"診",
-"诋"=>"詆",
-"诌"=>"謅",
-"词"=>"詞",
-"诎"=>"詘",
-"诏"=>"詔",
-"诐"=>"詖",
-"译"=>"譯",
-"诒"=>"詒",
-"诓"=>"誆",
-"诔"=>"誄",
-"试"=>"試",
-"诖"=>"詿",
-"诗"=>"詩",
-"诘"=>"詰",
-"诙"=>"詼",
-"诚"=>"誠",
-"诛"=>"誅",
-"诜"=>"詵",
-"话"=>"話",
-"诞"=>"誕",
-"诟"=>"詬",
-"诠"=>"詮",
-"诡"=>"詭",
-"询"=>"詢",
-"诣"=>"詣",
-"诤"=>"諍",
-"该"=>"該",
-"详"=>"詳",
-"诧"=>"詫",
-"诨"=>"諢",
-"诩"=>"詡",
-"诪"=>"譸",
-"诫"=>"誡",
-"诬"=>"誣",
-"语"=>"語",
-"诮"=>"誚",
-"误"=>"誤",
-"诰"=>"誥",
-"诱"=>"誘",
-"诲"=>"誨",
-"诳"=>"誑",
-"诵"=>"誦",
-"诶"=>"誒",
-"请"=>"請",
-"诸"=>"諸",
-"诹"=>"諏",
-"诺"=>"諾",
-"读"=>"讀",
-"诼"=>"諑",
-"诽"=>"誹",
-"课"=>"課",
-"诿"=>"諉",
-"谀"=>"諛",
-"谁"=>"誰",
-"谂"=>"諗",
-"调"=>"調",
-"谄"=>"諂",
-"谅"=>"諒",
-"谆"=>"諄",
-"谇"=>"誶",
-"谈"=>"談",
-"谊"=>"誼",
-"谋"=>"謀",
-"谌"=>"諶",
-"谍"=>"諜",
-"谎"=>"謊",
-"谏"=>"諫",
-"谐"=>"諧",
-"谑"=>"謔",
-"谒"=>"謁",
-"谓"=>"謂",
-"谔"=>"諤",
-"谕"=>"諭",
-"谖"=>"諼",
-"谗"=>"讒",
-"谘"=>"諮",
-"谙"=>"諳",
-"谚"=>"諺",
-"谛"=>"諦",
-"谜"=>"謎",
-"谝"=>"諞",
-"谞"=>"諝",
-"谟"=>"謨",
-"谠"=>"讜",
-"谡"=>"謖",
-"谢"=>"謝",
-"谤"=>"謗",
-"谥"=>"謚",
-"谦"=>"謙",
-"谧"=>"謐",
-"谨"=>"謹",
-"谩"=>"謾",
-"谪"=>"謫",
-"谬"=>"謬",
-"谭"=>"譚",
-"谮"=>"譖",
-"谯"=>"譙",
-"谰"=>"讕",
-"谱"=>"譜",
-"谲"=>"譎",
-"谳"=>"讞",
-"谴"=>"譴",
-"谵"=>"譫",
-"谶"=>"讖",
-"豮"=>"豶",
-"䝙"=>"貙",
-"䞐"=>"賰",
-"贝"=>"貝",
-"贞"=>"貞",
-"负"=>"負",
-"贠"=>"貟",
-"贡"=>"貢",
-"财"=>"財",
-"责"=>"責",
-"贤"=>"賢",
-"败"=>"敗",
-"账"=>"賬",
-"货"=>"貨",
-"质"=>"質",
-"贩"=>"販",
-"贪"=>"貪",
-"贫"=>"貧",
-"贬"=>"貶",
-"购"=>"購",
-"贮"=>"貯",
-"贯"=>"貫",
-"贰"=>"貳",
-"贱"=>"賤",
-"贲"=>"賁",
-"贳"=>"貰",
-"贴"=>"貼",
-"贵"=>"貴",
-"贶"=>"貺",
-"贷"=>"貸",
-"贸"=>"貿",
-"费"=>"費",
-"贺"=>"賀",
-"贻"=>"貽",
-"贼"=>"賊",
-"贽"=>"贄",
-"贾"=>"賈",
-"贿"=>"賄",
-"赀"=>"貲",
-"赁"=>"賃",
-"赂"=>"賂",
-"资"=>"資",
-"赅"=>"賅",
-"赆"=>"贐",
-"赇"=>"賕",
-"赈"=>"賑",
-"赉"=>"賚",
-"赊"=>"賒",
-"赋"=>"賦",
-"赌"=>"賭",
-"赎"=>"贖",
-"赏"=>"賞",
-"赐"=>"賜",
-"赑"=>"贔",
-"赒"=>"賙",
-"赓"=>"賡",
-"赔"=>"賠",
-"赕"=>"賧",
-"赖"=>"賴",
-"赗"=>"賵",
-"赘"=>"贅",
-"赙"=>"賻",
-"赚"=>"賺",
-"赛"=>"賽",
-"赜"=>"賾",
-"赟"=>"贇",
-"赠"=>"贈",
-"赡"=>"贍",
-"赢"=>"贏",
-"赣"=>"贛",
-"赪"=>"赬",
-"赵"=>"趙",
-"赶"=>"趕",
-"趋"=>"趨",
-"趱"=>"趲",
-"趸"=>"躉",
-"跃"=>"躍",
-"跄"=>"蹌",
-"跞"=>"躒",
-"践"=>"踐",
-"跶"=>"躂",
-"跷"=>"蹺",
-"跸"=>"蹕",
-"跹"=>"躚",
-"跻"=>"躋",
-"踊"=>"踴",
-"踌"=>"躊",
-"踪"=>"蹤",
-"踬"=>"躓",
-"踯"=>"躑",
-"蹑"=>"躡",
-"蹒"=>"蹣",
-"蹰"=>"躕",
-"蹿"=>"躥",
-"躏"=>"躪",
-"躜"=>"躦",
-"躯"=>"軀",
-"车"=>"車",
-"轧"=>"軋",
-"轨"=>"軌",
-"轩"=>"軒",
-"轪"=>"軑",
-"轫"=>"軔",
-"转"=>"轉",
-"轭"=>"軛",
-"轮"=>"輪",
-"软"=>"軟",
-"轰"=>"轟",
-"轱"=>"軲",
-"轲"=>"軻",
-"轳"=>"轤",
-"轴"=>"軸",
-"轵"=>"軹",
-"轶"=>"軼",
-"轷"=>"軤",
-"轸"=>"軫",
-"轹"=>"轢",
-"轺"=>"軺",
-"轻"=>"輕",
-"轼"=>"軾",
-"载"=>"載",
-"轾"=>"輊",
-"轿"=>"轎",
-"辀"=>"輈",
-"辁"=>"輇",
-"辂"=>"輅",
-"较"=>"較",
-"辄"=>"輒",
-"辅"=>"輔",
-"辆"=>"輛",
-"辇"=>"輦",
-"辈"=>"輩",
-"辉"=>"輝",
-"辊"=>"輥",
-"辋"=>"輞",
-"辌"=>"輬",
-"辍"=>"輟",
-"辎"=>"輜",
-"辏"=>"輳",
-"辐"=>"輻",
-"辑"=>"輯",
-"辒"=>"轀",
-"输"=>"輸",
-"辔"=>"轡",
-"辕"=>"轅",
-"辖"=>"轄",
-"辗"=>"輾",
-"辘"=>"轆",
-"辙"=>"轍",
-"辚"=>"轔",
-"辞"=>"辭",
-"辩"=>"辯",
-"辫"=>"辮",
-"边"=>"邊",
-"辽"=>"遼",
-"达"=>"達",
-"迁"=>"遷",
-"过"=>"過",
-"迈"=>"邁",
-"运"=>"運",
-"还"=>"還",
-"这"=>"這",
-"进"=>"進",
-"远"=>"遠",
-"违"=>"違",
-"连"=>"連",
-"迟"=>"遲",
-"迩"=>"邇",
-"迳"=>"逕",
-"迹"=>"跡",
-"选"=>"選",
-"逊"=>"遜",
-"递"=>"遞",
-"逦"=>"邐",
-"逻"=>"邏",
-"遗"=>"遺",
-"遥"=>"遙",
-"邓"=>"鄧",
-"邝"=>"鄺",
-"邬"=>"鄔",
-"邮"=>"郵",
-"邹"=>"鄒",
-"邺"=>"鄴",
-"邻"=>"鄰",
-"郏"=>"郟",
-"郐"=>"鄶",
-"郑"=>"鄭",
-"郓"=>"鄆",
-"郦"=>"酈",
-"郧"=>"鄖",
-"郸"=>"鄲",
-"酂"=>"酇",
-"酦"=>"醱",
-"酱"=>"醬",
-"酽"=>"釅",
-"酾"=>"釃",
-"酿"=>"釀",
-"释"=>"釋",
-"鉴"=>"鑒",
-"銮"=>"鑾",
-"錾"=>"鏨",
-"𨱏"=>"鎝",
-"钅"=>"釒",
-"钆"=>"釓",
-"钇"=>"釔",
-"针"=>"針",
-"钉"=>"釘",
-"钊"=>"釗",
-"钋"=>"釙",
-"钌"=>"釕",
-"钍"=>"釷",
-"钎"=>"釺",
-"钏"=>"釧",
-"钐"=>"釤",
-"钑"=>"鈒",
-"钒"=>"釩",
-"钓"=>"釣",
-"钔"=>"鍆",
-"钕"=>"釹",
-"钖"=>"鍚",
-"钗"=>"釵",
-"钘"=>"鈃",
-"钙"=>"鈣",
-"钚"=>"鈈",
-"钛"=>"鈦",
-"钜"=>"鉅",
-"钝"=>"鈍",
-"钞"=>"鈔",
-"钠"=>"鈉",
-"钡"=>"鋇",
-"钢"=>"鋼",
-"钣"=>"鈑",
-"钤"=>"鈐",
-"钥"=>"鑰",
-"钦"=>"欽",
-"钧"=>"鈞",
-"钨"=>"鎢",
-"钪"=>"鈧",
-"钫"=>"鈁",
-"钬"=>"鈥",
-"钭"=>"鈄",
-"钮"=>"鈕",
-"钯"=>"鈀",
-"钰"=>"鈺",
-"钱"=>"錢",
-"钲"=>"鉦",
-"钳"=>"鉗",
-"钴"=>"鈷",
-"钶"=>"鈳",
-"钷"=>"鉕",
-"钸"=>"鈽",
-"钹"=>"鈸",
-"钺"=>"鉞",
-"钻"=>"鑽",
-"钼"=>"鉬",
-"钽"=>"鉭",
-"钾"=>"鉀",
-"钿"=>"鈿",
-"铀"=>"鈾",
-"铁"=>"鐵",
-"铂"=>"鉑",
-"铃"=>"鈴",
-"铄"=>"鑠",
-"铅"=>"鉛",
-"铆"=>"鉚",
-"铇"=>"鉋",
-"铈"=>"鈰",
-"铉"=>"鉉",
-"铊"=>"鉈",
-"铋"=>"鉍",
-"铌"=>"鈮",
-"铍"=>"鈹",
-"铎"=>"鐸",
-"铏"=>"鉶",
-"铐"=>"銬",
-"铑"=>"銠",
-"铒"=>"鉺",
-"铓"=>"鋩",
-"铔"=>"錏",
-"铕"=>"銪",
-"铖"=>"鋮",
-"铗"=>"鋏",
-"铘"=>"鋣",
-"铙"=>"鐃",
-"铚"=>"銍",
-"铛"=>"鐺",
-"铜"=>"銅",
-"铝"=>"鋁",
-"铞"=>"銱",
-"铟"=>"銦",
-"铠"=>"鎧",
-"铡"=>"鍘",
-"铢"=>"銖",
-"铣"=>"銑",
-"铤"=>"鋌",
-"铥"=>"銩",
-"铦"=>"銛",
-"铧"=>"鏵",
-"铨"=>"銓",
-"铩"=>"鎩",
-"铪"=>"鉿",
-"铫"=>"銚",
-"铬"=>"鉻",
-"铭"=>"銘",
-"铮"=>"錚",
-"铯"=>"銫",
-"铰"=>"鉸",
-"铱"=>"銥",
-"铲"=>"鏟",
-"铳"=>"銃",
-"铴"=>"鐋",
-"铵"=>"銨",
-"银"=>"銀",
-"铷"=>"銣",
-"铸"=>"鑄",
-"铹"=>"鐒",
-"铺"=>"鋪",
-"铻"=>"鋙",
-"铼"=>"錸",
-"铽"=>"鋱",
-"链"=>"鏈",
-"铿"=>"鏗",
-"销"=>"銷",
-"锁"=>"鎖",
-"锂"=>"鋰",
-"锃"=>"鋥",
-"锄"=>"鋤",
-"锅"=>"鍋",
-"锆"=>"鋯",
-"锇"=>"鋨",
-"锉"=>"銼",
-"锊"=>"鋝",
-"锋"=>"鋒",
-"锌"=>"鋅",
-"锍"=>"鋶",
-"锎"=>"鐦",
-"锏"=>"鐧",
-"锑"=>"銻",
-"锒"=>"鋃",
-"锓"=>"鋟",
-"锔"=>"鋦",
-"锕"=>"錒",
-"锖"=>"錆",
-"锗"=>"鍺",
-"锘"=>"鍩",
-"错"=>"錯",
-"锚"=>"錨",
-"锛"=>"錛",
-"锜"=>"錡",
-"锝"=>"鍀",
-"锞"=>"錁",
-"锟"=>"錕",
-"锠"=>"錩",
-"锡"=>"錫",
-"锢"=>"錮",
-"锣"=>"鑼",
-"锥"=>"錐",
-"锦"=>"錦",
-"锧"=>"鑕",
-"锩"=>"錈",
-"锪"=>"鍃",
-"锫"=>"錇",
-"锬"=>"錟",
-"锭"=>"錠",
-"键"=>"鍵",
-"锯"=>"鋸",
-"锰"=>"錳",
-"锱"=>"錙",
-"锲"=>"鍥",
-"锳"=>"鍈",
-"锴"=>"鍇",
-"锵"=>"鏘",
-"锶"=>"鍶",
-"锷"=>"鍔",
-"锸"=>"鍤",
-"锹"=>"鍬",
-"锺"=>"鍾",
-"锻"=>"鍛",
-"锼"=>"鎪",
-"锽"=>"鍠",
-"锾"=>"鍰",
-"锿"=>"鎄",
-"镀"=>"鍍",
-"镁"=>"鎂",
-"镂"=>"鏤",
-"镃"=>"鎡",
-"镄"=>"鐨",
-"镅"=>"鎇",
-"镆"=>"鏌",
-"镇"=>"鎮",
-"镈"=>"鎛",
-"镉"=>"鎘",
-"镊"=>"鑷",
-"镋"=>"鎲",
-"镍"=>"鎳",
-"镎"=>"鎿",
-"镏"=>"鎦",
-"镐"=>"鎬",
-"镑"=>"鎊",
-"镒"=>"鎰",
-"镓"=>"鎵",
-"镔"=>"鑌",
-"镕"=>"鎔",
-"镖"=>"鏢",
-"镗"=>"鏜",
-"镘"=>"鏝",
-"镙"=>"鏍",
-"镚"=>"鏰",
-"镛"=>"鏞",
-"镜"=>"鏡",
-"镝"=>"鏑",
-"镞"=>"鏃",
-"镟"=>"鏇",
-"镠"=>"鏐",
-"镡"=>"鐔",
-"镣"=>"鐐",
-"镤"=>"鏷",
-"镥"=>"鑥",
-"镦"=>"鐓",
-"镧"=>"鑭",
-"镨"=>"鐠",
-"镩"=>"鑹",
-"镪"=>"鏹",
-"镫"=>"鐙",
-"镬"=>"鑊",
-"镭"=>"鐳",
-"镮"=>"鐶",
-"镯"=>"鐲",
-"镰"=>"鐮",
-"镱"=>"鐿",
-"镲"=>"鑔",
-"镳"=>"鑣",
-"镴"=>"鑞",
-"镵"=>"鑱",
-"镶"=>"鑲",
-"长"=>"長",
-"门"=>"門",
-"闩"=>"閂",
-"闪"=>"閃",
-"闫"=>"閆",
-"闬"=>"閈",
-"闭"=>"閉",
-"问"=>"問",
-"闯"=>"闖",
-"闰"=>"閏",
-"闱"=>"闈",
-"闲"=>"閑",
-"闳"=>"閎",
-"间"=>"間",
-"闵"=>"閔",
-"闶"=>"閌",
-"闷"=>"悶",
-"闸"=>"閘",
-"闹"=>"鬧",
-"闺"=>"閨",
-"闻"=>"聞",
-"闼"=>"闥",
-"闽"=>"閩",
-"闾"=>"閭",
-"闿"=>"闓",
-"阀"=>"閥",
-"阁"=>"閣",
-"阂"=>"閡",
-"阃"=>"閫",
-"阄"=>"鬮",
-"阆"=>"閬",
-"阇"=>"闍",
-"阈"=>"閾",
-"阉"=>"閹",
-"阊"=>"閶",
-"阋"=>"鬩",
-"阌"=>"閿",
-"阍"=>"閽",
-"阎"=>"閻",
-"阏"=>"閼",
-"阐"=>"闡",
-"阑"=>"闌",
-"阒"=>"闃",
-"阓"=>"闠",
-"阔"=>"闊",
-"阕"=>"闋",
-"阖"=>"闔",
-"阗"=>"闐",
-"阘"=>"闒",
-"阙"=>"闕",
-"阚"=>"闞",
-"阛"=>"闤",
-"队"=>"隊",
-"阳"=>"陽",
-"阴"=>"陰",
-"阵"=>"陣",
-"阶"=>"階",
-"际"=>"際",
-"陆"=>"陸",
-"陇"=>"隴",
-"陈"=>"陳",
-"陉"=>"陘",
-"陕"=>"陝",
-"陧"=>"隉",
-"陨"=>"隕",
-"险"=>"險",
-"随"=>"隨",
-"隐"=>"隱",
-"隶"=>"隸",
-"隽"=>"雋",
-"难"=>"難",
-"雏"=>"雛",
-"雠"=>"讎",
-"雳"=>"靂",
-"雾"=>"霧",
-"霁"=>"霽",
-"霡"=>"霢",
-"霭"=>"靄",
-"靓"=>"靚",
-"静"=>"靜",
-"靥"=>"靨",
-"䩄"=>"靦",
-"鞑"=>"韃",
-"鞒"=>"鞽",
-"鞯"=>"韉",
-"韦"=>"韋",
-"韧"=>"韌",
-"韨"=>"韍",
-"韩"=>"韓",
-"韪"=>"韙",
-"韫"=>"韞",
-"韬"=>"韜",
-"韵"=>"韻",
-"页"=>"頁",
-"顶"=>"頂",
-"顷"=>"頃",
-"顸"=>"頇",
-"项"=>"項",
-"顺"=>"順",
-"顼"=>"頊",
-"顽"=>"頑",
-"顾"=>"顧",
-"顿"=>"頓",
-"颀"=>"頎",
-"颁"=>"頒",
-"颂"=>"頌",
-"颃"=>"頏",
-"预"=>"預",
-"颅"=>"顱",
-"领"=>"領",
-"颇"=>"頗",
-"颈"=>"頸",
-"颉"=>"頡",
-"颊"=>"頰",
-"颋"=>"頲",
-"颌"=>"頜",
-"颍"=>"潁",
-"颎"=>"熲",
-"颏"=>"頦",
-"颐"=>"頤",
-"频"=>"頻",
-"颒"=>"頮",
-"颔"=>"頷",
-"颕"=>"頴",
-"颖"=>"穎",
-"颗"=>"顆",
-"题"=>"題",
-"颙"=>"顒",
-"颚"=>"顎",
-"颛"=>"顓",
-"额"=>"額",
-"颞"=>"顳",
-"颟"=>"顢",
-"颠"=>"顛",
-"颡"=>"顙",
-"颢"=>"顥",
-"颤"=>"顫",
-"颥"=>"顬",
-"颦"=>"顰",
-"颧"=>"顴",
-"风"=>"風",
-"飏"=>"颺",
-"飐"=>"颭",
-"飑"=>"颮",
-"飒"=>"颯",
-"飓"=>"颶",
-"飔"=>"颸",
-"飕"=>"颼",
-"飖"=>"颻",
-"飗"=>"飀",
-"飘"=>"飄",
-"飙"=>"飆",
-"飚"=>"飈",
-"飞"=>"飛",
-"飨"=>"饗",
-"餍"=>"饜",
-"饣"=>"飠",
-"饤"=>"飣",
-"饦"=>"飥",
-"饧"=>"餳",
-"饨"=>"飩",
-"饩"=>"餼",
-"饪"=>"飪",
-"饫"=>"飫",
-"饬"=>"飭",
-"饭"=>"飯",
-"饮"=>"飲",
-"饯"=>"餞",
-"饰"=>"飾",
-"饱"=>"飽",
-"饲"=>"飼",
-"饳"=>"飿",
-"饴"=>"飴",
-"饵"=>"餌",
-"饶"=>"饒",
-"饷"=>"餉",
-"饸"=>"餄",
-"饹"=>"餎",
-"饺"=>"餃",
-"饻"=>"餏",
-"饼"=>"餅",
-"饽"=>"餑",
-"饾"=>"餖",
-"饿"=>"餓",
-"馀"=>"餘",
-"馁"=>"餒",
-"馂"=>"餕",
-"馃"=>"餜",
-"馄"=>"餛",
-"馅"=>"餡",
-"馆"=>"館",
-"馇"=>"餷",
-"馈"=>"饋",
-"馉"=>"餶",
-"馊"=>"餿",
-"馋"=>"饞",
-"馌"=>"饁",
-"馍"=>"饃",
-"馎"=>"餺",
-"馏"=>"餾",
-"馐"=>"饈",
-"馑"=>"饉",
-"馒"=>"饅",
-"馓"=>"饊",
-"馔"=>"饌",
-"馕"=>"饢",
-"䯄"=>"騧",
-"马"=>"馬",
-"驭"=>"馭",
-"驮"=>"馱",
-"驯"=>"馴",
-"驰"=>"馳",
-"驱"=>"驅",
-"驲"=>"馹",
-"驳"=>"駁",
-"驴"=>"驢",
-"驵"=>"駔",
-"驶"=>"駛",
-"驷"=>"駟",
-"驸"=>"駙",
-"驹"=>"駒",
-"驺"=>"騶",
-"驻"=>"駐",
-"驼"=>"駝",
-"驽"=>"駑",
-"驾"=>"駕",
-"驿"=>"驛",
-"骀"=>"駘",
-"骁"=>"驍",
-"骃"=>"駰",
-"骄"=>"驕",
-"骅"=>"驊",
-"骆"=>"駱",
-"骇"=>"駭",
-"骈"=>"駢",
-"骉"=>"驫",
-"骊"=>"驪",
-"骋"=>"騁",
-"验"=>"驗",
-"骍"=>"騂",
-"骎"=>"駸",
-"骏"=>"駿",
-"骐"=>"騏",
-"骑"=>"騎",
-"骒"=>"騍",
-"骓"=>"騅",
-"骔"=>"騌",
-"骕"=>"驌",
-"骖"=>"驂",
-"骗"=>"騙",
-"骘"=>"騭",
-"骙"=>"騤",
-"骚"=>"騷",
-"骛"=>"騖",
-"骜"=>"驁",
-"骝"=>"騮",
-"骞"=>"騫",
-"骟"=>"騸",
-"骠"=>"驃",
-"骡"=>"騾",
-"骢"=>"驄",
-"骣"=>"驏",
-"骤"=>"驟",
-"骥"=>"驥",
-"骦"=>"驦",
-"骧"=>"驤",
-"髅"=>"髏",
-"髋"=>"髖",
-"髌"=>"髕",
-"鬓"=>"鬢",
-"魇"=>"魘",
-"魉"=>"魎",
-"鱼"=>"魚",
-"鱽"=>"魛",
-"鱾"=>"魢",
-"鱿"=>"魷",
-"鲀"=>"魨",
-"鲁"=>"魯",
-"鲂"=>"魴",
-"鲃"=>"䰾",
-"鲄"=>"魺",
-"鲅"=>"鮁",
-"鲆"=>"鮃",
-"鲈"=>"鱸",
-"鲉"=>"鮋",
-"鲊"=>"鮓",
-"鲋"=>"鮒",
-"鲌"=>"鮊",
-"鲍"=>"鮑",
-"鲎"=>"鱟",
-"鲏"=>"鮍",
-"鲐"=>"鮐",
-"鲑"=>"鮭",
-"鲒"=>"鮚",
-"鲓"=>"鮳",
-"鲔"=>"鮪",
-"鲕"=>"鮞",
-"鲖"=>"鮦",
-"鲗"=>"鰂",
-"鲘"=>"鮜",
-"鲙"=>"鱠",
-"鲚"=>"鱭",
-"鲛"=>"鮫",
-"鲜"=>"鮮",
-"鲝"=>"鮺",
-"鲟"=>"鱘",
-"鲠"=>"鯁",
-"鲡"=>"鱺",
-"鲢"=>"鰱",
-"鲣"=>"鰹",
-"鲤"=>"鯉",
-"鲥"=>"鰣",
-"鲦"=>"鰷",
-"鲧"=>"鯀",
-"鲨"=>"鯊",
-"鲩"=>"鯇",
-"鲪"=>"鮶",
-"鲫"=>"鯽",
-"鲬"=>"鯒",
-"鲭"=>"鯖",
-"鲮"=>"鯪",
-"鲯"=>"鯕",
-"鲰"=>"鯫",
-"鲱"=>"鯡",
-"鲲"=>"鯤",
-"鲳"=>"鯧",
-"鲴"=>"鯝",
-"鲵"=>"鯢",
-"鲶"=>"鯰",
-"鲷"=>"鯛",
-"鲸"=>"鯨",
-"鲹"=>"鰺",
-"鲺"=>"鯴",
-"鲻"=>"鯔",
-"鲼"=>"鱝",
-"鲽"=>"鰈",
-"鲾"=>"鰏",
-"鲿"=>"鱨",
-"鳀"=>"鯷",
-"鳁"=>"鰮",
-"鳂"=>"鰃",
-"鳃"=>"鰓",
-"鳅"=>"鰍",
-"鳆"=>"鰒",
-"鳇"=>"鰉",
-"鳈"=>"鰁",
-"鳉"=>"鱂",
-"鳊"=>"鯿",
-"鳋"=>"鰠",
-"鳌"=>"鰲",
-"鳍"=>"鰭",
-"鳎"=>"鰨",
-"鳏"=>"鰥",
-"鳐"=>"鰩",
-"鳑"=>"鰟",
-"鳒"=>"鰜",
-"鳓"=>"鰳",
-"鳔"=>"鰾",
-"鳕"=>"鱈",
-"鳖"=>"鱉",
-"鳗"=>"鰻",
-"鳘"=>"鰵",
-"鳙"=>"鱅",
-"鳚"=>"䲁",
-"鳛"=>"鰼",
-"鳜"=>"鱖",
-"鳝"=>"鱔",
-"鳞"=>"鱗",
-"鳟"=>"鱒",
-"鳠"=>"鱯",
-"鳡"=>"鱤",
-"鳢"=>"鱧",
-"鳣"=>"鱣",
-"䴓"=>"鳾",
-"䴕"=>"鴷",
-"䴔"=>"鵁",
-"䴖"=>"鶄",
-"䴗"=>"鶪",
-"䴘"=>"鷈",
-"䴙"=>"鷿",
-"㶉"=>"鸂",
-"鸟"=>"鳥",
-"鸠"=>"鳩",
-"鸢"=>"鳶",
-"鸣"=>"鳴",
-"鸤"=>"鳲",
-"鸥"=>"鷗",
-"鸦"=>"鴉",
-"鸧"=>"鶬",
-"鸨"=>"鴇",
-"鸩"=>"鴆",
-"鸪"=>"鴣",
-"鸫"=>"鶇",
-"鸬"=>"鸕",
-"鸭"=>"鴨",
-"鸮"=>"鴞",
-"鸯"=>"鴦",
-"鸰"=>"鴒",
-"鸱"=>"鴟",
-"鸲"=>"鴝",
-"鸳"=>"鴛",
-"鸴"=>"鷽",
-"鸵"=>"鴕",
-"鸶"=>"鷥",
-"鸷"=>"鷙",
-"鸸"=>"鴯",
-"鸹"=>"鴰",
-"鸺"=>"鵂",
-"鸻"=>"鴴",
-"鸼"=>"鵃",
-"鸽"=>"鴿",
-"鸾"=>"鸞",
-"鸿"=>"鴻",
-"鹀"=>"鵐",
-"鹁"=>"鵓",
-"鹂"=>"鸝",
-"鹃"=>"鵑",
-"鹄"=>"鵠",
-"鹅"=>"鵝",
-"鹆"=>"鵒",
-"鹇"=>"鷳",
-"鹈"=>"鵜",
-"鹉"=>"鵡",
-"鹊"=>"鵲",
-"鹋"=>"鶓",
-"鹌"=>"鵪",
-"鹍"=>"鵾",
-"鹎"=>"鵯",
-"鹏"=>"鵬",
-"鹐"=>"鵮",
-"鹑"=>"鶉",
-"鹒"=>"鶊",
-"鹓"=>"鵷",
-"鹔"=>"鷫",
-"鹕"=>"鶘",
-"鹖"=>"鶡",
-"鹗"=>"鶚",
-"鹘"=>"鶻",
-"鹙"=>"鶖",
-"鹛"=>"鶥",
-"鹜"=>"鶩",
-"鹝"=>"鷊",
-"鹞"=>"鷂",
-"鹟"=>"鶲",
-"鹠"=>"鶹",
-"鹡"=>"鶺",
-"鹢"=>"鷁",
-"鹣"=>"鶼",
-"鹤"=>"鶴",
-"鹥"=>"鷖",
-"鹦"=>"鸚",
-"鹧"=>"鷓",
-"鹨"=>"鷚",
-"鹩"=>"鷯",
-"鹪"=>"鷦",
-"鹫"=>"鷲",
-"鹬"=>"鷸",
-"鹭"=>"鷺",
-"鹯"=>"鸇",
-"鹰"=>"鷹",
-"鹱"=>"鸌",
-"鹲"=>"鸏",
-"鹳"=>"鸛",
-"鹴"=>"鸘",
-"鹾"=>"鹺",
-"麦"=>"麥",
-"麸"=>"麩",
-"黄"=>"黃",
-"黉"=>"黌",
-"黡"=>"黶",
-"黩"=>"黷",
-"黪"=>"黲",
-"黾"=>"黽",
-"鼋"=>"黿",
-"鼍"=>"鼉",
-"鼗"=>"鞀",
-"鼹"=>"鼴",
-"齐"=>"齊",
-"齑"=>"齏",
-"齿"=>"齒",
-"龀"=>"齔",
-"龁"=>"齕",
-"龂"=>"齗",
-"龃"=>"齟",
-"龄"=>"齡",
-"龅"=>"齙",
-"龆"=>"齠",
-"龇"=>"齜",
-"龈"=>"齦",
-"龉"=>"齬",
-"龊"=>"齪",
-"龋"=>"齲",
-"龌"=>"齷",
-"龙"=>"龍",
-"龚"=>"龔",
-"龛"=>"龕",
-"龟"=>"龜",
-
-"0多只" => "0多隻",
-"0天后" => "0天後",
-"1天后" => "1天後",
-"2天后" => "2天後",
-"3天后" => "3天後",
-"4天后" => "4天後",
-"5天后" => "5天後",
-"6天后" => "6天後",
-"7天后" => "7天後",
-"8天后" => "8天後",
-"9天后" => "9天後",
-"一干二净" => "一乾二淨",
-"一并" => "一併",
-"一前一后" => "一前一後",
-"一划" => "一劃",
-"一口钟" => "一口鐘",
-"一地里" => "一地裡",
-"一伙" => "一夥",
-"一天后" => "一天後",
-"一干人" => "一干人",
-"一别头" => "一彆頭",
-"一树百获" => "一樹百穫",
-"一准" => "一準",
-"一争两丑" => "一爭兩醜",
-"一箭双雕" => "一箭雙鵰",
-"一扎" => "一紮",
-"一冲" => "一衝",
-"一锅面" => "一鍋麵",
-"一只" => "一隻",
-"一发千钧" => "一髮千鈞",
-"一哄而散" => "一鬨而散",
-"丁丁当当" => "丁丁當當",
-"七划" => "七劃",
-"七天后" => "七天後",
-"七情六欲" => "七情六慾",
-"七扎" => "七紮",
-"七只" => "七隻",
-"万俟" => "万俟",
-"万旗" => "万旗",
-"三天后" => "三天後",
-"三棱锥" => "三稜錐",
-"三扎" => "三紮",
-"三统历" => "三統曆",
-"三复" => "三複",
-"三只" => "三隻",
-"三余" => "三餘",
-"上梁" => "上樑",
-"上签名" => "上簽名",
-"上签字" => "上簽字",
-"上签写" => "上簽寫",
-"上签收" => "上簽收",
-"上签" => "上籤",
-"上药" => "上藥",
-"下于" => "下於",
-"下签" => "下籤",
-"下药" => "下藥",
-"下余" => "下餘",
-"不下于" => "不下於",
-"不亚于" => "不亞於",
-"不占" => "不佔",
-"不前不后" => "不前不後",
-"不可救药" => "不可救藥",
-"不同于" => "不同於",
-"不嫌母丑" => "不嫌母醜",
-"不寒而栗" => "不寒而慄",
-"不屑于" => "不屑於",
-"不干不净" => "不幹不淨",
-"不干性油" => "不幹性油",
-"不采" => "不採",
-"不断发" => "不斷發",
-"不准" => "不準",
-"不为牛后" => "不為牛後",
-"不知就里" => "不知就裡",
-"不知所云" => "不知所云",
-"不谷" => "不穀",
-"不绝于耳" => "不絕於耳",
-"不致于" => "不致於",
-"不良于行" => "不良於行",
-"不药而癒" => "不藥而癒",
-"不讬" => "不託",
-"不逊于" => "不遜於",
-"不丑" => "不醜",
-"不锈钢" => "不鏽鋼",
-"世界杯" => "世界盃",
-"丢丑" => "丟醜",
-"并存着" => "並存著",
-"并于" => "並於",
-"并发动" => "並發動",
-"并发展" => "並發展",
-"并发现" => "並發現",
-"并发表" => "並發表",
-"中仑" => "中崙",
-"中岳" => "中嶽",
-"中于" => "中於",
-"中美发表" => "中美發表",
-"中药" => "中藥",
-"丰仪" => "丰儀",
-"丰南" => "丰南",
-"丰台" => "丰台",
-"丰姿" => "丰姿",
-"丰度" => "丰度",
-"丰情" => "丰情",
-"丰标不凡" => "丰標不凡",
-"丰神" => "丰神",
-"丰茸" => "丰茸",
-"丰采" => "丰采",
-"丰韵" => "丰韵",
-"丰韵" => "丰韻",
-"丸药" => "丸藥",
-"丹药" => "丹藥",
-"主仆" => "主僕",
-"主干" => "主幹",
-"么么小丑" => "么麼小丑",
-"之后" => "之後",
-"之于" => "之於",
-"之余" => "之餘",
-"九世之雠" => "九世之讎",
-"九划" => "九劃",
-"九天后" => "九天後",
-"九谷" => "九穀",
-"九扎" => "九紮",
-"九只" => "九隻",
-"乳臭未干" => "乳臭未乾",
-"干上" => "乾上",
-"干干" => "乾乾",
-"干干儿的" => "乾乾兒的",
-"干干净净" => "乾乾淨淨",
-"干了" => "乾了",
-"干井" => "乾井",
-"干个" => "乾個",
-"干儿" => "乾兒",
-"干儿子" => "乾兒子",
-"干冰" => "乾冰",
-"干冷" => "乾冷",
-"干刻版" => "乾刻版",
-"干剥剥" => "乾剝剝",
-"干卦" => "乾卦",
-"干吊着下巴" => "乾吊著下巴",
-"干和" => "乾和",
-"干咳" => "乾咳",
-"干咽" => "乾咽",
-"干哥" => "乾哥",
-"干哭" => "乾哭",
-"干唱" => "乾唱",
-"干啼" => "乾啼",
-"干乔" => "乾喬",
-"干呕" => "乾嘔",
-"干哕" => "乾噦",
-"干嚎" => "乾嚎",
-"干回付" => "乾回付",
-"干圆洁净" => "乾圓潔淨",
-"干地" => "乾地",
-"干坤" => "乾坤",
-"干坞" => "乾塢",
-"干女" => "乾女",
-"干女儿" => "乾女兒",
-"干奴才" => "乾奴才",
-"干妹" => "乾妹",
-"干姊" => "乾姊",
-"干娘" => "乾娘",
-"干妈" => "乾媽",
-"干子" => "乾子",
-"干季" => "乾季",
-"干尸" => "乾屍",
-"干屎橛" => "乾屎橛",
-"干巴" => "乾巴",
-"干巴巴" => "乾巴巴",
-"干式" => "乾式",
-"干弟" => "乾弟",
-"干得" => "乾得",
-"干急" => "乾急",
-"干性" => "乾性",
-"干打雷" => "乾打雷",
-"干折" => "乾折",
-"干掉" => "乾掉",
-"干撂台" => "乾撂台",
-"干撇下" => "乾撇下",
-"干擦" => "乾擦",
-"干支剌" => "乾支剌",
-"干支支" => "乾支支",
-"干敲梆子不卖油" => "乾敲梆子不賣油",
-"干料" => "乾料",
-"干旱" => "乾旱",
-"干暖" => "乾暖",
-"干材" => "乾材",
-"干村沙" => "乾村沙",
-"干杯" => "乾杯",
-"干果" => "乾果",
-"干枯" => "乾枯",
-"干柴" => "乾柴",
-"干柴烈火" => "乾柴烈火",
-"干梅" => "乾梅",
-"干死" => "乾死",
-"干池" => "乾池",
-"干没" => "乾沒",
-"干洗" => "乾洗",
-"干涸" => "乾涸",
-"干凉" => "乾涼",
-"干净" => "乾淨",
-"干渠" => "乾渠",
-"干渴" => "乾渴",
-"干沟" => "乾溝",
-"干漆" => "乾漆",
-"干涩" => "乾澀",
-"干湿" => "乾濕",
-"干熬" => "乾熬",
-"干热" => "乾熱",
-"干灯盏" => "乾燈盞",
-"干燥" => "乾燥",
-"干爸" => "乾爸",
-"干爹" => "乾爹",
-"干爽" => "乾爽",
-"干片" => "乾片",
-"干生受" => "乾生受",
-"干生子" => "乾生子",
-"干产" => "乾產",
-"干田" => "乾田",
-"干疥" => "乾疥",
-"干瘦" => "乾瘦",
-"干瘪" => "乾癟",
-"干癣" => "乾癬",
-"干白儿" => "乾白兒",
-"干的" => "乾的",
-"干眼" => "乾眼",
-"干眼病" => "乾眼病",
-"干瞪眼" => "乾瞪眼",
-"干礼" => "乾禮",
-"干稿" => "乾稿",
-"干笑" => "乾笑",
-"干等" => "乾等",
-"干篾片" => "乾篾片",
-"干粉" => "乾粉",
-"干粮" => "乾糧",
-"干结" => "乾結",
-"干丝" => "乾絲",
-"干绷" => "乾繃",
-"干耗" => "乾耗",
-"干肉片" => "乾肉片",
-"干股" => "乾股",
-"干肥" => "乾肥",
-"干脆" => "乾脆",
-"干花" => "乾花",
-"干刍" => "乾芻",
-"干苔" => "乾苔",
-"干茨腊" => "乾茨臘",
-"干茶钱" => "乾茶錢",
-"干草" => "乾草",
-"干菜" => "乾菜",
-"干落" => "乾落",
-"干着" => "乾著",
-"干着急" => "乾著急",
-"干姜" => "乾薑",
-"干薪" => "乾薪",
-"干虔" => "乾虔",
-"干号" => "乾號",
-"干衣" => "乾衣",
-"干裂" => "乾裂",
-"干亲" => "乾親",
-"干贝" => "乾貝",
-"干货" => "乾貨",
-"干躁" => "乾躁",
-"干逼" => "乾逼",
-"干酪" => "乾酪",
-"干酵母" => "乾酵母",
-"干醋" => "乾醋",
-"干量" => "乾量",
-"干阿奶" => "乾阿奶",
-"干隆" => "乾隆",
-"干雷" => "乾雷",
-"干电" => "乾電",
-"干电池" => "乾電池",
-"干霍乱" => "乾霍亂",
-"干颡" => "乾顙",
-"干台" => "乾颱",
-"干饭" => "乾飯",
-"干馆" => "乾館",
-"干糇" => "乾餱",
-"干馏" => "乾餾",
-"干鱼" => "乾魚",
-"干鲜" => "乾鮮",
-"干面" => "乾麵",
-"乱发" => "亂髮",
-"乱哄" => "亂鬨",
-"乱哄不过来" => "亂鬨不過來",
-"事后" => "事後",
-"二不棱登" => "二不稜登",
-"二划" => "二劃",
-"二天后" => "二天後",
-"二缶钟惑" => "二缶鐘惑",
-"二里头" => "二里頭",
-"二只" => "二隻",
-"于余曲折" => "于餘曲折",
-"云乎" => "云乎",
-"云云" => "云云",
-"云为" => "云為",
-"云然" => "云然",
-"云尔" => "云爾",
-"互于" => "互於",
-"五划" => "五劃",
-"五天后" => "五天後",
-"五岳" => "五嶽",
-"五谷" => "五穀",
-"五扎" => "五紮",
-"五行生克" => "五行生剋",
-"五只" => "五隻",
-"五出" => "五齣",
-"井干摧败" => "井榦摧敗",
-"亚于" => "亞於",
-"交于" => "交於",
-"交讬" => "交託",
-"交游广阔" => "交遊廣闊",
-"交哄" => "交鬨",
-"亮丑" => "亮醜",
-"亮钟" => "亮鐘",
-"人云亦云" => "人云亦云",
-"人参加" => "人參加",
-"人参展" => "人參展",
-"人参战" => "人參戰",
-"人参拜" => "人參拜",
-"人参政" => "人參政",
-"人参照" => "人參照",
-"人参看" => "人參看",
-"人参禅" => "人參禪",
-"人参考" => "人參考",
-"人参与" => "人參與",
-"人参见" => "人參見",
-"人参观" => "人參觀",
-"人参谋" => "人參謀",
-"人参议" => "人參議",
-"人参赞" => "人參贊",
-"人参透" => "人參透",
-"人参选" => "人參選",
-"人参酌" => "人參酌",
-"人参阅" => "人參閱",
-"人后" => "人後",
-"人欲" => "人慾",
-"人物志" => "人物誌",
-"人参" => "人蔘",
-"什锦面" => "什錦麵",
-"什么" => "什麼",
-"今后" => "今後",
-"介于" => "介於",
-"付讬" => "付託",
-"仙药" => "仙藥",
-"以后" => "以後",
-"任教于" => "任教於",
-"任于" => "任於",
-"仿制" => "仿製",
-"企划" => "企劃",
-"伊府面" => "伊府麵",
-"伊斯兰教历" => "伊斯蘭教曆",
-"伊斯兰历" => "伊斯蘭曆",
-"伊郁" => "伊鬱",
-"伏几" => "伏几",
-"伙头" => "伙頭",
-"似于" => "似於",
-"但云" => "但云",
-"布于" => "佈於",
-"位于" => "位於",
-"低于" => "低於",
-"占上风" => "佔上風",
-"占下" => "佔下",
-"占了" => "佔了",
-"占位" => "佔位",
-"占住" => "佔住",
-"占占" => "佔佔",
-"占便宜" => "佔便宜",
-"占个" => "佔個",
-"占优势" => "佔優勢",
-"占先" => "佔先",
-"占光" => "佔光",
-"占到" => "佔到",
-"占去" => "佔去",
-"占取" => "佔取",
-"占在" => "佔在",
-"占地" => "佔地",
-"占多数" => "佔多數",
-"占好" => "佔好",
-"占得" => "佔得",
-"占掉" => "佔掉",
-"占据" => "佔據",
-"占有" => "佔有",
-"占满" => "佔滿",
-"占为" => "佔為",
-"占用" => "佔用",
-"占尽" => "佔盡",
-"占线" => "佔線",
-"占起" => "佔起",
-"占超过" => "佔超過",
-"占过" => "佔過",
-"占领" => "佔領",
-"余光中" => "余光中",
-"余光生" => "余光生",
-"佛罗棱萨" => "佛羅稜薩",
-"作奸犯科" => "作姦犯科",
-"作准" => "作準",
-"作庄" => "作莊",
-"你才子发昏" => "你纔子發昏",
-"并一不二" => "併一不二",
-"并入" => "併入",
-"并兼" => "併兼",
-"并到" => "併到",
-"并合" => "併合",
-"并名" => "併名",
-"并吞" => "併吞",
-"并拢" => "併攏",
-"并案" => "併案",
-"并流" => "併流",
-"并火" => "併火",
-"并为" => "併為",
-"并产" => "併產",
-"并当" => "併當",
-"并叠" => "併疊",
-"并发" => "併發",
-"并科" => "併科",
-"并网" => "併網",
-"并线" => "併線",
-"并肩子" => "併肩子",
-"并购" => "併購",
-"并除" => "併除",
-"并骨" => "併骨",
-"来于" => "來於",
-"来自于" => "來自於",
-"来复" => "來複",
-"侍仆" => "侍僕",
-"供制" => "供製",
-"依依不舍" => "依依不捨",
-"依讬" => "依託",
-"依附于" => "依附於",
-"侵占" => "侵佔",
-"侵并" => "侵併",
-"便于" => "便於",
-"便药" => "便藥",
-"系数" => "係數",
-"系为" => "係為",
-"保险柜" => "保險柜",
-"信讬" => "信託",
-"修改后" => "修改後",
-"修胡刀" => "修鬍刀",
-"俯冲" => "俯衝",
-"个里" => "個裡",
-"幸免" => "倖免",
-"幸存" => "倖存",
-"幸幸" => "倖幸",
-"倛丑" => "倛醜",
-"借助于" => "借助於",
-"借着" => "借著",
-"借讬" => "借託",
-"倦游" => "倦遊",
-"假力于人" => "假力於人",
-"假药" => "假藥",
-"假讬" => "假託",
-"假发" => "假髮",
-"偎干" => "偎乾",
-"偎干就湿" => "偎乾就濕",
-"偏后" => "偏後",
-"偏于" => "偏於",
-"做庄" => "做莊",
-"停停当当" => "停停當當",
-"停征" => "停徵",
-"停制" => "停製",
-"偷鸡不着" => "偷雞不著",
-"伪药" => "偽藥",
-"备注" => "備註",
-"家伙" => "傢伙",
-"家俱" => "傢俱",
-"家具" => "傢具",
-"催并" => "催併",
-"佣人" => "傭人",
-"佣兵" => "傭兵",
-"佣工" => "傭工",
-"佣懒" => "傭懶",
-"佣书" => "傭書",
-"佣金" => "傭金",
-"伤痕累累" => "傷痕纍纍",
-"傻里傻气" => "傻裡傻氣",
-"倾向于" => "傾向於",
-"倾家荡产" => "傾家蕩產",
-"倾复" => "傾複",
-"仆人" => "僕人",
-"仆使" => "僕使",
-"仆仆" => "僕僕",
-"仆仆风尘" => "僕僕風塵",
-"仆僮" => "僕僮",
-"仆吏" => "僕吏",
-"仆固怀恩" => "僕固懷恩",
-"仆夫" => "僕夫",
-"仆姑" => "僕姑",
-"仆妇" => "僕婦",
-"仆射" => "僕射",
-"仆少" => "僕少",
-"仆役" => "僕役",
-"仆从" => "僕從",
-"仆憎" => "僕憎",
-"仆欧" => "僕歐",
-"仆程" => "僕程",
-"仆虽罢驽" => "僕雖罷駑",
-"侥幸" => "僥倖",
-"僮仆" => "僮僕",
-"雇主" => "僱主",
-"雇人" => "僱人",
-"雇佣" => "僱佣",
-"雇到" => "僱到",
-"雇员" => "僱員",
-"雇工" => "僱工",
-"雇用" => "僱用",
-"雇农" => "僱農",
-"仪范" => "儀範",
-"仪表" => "儀錶",
-"亿多只" => "億多隻",
-"亿天后" => "億天後",
-"亿只" => "億隻",
-"俭朴" => "儉樸",
-"儒略改革历" => "儒略改革曆",
-"儒略历" => "儒略曆",
-"尽尽" => "儘儘",
-"尽先" => "儘先",
-"尽其所有" => "儘其所有",
-"尽力" => "儘力",
-"尽快" => "儘快",
-"尽早" => "儘早",
-"尽是" => "儘是",
-"尽管" => "儘管",
-"尽速" => "儘速",
-"优于" => "優於",
-"优游" => "優遊",
-"兀术" => "兀朮",
-"元凶" => "元兇",
-"充饥" => "充饑",
-"凶器" => "兇器",
-"凶徒" => "兇徒",
-"凶手" => "兇手",
-"凶案" => "兇案",
-"凶残" => "兇殘",
-"凶杀" => "兇殺",
-"先占" => "先佔",
-"先后" => "先後",
-"先忧后乐" => "先憂後樂",
-"先采" => "先採",
-"先攻后守" => "先攻後守",
-"先于" => "先於",
-"先盛后衰" => "先盛後衰",
-"先礼后兵" => "先禮後兵",
-"先义后利" => "先義後利",
-"先苦后甘" => "先苦後甘",
-"先赢后输" => "先贏後輸",
-"先进后出" => "先進後出",
-"光采" => "光採",
-"光致致" => "光緻緻",
-"克药" => "克藥",
-"克复" => "克複",
-"免于" => "免於",
-"党参" => "党參",
-"党太尉" => "党太尉",
-"党进" => "党進",
-"党项" => "党項",
-"入夜后" => "入夜後",
-"入伙" => "入夥",
-"内制" => "內製",
-"内斗" => "內鬥",
-"内哄" => "內鬨",
-"全干" => "全乾",
-"两天后" => "兩天後",
-"两扎" => "兩紮",
-"两只" => "兩隻",
-"八天后" => "八天後",
-"八字胡" => "八字鬍",
-"八扎" => "八紮",
-"八只" => "八隻",
-"公仔面" => "公仔麵",
-"公仆" => "公僕",
-"公干" => "公幹",
-"公历" => "公曆",
-"公诸于世" => "公諸於世",
-"公厘" => "公釐",
-"公余" => "公餘",
-"六划" => "六劃",
-"六天后" => "六天後",
-"六扎" => "六紮",
-"六冲" => "六衝",
-"六只" => "六隻",
-"六出" => "六齣",
-"其后" => "其後",
-"其次辟地" => "其次辟地",
-"其余" => "其餘",
-"典范" => "典範",
-"兼并" => "兼併",
-"冉有仆" => "冉有僕",
-"再于" => "再於",
-"冤雠" => "冤讎",
-"冥蒙" => "冥濛",
-"冬山庄" => "冬山庄",
-"冬游" => "冬遊",
-"冶游" => "冶遊",
-"冷面相" => "冷面相",
-"冷面" => "冷麵",
-"凌蒙初" => "凌濛初",
-"凌藉" => "凌藉",
-"几几" => "几几",
-"几案" => "几案",
-"几丝" => "几絲",
-"凡于" => "凡於",
-"凹洞里" => "凹洞裡",
-"出乖露丑" => "出乖露醜",
-"出于" => "出於",
-"出自于" => "出自於",
-"出谋划策" => "出謀劃策",
-"出游" => "出遊",
-"出丑" => "出醜",
-"出锤" => "出鎚",
-"刀削面" => "刀削麵",
-"分布" => "分佈",
-"分占" => "分佔",
-"分钟" => "分鐘",
-"刑余" => "刑餘",
-"划着" => "划著",
-"划着走" => "划著走",
-"划龙舟" => "划龍舟",
-"别后" => "別後",
-"别日南鸿才北去" => "別日南鴻纔北去",
-"别致" => "別緻",
-"别着" => "別著",
-"别辟" => "別闢",
-"别只" => "別隻",
-"利欲" => "利慾",
-"利于" => "利於",
-"刮着" => "刮著",
-"刮风下雪倒便宜" => "刮風下雪倒便宜",
-"刮胡刀" => "刮鬍刀",
-"制签" => "制籤",
-"刺绣" => "刺繡",
-"刻划" => "刻劃",
-"刻于" => "刻於",
-"刻钟" => "刻鐘",
-"剃发" => "剃髮",
-"剃须" => "剃鬚",
-"削发" => "削髮",
-"削面" => "削麵",
-"克制" => "剋制",
-"克星" => "剋星",
-"克死" => "剋死",
-"克薄" => "剋薄",
-"前仰后合" => "前仰後合",
-"前倨后恭" => "前倨後恭",
-"前呼后拥" => "前呼後擁",
-"前后" => "前後",
-"前思后想" => "前思後想",
-"前挽后推" => "前挽後推",
-"前短后长" => "前短後長",
-"刚干" => "剛乾",
-"刚雇" => "剛僱",
-"刚才一载" => "剛纔一載",
-"剩余" => "剩餘",
-"剪牡丹喂牛" => "剪牡丹喂牛",
-"剪䌽" => "剪綵",
-"剪发" => "剪髮",
-"割舍" => "割捨",
-"创制" => "創製",
-"划一" => "劃一",
-"划上" => "劃上",
-"划下" => "劃下",
-"划了" => "劃了",
-"划出" => "劃出",
-"划分" => "劃分",
-"划到" => "劃到",
-"划划" => "劃劃",
-"划去" => "劃去",
-"划在" => "劃在",
-"划地" => "劃地",
-"划定" => "劃定",
-"划得" => "劃得",
-"划成" => "劃成",
-"划掉" => "劃掉",
-"划拨" => "劃撥",
-"划时代" => "劃時代",
-"划款" => "劃款",
-"划归" => "劃歸",
-"划法" => "劃法",
-"划清" => "劃清",
-"划界" => "劃界",
-"划破" => "劃破",
-"划线" => "劃線",
-"划足" => "劃足",
-"划开" => "劃開",
-"力争上游" => "力爭上遊",
-"功致" => "功緻",
-"加害于" => "加害於",
-"加工余量" => "加工餘量",
-"加卷" => "加捲",
-"加于" => "加於",
-"加药" => "加藥",
-"加注" => "加註",
-"劣于" => "劣於",
-"助于" => "助於",
-"劫余" => "劫餘",
-"勃郁" => "勃鬱",
-"勇于" => "勇於",
-"动荡" => "動蕩",
-"胜于" => "勝於",
-"劳力士表" => "勞力士錶",
-"勤仆" => "勤僕",
-"勤朴" => "勤樸",
-"勾干" => "勾幹",
-"勾心斗角" => "勾心鬥角",
-"勿施于人" => "勿施於人",
-"包谷" => "包穀",
-"包扎" => "包紮",
-"北岳" => "北嶽",
-"北回线" => "北迴線",
-"北回铁路" => "北迴鐵路",
-"匡复" => "匡複",
-"匪干" => "匪幹",
-"匿于" => "匿於",
-"区划" => "區劃",
-"十划" => "十劃",
-"十多只" => "十多隻",
-"十天后" => "十天後",
-"十卷" => "十捲",
-"十扎" => "十紮",
-"十只" => "十隻",
-"十出" => "十齣",
-"千多只" => "千多隻",
-"千天后" => "千天後",
-"千扎" => "千紮",
-"千丝万缕" => "千絲萬縷",
-"千回百折" => "千迴百折",
-"千回百转" => "千迴百轉",
-"千钧一发" => "千鈞一髮",
-"千只" => "千隻",
-"午后" => "午後",
-"半于" => "半於",
-"半只" => "半隻",
-"南岳" => "南嶽",
-"南筑" => "南筑",
-"南回线" => "南迴線",
-"南回铁路" => "南迴鐵路",
-"南游" => "南遊",
-"博汇" => "博彙",
-"博采" => "博採",
-"印累绶若" => "印纍綬若",
-"印制" => "印製",
-"危于" => "危於",
-"卷发" => "卷髮",
-"卷须" => "卷鬚",
-"厂部" => "厂部",
-"原子钟" => "原子鐘",
-"原于" => "原於",
-"历物之意" => "厤物之意",
-"参与" => "參与",
-"参与者" => "參与者",
-"参合" => "參合",
-"参考价值" => "參考價值",
-"参与" => "參與",
-"参与人员" => "參與人員",
-"参与制" => "參與制",
-"参与感" => "參與感",
-"参与者" => "參與者",
-"参观团" => "參觀團",
-"参观团体" => "參觀團體",
-"参阅" => "參閱",
-"及于" => "及於",
-"反于" => "反於",
-"反朴" => "反樸",
-"反冲" => "反衝",
-"反复制" => "反複製",
-"反复" => "反覆",
-"取信于" => "取信於",
-"取舍" => "取捨",
-"取材于" => "取材於",
-"取决于" => "取決於",
-"取法于" => "取法於",
-"受人之讬" => "受人之託",
-"受制于人" => "受制於人",
-"受讬" => "受託",
-"受阻于" => "受阻於",
-"口干" => "口乾",
-"口燥脣干" => "口燥脣乾",
-"口腹之欲" => "口腹之慾",
-"口血未干" => "口血未乾",
-"口里" => "口裡",
-"古柯硷" => "古柯鹼",
-"古朴" => "古樸",
-"另于" => "另於",
-"另辟" => "另闢",
-"叩钟" => "叩鐘",
-"只占" => "只佔",
-"只采" => "只採",
-"只冲" => "只衝",
-"叮当" => "叮噹",
-"可于" => "可於",
-"可紧可松" => "可緊可鬆",
-"台后" => "台後",
-"台历" => "台曆",
-"台制" => "台製",
-"右后" => "右後",
-"叶恭弘" => "叶恭弘",
-"叶 恭弘" => "叶 恭弘",
-"叶 恭弘" => "叶 恭弘",
-"叶音" => "叶音",
-"叶韵" => "叶韻",
-"吃板刀面" => "吃板刀麵",
-"吃着不尽" => "吃著不盡",
-"吃姜" => "吃薑",
-"吃药" => "吃藥",
-"吃里扒外" => "吃裡扒外",
-"吃里爬外" => "吃裡爬外",
-"吃豆干" => "吃豆乾",
-"吃辣面" => "吃辣麵",
-"吃错药" => "吃錯藥",
-"各辟" => "各闢",
-"合并" => "合併",
-"合伙" => "合夥",
-"合采" => "合採",
-"合于" => "合於",
-"合历" => "合曆",
-"合着" => "合著",
-"合着者" => "合著者",
-"吊带裤" => "吊帶褲",
-"吊挂着" => "吊掛著",
-"吊着" => "吊著",
-"吊裤" => "吊褲",
-"吊裤带" => "吊褲帶",
-"吊钟" => "吊鐘",
-"同伙" => "同夥",
-"同于" => "同於",
-"名闻于世" => "名聞於世",
-"后发座" => "后髮座",
-"向后" => "向後",
-"向着" => "向著",
-"吞并" => "吞併",
-"吟游" => "吟遊",
-"吹干" => "吹乾",
-"吹发" => "吹髮",
-"呆致致" => "呆緻緻",
-"呆里呆气" => "呆裡呆氣",
-"周历" => "周曆",
-"周杰伦" => "周杰倫",
-"周游" => "周遊",
-"呼吁" => "呼籲",
-"咬姜呷醋" => "咬薑呷醋",
-"咯当" => "咯噹",
-"咳嗽药" => "咳嗽藥",
-"哀吊" => "哀弔",
-"品汇" => "品彙",
-"员山庄" => "員山庄",
-"哪里" => "哪裡",
-"哭脏" => "哭髒",
-"唇干" => "唇乾",
-"唱游" => "唱遊",
-"唾面自干" => "唾面自乾",
-"唾余" => "唾餘",
-"商历" => "商曆",
-"问政于民" => "問政於民",
-"问道于盲" => "問道於盲",
-"啷当" => "啷噹",
-"善后" => "善後",
-"善于" => "善於",
-"喜形于色" => "喜形於色",
-"喧哄" => "喧鬨",
-"丧钟" => "喪鐘",
-"单干" => "單幹",
-"单打独斗" => "單打獨鬥",
-"单只" => "單隻",
-"嗑药" => "嗑藥",
-"嗣后" => "嗣後",
-"嘉谷" => "嘉穀",
-"嘴里" => "嘴裡",
-"恶心" => "噁心",
-"噙齿戴发" => "噙齒戴髮",
-"当啷" => "噹啷",
-"当当" => "噹噹",
-"噜苏" => "嚕囌",
-"向导" => "嚮導",
-"向往" => "嚮往",
-"向应" => "嚮應",
-"向迩" => "嚮邇",
-"严于" => "嚴於",
-"严丝合缝" => "嚴絲合縫",
-"嚼谷" => "嚼穀",
-"囉囉苏苏" => "囉囉囌囌",
-"囉苏" => "囉囌",
-"嘱讬" => "囑託",
-"四分历" => "四分曆",
-"四天后" => "四天後",
-"四舍五入" => "四捨五入",
-"四扎" => "四紮",
-"四只" => "四隻",
-"四出" => "四齣",
-"回采" => "回採",
-"回历" => "回曆",
-"回丝" => "回絲",
-"回着" => "回著",
-"回荡" => "回蕩",
-"回游" => "回遊",
-"因于" => "因於",
-"困于" => "困於",
-"困兽之斗" => "困獸之鬥",
-"困兽犹斗" => "困獸猶鬥",
-"固于" => "固於",
-"囿于" => "囿於",
-"囿于一时" => "囿於一時",
-"囿于成见" => "囿於成見",
-"圈子里" => "圈子裡",
-"圈梁" => "圈樑",
-"圈里" => "圈裡",
-"国之桢干" => "國之楨榦",
-"国于" => "國於",
-"国历" => "國曆",
-"国历代" => "國歷代",
-"国历史" => "國歷史",
-"国雠" => "國讎",
-"园里" => "園裡",
-"园游会" => "園遊會",
-"图里" => "圖裡",
-"土里" => "土裡",
-"土制" => "土製",
-"在于" => "在於",
-"地志" => "地誌",
-"地丑德齐" => "地醜德齊",
-"坏于" => "坏於",
-"坐钟" => "坐鐘",
-"坑里" => "坑裡",
-"坤范" => "坤範",
-"坦荡" => "坦蕩",
-"坱郁" => "坱鬱",
-"垂直于" => "垂直於",
-"垂发" => "垂髮",
-"型范" => "型範",
-"埃及历" => "埃及曆",
-"城里" => "城裡",
-"基干" => "基幹",
-"基于" => "基於",
-"基准" => "基準",
-"坚致" => "堅緻",
-"涂着" => "塗著",
-"涂药" => "塗藥",
-"塞耳盗钟" => "塞耳盜鐘",
-"塞药" => "塞藥",
-"墓志" => "墓誌",
-"增辟" => "增闢",
-"墨沈" => "墨沈",
-"堕胎药" => "墮胎藥",
-"垦复" => "墾複",
-"垦辟" => "墾闢",
-"垄断价格" => "壟斷價格",
-"垄断资产" => "壟斷資產",
-"垄断集团" => "壟斷集團",
-"壮面" => "壯麵",
-"壹郁" => "壹鬱",
-"壶里" => "壺裡",
-"壸范" => "壼範",
-"寿面" => "壽麵",
-"夏天里" => "夏天裡",
-"夏历" => "夏曆",
-"夏历史" => "夏歷史",
-"夏游" => "夏遊",
-"外强中干" => "外強中乾",
-"外制" => "外製",
-"多划" => "多劃",
-"多只是" => "多只是",
-"多天后" => "多天後",
-"多于" => "多於",
-"多冲" => "多衝",
-"多丑" => "多醜",
-"多只" => "多隻",
-"多余" => "多餘",
-"多么" => "多麼",
-"夜光表" => "夜光錶",
-"夜里" => "夜裡",
-"夜游" => "夜遊",
-"梦里" => "夢裡",
-"梦游" => "夢遊",
-"伙伴" => "夥伴",
-"伙友" => "夥友",
-"伙同" => "夥同",
-"伙众" => "夥眾",
-"伙计" => "夥計",
-"大伙" => "大夥",
-"大干" => "大幹",
-"大批涌到" => "大批湧到",
-"大于" => "大於",
-"大明历" => "大明曆",
-"大历" => "大曆",
-"大梁" => "大樑",
-"大目干连" => "大目乾連",
-"大衍历" => "大衍曆",
-"大言非夸" => "大言非夸",
-"大丑" => "大醜",
-"大锤" => "大鎚",
-"大只" => "大隻",
-"天干物燥" => "天乾物燥",
-"天克地冲" => "天克地衝",
-"天后" => "天後",
-"0天后" => "0天後",
-"天文钟" => "天文鐘",
-"天然硷" => "天然鹼",
-"天翻地复" => "天翻地覆",
-"天复地载" => "天覆地載",
-"太仆" => "太僕",
-"太初历" => "太初曆",
-"夯干" => "夯幹",
-"失信于人" => "失信於人",
-"失于" => "失於",
-"夸人" => "夸人",
-"夸克" => "夸克",
-"夸姣" => "夸姣",
-"夸容" => "夸容",
-"夸毗" => "夸毗",
-"夸父" => "夸父",
-"夸特" => "夸特",
-"夸丽" => "夸麗",
-"奇丑" => "奇醜",
-"奏折" => "奏摺",
-"奏于" => "奏於",
-"夺斗" => "奪鬥",
-"奋斗" => "奮鬥",
-"女佣" => "女傭",
-"女仆" => "女僕",
-"奴仆" => "奴僕",
-"好干" => "好乾",
-"好家伙" => "好傢夥",
-"好于" => "好於",
-"好签" => "好籤",
-"好丑" => "好醜",
-"如于" => "如於",
-"如果干" => "如果幹",
-"如法泡制" => "如法泡製",
-"妙药" => "妙藥",
-"始于" => "始於",
-"委罪于人" => "委罪於人",
-"委讬" => "委託",
-"姜丝" => "姜絲",
-"奸夫" => "姦夫",
-"奸妇" => "姦婦",
-"奸情" => "姦情",
-"奸杀" => "姦殺",
-"奸污" => "姦汙",
-"奸淫" => "姦淫",
-"奸邪" => "姦邪",
-"威棱" => "威稜",
-"婚后" => "婚後",
-"婢仆" => "婢僕",
-"嫁于" => "嫁於",
-"嫁祸于人" => "嫁禍於人",
-"嫌好道丑" => "嫌好道醜",
-"娴于" => "嫻於",
-"嬉游" => "嬉遊",
-"嬴余" => "嬴餘",
-"子之丰兮" => "子之丰兮",
-"字汇" => "字彙",
-"字里行间" => "字裡行間",
-"存十一于千百" => "存十一於千百",
-"存折" => "存摺",
-"季后赛" => "季後賽",
-"孤寡不谷" => "孤寡不穀",
-"宇宙志" => "宇宙誌",
-"安于" => "安於",
-"安沈铁路" => "安瀋鐵路",
-"安眠药" => "安眠藥",
-"安胎药" => "安胎藥",
-"完工后" => "完工後",
-"完成后" => "完成後",
-"宗周钟" => "宗周鐘",
-"官地为采" => "官地為寀",
-"官历" => "官曆",
-"官庄" => "官莊",
-"定于" => "定於",
-"定准" => "定準",
-"定制" => "定製",
-"定都于" => "定都於",
-"宜于" => "宜於",
-"宦游" => "宦遊",
-"宫里" => "宮裡",
-"害于" => "害於",
-"宴游" => "宴遊",
-"家仆" => "家僕",
-"家庄" => "家莊",
-"家里" => "家裡",
-"家丑" => "家醜",
-"容后说明" => "容後說明",
-"容于" => "容於",
-"容范" => "容範",
-"寄于" => "寄於",
-"寄讬" => "寄託",
-"寇雠" => "寇讎",
-"富于" => "富於",
-"富余" => "富餘",
-"寒栗" => "寒慄",
-"寒于" => "寒於",
-"寓兵于农" => "寓兵於農",
-"寓教于乐" => "寓教於樂",
-"寓于" => "寓於",
-"寡欲" => "寡慾",
-"实干" => "實幹",
-"写字台" => "寫字檯",
-"宽于" => "寬於",
-"宽余" => "寬餘",
-"宽松" => "寬鬆",
-"寮采" => "寮寀",
-"宝山庄" => "寶山庄",
-"宝历" => "寶曆",
-"封面里" => "封面裡",
-"射雕" => "射鵰",
-"将于" => "將於",
-"专美于前" => "專美於前",
-"专注" => "專註",
-"对折" => "對摺",
-"对于" => "對於",
-"对准" => "對準",
-"对华发动" => "對華發動",
-"对表" => "對錶",
-"导游" => "導遊",
-"小仆" => "小僕",
-"小伙子" => "小夥子",
-"小于" => "小於",
-"小米面" => "小米麵",
-"小只" => "小隻",
-"少采" => "少採",
-"少于" => "少於",
-"就于" => "就於",
-"就范" => "就範",
-"就读于" => "就讀於",
-"尸魂界" => "尸魂界",
-"尼克松" => "尼克鬆",
-"局里" => "局裡",
-"居于" => "居於",
-"屈服于" => "屈服於",
-"屋子里" => "屋子裡",
-"屋梁" => "屋樑",
-"屋里" => "屋裡",
-"屑于" => "屑於",
-"屡顾尔仆" => "屢顧爾僕",
-"属意于" => "屬意於",
-"属于" => "屬於",
-"屯扎" => "屯紮",
-"屯里" => "屯裡",
-"山崩钟应" => "山崩鐘應",
-"山岳" => "山嶽",
-"山后" => "山後",
-"山梁" => "山樑",
-"山庄" => "山莊",
-"山药" => "山藥",
-"山里" => "山裡",
-"峰回" => "峰迴",
-"昆剧" => "崑劇",
-"昆山" => "崑山",
-"昆仑" => "崑崙",
-"昆曲" => "崑曲",
-"昆腔" => "崑腔",
-"昆苏" => "崑蘇",
-"昆调" => "崑調",
-"仑背" => "崙背",
-"嶒棱" => "嶒稜",
-"岳麓山" => "嶽麓山",
-"川谷" => "川穀",
-"巡回" => "巡迴",
-"巡游" => "巡遊",
-"工于" => "工於",
-"工致" => "工緻",
-"左后" => "左後",
-"左冲右突" => "左衝右突",
-"巧妇做不得无面馎饦" => "巧婦做不得無麵餺飥",
-"巧干" => "巧幹",
-"巧历" => "巧曆",
-"差于" => "差於",
-"已于" => "已於",
-"巴尔干" => "巴爾幹",
-"巷里" => "巷裡",
-"市里" => "市裡",
-"布谷" => "布穀",
-"希伯来历" => "希伯來曆",
-"帘子" => "帘子",
-"帘布" => "帘布",
-"师范" => "師範",
-"席卷" => "席捲",
-"带团参加" => "帶團參加",
-"带发修行" => "帶髮修行",
-"幕后" => "幕後",
-"帮佣" => "幫傭",
-"干系" => "干係",
-"干着急" => "干著急",
-"平平当当" => "平平當當",
-"平准" => "平準",
-"年后" => "年後",
-"年历" => "年曆",
-"年历史" => "年歷史",
-"年谷" => "年穀",
-"年里" => "年裡",
-"并州" => "并州",
-"干上" => "幹上",
-"干下去" => "幹下去",
-"干了" => "幹了",
-"干事" => "幹事",
-"干些" => "幹些",
-"干人" => "幹人",
-"干什么" => "幹什麼",
-"干个" => "幹個",
-"干劲" => "幹勁",
-"干吏" => "幹吏",
-"干员" => "幹員",
-"干吗" => "幹嗎",
-"干嘛" => "幹嘛",
-"干坏事" => "幹壞事",
-"干完" => "幹完",
-"干家" => "幹家",
-"干得" => "幹得",
-"干性油" => "幹性油",
-"干才" => "幹才",
-"干掉" => "幹掉",
-"干探" => "幹探",
-"干校" => "幹校",
-"干活" => "幹活",
-"干流" => "幹流",
-"干济" => "幹濟",
-"干营生" => "幹營生",
-"干父之蛊" => "幹父之蠱",
-"干球温度" => "幹球溫度",
-"干当" => "幹當",
-"干的停当" => "幹的停當",
-"干细胞" => "幹細胞",
-"干线" => "幹線",
-"干练" => "幹練",
-"干缺" => "幹缺",
-"干蛊" => "幹蠱",
-"干警" => "幹警",
-"干起来" => "幹起來",
-"干路" => "幹路",
-"干道" => "幹道",
-"干部" => "幹部",
-"干革命" => "幹革命",
-"干头" => "幹頭",
-"干么" => "幹麼",
-"几划" => "幾劃",
-"几天后" => "幾天後",
-"几于" => "幾於",
-"几丝" => "幾絲",
-"几只" => "幾隻",
-"几出" => "幾齣",
-"广部" => "广部",
-"府干卿" => "府干卿",
-"府干扰" => "府干擾",
-"府干政" => "府干政",
-"府干涉" => "府干涉",
-"府干犯" => "府干犯",
-"府干预" => "府干預",
-"府干" => "府幹",
-"府后" => "府後",
-"座钟" => "座鐘",
-"康采恩" => "康採恩",
-"康庄" => "康莊",
-"厨余" => "廚餘",
-"庙里" => "廟裡",
-"广舍" => "廣捨",
-"延后" => "延後",
-"建于" => "建於",
-"建都于" => "建都於",
-"弄干" => "弄乾",
-"弄丑" => "弄醜",
-"弄脏" => "弄髒",
-"弄松" => "弄鬆",
-"吊儿郎当" => "弔兒郎當",
-"吊卷" => "弔卷",
-"吊古" => "弔古",
-"吊唁" => "弔唁",
-"吊丧" => "弔喪",
-"吊孝" => "弔孝",
-"吊客" => "弔客",
-"吊带" => "弔帶",
-"吊慰" => "弔慰",
-"吊挂" => "弔掛",
-"吊文" => "弔文",
-"吊死" => "弔死",
-"吊民伐罪" => "弔民伐罪",
-"吊祭" => "弔祭",
-"弘历" => "弘曆",
-"弱于" => "弱於",
-"弱硷" => "弱鹼",
-"张三丰" => "張三丰",
-"强占" => "強佔",
-"强奸" => "強姦",
-"强干" => "強幹",
-"强于" => "強於",
-"强硷" => "強鹼",
-"别口气" => "彆口氣",
-"别强" => "彆強",
-"别扭" => "彆扭",
-"别拗" => "彆拗",
-"别气" => "彆氣",
-"别着" => "彆著",
-"弹子台" => "彈子檯",
-"弹药" => "彈藥",
-"汇报" => "彙報",
-"汇整" => "彙整",
-"汇编" => "彙編",
-"汇纂" => "彙纂",
-"汇辑" => "彙輯",
-"汇集" => "彙集",
-"形单影只" => "形單影隻",
-"形于" => "形於",
-"役于" => "役於",
-"役于外物" => "役於外物",
-"往来于" => "往來於",
-"往后" => "往後",
-"往里" => "往裡",
-"往复" => "往複",
-"很干" => "很乾",
-"律历志" => "律曆志",
-"后上" => "後上",
-"后下" => "後下",
-"后世" => "後世",
-"后主" => "後主",
-"后事" => "後事",
-"后人" => "後人",
-"后代" => "後代",
-"后仰" => "後仰",
-"后件" => "後件",
-"后任" => "後任",
-"后作" => "後作",
-"后来" => "後來",
-"后偏" => "後偏",
-"后备" => "後備",
-"后传" => "後傳",
-"后分" => "後分",
-"后到" => "後到",
-"后力不继" => "後力不繼",
-"后劲" => "後勁",
-"后勤" => "後勤",
-"后区" => "後區",
-"后半" => "後半",
-"后印" => "後印",
-"后去" => "後去",
-"后台" => "後台",
-"后向" => "後向",
-"后周" => "後周",
-"后唐" => "後唐",
-"后嗣" => "後嗣",
-"后园" => "後園",
-"后图" => "後圖",
-"后土" => "後土",
-"后埔" => "後埔",
-"后堂" => "後堂",
-"后尘" => "後塵",
-"后壁" => "後壁",
-"后天" => "後天",
-"后奏" => "後奏",
-"后娘" => "後娘",
-"后学" => "後學",
-"后宫" => "後宮",
-"后山" => "後山",
-"后巷" => "後巷",
-"后市" => "後市",
-"后年" => "後年",
-"后几" => "後幾",
-"后庄" => "後庄",
-"后序" => "後序",
-"后座" => "後座",
-"后悔" => "後悔",
-"后患" => "後患",
-"后房" => "後房",
-"后手" => "後手",
-"后排" => "後排",
-"后掠角" => "後掠角",
-"后接" => "後接",
-"后援" => "後援",
-"后撤" => "後撤",
-"后攻" => "後攻",
-"后放" => "後放",
-"后效" => "後效",
-"后文" => "後文",
-"后方" => "後方",
-"后于" => "後於",
-"后日" => "後日",
-"后晋" => "後晉",
-"后晌" => "後晌",
-"后晚" => "後晚",
-"后景" => "後景",
-"后会" => "後會",
-"后有" => "後有",
-"后望镜" => "後望鏡",
-"后期" => "後期",
-"后果" => "後果",
-"后桅" => "後桅",
-"后梁" => "後梁",
-"后桥" => "後橋",
-"后步" => "後步",
-"后段" => "後段",
-"后殿" => "後殿",
-"后母" => "後母",
-"后派" => "後派",
-"后浪" => "後浪",
-"后凉" => "後涼",
-"后港" => "後港",
-"后汉" => "後漢",
-"后为" => "後為",
-"后无来者" => "後無來者",
-"后燕" => "後燕",
-"后生" => "後生",
-"后用" => "後用",
-"后由" => "後由",
-"后盾" => "後盾",
-"后知" => "後知",
-"后福" => "後福",
-"后秃" => "後禿",
-"后秦" => "後秦",
-"后空翻" => "後空翻",
-"后窗" => "後窗",
-"后站" => "後站",
-"后端" => "後端",
-"后竹围" => "後竹圍",
-"后节" => "後節",
-"后篇" => "後篇",
-"后继" => "後繼",
-"后续" => "後續",
-"后置" => "後置",
-"后者" => "後者",
-"后肢" => "後肢",
-"后背" => "後背",
-"后脑" => "後腦",
-"后脚" => "後腳",
-"后腿" => "後腿",
-"后膛" => "後膛",
-"后花园" => "後花園",
-"后菜园" => "後菜園",
-"后叶" => "後葉",
-"后行" => "後行",
-"后街" => "後街",
-"后卫" => "後衛",
-"后裔" => "後裔",
-"后䙓" => "後襬",
-"后视镜" => "後視鏡",
-"后计" => "後計",
-"后记" => "後記",
-"后设" => "後設",
-"后读" => "後讀",
-"后走" => "後走",
-"后起" => "後起",
-"后赵" => "後趙",
-"后足" => "後足",
-"后跟" => "後跟",
-"后路" => "後路",
-"后身" => "後身",
-"后车" => "後車",
-"后辈" => "後輩",
-"后轮" => "後輪",
-"后转" => "後轉",
-"后述" => "後述",
-"后退" => "後退",
-"后送" => "後送",
-"后进" => "後進",
-"后过" => "後過",
-"后遗症" => "後遺症",
-"后边" => "後邊",
-"后部" => "後部",
-"后镜" => "後鏡",
-"后门" => "後門",
-"后防" => "後防",
-"后院" => "後院",
-"后集" => "後集",
-"后面" => "後面",
-"后项" => "後項",
-"后头" => "後頭",
-"后颈" => "後頸",
-"后顾" => "後顧",
-"后魏" => "後魏",
-"后点" => "後點",
-"后龙" => "後龍",
-"徐干" => "徐幹",
-"徒讬空言" => "徒託空言",
-"得于" => "得於",
-"徜徉于" => "徜徉於",
-"从事于" => "從事於",
-"从于" => "從於",
-"从里到外" => "從裡到外",
-"从里向外" => "從裡向外",
-"复始" => "復始",
-"复雠" => "復讎",
-"征信" => "徵信",
-"征候" => "徵候",
-"征兆" => "徵兆",
-"征兵" => "徵兵",
-"征到" => "徵到",
-"征募" => "徵募",
-"征友" => "徵友",
-"征召" => "徵召",
-"征引" => "徵引",
-"征得" => "徵得",
-"征收" => "徵收",
-"征文" => "徵文",
-"征求" => "徵求",
-"征状" => "徵狀",
-"征用" => "徵用",
-"征税" => "徵稅",
-"征稿" => "徵稿",
-"征结" => "徵結",
-"征聘" => "徵聘",
-"征训" => "徵訓",
-"征询" => "徵詢",
-"征调" => "徵調",
-"征象" => "徵象",
-"征购" => "徵購",
-"征集" => "徵集",
-"征验出" => "徵驗出",
-"心愿" => "心愿",
-"心于" => "心於",
-"心细如发" => "心細如髮",
-"心荡神驰" => "心蕩神馳",
-"心药" => "心藥",
-"心里" => "心裡",
-"心余" => "心餘",
-"志于" => "志於",
-"忙并" => "忙併",
-"忙于" => "忙於",
-"忙里" => "忙裡",
-"忠仆" => "忠僕",
-"忠于" => "忠於",
-"快干" => "快乾",
-"快干" => "快幹",
-"快快当当" => "快快當當",
-"快冲" => "快衝",
-"忽前忽后" => "忽前忽後",
-"怎么" => "怎麼",
-"怎么着" => "怎麼著",
-"怒形于色" => "怒形於色",
-"怒于" => "怒於",
-"怒发冲冠" => "怒髮衝冠",
-"思前思后" => "思前思後",
-"思前想后" => "思前想後",
-"急于" => "急於",
-"急冲而下" => "急衝而下",
-"性征" => "性徵",
-"性欲" => "性慾",
-"怪里怪气" => "怪裡怪氣",
-"怫郁" => "怫鬱",
-"息谷" => "息穀",
-"恰才" => "恰纔",
-"悍药" => "悍藥",
-"悒郁" => "悒鬱",
-"悒郁寡欢" => "悒鬱寡歡",
-"悠游" => "悠遊",
-"闷着头儿干" => "悶著頭兒幹",
-"悸栗" => "悸慄",
-"情欲" => "情慾",
-"惇朴" => "惇樸",
-"恶直丑正" => "惡直醜正",
-"惴栗" => "惴慄",
-"意大利面" => "意大利麵",
-"意面" => "意麵",
-"爱困" => "愛睏",
-"感冒药" => "感冒藥",
-"感于" => "感於",
-"愧于" => "愧於",
-"愿朴" => "愿樸",
-"愿而恭" => "愿而恭",
-"慌里慌张" => "慌裡慌張",
-"惯于" => "慣於",
-"慰藉" => "慰藉",
-"庆吊" => "慶弔",
-"庆历" => "慶曆",
-"欲令智昏" => "慾令智昏",
-"欲壑难填" => "慾壑難填",
-"欲念" => "慾念",
-"欲望" => "慾望",
-"欲海" => "慾海",
-"欲火" => "慾火",
-"欲障" => "慾障",
-"忧形于色" => "憂形於色",
-"忧郁" => "憂鬱",
-"凭吊" => "憑弔",
-"凭藉着" => "憑藉著",
-"恳讬" => "懇託",
-"懈松" => "懈鬆",
-"应征" => "應徵",
-"应钟" => "應鐘",
-"蒙懂" => "懞懂",
-"蒙蒙懂懂" => "懞懞懂懂",
-"蒙直" => "懞直",
-"惩前毖后" => "懲前毖後",
-"懒于" => "懶於",
-"怀里" => "懷裡",
-"怀表" => "懷錶",
-"悬梁" => "懸樑",
-"悬臂梁" => "懸臂樑",
-"悬钟" => "懸鐘",
-"惧于" => "懼於",
-"懿范" => "懿範",
-"恋恋不舍" => "戀戀不捨",
-"成于" => "成於",
-"成药" => "成藥",
-"或于" => "或於",
-"戬谷" => "戩穀",
-"截发" => "截髮",
-"战天斗地" => "戰天鬥地",
-"战后" => "戰後",
-"战栗" => "戰慄",
-"战斗" => "戰鬥",
-"戴表" => "戴錶",
-"房里" => "房裡",
-"扁拟谷盗虫" => "扁擬穀盜蟲",
-"手冢治虫" => "手塚治虫",
-"手折" => "手摺",
-"手里" => "手裡",
-"手表" => "手錶",
-"手松" => "手鬆",
-"才干" => "才幹",
-"打干哕" => "打乾噦",
-"打并" => "打併",
-"打卡钟" => "打卡鐘",
-"打干" => "打幹",
-"打拼" => "打拚",
-"打谷" => "打穀",
-"打钟" => "打鐘",
-"打斗" => "打鬥",
-"扞御" => "扞禦",
-"扯面" => "扯麵",
-"批准的" => "批准的",
-"批复" => "批複",
-"批注" => "批註",
-"批斗" => "批鬥",
-"承先启后" => "承先啟後",
-"承制" => "承製",
-"抑郁" => "抑鬱",
-"抓奸" => "抓姦",
-"抓药" => "抓藥",
-"抓斗" => "抓鬥",
-"投药" => "投藥",
-"抗癌药" => "抗癌藥",
-"抗御" => "抗禦",
-"抗药" => "抗藥",
-"抗硷" => "抗鹼",
-"折冲" => "折衝",
-"披榛采兰" => "披榛採蘭",
-"披头散发" => "披頭散髮",
-"披发" => "披髮",
-"抱朴而长吟兮" => "抱朴而長吟兮",
-"抱素怀朴" => "抱素懷樸",
-"抵御" => "抵禦",
-"抹干" => "抹乾",
-"抽公签" => "抽公籤",
-"抽签" => "抽籤",
-"抿发" => "抿髮",
-"拆伙" => "拆夥",
-"拈须" => "拈鬚",
-"拉纤" => "拉縴",
-"拉面" => "拉麵",
-"拒人于" => "拒人於",
-"拒于" => "拒於",
-"拓朴" => "拓樸",
-"拗别" => "拗彆",
-"拘于" => "拘於",
-"拘泥于" => "拘泥於",
-"拙于" => "拙於",
-"拙朴" => "拙樸",
-"拼命" => "拚命",
-"拼舍" => "拚捨",
-"拼死" => "拚死",
-"拼斗" => "拚鬥",
-"拜讬" => "拜託",
-"括发" => "括髮",
-"拭干" => "拭乾",
-"拮据" => "拮据",
-"拿准" => "拿準",
-"拿破仑" => "拿破崙",
-"指手划脚" => "指手劃腳",
-"振荡" => "振蕩",
-"捆扎" => "捆紮",
-"捉奸" => "捉姦",
-"捉发" => "捉髮",
-"捍御" => "捍禦",
-"捏面人" => "捏麵人",
-"舍不得" => "捨不得",
-"舍出" => "捨出",
-"舍去" => "捨去",
-"舍命" => "捨命",
-"舍堕" => "捨墮",
-"舍安就危" => "捨安就危",
-"舍实" => "捨實",
-"舍己从人" => "捨己從人",
-"舍己救人" => "捨己救人",
-"舍己为人" => "捨己為人",
-"舍己为公" => "捨己為公",
-"舍己为国" => "捨己為國",
-"舍得" => "捨得",
-"舍我其谁" => "捨我其誰",
-"舍本逐末" => "捨本逐末",
-"舍弃" => "捨棄",
-"舍死忘生" => "捨死忘生",
-"舍生" => "捨生",
-"舍短取长" => "捨短取長",
-"舍身" => "捨身",
-"舍车保帅" => "捨車保帥",
-"舍近求远" => "捨近求遠",
-"卷住" => "捲住",
-"卷来" => "捲來",
-"卷儿" => "捲兒",
-"卷入" => "捲入",
-"卷动" => "捲動",
-"卷去" => "捲去",
-"卷图" => "捲圖",
-"卷土重来" => "捲土重來",
-"卷尺" => "捲尺",
-"卷心菜" => "捲心菜",
-"卷成" => "捲成",
-"卷曲" => "捲曲",
-"卷款逃走" => "捲款逃走",
-"卷毛" => "捲毛",
-"卷烟" => "捲煙",
-"卷筒" => "捲筒",
-"卷帘" => "捲簾",
-"卷纸" => "捲紙",
-"卷缩" => "捲縮",
-"卷舌" => "捲舌",
-"卷菸" => "捲菸",
-"卷袖" => "捲袖",
-"卷走" => "捲走",
-"卷起" => "捲起",
-"卷轴" => "捲軸",
-"卷逃" => "捲逃",
-"卷铺盖" => "捲鋪蓋",
-"卷云" => "捲雲",
-"卷风" => "捲風",
-"卷发" => "捲髮",
-"捵面" => "捵麵",
-"扫荡" => "掃蕩",
-"掌柜" => "掌柜",
-"排骨面" => "排骨麵",
-"挂帘" => "掛帘",
-"挂钟" => "掛鐘",
-"挂面" => "掛麵",
-"采下" => "採下",
-"采伐" => "採伐",
-"采住" => "採住",
-"采信" => "採信",
-"采光" => "採光",
-"采到" => "採到",
-"采制" => "採制",
-"采区" => "採區",
-"采去" => "採去",
-"采取" => "採取",
-"采回" => "採回",
-"采在" => "採在",
-"采好" => "採好",
-"采得" => "採得",
-"采拾" => "採拾",
-"采挖" => "採挖",
-"采掘" => "採掘",
-"采摘" => "採摘",
-"采摭" => "採摭",
-"采择" => "採擇",
-"采撷" => "採擷",
-"采收" => "採收",
-"采料" => "採料",
-"采暖" => "採暖",
-"采桑" => "採桑",
-"采样" => "採樣",
-"采樵人" => "採樵人",
-"采树种" => "採樹種",
-"采气" => "採氣",
-"采油" => "採油",
-"采为" => "採為",
-"采煤" => "採煤",
-"采猎" => "採獵",
-"采珠" => "採珠",
-"采生折割" => "採生折割",
-"采用" => "採用",
-"采的" => "採的",
-"采石" => "採石",
-"采石场" => "採石場",
-"采石厂" => "採石廠",
-"采砂场" => "採砂場",
-"采矿" => "採礦",
-"采种" => "採種",
-"采空区" => "採空區",
-"采空采穗" => "採空採穗",
-"采纳" => "採納",
-"采给" => "採給",
-"采花" => "採花",
-"采芹人" => "採芹人",
-"采茶" => "採茶",
-"采菊" => "採菊",
-"采莲" => "採蓮",
-"采薇" => "採薇",
-"采药" => "採藥",
-"采行" => "採行",
-"采补" => "採補",
-"采访" => "採訪",
-"采证" => "採證",
-"采买" => "採買",
-"采购" => "採購",
-"采办" => "採辦",
-"采运" => "採運",
-"采过" => "採過",
-"采选" => "採選",
-"采金" => "採金",
-"采录" => "採錄",
-"采铁" => "採鐵",
-"采集" => "採集",
-"采风" => "採風",
-"采风问俗" => "採風問俗",
-"采食" => "採食",
-"采盐" => "採鹽",
-"掣签" => "掣籤",
-"接着说" => "接著說",
-"推讬" => "推託",
-"提子干" => "提子乾",
-"提心吊胆" => "提心弔膽",
-"提摩太后书" => "提摩太後書",
-"插于" => "插於",
-"插足于" => "插足於",
-"换签" => "換籤",
-"换药" => "換藥",
-"换只" => "換隻",
-"换发" => "換髮",
-"揩干" => "揩乾",
-"揪采" => "揪採",
-"揭丑" => "揭醜",
-"挥手表" => "揮手表",
-"搋面" => "搋麵",
-"损于" => "損於",
-"搏斗" => "搏鬥",
-"摇荡" => "搖蕩",
-"搭干铺" => "搭乾鋪",
-"搭伙" => "搭夥",
-"抢占" => "搶佔",
-"搽药" => "搽藥",
-"摧坚获丑" => "摧堅獲醜",
-"摭采" => "摭採",
-"摸棱" => "摸稜",
-"折合" => "摺合",
-"折奏" => "摺奏",
-"折子" => "摺子",
-"折尺" => "摺尺",
-"折扇" => "摺扇",
-"折梯" => "摺梯",
-"折椅" => "摺椅",
-"折叠" => "摺疊",
-"折痕" => "摺痕",
-"折篷" => "摺篷",
-"折纸" => "摺紙",
-"折裙" => "摺裙",
-"捞干" => "撈乾",
-"捞面" => "撈麵",
-"撚须" => "撚鬚",
-"撞木钟" => "撞木鐘",
-"撞球台" => "撞球檯",
-"撞钟" => "撞鐘",
-"撞阵冲军" => "撞陣衝軍",
-"撤并" => "撤併",
-"撤后" => "撤後",
-"拨谷" => "撥穀",
-"播于" => "播於",
-"扑冬" => "撲鼕",
-"擀面" => "擀麵",
-"擅于" => "擅於",
-"击钟" => "擊鐘",
-"担仔面" => "擔仔麵",
-"担担面" => "擔擔麵",
-"担着" => "擔著",
-"担负着" => "擔負著",
-"据云" => "據云",
-"据干而窥井底" => "據榦而窺井底",
-"挤身于" => "擠身於",
-"擢发" => "擢髮",
-"擢发难数" => "擢髮難數",
-"擦干" => "擦乾",
-"擦药" => "擦藥",
-"拟于" => "擬於",
-"拧干" => "擰乾",
-"摆钟" => "擺鐘",
-"摄于" => "攝於",
-"摄制" => "攝製",
-"支干" => "支幹",
-"收获" => "收穫",
-"改征" => "改徵",
-"改于" => "改於",
-"攻占" => "攻佔",
-"放蒙挣" => "放懞掙",
-"放荡" => "放蕩",
-"放松" => "放鬆",
-"故于" => "故於",
-"敏于" => "敏於",
-"败于" => "敗於",
-"叙说着" => "敘說著",
-"教于" => "教於",
-"敢干" => "敢幹",
-"敢于" => "敢於",
-"散伙" => "散夥",
-"散于" => "散於",
-"散荡" => "散蕩",
-"敦朴" => "敦樸",
-"敬挽" => "敬輓",
-"敲钟" => "敲鐘",
-"整只" => "整隻",
-"敌后" => "敵後",
-"敷药" => "敷藥",
-"数天后" => "數天後",
-"数罪并罚" => "數罪併罰",
-"数与虏确" => "數與虜确",
-"文汇报" => "文匯報",
-"文思泉涌" => "文思泉湧",
-"斗转参横" => "斗轉參橫",
-"斗哄" => "斗鬨",
-"料斗" => "料鬥",
-"斫雕为朴" => "斫雕為樸",
-"新历" => "新曆",
-"新历史" => "新歷史",
-"新扎" => "新紮",
-"新庄" => "新莊",
-"斲雕为朴" => "斲雕為樸",
-"断后" => "斷後",
-"断发" => "斷髮",
-"方便面" => "方便麵",
-"方几" => "方几",
-"于一役" => "於一役",
-"于七" => "於七",
-"于世" => "於世",
-"于事" => "於事",
-"于事无补" => "於事無補",
-"于人" => "於人",
-"于今" => "於今",
-"于他" => "於他",
-"于伏" => "於伏",
-"于何" => "於何",
-"于你" => "於你",
-"于前" => "於前",
-"于劣" => "於劣",
-"于勤" => "於勤",
-"于呼哀哉" => "於呼哀哉",
-"于国" => "於國",
-"于坏" => "於坏",
-"于垂" => "於垂",
-"于夫罗" => "於夫羅",
-"于她" => "於她",
-"于好" => "於好",
-"于始" => "於始",
-"于它" => "於它",
-"于家" => "於家",
-"于密" => "於密",
-"于左" => "於左",
-"于差" => "於差",
-"于己" => "於己",
-"于市" => "於市",
-"于幕" => "於幕",
-"于幼华" => "於幼華",
-"于弱" => "於弱",
-"于强" => "於強",
-"于征" => "於征",
-"于后" => "於後",
-"于心" => "於心",
-"于心何忍" => "於心何忍",
-"于思" => "於思",
-"于怀" => "於懷",
-"于我" => "於我",
-"于斯" => "於斯",
-"于于" => "於於",
-"于是" => "於是",
-"于时" => "於時",
-"于梨华" => "於梨華",
-"于乐" => "於樂",
-"于此" => "於此",
-"于民" => "於民",
-"于法" => "於法",
-"于法无据" => "於法無據",
-"于潜县" => "於潛縣",
-"于火" => "於火",
-"于焉" => "於焉",
-"于墙" => "於牆",
-"于物" => "於物",
-"于田" => "於田",
-"于毕" => "於畢",
-"于尽" => "於盡",
-"于盲" => "於盲",
-"于祂" => "於祂",
-"于穆" => "於穆",
-"于终" => "於終",
-"于美" => "於美",
-"于色" => "於色",
-"于行" => "於行",
-"于衷" => "於衷",
-"于该" => "於該",
-"于农" => "於農",
-"于途" => "於途",
-"于丑" => "於醜",
-"于野" => "於野",
-"于陆" => "於陸",
-"于飞" => "於飛",
-"施舍" => "施捨",
-"施于" => "施於",
-"施舍之道" => "施舍之道",
-"施药" => "施藥",
-"旁征博引" => "旁徵博引",
-"旁注" => "旁註",
-"旅游" => "旅遊",
-"旋干转坤" => "旋乾轉坤",
-"旋绕着" => "旋繞著",
-"旋回" => "旋迴",
-"族里" => "族裡",
-"日后" => "日後",
-"日历" => "日曆",
-"日志" => "日誌",
-"早于" => "早於",
-"旱干" => "旱乾",
-"昆仑" => "昆崙",
-"升平" => "昇平",
-"升阳" => "昇陽",
-"明征" => "明徵",
-"明于" => "明於",
-"明窗净几" => "明窗淨几",
-"明范" => "明範",
-"明里" => "明裡",
-"易于" => "易於",
-"星历" => "星曆",
-"星辰表" => "星辰錶",
-"星斗" => "星鬥",
-"春假里" => "春假裡",
-"春天里" => "春天裡",
-"春药" => "春藥",
-"春游" => "春遊",
-"昧于" => "昧於",
-"时钟" => "時鐘",
-"晃荡" => "晃蕩",
-"晋升" => "晉陞",
-"晒干" => "晒乾",
-"晞发" => "晞髮",
-"晨钟" => "晨鐘",
-"晨钟暮鼓" => "晨鐘暮鼓",
-"景致" => "景緻",
-"晾干" => "晾乾",
-"晕船药" => "暈船藥",
-"晕车药" => "暈車藥",
-"暗地里" => "暗地裡",
-"暗沟里" => "暗溝裡",
-"暗里" => "暗裡",
-"暗斗" => "暗鬥",
-"畅游" => "暢遊",
-"暂于" => "暫於",
-"暮鼓晨钟" => "暮鼓晨鐘",
-"历元" => "曆元",
-"历命" => "曆命",
-"历始" => "曆始",
-"历室" => "曆室",
-"历尾" => "曆尾",
-"历数" => "曆數",
-"历日" => "曆日",
-"历书" => "曆書",
-"历本" => "曆本",
-"历法" => "曆法",
-"历纪" => "曆紀",
-"晒干" => "曬乾",
-"晒谷" => "曬穀",
-"更仆难数" => "更僕難數",
-"更签" => "更籤",
-"书后" => "書後",
-"书呆子" => "書獃子",
-"书签" => "書籤",
-"曾于" => "曾於",
-"曾朴" => "曾樸",
-"最后" => "最後",
-"会占" => "會佔",
-"会干" => "會幹",
-"会后" => "會後",
-"会于" => "會於",
-"会里" => "會裡",
-"月历" => "月曆",
-"有仆" => "有僕",
-"有助于" => "有助於",
-"有害于" => "有害於",
-"有损于" => "有損於",
-"有求于人" => "有求於人",
-"有奖征答" => "有獎徵答",
-"有益于" => "有益於",
-"有棱有角" => "有稜有角",
-"有只" => "有隻",
-"有余" => "有餘",
-"有发头陀寺" => "有髮頭陀寺",
-"服务于" => "服務於",
-"服从于" => "服從於",
-"服于" => "服於",
-"服药" => "服藥",
-"朝干夕惕" => "朝乾夕惕",
-"朝后" => "朝後",
-"朝钟" => "朝鐘",
-"朦胧" => "朦朧",
-"木偶戏扎" => "木偶戲紮",
-"木制" => "木製",
-"未干" => "未乾",
-"末药" => "末藥",
-"术赤" => "朮赤",
-"朱庆余" => "朱慶餘",
-"朱理安历" => "朱理安曆",
-"李连杰" => "李連杰",
-"村庄" => "村莊",
-"村落发" => "村落發",
-"村里" => "村裡",
-"束发" => "束髮",
-"杯干" => "杯乾",
-"杰特" => "杰特",
-"东岳" => "東嶽",
-"东冲西突" => "東衝西突",
-"东游" => "東遊",
-"松山庄" => "松山庄",
-"松柏后凋" => "松柏後凋",
-"板着脸" => "板著臉",
-"板荡" => "板蕩",
-"枕经藉史" => "枕經藉史",
-"枕藉" => "枕藉",
-"林宏岳" => "林宏嶽",
-"林郁方" => "林郁方",
-"林钟" => "林鐘",
-"果干" => "果乾",
-"果子干" => "果子乾",
-"果于" => "果於",
-"枝不得大于干" => "枝不得大於榦",
-"枝干" => "枝幹",
-"枯干" => "枯乾",
-"某只" => "某隻",
-"染指于" => "染指於",
-"染发" => "染髮",
-"柜上" => "柜上",
-"柜台" => "柜台",
-"柜子" => "柜子",
-"柱梁" => "柱樑",
-"校准" => "校準",
-"校雠学" => "校讎學",
-"核准的" => "核准的",
-"格于" => "格於",
-"格范" => "格範",
-"格斗" => "格鬥",
-"桂圆干" => "桂圓乾",
-"案发后" => "案發後",
-"桌历" => "桌曆",
-"桑干" => "桑乾",
-"条干" => "條幹",
-"梨干" => "梨乾",
-"械斗" => "械鬥",
-"弃舍" => "棄捨",
-"棉制" => "棉製",
-"棒子面" => "棒子麵",
-"枣庄" => "棗莊",
-"栋梁" => "棟樑",
-"棫朴" => "棫樸",
-"栖于" => "棲於",
-"植发" => "植髮",
-"椰枣干" => "椰棗乾",
-"楚庄王" => "楚莊王",
-"桢干" => "楨幹",
-"业余" => "業餘",
-"榨干" => "榨乾",
-"乐意于" => "樂意於",
-"乐于" => "樂於",
-"樊于期" => "樊於期",
-"梁上" => "樑上",
-"梁子" => "樑子",
-"梁书" => "樑書",
-"梁柱" => "樑柱",
-"标志着" => "標志著",
-"标标致致" => "標標致致",
-"标准" => "標準",
-"标签" => "標籤",
-"标致" => "標緻",
-"标志" => "標誌",
-"模棱" => "模稜",
-"模范" => "模範",
-"模范棒棒堂" => "模范棒棒堂",
-"模制" => "模製",
-"样范" => "樣範",
-"樵采" => "樵採",
-"朴修斯" => "樸修斯",
-"朴厚" => "樸厚",
-"朴学" => "樸學",
-"朴实" => "樸實",
-"朴念仁" => "樸念仁",
-"朴拙" => "樸拙",
-"朴樕" => "樸樕",
-"朴父" => "樸父",
-"朴直" => "樸直",
-"朴素" => "樸素",
-"朴讷" => "樸訥",
-"朴质" => "樸質",
-"朴鄙" => "樸鄙",
-"朴重" => "樸重",
-"朴野" => "樸野",
-"朴野无文" => "樸野無文",
-"朴钝" => "樸鈍",
-"朴陋" => "樸陋",
-"朴马" => "樸馬",
-"朴鲁" => "樸魯",
-"树干" => "樹幹",
-"树干" => "樹榦",
-"树梁" => "樹樑",
-"桥梁" => "橋樑",
-"机绣" => "機繡",
-"横征暴敛" => "橫徵暴斂",
-"横梁" => "橫樑",
-"横冲" => "橫衝",
-"台子" => "檯子",
-"台布" => "檯布",
-"台灯" => "檯燈",
-"台球" => "檯球",
-"台面" => "檯面",
-"柜台" => "櫃檯",
-"栉发工" => "櫛髮工",
-"次于" => "次於",
-"欺蒙" => "欺矇",
-"歇后" => "歇後",
-"歌钟" => "歌鐘",
-"欧游" => "歐遊",
-"止咳药" => "止咳藥",
-"止于" => "止於",
-"止痛药" => "止痛藥",
-"止血药" => "止血藥",
-"正官庄" => "正官庄",
-"正后" => "正後",
-"正于" => "正於",
-"正当着" => "正當著",
-"此后" => "此後",
-"武斗" => "武鬥",
-"归并" => "歸併",
-"归功于" => "歸功於",
-"归咎于" => "歸咎於",
-"归因于" => "歸因於",
-"归于" => "歸於",
-"归罪于" => "歸罪於",
-"归随于" => "歸隨於",
-"归顺于" => "歸順於",
-"归余" => "歸餘",
-"死伤相藉" => "死傷相藉",
-"死后" => "死後",
-"死于" => "死於",
-"死里求生" => "死裡求生",
-"死里逃生" => "死裡逃生",
-"殖谷" => "殖穀",
-"残余" => "殘餘",
-"杀虫药" => "殺虫藥",
-"杀虫药" => "殺蟲藥",
-"壳里" => "殼裡",
-"殿后" => "殿後",
-"殿钟自鸣" => "殿鐘自鳴",
-"毁于" => "毀於",
-"毁钟为铎" => "毀鐘為鐸",
-"母范" => "母範",
-"每于" => "每於",
-"每只" => "每隻",
-"毒药" => "毒藥",
-"比划" => "比劃",
-"毛姜" => "毛薑",
-"毛发" => "毛髮",
-"毫厘" => "毫釐",
-"毫发" => "毫髮",
-"气郁" => "氣鬱",
-"氤郁" => "氤鬱",
-"氯霉素" => "氯黴素",
-"水准" => "水準",
-"水里" => "水裡",
-"水里乡" => "水里鄉",
-"水硷" => "水鹼",
-"永历" => "永曆",
-"求助于" => "求助於",
-"求教于" => "求教於",
-"求知欲" => "求知慾",
-"求签" => "求籤",
-"汗硷" => "汗鹼",
-"污蔑" => "汙衊",
-"池里" => "池裡",
-"污蔑" => "污衊",
-"汲于" => "汲於",
-"汲汲于" => "汲汲於",
-"决斗" => "決鬥",
-"沈淀" => "沈澱",
-"沈着" => "沈著",
-"沈郁" => "沈鬱",
-"沉湎于" => "沉湎於",
-"沉溺于" => "沉溺於",
-"沉淀" => "沉澱",
-"沉迷于" => "沉迷於",
-"沉醉于" => "沉醉於",
-"沉郁" => "沉鬱",
-"没干没净" => "沒乾沒淨",
-"没事干" => "沒事幹",
-"没干" => "沒幹",
-"没梢干" => "沒梢幹",
-"没样范" => "沒樣範",
-"没药" => "沒藥",
-"冲冠发怒" => "沖冠髮怒",
-"冲着" => "沖著",
-"沙里淘金" => "沙裡淘金",
-"河流汇集" => "河流匯集",
-"河里" => "河裡",
-"油漆未干" => "油漆未乾",
-"油面" => "油麵",
-"泛游" => "泛遊",
-"泡面" => "泡麵",
-"波棱菜" => "波稜菜",
-"波发藻" => "波髮藻",
-"泥于" => "泥於",
-"注云" => "注云",
-"泱郁" => "泱鬱",
-"泳气钟" => "泳氣鐘",
-"洄游" => "洄遊",
-"洋面" => "洋麵",
-"洗手不干" => "洗手不幹",
-"洗发" => "洗髮",
-"洗发精" => "洗髮精",
-"洛钟东应" => "洛鐘東應",
-"洪范" => "洪範",
-"洪钟" => "洪鐘",
-"汹涌" => "洶湧",
-"活动于" => "活動於",
-"派团参加" => "派團參加",
-"流于" => "流於",
-"流荡" => "流蕩",
-"流行于" => "流行於",
-"浩荡" => "浩蕩",
-"浪琴表" => "浪琴錶",
-"浪荡" => "浪蕩",
-"浮于" => "浮於",
-"浮荡" => "浮蕩",
-"浮游" => "浮遊",
-"浮松" => "浮鬆",
-"海干" => "海乾",
-"浸于" => "浸於",
-"涂着" => "涂著",
-"涂谨申" => "涂謹申",
-"消炎药" => "消炎藥",
-"消肿药" => "消腫藥",
-"涉足于" => "涉足於",
-"液晶表" => "液晶錶",
-"涳蒙" => "涳濛",
-"涸干" => "涸乾",
-"凉面" => "涼麵",
-"淑范" => "淑範",
-"泪干" => "淚乾",
-"泪如泉涌" => "淚如泉湧",
-"淡于" => "淡於",
-"淡蒙蒙" => "淡濛濛",
-"淡朱" => "淡硃",
-"净余" => "淨餘",
-"净发" => "淨髮",
-"淫欲" => "淫慾",
-"淫荡" => "淫蕩",
-"深于" => "深於",
-"淳朴" => "淳樸",
-"港制" => "港製",
-"游荡" => "游蕩",
-"浑朴" => "渾樸",
-"凑合着" => "湊合著",
-"湖里" => "湖裡",
-"湘绣" => "湘繡",
-"湘累" => "湘纍",
-"湟潦生苹" => "湟潦生苹",
-"涌上" => "湧上",
-"涌来" => "湧來",
-"涌入" => "湧入",
-"涌出" => "湧出",
-"涌向" => "湧向",
-"涌泉" => "湧泉",
-"涌现" => "湧現",
-"涌起" => "湧起",
-"涌进" => "湧進",
-"湮郁" => "湮鬱",
-"汤下面" => "湯下麵",
-"汤团" => "湯糰",
-"汤药" => "湯藥",
-"汤面" => "湯麵",
-"源于" => "源於",
-"准备" => "準備",
-"准儿" => "準兒",
-"准则" => "準則",
-"准噶尔" => "準噶爾",
-"准定" => "準定",
-"准平原" => "準平原",
-"准度" => "準度",
-"准据" => "準據",
-"准新娘" => "準新娘",
-"准新郎" => "準新郎",
-"准星" => "準星",
-"准是" => "準是",
-"准时" => "準時",
-"准会" => "準會",
-"准决赛" => "準決賽",
-"准的" => "準的",
-"准确" => "準確",
-"准线" => "準線",
-"准绳" => "準繩",
-"准话" => "準話",
-"准头" => "準頭",
-"溟蒙" => "溟濛",
-"溢于" => "溢於",
-"溢于言表" => "溢於言表",
-"溲面" => "溲麵",
-"溶于" => "溶於",
-"溺于" => "溺於",
-"滃郁" => "滃鬱",
-"滑藉" => "滑藉",
-"汇丰" => "滙豐",
-"卤味" => "滷味",
-"卤水" => "滷水",
-"卤汁" => "滷汁",
-"卤湖" => "滷湖",
-"卤肉" => "滷肉",
-"卤菜" => "滷菜",
-"卤蛋" => "滷蛋",
-"卤制" => "滷製",
-"卤鸡" => "滷雞",
-"卤面" => "滷麵",
-"满于" => "滿於",
-"满满当当" => "滿滿當當",
-"漂荡" => "漂蕩",
-"沤郁" => "漚鬱",
-"汉弥登钟" => "漢彌登鐘",
-"漫游" => "漫遊",
-"潜水钟" => "潛水鐘",
-"潭里" => "潭裡",
-"潮涌" => "潮湧",
-"溃于" => "潰於",
-"澄澹精致" => "澄澹精致",
-"澒蒙" => "澒濛",
-"泽渗漓而下降" => "澤滲灕而下降",
-"淀粉" => "澱粉",
-"澹台" => "澹臺",
-"激于" => "激於",
-"激荡" => "激蕩",
-"浓于" => "濃於",
-"浓发" => "濃髮",
-"蒙汜" => "濛汜",
-"蒙蒙细雨" => "濛濛細雨",
-"蒙雾" => "濛霧",
-"蒙松雨" => "濛鬆雨",
-"泻药" => "瀉藥",
-"沈吉线" => "瀋吉線",
-"沈山线" => "瀋山線",
-"沈州" => "瀋州",
-"沈水" => "瀋水",
-"沈河" => "瀋河",
-"沈海" => "瀋海",
-"沈海铁路" => "瀋海鐵路",
-"沈阳" => "瀋陽",
-"濒于" => "瀕於",
-"弥山遍野" => "瀰山遍野",
-"弥漫" => "瀰漫",
-"弥漫着" => "瀰漫著",
-"弥弥" => "瀰瀰",
-"灌于" => "灌於",
-"灌药" => "灌藥",
-"漓湘" => "灕湘",
-"漓然" => "灕然",
-"火并" => "火併",
-"火签" => "火籤",
-"火药" => "火藥",
-"灰蒙" => "灰濛",
-"灰蒙蒙" => "灰濛濛",
-"炆面" => "炆麵",
-"炒面" => "炒麵",
-"炮制" => "炮製",
-"炸药" => "炸藥",
-"炸酱面" => "炸醬麵",
-"为准" => "為準",
-"为着" => "為著",
-"乌发" => "烏髮",
-"乌龙面" => "烏龍麵",
-"烘干" => "烘乾",
-"烘制" => "烘製",
-"烤干" => "烤乾",
-"焙干" => "焙乾",
-"无助于" => "無助於",
-"无动于衷" => "無動於衷",
-"无可救药" => "無可救藥",
-"无后" => "無後",
-"无损于" => "無損於",
-"无梁楼盖" => "無樑樓蓋",
-"无济于事" => "無濟於事",
-"无畏于" => "無畏於",
-"无补于事" => "無補於事",
-"无视于" => "無視於",
-"无余" => "無餘",
-"然后" => "然後",
-"然身死才数月耳" => "然身死纔數月耳",
-"炼药" => "煉藥",
-"炼制" => "煉製",
-"煎药" => "煎藥",
-"煎面" => "煎麵",
-"烟卷" => "煙捲",
-"烟斗丝" => "煙斗絲",
-"烟斗" => "煙鬥",
-"烟硷" => "煙鹼",
-"照占" => "照佔",
-"照入签" => "照入籤",
-"照准" => "照準",
-"煨干" => "煨乾",
-"煮面" => "煮麵",
-"熔于" => "熔於",
-"熨斗" => "熨鬥",
-"熬药" => "熬藥",
-"热衷于" => "熱衷於",
-"炖药" => "燉藥",
-"燎发" => "燎髮",
-"烧干" => "燒乾",
-"烧硷" => "燒鹼",
-"燕几" => "燕几",
-"燕游" => "燕遊",
-"烫发" => "燙髮",
-"烫面" => "燙麵",
-"营干" => "營幹",
-"烬余" => "燼餘",
-"争先恐后" => "爭先恐後",
-"争奇斗艳" => "爭奇鬥艷",
-"争奇斗豔" => "爭奇鬥豔",
-"争妍斗胜" => "爭妍鬥勝",
-"争妍斗豔" => "爭妍鬥豔",
-"争斗" => "爭鬥",
-"爰定祥历" => "爰定祥厤",
-"爽荡" => "爽蕩",
-"尔冬升" => "爾冬陞",
-"尔后" => "爾後",
-"片言只语" => "片言隻語",
-"牙签" => "牙籤",
-"牛肉面" => "牛肉麵",
-"牛只" => "牛隻",
-"物欲" => "物慾",
-"特征" => "特徵",
-"特效药" => "特效藥",
-"特于" => "特於",
-"特制" => "特製",
-"牵一发" => "牽一髮",
-"牵系" => "牽繫",
-"荦确" => "犖确",
-"狂并潮" => "狂併潮",
-"狃于" => "狃於",
-"狃于成见" => "狃於成見",
-"狐藉虎威" => "狐藉虎威",
-"狼藉" => "狼藉",
-"猛于" => "猛於",
-"猛冲" => "猛衝",
-"猜三划五" => "猜三划五",
-"奖杯" => "獎盃",
-"独占" => "獨佔",
-"独占鳌头" => "獨佔鰲頭",
-"兽欲" => "獸慾",
-"献丑" => "獻醜",
-"率团参加" => "率團參加",
-"玉历" => "玉曆",
-"王庄" => "王莊",
-"王余鱼" => "王餘魚",
-"班里" => "班裡",
-"理发" => "理髮",
-"琴钟" => "琴鐘",
-"瑶签" => "瑤籤",
-"环游" => "環遊",
-"甘于" => "甘於",
-"甚于" => "甚於",
-"甚么" => "甚麼",
-"甜水面" => "甜水麵",
-"甜面酱" => "甜麵醬",
-"生力面" => "生力麵",
-"生于" => "生於",
-"生姜" => "生薑",
-"生锈" => "生鏽",
-"生发" => "生髮",
-"产后" => "產後",
-"用于" => "用於",
-"用药" => "用藥",
-"甩发" => "甩髮",
-"田谷" => "田穀",
-"田庄" => "田莊",
-"田里" => "田裡",
-"由于" => "由於",
-"男仆" => "男僕",
-"男用表" => "男用錶",
-"畏于" => "畏於",
-"留后" => "留後",
-"留发" => "留髮",
-"毕于" => "畢於",
-"毕业于" => "畢業於",
-"毕生发展" => "畢生發展",
-"划着" => "畫著",
-"异于" => "異於",
-"当一天和尚撞一天钟" => "當一天和尚撞一天鐘",
-"当家才知柴米价" => "當家纔知柴米價",
-"当于" => "當於",
-"当当丁丁" => "當當丁丁",
-"当着" => "當著",
-"疏于" => "疏於",
-"疏松" => "疏鬆",
-"疑凶" => "疑兇",
-"疲于" => "疲於",
-"疲于奔命" => "疲於奔命",
-"疲困" => "疲睏",
-"病后" => "病後",
-"病征" => "病徵",
-"病症" => "病癥",
-"症候" => "癥候",
-"症状" => "癥狀",
-"症结" => "癥結",
-"发干" => "發乾",
-"发于" => "發於",
-"发汗药" => "發汗藥",
-"发呆" => "發獃",
-"发签" => "發籤",
-"发着" => "發著",
-"发松" => "發鬆",
-"发面" => "發麵",
-"白干" => "白乾",
-"白术" => "白朮",
-"白朴" => "白樸",
-"白发其事" => "白發其事",
-"白粉面" => "白粉麵",
-"白发" => "白髮",
-"白霉" => "白黴",
-"百多只" => "百多隻",
-"百天后" => "百天後",
-"百拙千丑" => "百拙千醜",
-"百扎" => "百紮",
-"百花历" => "百花曆",
-"百只" => "百隻",
-"皇历" => "皇曆",
-"皇极历" => "皇極曆",
-"皇庄" => "皇莊",
-"皓发" => "皓髮",
-"皮里阳秋" => "皮裏陽秋",
-"皮里春秋" => "皮裡春秋",
-"皮制" => "皮製",
-"皮松" => "皮鬆",
-"皱别" => "皺彆",
-"皱折" => "皺摺",
-"盈余" => "盈餘",
-"益于" => "益於",
-"盒里" => "盒裡",
-"盛德遗范" => "盛德遺範",
-"盛行于" => "盛行於",
-"盛赞" => "盛讚",
-"盗采" => "盜採",
-"盗钟" => "盜鐘",
-"监制" => "監製",
-"盘里" => "盤裡",
-"盘回" => "盤迴",
-"卢棱伽" => "盧稜伽",
-"盲干" => "盲幹",
-"直接参与" => "直接參与",
-"直于" => "直於",
-"直冲" => "直衝",
-"相并" => "相併",
-"相克" => "相剋",
-"相同于" => "相同於",
-"相干" => "相干",
-"相于" => "相於",
-"相冲" => "相衝",
-"相斗" => "相鬥",
-"看准" => "看準",
-"真凶" => "真兇",
-"眼帘" => "眼帘",
-"眼眶里" => "眼眶裡",
-"眼药" => "眼藥",
-"眼里" => "眼裡",
-"困乏" => "睏乏",
-"困觉" => "睏覺",
-"睡着了" => "睡著了",
-"瞄准" => "瞄準",
-"瞠乎后矣" => "瞠乎後矣",
-"了望" => "瞭望",
-"了然" => "瞭然",
-"了若指掌" => "瞭若指掌",
-"瞻前顾后" => "瞻前顧後",
-"蒙事" => "矇事",
-"蒙昧无知" => "矇昧無知",
-"蒙松雨" => "矇松雨",
-"蒙混" => "矇混",
-"蒙瞍" => "矇瞍",
-"蒙眬" => "矇矓",
-"蒙聩" => "矇聵",
-"蒙着" => "矇著",
-"蒙着锅儿" => "矇著鍋兒",
-"蒙头转" => "矇頭轉",
-"蒙骗" => "矇騙",
-"瞩讬" => "矚託",
-"短于" => "短於",
-"短发" => "短髮",
-"石棱棱" => "石稜稜",
-"石英表" => "石英錶",
-"石钟乳" => "石鐘乳",
-"石硷" => "石鹼",
-"研制" => "研製",
-"砰当" => "砰噹",
-"朱唇皓齿" => "硃唇皓齒",
-"朱批" => "硃批",
-"朱砂" => "硃砂",
-"朱笔" => "硃筆",
-"朱红色" => "硃紅色",
-"朱色" => "硃色",
-"硫化硷" => "硫化鹼",
-"硬干" => "硬幹",
-"确瘠" => "确瘠",
-"碑志" => "碑誌",
-"碰钟" => "碰鐘",
-"磁制" => "磁製",
-"磨制" => "磨製",
-"硗确" => "磽确",
-"碍于" => "礙於",
-"砻谷机" => "礱穀機",
-"示范" => "示範",
-"社里" => "社裡",
-"祝发" => "祝髮",
-"神荼郁垒" => "神荼鬱壘",
-"神游" => "神遊",
-"神雕像" => "神雕像",
-"神雕" => "神鵰",
-"祭吊" => "祭弔",
-"禁欲" => "禁慾",
-"禁药" => "禁藥",
-"祸于" => "禍於",
-"御侮" => "禦侮",
-"御寇" => "禦寇",
-"御寒" => "禦寒",
-"御敌" => "禦敵",
-"礼赞" => "禮讚",
-"禾谷" => "禾穀",
-"秃妃之发" => "禿妃之髮",
-"秃发" => "禿髮",
-"秀发" => "秀髮",
-"私下里" => "私下裡",
-"私欲" => "私慾",
-"私斗" => "私鬥",
-"秋天里" => "秋天裡",
-"秋后" => "秋後",
-"秋裤" => "秋褲",
-"秋游" => "秋遊",
-"秋阴入井干" => "秋陰入井幹",
-"秋发" => "秋髮",
-"种师中" => "种師中",
-"种师道" => "种師道",
-"种放" => "种放",
-"科范" => "科範",
-"秒表" => "秒錶",
-"秒钟" => "秒鐘",
-"稀松" => "稀鬆",
-"稍后" => "稍後",
-"棱台" => "稜台",
-"棱子" => "稜子",
-"棱层" => "稜層",
-"棱柱" => "稜柱",
-"棱棱" => "稜稜",
-"棱棱睁睁" => "稜稜睜睜",
-"棱等登" => "稜等登",
-"棱线" => "稜線",
-"棱缝" => "稜縫",
-"棱角" => "稜角",
-"棱锥" => "稜錐",
-"棱镜" => "稜鏡",
-"棱体" => "稜體",
-"种谷" => "種穀",
-"称赞" => "稱讚",
-"稻谷" => "稻穀",
-"稽征" => "稽徵",
-"谷仓" => "穀倉",
-"谷圭" => "穀圭",
-"谷场" => "穀場",
-"谷子" => "穀子",
-"谷日" => "穀日",
-"谷旦" => "穀旦",
-"谷壳" => "穀殼",
-"谷物" => "穀物",
-"谷皮" => "穀皮",
-"谷神" => "穀神",
-"谷米" => "穀米",
-"谷粒" => "穀粒",
-"谷舱" => "穀艙",
-"谷苗" => "穀苗",
-"谷草" => "穀草",
-"谷贵饿农" => "穀貴餓農",
-"谷贱伤农" => "穀賤傷農",
-"谷道" => "穀道",
-"谷雨" => "穀雨",
-"谷类" => "穀類",
-"谷食" => "穀食",
-"穆罕默德历" => "穆罕默德曆",
-"积极参与" => "積极參与",
-"积极参加" => "積极參加",
-"积谷" => "積穀",
-"积郁" => "積鬱",
-"稳扎" => "穩紮",
-"空荡" => "空蕩",
-"空钟" => "空鐘",
-"空余" => "空餘",
-"窗帘" => "窗帘",
-"窗明几净" => "窗明几淨",
-"窗台" => "窗檯",
-"窝里" => "窩裡",
-"穷于" => "窮於",
-"穷追不舍" => "窮追不捨",
-"窃钟掩耳" => "竊鐘掩耳",
-"立于" => "立於",
-"立范" => "立範",
-"站干岸儿" => "站乾岸兒",
-"竟于" => "竟於",
-"童仆" => "童僕",
-"端庄" => "端莊",
-"竞斗" => "競鬥",
-"竹签" => "竹籤",
-"笆斗" => "笆鬥",
-"笑里藏刀" => "笑裡藏刀",
-"笔划" => "筆劃",
-"等同于" => "等同於",
-"等于" => "等於",
-"筋斗" => "筋鬥",
-"笋干" => "筍乾",
-"筑前" => "筑前",
-"筑北" => "筑北",
-"筑州" => "筑州",
-"筑后" => "筑後",
-"筑波" => "筑波",
-"筑紫" => "筑紫",
-"筑肥" => "筑肥",
-"筑西" => "筑西",
-"筑邦" => "筑邦",
-"筑阳" => "筑陽",
-"答复" => "答覆",
-"策划" => "策劃",
-"筵几" => "筵几",
-"箕斗" => "箕鬥",
-"算发" => "算髮",
-"管干" => "管幹",
-"节欲" => "節慾",
-"节余" => "節餘",
-"范例" => "範例",
-"范围" => "範圍",
-"范式" => "範式",
-"范文" => "範文",
-"范本" => "範本",
-"范畴" => "範疇",
-"简朴" => "簡樸",
-"签着" => "簽著",
-"筹划" => "籌劃",
-"签幐" => "籤幐",
-"签押" => "籤押",
-"签条" => "籤條",
-"签诗" => "籤詩",
-"吁天" => "籲天",
-"吁求" => "籲求",
-"吁请" => "籲請",
-"米谷" => "米穀",
-"粉拳绣腿" => "粉拳繡腿",
-"粉签子" => "粉籤子",
-"粗制" => "粗製",
-"精干" => "精幹",
-"精采" => "精採",
-"精于" => "精於",
-"精明强干" => "精明強幹",
-"精准" => "精準",
-"精致" => "精緻",
-"精制" => "精製",
-"精通于" => "精通於",
-"精辟" => "精闢",
-"精松" => "精鬆",
-"糊里糊涂" => "糊裡糊塗",
-"糕干" => "糕乾",
-"粪秽蔑面" => "糞穢衊面",
-"团子" => "糰子",
-"系着" => "系著",
-"纪元后" => "紀元後",
-"纪历" => "紀曆",
-"红发" => "紅髮",
-"红霉素" => "紅黴素",
-"纡回" => "紆迴",
-"纡余" => "紆餘",
-"纡郁" => "紆鬱",
-"纯朴" => "純樸",
-"纯硷" => "純鹼",
-"纸扎" => "紙紮",
-"素朴" => "素樸",
-"素藉" => "素藉",
-"素食面" => "素食麵",
-"素面" => "素麵",
-"索面" => "索麵",
-"紫姜" => "紫薑",
-"扎上" => "紮上",
-"扎下" => "紮下",
-"扎好" => "紮好",
-"扎实" => "紮實",
-"扎寨" => "紮寨",
-"扎带子" => "紮帶子",
-"扎成" => "紮成",
-"扎根" => "紮根",
-"扎营" => "紮營",
-"扎紧" => "紮緊",
-"扎起" => "紮起",
-"扎铁" => "紮鐵",
-"细不容发" => "細不容髮",
-"细致" => "細緻",
-"终于" => "終於",
-"组里" => "組裡",
-"结伴同游" => "結伴同遊",
-"结伙" => "結夥",
-"结扎" => "結紮",
-"结余" => "結餘",
-"结发" => "結髮",
-"绝对参照" => "絕對參照",
-"绝后" => "絕後",
-"绝于" => "絕於",
-"绞干" => "絞乾",
-"络绎于途" => "絡繹於途",
-"给于" => "給於",
-"丝来线去" => "絲來線去",
-"丝布" => "絲布",
-"丝恩发怨" => "絲恩髮怨",
-"丝板" => "絲板",
-"丝瓜布" => "絲瓜布",
-"丝绒布" => "絲絨布",
-"丝线" => "絲線",
-"丝织厂" => "絲織廠",
-"丝虫" => "絲蟲",
-"丝发" => "絲髮",
-"绑扎" => "綁紮",
-"綑扎" => "綑紮",
-"绿发" => "綠髮",
-"绿霉素" => "綠黴素",
-"绸缎庄" => "綢緞莊",
-"维系" => "維繫",
-"绾发" => "綰髮",
-"网里" => "網裡",
-"网志" => "網誌",
-"紧绷" => "緊繃",
-"紧绷着" => "緊繃著",
-"紧追不舍" => "緊追不捨",
-"绪余" => "緒餘",
-"缉凶" => "緝兇",
-"编制" => "編製",
-"编钟" => "編鐘",
-"编余" => "編餘",
-"编发" => "編髮",
-"缓征" => "緩徵",
-"缓冲" => "緩衝",
-"致密" => "緻密",
-"萦回" => "縈迴",
-"缜致" => "縝緻",
-"县里" => "縣裡",
-"县志" => "縣誌",
-"缝里" => "縫裡",
-"缝制" => "縫製",
-"缩栗" => "縮慄",
-"纵欲" => "縱慾",
-"纤夫" => "縴夫",
-"繁复" => "繁複",
-"绷住" => "繃住",
-"绷子" => "繃子",
-"绷带" => "繃帶",
-"绷紧" => "繃緊",
-"绷脸" => "繃臉",
-"绷着" => "繃著",
-"绷着脸" => "繃著臉",
-"绷着脸儿" => "繃著臉兒",
-"绷开" => "繃開",
-"繐帏飘井干" => "繐幃飄井幹",
-"绕梁" => "繞樑",
-"绣得" => "繡得",
-"绣房" => "繡房",
-"绣毯" => "繡毯",
-"绣球" => "繡球",
-"绣的" => "繡的",
-"绣花" => "繡花",
-"绣衣" => "繡衣",
-"绣起" => "繡起",
-"绣阁" => "繡閣",
-"绣鞋" => "繡鞋",
-"绘制" => "繪製",
-"系上" => "繫上",
-"系到" => "繫到",
-"系囚" => "繫囚",
-"系心" => "繫心",
-"系念" => "繫念",
-"系怀" => "繫懷",
-"系于" => "繫於",
-"系系" => "繫系",
-"系紧" => "繫緊",
-"系绳" => "繫繩",
-"系着" => "繫著",
-"系辞" => "繫辭",
-"累囚" => "纍囚",
-"累堆" => "纍堆",
-"累瓦结绳" => "纍瓦結繩",
-"累绁" => "纍紲",
-"累臣" => "纍臣",
-"缠斗" => "纏鬥",
-"才则" => "纔則",
-"才可容颜十五余" => "纔可容顏十五餘",
-"才得两年" => "纔得兩年",
-"才此" => "纔此",
-"坛子" => "罈子",
-"坛坛罐罐" => "罈罈罐罐",
-"坛騞" => "罈騞",
-"置于" => "置於",
-"骂着" => "罵著",
-"美仑" => "美崙",
-"美于" => "美於",
-"美制" => "美製",
-"美丑" => "美醜",
-"美发" => "美髮",
-"羞于" => "羞於",
-"群丑" => "群醜",
-"羨余" => "羨餘",
-"义仆" => "義僕",
-"义形于色" => "義形於色",
-"义庄" => "義莊",
-"习于" => "習於",
-"翕辟" => "翕闢",
-"翱游" => "翱遊",
-"翻涌" => "翻湧",
-"翻云复雨" => "翻雲覆雨",
-"翻松" => "翻鬆",
-"老干" => "老乾",
-"老仆" => "老僕",
-"老干部" => "老幹部",
-"老蒙" => "老懞",
-"老于" => "老於",
-"老于世故" => "老於世故",
-"老庄" => "老莊",
-"老姜" => "老薑",
-"老板" => "老闆",
-"考后" => "考後",
-"而后" => "而後",
-"而于" => "而於",
-"耐硷" => "耐鹼",
-"耕佣" => "耕傭",
-"耕获" => "耕穫",
-"耳后" => "耳後",
-"耳余" => "耳餘",
-"耽于" => "耽於",
-"耿于" => "耿於",
-"耿耿于怀" => "耿耿於懷",
-"聊斋志异" => "聊齋志異",
-"联系" => "聯係",
-"联于" => "聯於",
-"联系" => "聯繫",
-"声如洪钟" => "聲如洪鐘",
-"听于" => "聽於",
-"肉干" => "肉乾",
-"肉欲" => "肉慾",
-"肉丝面" => "肉絲麵",
-"肉羹面" => "肉羹麵",
-"肉松" => "肉鬆",
-"肝郁" => "肝鬱",
-"股栗" => "股慄",
-"肥筑方言" => "肥筑方言",
-"胃药" => "胃藥",
-"背向着" => "背向著",
-"背地里" => "背地裡",
-"背后" => "背後",
-"胎发" => "胎髮",
-"胜肽" => "胜肽",
-"胜键" => "胜鍵",
-"胡云" => "胡云",
-"胡朴安" => "胡樸安",
-"胡里胡涂" => "胡裡胡塗",
-"能干" => "能幹",
-"脉冲" => "脈衝",
-"脊梁" => "脊樑",
-"脱谷机" => "脫穀機",
-"脱发" => "脫髮",
-"腊味" => "腊味",
-"腊笔" => "腊筆",
-"腐干" => "腐乾",
-"脑子里" => "腦子裡",
-"脑干" => "腦幹",
-"脑后" => "腦後",
-"腰里" => "腰裡",
-"脚注" => "腳註",
-"膏药" => "膏藥",
-"臣仆" => "臣僕",
-"臣服于" => "臣服於",
-"卧游" => "臥遊",
-"臧谷亡羊" => "臧穀亡羊",
-"自于" => "自於",
-"自制" => "自製",
-"自觉自愿" => "自覺自愿",
-"自鸣钟" => "自鳴鐘",
-"至于" => "至於",
-"致力于" => "致力於",
-"致于" => "致於",
-"臻于" => "臻於",
-"臻于完善" => "臻於完善",
-"舂谷" => "舂穀",
-"兴致" => "興緻",
-"举手表" => "舉手表",
-"旧庄" => "舊庄",
-"旧历" => "舊曆",
-"旧药" => "舊藥",
-"舌干脣焦" => "舌乾脣焦",
-"舌后" => "舌後",
-"舒卷" => "舒捲",
-"航海历" => "航海曆",
-"船只" => "船隻",
-"舰只" => "艦隻",
-"良药" => "良藥",
-"色欲" => "色慾",
-"艸木丰丰" => "艸木丰丰",
-"芍药" => "芍藥",
-"芒果干" => "芒果乾",
-"花拳绣腿" => "花拳繡腿",
-"花卷" => "花捲",
-"花盆里" => "花盆裡",
-"花药" => "花藥",
-"花哄" => "花鬨",
-"苑里" => "苑裡",
-"苛性硷" => "苛性鹼",
-"若于" => "若於",
-"苦干" => "苦幹",
-"苦于" => "苦於",
-"苦药" => "苦藥",
-"苦里" => "苦裏",
-"苦斗" => "苦鬥",
-"苹萦" => "苹縈",
-"茵藉" => "茵藉",
-"茶几" => "茶几",
-"茶庄" => "茶莊",
-"茶余" => "茶餘",
-"茶面" => "茶麵",
-"草丛里" => "草叢裡",
-"草药" => "草藥",
-"荷花淀" => "荷花澱",
-"庄主" => "莊主",
-"庄周" => "莊周",
-"庄员" => "莊員",
-"庄严" => "莊嚴",
-"庄园" => "莊園",
-"庄子" => "莊子",
-"庄家" => "莊家",
-"庄户" => "莊戶",
-"庄敬" => "莊敬",
-"庄田" => "莊田",
-"庄稼" => "莊稼",
-"庄里" => "莊裡",
-"庄重" => "莊重",
-"庄院" => "莊院",
-"茎干" => "莖幹",
-"莽荡" => "莽蕩",
-"菌丝体" => "菌絲體",
-"菜干" => "菜乾",
-"菠棱菜" => "菠稜菜",
-"菠萝干" => "菠蘿乾",
-"华发" => "華髮",
-"菸硷" => "菸鹼",
-"万多只" => "萬多隻",
-"万天后" => "萬天後",
-"万历" => "萬曆",
-"万签插架" => "萬籤插架",
-"万扎" => "萬紮",
-"万象" => "萬象",
-"万只" => "萬隻",
-"万余" => "萬餘",
-"落后" => "落後",
-"落于" => "落於",
-"落发" => "落髮",
-"着儿" => "著兒",
-"着书立说" => "著書立說",
-"着色软体" => "著色軟體",
-"着重指出" => "著重指出",
-"着录" => "著錄",
-"着录规则" => "著錄規則",
-"葡萄干" => "葡萄乾",
-"董氏封发" => "董氏封髮",
-"蒙汗药" => "蒙汗藥",
-"蒙雾露" => "蒙霧露",
-"蒜发" => "蒜髮",
-"苍术" => "蒼朮",
-"苍郁" => "蒼鬱",
-"蓄发" => "蓄髮",
-"蓄须" => "蓄鬚",
-"蓊郁" => "蓊鬱",
-"盖于" => "蓋於",
-"蓬发" => "蓬髮",
-"蓬松" => "蓬鬆",
-"参绥" => "蔘綏",
-"荞麦面" => "蕎麥麵",
-"荡来荡去" => "蕩來蕩去",
-"荡女" => "蕩女",
-"荡妇" => "蕩婦",
-"荡寇" => "蕩寇",
-"荡平" => "蕩平",
-"荡涤" => "蕩滌",
-"荡漾" => "蕩漾",
-"荡然" => "蕩然",
-"荡舟" => "蕩舟",
-"荡船" => "蕩船",
-"荡荡" => "蕩蕩",
-"萧参" => "蕭蔘",
-"薄幸" => "薄倖",
-"薄干" => "薄幹",
-"姜是老的辣" => "薑是老的辣",
-"姜末" => "薑末",
-"姜桂" => "薑桂",
-"姜母" => "薑母",
-"姜汁" => "薑汁",
-"姜汤" => "薑湯",
-"姜片" => "薑片",
-"姜糖" => "薑糖",
-"姜丝" => "薑絲",
-"姜老辣" => "薑老辣",
-"姜蓉" => "薑蓉",
-"姜饼" => "薑餅",
-"姜黄" => "薑黃",
-"薙发" => "薙髮",
-"薝卜" => "薝蔔",
-"藉以" => "藉以",
-"藉卉" => "藉卉",
-"藉寇兵" => "藉寇兵",
-"藉手" => "藉手",
-"藉槁" => "藉槁",
-"藉机" => "藉機",
-"藉此" => "藉此",
-"藉甚" => "藉甚",
-"藉由" => "藉由",
-"藉箸代筹" => "藉箸代籌",
-"藉草枕块" => "藉草枕塊",
-"藉着" => "藉著",
-"藉藉" => "藉藉",
-"藉资" => "藉資",
-"藏匿于" => "藏匿於",
-"藏于" => "藏於",
-"藏历" => "藏曆",
-"藏蒙歌儿" => "藏矇歌兒",
-"藤制" => "藤製",
-"药丸" => "藥丸",
-"药典" => "藥典",
-"药到病除" => "藥到病除",
-"药剂" => "藥劑",
-"药力" => "藥力",
-"药包" => "藥包",
-"药名" => "藥名",
-"药味" => "藥味",
-"药品" => "藥品",
-"药商" => "藥商",
-"药单" => "藥單",
-"药婆" => "藥婆",
-"药学" => "藥學",
-"药害" => "藥害",
-"药专" => "藥專",
-"药局" => "藥局",
-"药师" => "藥師",
-"药店" => "藥店",
-"药厂" => "藥廠",
-"药引" => "藥引",
-"药性" => "藥性",
-"药房" => "藥房",
-"药效" => "藥效",
-"药方" => "藥方",
-"药材" => "藥材",
-"药棉" => "藥棉",
-"药水" => "藥水",
-"药油" => "藥油",
-"药液" => "藥液",
-"药渣" => "藥渣",
-"药片" => "藥片",
-"药物" => "藥物",
-"药王" => "藥王",
-"药理" => "藥理",
-"药瓶" => "藥瓶",
-"药用" => "藥用",
-"药皂" => "藥皂",
-"药盒" => "藥盒",
-"药石" => "藥石",
-"药科" => "藥科",
-"药箱" => "藥箱",
-"药签" => "藥籤",
-"药粉" => "藥粉",
-"药糖" => "藥糖",
-"药线" => "藥線",
-"药罐" => "藥罐",
-"药膏" => "藥膏",
-"药舖" => "藥舖",
-"药茶" => "藥茶",
-"药草" => "藥草",
-"药行" => "藥行",
-"药贩" => "藥販",
-"药费" => "藥費",
-"药酒" => "藥酒",
-"药量" => "藥量",
-"药针" => "藥針",
-"药铺" => "藥鋪",
-"药头" => "藥頭",
-"药饵" => "藥餌",
-"药面儿" => "藥麵兒",
-"苏昆" => "蘇崑",
-"蕴含着" => "蘊含著",
-"蕴涵着" => "蘊涵著",
-"蕴藉" => "蘊藉",
-"苹果干" => "蘋果乾",
-"萝卜" => "蘿蔔",
-"萝卜干" => "蘿蔔乾",
-"虎须" => "虎鬚",
-"处于" => "處於",
-"号志" => "號誌",
-"虫部" => "虫部",
-"蛇发女妖" => "蛇髮女妖",
-"蛔虫药" => "蛔蟲藥",
-"蜂涌" => "蜂湧",
-"蛏干" => "蟶乾",
-"蛮干" => "蠻幹",
-"血余" => "血餘",
-"行事历" => "行事曆",
-"行凶" => "行兇",
-"行凶后" => "行兇後",
-"行于" => "行於",
-"胡同" => "衚衕",
-"冲上" => "衝上",
-"冲下" => "衝下",
-"冲来" => "衝來",
-"冲倒" => "衝倒",
-"冲出" => "衝出",
-"冲到" => "衝到",
-"冲刺" => "衝刺",
-"冲克" => "衝剋",
-"冲力" => "衝力",
-"冲劲" => "衝勁",
-"冲动" => "衝動",
-"冲去" => "衝去",
-"冲口" => "衝口",
-"冲垮" => "衝垮",
-"冲堂" => "衝堂",
-"冲坚陷阵" => "衝堅陷陣",
-"冲压" => "衝壓",
-"冲天" => "衝天",
-"冲州撞府" => "衝州撞府",
-"冲心" => "衝心",
-"冲掉" => "衝掉",
-"冲撞" => "衝撞",
-"冲击" => "衝擊",
-"冲散" => "衝散",
-"冲杀" => "衝殺",
-"冲决" => "衝決",
-"冲波" => "衝波",
-"冲浪" => "衝浪",
-"冲激" => "衝激",
-"冲然" => "衝然",
-"冲盹" => "衝盹",
-"冲破" => "衝破",
-"冲程" => "衝程",
-"冲突" => "衝突",
-"冲线" => "衝線",
-"冲着" => "衝著",
-"冲要" => "衝要",
-"冲起" => "衝起",
-"冲车" => "衝車",
-"冲进" => "衝進",
-"冲过" => "衝過",
-"冲量" => "衝量",
-"冲锋" => "衝鋒",
-"冲陷" => "衝陷",
-"冲头阵" => "衝頭陣",
-"冲风" => "衝風",
-"表征" => "表徵",
-"表里" => "表裡",
-"衷于" => "衷於",
-"袖里" => "袖裡",
-"被里" => "被裡",
-"被复" => "被複",
-"被复着" => "被覆著",
-"裁并" => "裁併",
-"裁制" => "裁製",
-"里勾外连" => "裏勾外連",
-"里手" => "裏手",
-"里海" => "裏海",
-"里面" => "裏面",
-"补药" => "補藥",
-"补血药" => "補血藥",
-"补注" => "補註",
-"里外" => "裡外",
-"里子" => "裡子",
-"里屋" => "裡屋",
-"里层" => "裡層",
-"里布" => "裡布",
-"里带" => "裡帶",
-"里弦" => "裡弦",
-"里应外合" => "裡應外合",
-"里脊" => "裡脊",
-"里衣" => "裡衣",
-"里通外国" => "裡通外國",
-"里通外敌" => "裡通外敵",
-"里边" => "裡邊",
-"里间" => "裡間",
-"里面" => "裡面",
-"里头" => "裡頭",
-"制件" => "製件",
-"制作" => "製作",
-"制做" => "製做",
-"制备" => "製備",
-"制冰" => "製冰",
-"制冷" => "製冷",
-"制剂" => "製劑",
-"制品" => "製品",
-"制图" => "製圖",
-"制成" => "製成",
-"制法" => "製法",
-"制为" => "製為",
-"制片" => "製片",
-"制版" => "製版",
-"制程" => "製程",
-"制糖" => "製糖",
-"制纸" => "製紙",
-"制药" => "製藥",
-"制表" => "製表",
-"制裁" => "製裁",
-"制造" => "製造",
-"制革" => "製革",
-"制鞋" => "製鞋",
-"制盐" => "製鹽",
-"复仞年如" => "複仞年如",
-"复以百万" => "複以百萬",
-"复位" => "複位",
-"复信" => "複信",
-"复元音" => "複元音",
-"复分数" => "複分數",
-"复分析" => "複分析",
-"复列" => "複列",
-"复利" => "複利",
-"复印" => "複印",
-"复原" => "複原",
-"复句" => "複句",
-"复合" => "複合",
-"复名" => "複名",
-"复员" => "複員",
-"复壁" => "複壁",
-"复壮" => "複壯",
-"复姓" => "複姓",
-"复字键" => "複字鍵",
-"复审" => "複審",
-"复写" => "複寫",
-"复平面" => "複平面",
-"复式" => "複式",
-"复复" => "複復",
-"复数" => "複數",
-"复本" => "複本",
-"复查" => "複查",
-"复核" => "複核",
-"复检" => "複檢",
-"复次" => "複次",
-"复比" => "複比",
-"复决" => "複決",
-"复流" => "複流",
-"复测" => "複測",
-"复亩珍" => "複畝珍",
-"复发" => "複發",
-"复目" => "複目",
-"复眼" => "複眼",
-"复种" => "複種",
-"复线" => "複線",
-"复习" => "複習",
-"复色" => "複色",
-"复叶" => "複葉",
-"复盖" => "複蓋",
-"复制" => "複製",
-"复诊" => "複診",
-"复词" => "複詞",
-"复试" => "複試",
-"复课" => "複課",
-"复议" => "複議",
-"复变函数" => "複變函數",
-"复赛" => "複賽",
-"复辅音" => "複輔音",
-"复述" => "複述",
-"复选" => "複選",
-"复钱" => "複錢",
-"复杂" => "複雜",
-"复电" => "複電",
-"复音" => "複音",
-"复韵" => "複韻",
-"衬里" => "襯裡",
-"西岳" => "西嶽",
-"西历" => "西曆",
-"西历史" => "西歷史",
-"西药" => "西藥",
-"西游" => "西遊",
-"要冲" => "要衝",
-"要么" => "要麼",
-"复亡" => "覆亡",
-"复命" => "覆命",
-"复巢之下无完卵" => "覆巢之下無完卵",
-"复水难收" => "覆水難收",
-"复没" => "覆沒",
-"复着" => "覆著",
-"复盖" => "覆蓋",
-"复盖着" => "覆蓋著",
-"复辙" => "覆轍",
-"复雨翻云" => "覆雨翻雲",
-"见于" => "見於",
-"见棱见角" => "見稜見角",
-"见素抱朴" => "見素抱樸",
-"见钟不打" => "見鐘不打",
-"规划" => "規劃",
-"规范" => "規範",
-"视于" => "視於",
-"观采" => "觀採",
-"角落发" => "角落發",
-"角落里" => "角落裡",
-"觚棱" => "觚稜",
-"解雇" => "解僱",
-"解痛药" => "解痛藥",
-"解药" => "解藥",
-"解发佯狂" => "解髮佯狂",
-"触须" => "觸鬚",
-"言大而夸" => "言大而夸",
-"言辩而确" => "言辯而确",
-"订于" => "訂於",
-"订制" => "訂製",
-"计划" => "計劃",
-"讬了" => "託了",
-"讬事" => "託事",
-"讬交" => "託交",
-"讬人" => "託人",
-"讬付" => "託付",
-"讬古讽今" => "託古諷今",
-"讬名" => "託名",
-"讬咎" => "託咎",
-"讬梦" => "託夢",
-"讬大" => "託大",
-"讬孤" => "託孤",
-"讬故" => "託故",
-"讬疾" => "託疾",
-"讬病" => "託病",
-"讬福" => "託福",
-"讬管" => "託管",
-"讬言" => "託言",
-"讬词" => "託詞",
-"讬买" => "託買",
-"讬卖" => "託賣",
-"讬身" => "託身",
-"讬辞" => "託辭",
-"讬运" => "託運",
-"讬过" => "託過",
-"设于" => "設於",
-"许愿起经" => "許愿起經",
-"诉说着" => "訴說著",
-"注上" => "註上",
-"注册" => "註冊",
-"注失" => "註失",
-"注定" => "註定",
-"注明" => "註明",
-"注标" => "註標",
-"注生娘娘" => "註生娘娘",
-"注疏" => "註疏",
-"注脚" => "註腳",
-"注解" => "註解",
-"注译" => "註譯",
-"注释" => "註釋",
-"注销" => "註銷",
-"评注" => "評註",
-"词干" => "詞幹",
-"词汇" => "詞彙",
-"词余" => "詞餘",
-"询于" => "詢於",
-"诗云" => "詩云",
-"诗钟" => "詩鐘",
-"诗余" => "詩餘",
-"话里有话" => "話裡有話",
-"该于" => "該於",
-"详注" => "詳註",
-"夸赞" => "誇讚",
-"志哀" => "誌哀",
-"志喜" => "誌喜",
-"志庆" => "誌慶",
-"志异" => "誌異",
-"诱奸" => "誘姦",
-"语云" => "語云",
-"语汇" => "語彙",
-"诚征" => "誠徵",
-"诚朴" => "誠樸",
-"诬蔑" => "誣衊",
-"说着" => "說著",
-"课后" => "課後",
-"课征" => "課徵",
-"课余" => "課餘",
-"调准" => "調準",
-"调制" => "調製",
-"请参阅" => "請參閱",
-"请讬" => "請託",
-"诸余" => "諸餘",
-"谋干" => "謀幹",
-"谢绝参观" => "謝絕參觀",
-"谬采虚声" => "謬採虛聲",
-"謷丑" => "謷醜",
-"证于" => "證於",
-"警世钟" => "警世鐘",
-"警钟" => "警鐘",
-"译注" => "譯註",
-"护发" => "護髮",
-"读后" => "讀後",
-"变丑" => "變醜",
-"雠隙" => "讎隙",
-"赞不绝口" => "讚不絕口",
-"赞佩" => "讚佩",
-"赞同" => "讚同",
-"赞叹" => "讚嘆",
-"赞扬" => "讚揚",
-"赞乐" => "讚樂",
-"赞歌" => "讚歌",
-"赞歎" => "讚歎",
-"赞美" => "讚美",
-"赞羨" => "讚羨",
-"赞许" => "讚許",
-"赞词" => "讚詞",
-"赞誉" => "讚譽",
-"赞赏" => "讚賞",
-"赞辞" => "讚辭",
-"赞颂" => "讚頌",
-"豆干" => "豆乾",
-"豆腐干" => "豆腐乾",
-"竖着" => "豎著",
-"丰滨" => "豐濱",
-"丰滨乡" => "豐濱鄉",
-"象征" => "象徵",
-"象征着" => "象徵著",
-"负债累累" => "負債纍纍",
-"贪欲" => "貪慾",
-"贵干" => "貴幹",
-"买凶" => "買兇",
-"贻范" => "貽範",
-"贾后" => "賈後",
-"赈饥" => "賑饑",
-"质朴" => "質樸",
-"赌台" => "賭檯",
-"赖于" => "賴於",
-"賸余" => "賸餘",
-"购并" => "購併",
-"购买欲" => "購買慾",
-"赢余" => "贏餘",
-"走后" => "走後",
-"起因于" => "起因於",
-"起复" => "起複",
-"起哄" => "起鬨",
-"赶制" => "趕製",
-"赶面棍" => "趕麵棍",
-"赵庄" => "趙莊",
-"趋于" => "趨於",
-"趱干" => "趲幹",
-"足于" => "足於",
-"跌荡" => "跌蕩",
-"跟前跟后" => "跟前跟後",
-"路签" => "路籤",
-"跳荡" => "跳蕩",
-"跳表" => "跳錶",
-"蹭棱子" => "蹭稜子",
-"躁郁" => "躁鬱",
-"躏藉" => "躪藉",
-"身后" => "身後",
-"身于" => "身於",
-"躯干" => "軀幹",
-"车库里" => "車庫裡",
-"车站里" => "車站裡",
-"车里" => "車裡",
-"轨范" => "軌範",
-"轩辟" => "軒闢",
-"载于" => "載於",
-"挽曲" => "輓曲",
-"挽歌" => "輓歌",
-"挽联" => "輓聯",
-"挽词" => "輓詞",
-"挽诗" => "輓詩",
-"轻于" => "輕於",
-"轻松" => "輕鬆",
-"轮奸" => "輪姦",
-"轮回" => "輪迴",
-"转台" => "轉檯",
-"转讬" => "轉託",
-"辟谷" => "辟穀",
-"办公台" => "辦公檯",
-"辞汇" => "辭彙",
-"辫发" => "辮髮",
-"农历" => "農曆",
-"农民历" => "農民曆",
-"农庄" => "農莊",
-"农药" => "農藥",
-"迂回" => "迂迴",
-"近似于" => "近似於",
-"近于" => "近於",
-"近日里" => "近日裡",
-"返朴" => "返樸",
-"迥然回异" => "迥然迴異",
-"迫于" => "迫於",
-"回光返照" => "迴光返照",
-"回向" => "迴向",
-"回圈" => "迴圈",
-"回廊" => "迴廊",
-"回形夹" => "迴形夾",
-"回文" => "迴文",
-"回旋" => "迴旋",
-"回流" => "迴流",
-"回环" => "迴環",
-"回荡" => "迴盪",
-"回纹针" => "迴紋針",
-"回绕" => "迴繞",
-"回肠" => "迴腸",
-"回荡" => "迴蕩",
-"回诵" => "迴誦",
-"回路" => "迴路",
-"回转" => "迴轉",
-"回递性" => "迴遞性",
-"回避" => "迴避",
-"回响" => "迴響",
-"回风" => "迴風",
-"迷幻药" => "迷幻藥",
-"迷于" => "迷於",
-"迷蒙" => "迷濛",
-"迷药" => "迷藥",
-"迷魂药" => "迷魂藥",
-"追凶" => "追兇",
-"退后" => "退後",
-"退烧药" => "退燒藥",
-"逋发" => "逋髮",
-"透辟" => "透闢",
-"这里" => "這裏",
-"这里" => "這裡",
-"这只" => "這隻",
-"这么" => "這麼",
-"这么着" => "這麼著",
-"通奸" => "通姦",
-"通心面" => "通心麵",
-"通于" => "通於",
-"通历" => "通曆",
-"速食面" => "速食麵",
-"连三并四" => "連三併四",
-"连占" => "連佔",
-"连采" => "連採",
-"连于" => "連於",
-"连系" => "連繫",
-"连庄" => "連莊",
-"周游" => "週遊",
-"进占" => "進佔",
-"逼并" => "逼併",
-"游了" => "遊了",
-"游人" => "遊人",
-"游伴" => "遊伴",
-"游侠" => "遊俠",
-"游动" => "遊動",
-"游园" => "遊園",
-"游子" => "遊子",
-"游学" => "遊學",
-"游客" => "遊客",
-"游宦" => "遊宦",
-"游山玩水" => "遊山玩水",
-"游必有方" => "遊必有方",
-"游憩" => "遊憩",
-"游戏" => "遊戲",
-"游手好闲" => "遊手好閑",
-"游手好闲" => "遊手好閒",
-"游星" => "遊星",
-"游乐" => "遊樂",
-"游标卡尺" => "遊標卡尺",
-"游历" => "遊歷",
-"游民" => "遊民",
-"游牧" => "遊牧",
-"游猎" => "遊獵",
-"游玩" => "遊玩",
-"游荡" => "遊盪",
-"游丝" => "遊絲",
-"游兴" => "遊興",
-"游船" => "遊船",
-"游艇" => "遊艇",
-"游荡" => "遊蕩",
-"游艺" => "遊藝",
-"游行" => "遊行",
-"游街" => "遊街",
-"游览" => "遊覽",
-"游记" => "遊記",
-"游说" => "遊說",
-"游资" => "遊資",
-"游走" => "遊走",
-"游踪" => "遊蹤",
-"游逛" => "遊逛",
-"游错" => "遊錯",
-"游离" => "遊離",
-"游骑兵" => "遊騎兵",
-"游魂" => "遊魂",
-"遍于" => "遍於",
-"过后" => "過後",
-"过于" => "過於",
-"过水面" => "過水麵",
-"道范" => "道範",
-"逊于" => "遜於",
-"递回" => "遞迴",
-"远于" => "遠於",
-"远县才至" => "遠縣纔至",
-"远游" => "遠遊",
-"遨游" => "遨遊",
-"适于" => "適於",
-"遮丑" => "遮醜",
-"迁怒于" => "遷怒於",
-"迁于" => "遷於",
-"遗范" => "遺範",
-"遗余" => "遺餘",
-"辽沈" => "遼瀋",
-"避孕药" => "避孕藥",
-"还占" => "還佔",
-"还采" => "還採",
-"还政于民" => "還政於民",
-"还于" => "還於",
-"还冲" => "還衝",
-"邋里邋遢" => "邋裡邋遢",
-"那里" => "那裡",
-"那只" => "那隻",
-"那么" => "那麼",
-"那么着" => "那麼著",
-"郁朴" => "郁樸",
-"郊游" => "郊遊",
-"郘钟" => "郘鐘",
-"部落发" => "部落發",
-"都于" => "都於",
-"乡愿" => "鄉愿",
-"郑凯云" => "鄭凱云",
-"配合着" => "配合著",
-"配水干管" => "配水幹管",
-"配药" => "配藥",
-"配制" => "配製",
-"酒后" => "酒後",
-"酒杯" => "酒盃",
-"酒坛" => "酒罈",
-"酒药" => "酒藥",
-"酒麴" => "酒麴",
-"酥松" => "酥鬆",
-"酸硷" => "酸鹼",
-"醇朴" => "醇樸",
-"醉心于" => "醉心於",
-"醉于" => "醉於",
-"醋坛" => "醋罈",
-"丑丫头" => "醜丫頭",
-"丑事" => "醜事",
-"丑人" => "醜人",
-"丑八怪" => "醜八怪",
-"丑剌剌" => "醜剌剌",
-"丑剧" => "醜劇",
-"丑化" => "醜化",
-"丑名" => "醜名",
-"丑咤" => "醜吒",
-"丑地" => "醜地",
-"丑夷" => "醜夷",
-"丑女效颦" => "醜女效顰",
-"丑妇" => "醜婦",
-"丑媳" => "醜媳",
-"丑小鸭" => "醜小鴨",
-"丑巴怪" => "醜巴怪",
-"丑恶" => "醜惡",
-"丑态" => "醜態",
-"丑于" => "醜於",
-"丑末" => "醜末",
-"丑样" => "醜樣",
-"丑死" => "醜死",
-"丑生" => "醜生",
-"丑闻" => "醜聞",
-"丑声四溢" => "醜聲四溢",
-"丑声远播" => "醜聲遠播",
-"丑脸" => "醜臉",
-"丑行" => "醜行",
-"丑诋" => "醜詆",
-"丑话" => "醜話",
-"丑语" => "醜語",
-"丑丑" => "醜醜",
-"丑陋" => "醜陋",
-"丑头怪脸" => "醜頭怪臉",
-"丑类" => "醜類",
-"酝藉" => "醞藉",
-"酝酿着" => "醞釀著",
-"医药" => "醫藥",
-"酿制" => "釀製",
-"衅钟" => "釁鐘",
-"采石之役" => "采石之役",
-"采石之战" => "采石之戰",
-"采石矶" => "采石磯",
-"釉药" => "釉藥",
-"里程表" => "里程錶",
-"重划" => "重劃",
-"重折" => "重摺",
-"重于" => "重於",
-"重罗面" => "重羅麵",
-"重复" => "重複",
-"重讬" => "重託",
-"重游" => "重遊",
-"重锤" => "重鎚",
-"野姜" => "野薑",
-"野游" => "野遊",
-"厘出" => "釐出",
-"厘定" => "釐定",
-"厘正" => "釐正",
-"厘清" => "釐清",
-"厘订" => "釐訂",
-"金仆姑" => "金僕姑",
-"金仑溪" => "金崙溪",
-"金表" => "金錶",
-"金钟" => "金鐘",
-"金鸡纳硷" => "金雞納鹼",
-"金发" => "金髮",
-"金斗" => "金鬥",
-"金霉素" => "金黴素",
-"钉锤" => "釘鎚",
-"铃响后" => "鈴響後",
-"钩心斗角" => "鉤心鬥角",
-"银朱" => "銀硃",
-"银发" => "銀髮",
-"铜制" => "銅製",
-"铜钟" => "銅鐘",
-"铝制" => "鋁製",
-"钢梁" => "鋼樑",
-"钢制" => "鋼製",
-"录着" => "錄著",
-"录制" => "錄製",
-"钱谷" => "錢穀",
-"钱庄" => "錢莊",
-"锦心绣口" => "錦心繡口",
-"锦绣花园" => "錦綉花園",
-"锦绣" => "錦繡",
-"表带" => "錶帶",
-"表店" => "錶店",
-"表厂" => "錶廠",
-"表板" => "錶板",
-"表壳" => "錶殼",
-"表盘" => "錶盤",
-"表蒙子" => "錶蒙子",
-"表针" => "錶針",
-"表链" => "錶鏈",
-"锻鍊出" => "鍛鍊出",
-"锲而不舍" => "鍥而不捨",
-"钟表" => "鍾錶",
-"锤儿" => "鎚兒",
-"锤子" => "鎚子",
-"锤头" => "鎚頭",
-"链霉素" => "鏈黴素",
-"锈病" => "鏽病",
-"锈菌" => "鏽菌",
-"锈蚀" => "鏽蝕",
-"钟不扣不鸣" => "鐘不扣不鳴",
-"钟不撞不鸣" => "鐘不撞不鳴",
-"钟乳洞" => "鐘乳洞",
-"钟乳石" => "鐘乳石",
-"钟在寺里" => "鐘在寺里",
-"钟塔" => "鐘塔",
-"钟山" => "鐘山",
-"钟形虫" => "鐘形蟲",
-"钟摆" => "鐘擺",
-"钟楼" => "鐘樓",
-"钟漏" => "鐘漏",
-"钟琴" => "鐘琴",
-"钟相" => "鐘相",
-"钟磬" => "鐘磬",
-"钟声" => "鐘聲",
-"钟表" => "鐘表",
-"钟表" => "鐘錶",
-"钟关" => "鐘關",
-"钟响" => "鐘響",
-"钟头" => "鐘頭",
-"钟鸣" => "鐘鳴",
-"钟鸣漏尽" => "鐘鳴漏盡",
-"钟点" => "鐘點",
-"钟鼎" => "鐘鼎",
-"钟鼓" => "鐘鼓",
-"铁锤" => "鐵鎚",
-"铁锈" => "鐵鏽",
-"铸钟" => "鑄鐘",
-"鑑于" => "鑑於",
-"鉴于" => "鑒於",
-"长于" => "長於",
-"长历" => "長曆",
-"长生药" => "長生藥",
-"长须鲸" => "長鬚鯨",
-"门前门后" => "門前門後",
-"门帘" => "門帘",
-"门里" => "門裡",
-"门斗" => "門鬥",
-"开列于后" => "開列於後",
-"开吊" => "開弔",
-"开征" => "開徵",
-"开采" => "開採",
-"开药" => "開藥",
-"开辟" => "開闢",
-"开哄" => "開鬨",
-"闲情逸致" => "閒情逸緻",
-"闲荡" => "閒蕩",
-"闲游" => "閒遊",
-"间不容发" => "間不容髮",
-"闵采尔" => "閔採爾",
-"合府" => "閤府",
-"闺范" => "閨範",
-"阃范" => "閫範",
-"关系" => "關係",
-"关系着" => "關係著",
-"关弓与我确" => "關弓與我确",
-"关于" => "關於",
-"辟佛" => "闢佛",
-"辟作" => "闢作",
-"辟划" => "闢劃",
-"辟土" => "闢土",
-"辟地" => "闢地",
-"辟室" => "闢室",
-"辟建" => "闢建",
-"辟为" => "闢為",
-"辟田" => "闢田",
-"辟筑" => "闢築",
-"辟谣" => "闢謠",
-"辟辟" => "闢辟",
-"辟邪以律" => "闢邪以律",
-"防御" => "防禦",
-"防范" => "防範",
-"防锈" => "防鏽",
-"防台" => "防颱",
-"阻于" => "阻於",
-"附于" => "附於",
-"附注" => "附註",
-"降压药" => "降壓藥",
-"降于" => "降於",
-"限于" => "限於",
-"升官" => "陞官",
-"除臭药" => "除臭藥",
-"阴干" => "陰乾",
-"阴历" => "陰曆",
-"阴郁" => "陰鬱",
-"陷于" => "陷於",
-"陆游" => "陸遊",
-"阳春面" => "陽春麵",
-"阳历" => "陽曆",
-"随后" => "隨後",
-"随于" => "隨於",
-"隐几" => "隱几",
-"隐于" => "隱於",
-"只字" => "隻字",
-"只影" => "隻影",
-"只手遮天" => "隻手遮天",
-"只眼" => "隻眼",
-"只言片语" => "隻言片語",
-"只身" => "隻身",
-"雅范" => "雅範",
-"雅致" => "雅緻",
-"集于" => "集於",
-"集于一身" => "集於一身",
-"集游法" => "集遊法",
-"雇佣" => "雇傭",
-"雇于" => "雇於",
-"雕梁划栋" => "雕樑畫棟",
-"双折" => "雙摺",
-"双胜类" => "雙胜類",
-"杂合面儿" => "雜合麵兒",
-"杂志" => "雜誌",
-"杂面" => "雜麵",
-"鸡奸" => "雞姦",
-"鸡丝" => "雞絲",
-"鸡丝面" => "雞絲麵",
-"鸡腿面" => "雞腿麵",
-"鸡只" => "雞隻",
-"离于" => "離於",
-"难容于" => "難容於",
-"难舍" => "難捨",
-"难于" => "難於",
-"雨后" => "雨後",
-"雪窗萤几" => "雪窗螢几",
-"雪里" => "雪裡",
-"云南白药" => "雲南白藥",
-"云笈七签" => "雲笈七籤",
-"云游" => "雲遊",
-"云须" => "雲鬚",
-"零多只" => "零多隻",
-"零天后" => "零天後",
-"零只" => "零隻",
-"电子表" => "電子錶",
-"电子钟" => "電子鐘",
-"电冲" => "電衝",
-"电表" => "電錶",
-"电钟" => "電鐘",
-"震栗" => "震慄",
-"震于" => "震於",
-"震荡" => "震蕩",
-"雾里" => "霧裡",
-"露丑" => "露醜",
-"霸占" => "霸佔",
-"霁范" => "霽範",
-"灵药" => "靈藥",
-"青山一发" => "青山一髮",
-"青苹" => "青苹",
-"青霉" => "青黴",
-"非占不可" => "非佔不可",
-"非于" => "非於",
-"靠后" => "靠後",
-"面朝着" => "面朝著",
-"面临着" => "面臨著",
-"鞋里" => "鞋裡",
-"鞣制" => "鞣製",
-"秋千" => "鞦韆",
-"鞭辟入里" => "鞭辟入裡",
-"韩国制" => "韓國製",
-"韩制" => "韓製",
-"音准" => "音準",
-"音声如钟" => "音聲如鐘",
-"韶山冲" => "韶山衝",
-"顺于" => "順於",
-"颂赞" => "頌讚",
-"预制" => "預製",
-"领袖欲" => "領袖慾",
-"头里" => "頭裡",
-"头发" => "頭髮",
-"颊须" => "頰鬚",
-"额我略历" => "額我略曆",
-"颜范" => "顏範",
-"颠干倒坤" => "顛乾倒坤",
-"颠复" => "顛覆",
-"类似于" => "類似於",
-"顾藉" => "顧藉",
-"颤栗" => "顫慄",
-"显着标志" => "顯著標志",
-"风干" => "風乾",
-"风土志" => "風土誌",
-"风尘仆仆" => "風塵僕僕",
-"风卷残云" => "風捲殘雲",
-"风物志" => "風物誌",
-"风范" => "風範",
-"风里" => "風裡",
-"风起云涌" => "風起雲湧",
-"风斗" => "風鬥",
-"台风" => "颱風",
-"刮了" => "颳了",
-"刮倒" => "颳倒",
-"刮去" => "颳去",
-"刮得" => "颳得",
-"刮着" => "颳著",
-"刮走" => "颳走",
-"刮起" => "颳起",
-"刮雪" => "颳雪",
-"刮风" => "颳風",
-"飘荡" => "飄蕩",
-"飘游" => "飄遊",
-"食欲" => "食慾",
-"食野之苹" => "食野之苹",
-"食面" => "食麵",
-"饭后" => "飯後",
-"饭后钟" => "飯後鐘",
-"饭团" => "飯糰",
-"饭庄" => "飯莊",
-"饲喂" => "飼餵",
-"饼干" => "餅乾",
-"馂余" => "餕餘",
-"余下" => "餘下",
-"余事" => "餘事",
-"余人" => "餘人",
-"余值" => "餘值",
-"余僇" => "餘僇",
-"余光" => "餘光",
-"余函数" => "餘函數",
-"余刃" => "餘刃",
-"余切" => "餘切",
-"余利" => "餘利",
-"余剩" => "餘剩",
-"余割" => "餘割",
-"余力" => "餘力",
-"余勇" => "餘勇",
-"余味" => "餘味",
-"余喘" => "餘喘",
-"余地" => "餘地",
-"余址" => "餘址",
-"余墨" => "餘墨",
-"余外" => "餘外",
-"余妙" => "餘妙",
-"余姚" => "餘姚",
-"余威" => "餘威",
-"余存" => "餘存",
-"余孽" => "餘孽",
-"余巾" => "餘巾",
-"余式定理" => "餘式定理",
-"余弦" => "餘弦",
-"余思" => "餘思",
-"余悸" => "餘悸",
-"余庆" => "餘慶",
-"余数" => "餘數",
-"余日" => "餘日",
-"余明" => "餘明",
-"余映" => "餘映",
-"余暇" => "餘暇",
-"余晖" => "餘暉",
-"余杭" => "餘杭",
-"余杯" => "餘杯",
-"余桃" => "餘桃",
-"余桶" => "餘桶",
-"余业" => "餘業",
-"余款" => "餘款",
-"余步" => "餘步",
-"余殃" => "餘殃",
-"余毒" => "餘毒",
-"余气" => "餘氣",
-"余氯" => "餘氯",
-"余波" => "餘波",
-"余温" => "餘溫",
-"余泽" => "餘澤",
-"余沥" => "餘瀝",
-"余烈" => "餘烈",
-"余热" => "餘熱",
-"余烬" => "餘燼",
-"余珍" => "餘珍",
-"余生" => "餘生",
-"余留" => "餘留",
-"余众" => "餘眾",
-"余窍" => "餘竅",
-"余粮" => "餘糧",
-"余绪" => "餘緒",
-"余缺" => "餘缺",
-"余罪" => "餘罪",
-"余羨" => "餘羨",
-"余声" => "餘聲",
-"余膏" => "餘膏",
-"余兴" => "餘興",
-"余蓄" => "餘蓄",
-"余荫" => "餘蔭",
-"余裕" => "餘裕",
-"余角" => "餘角",
-"余论" => "餘論",
-"余责" => "餘責",
-"余辉" => "餘輝",
-"余辜" => "餘辜",
-"余酲" => "餘酲",
-"余钱" => "餘錢",
-"余闰" => "餘閏",
-"余闲" => "餘閒",
-"余震" => "餘震",
-"余音" => "餘音",
-"余韵" => "餘韻",
-"余响" => "餘響",
-"余额" => "餘額",
-"余风" => "餘風",
-"余食" => "餘食",
-"余香" => "餘香",
-"余党" => "餘黨",
-"馄饨面" => "餛飩麵",
-"馆谷" => "館穀",
-"喂乳" => "餵乳",
-"喂了" => "餵了",
-"喂奶" => "餵奶",
-"喂给" => "餵給",
-"喂羊" => "餵羊",
-"喂猪" => "餵豬",
-"喂过" => "餵過",
-"喂鸡" => "餵雞",
-"喂食" => "餵食",
-"喂饱" => "餵飽",
-"喂养" => "餵養",
-"喂驴" => "餵驢",
-"喂鱼" => "餵魚",
-"喂鸭" => "餵鴨",
-"喂鹅" => "餵鵝",
-"饥寒" => "饑寒",
-"饥民" => "饑民",
-"饥渴" => "饑渴",
-"饥溺" => "饑溺",
-"饥饱" => "饑飽",
-"饥馑" => "饑饉",
-"首当其冲" => "首當其衝",
-"香干" => "香乾",
-"马干" => "馬乾",
-"马后" => "馬後",
-"马表" => "馬錶",
-"驻扎" => "駐紮",
-"骀荡" => "駘蕩",
-"骀藉" => "駘藉",
-"腾冲" => "騰衝",
-"惊赞" => "驚讚",
-"骨子里" => "骨子裡",
-"骨干" => "骨幹",
-"骨灰坛" => "骨灰罈",
-"骨坛" => "骨罈",
-"骨头里挣出来的钱才做得肉" => "骨頭裡掙出來的錢纔做得肉",
-"肮脏" => "骯髒",
-"脏乱" => "髒亂",
-"脏兮兮" => "髒兮兮",
-"脏字" => "髒字",
-"脏得" => "髒得",
-"脏心" => "髒心",
-"脏东西" => "髒東西",
-"脏水" => "髒水",
-"脏的" => "髒的",
-"脏词" => "髒詞",
-"脏话" => "髒話",
-"脏钱" => "髒錢",
-"体范" => "體範",
-"高几" => "高几",
-"高干" => "高幹",
-"高于" => "高於",
-"高升" => "高陞",
-"髡发" => "髡髮",
-"髭须" => "髭鬚",
-"发上指冠" => "髮上指冠",
-"发上冲冠" => "髮上沖冠",
-"发乳" => "髮乳",
-"发光可鉴" => "髮光可鑒",
-"发匪" => "髮匪",
-"发型" => "髮型",
-"发夹" => "髮夾",
-"发妻" => "髮妻",
-"发姐" => "髮姐",
-"发屋" => "髮屋",
-"发带" => "髮帶",
-"发廊" => "髮廊",
-"发式" => "髮式",
-"发引千钧" => "髮引千鈞",
-"发指" => "髮指",
-"发卷" => "髮捲",
-"发根" => "髮根",
-"发油" => "髮油",
-"发漂" => "髮漂",
-"发状" => "髮狀",
-"发癣" => "髮癬",
-"发短心长" => "髮短心長",
-"发禁" => "髮禁",
-"发笺" => "髮箋",
-"发纱" => "髮紗",
-"发结" => "髮結",
-"发丝" => "髮絲",
-"发网" => "髮網",
-"发脚" => "髮腳",
-"发肤" => "髮膚",
-"发胶" => "髮膠",
-"发菜" => "髮菜",
-"发蜡" => "髮蠟",
-"发踊冲冠" => "髮踴沖冠",
-"发辫" => "髮辮",
-"发针" => "髮針",
-"发钗" => "髮釵",
-"发长" => "髮長",
-"发际" => "髮際",
-"发霜" => "髮霜",
-"发饰" => "髮飾",
-"发髻" => "髮髻",
-"发鬓" => "髮鬢",
-"髼松" => "髼鬆",
-"鬅松" => "鬅鬆",
-"松一口气" => "鬆一口氣",
-"松了" => "鬆了",
-"松些" => "鬆些",
-"松劲" => "鬆勁",
-"松动" => "鬆動",
-"松口" => "鬆口",
-"松土" => "鬆土",
-"松宽" => "鬆寬",
-"松弛" => "鬆弛",
-"松快" => "鬆快",
-"松懈" => "鬆懈",
-"松手" => "鬆手",
-"松散" => "鬆散",
-"松柔" => "鬆柔",
-"松气" => "鬆氣",
-"松浮" => "鬆浮",
-"松绑" => "鬆綁",
-"松紧" => "鬆緊",
-"松缓" => "鬆緩",
-"松脆" => "鬆脆",
-"松脱" => "鬆脫",
-"松蛋" => "鬆蛋",
-"松起" => "鬆起",
-"松软" => "鬆軟",
-"松通" => "鬆通",
-"松开" => "鬆開",
-"松饼" => "鬆餅",
-"松松" => "鬆鬆",
-"鬈发" => "鬈髮",
-"胡子" => "鬍子",
-"胡梢" => "鬍梢",
-"胡渣" => "鬍渣",
-"胡髭" => "鬍髭",
-"胡须" => "鬍鬚",
-"鬒发" => "鬒髮",
-"须根" => "鬚根",
-"须毛" => "鬚毛",
-"须生" => "鬚生",
-"须眉" => "鬚眉",
-"须发" => "鬚髮",
-"须须" => "鬚鬚",
-"须鲨" => "鬚鯊",
-"须鲸" => "鬚鯨",
-"鬓发" => "鬢髮",
-"斗上" => "鬥上",
-"斗不过" => "鬥不過",
-"斗了" => "鬥了",
-"斗来斗去" => "鬥來鬥去",
-"斗倒" => "鬥倒",
-"斗劲" => "鬥勁",
-"斗口" => "鬥口",
-"斗嘴" => "鬥嘴",
-"斗士" => "鬥士",
-"斗子" => "鬥子",
-"斗弄" => "鬥弄",
-"斗志" => "鬥志",
-"斗成" => "鬥成",
-"斗批改" => "鬥批改",
-"斗技" => "鬥技",
-"斗智" => "鬥智",
-"斗殴" => "鬥毆",
-"斗气" => "鬥氣",
-"斗法" => "鬥法",
-"斗争" => "鬥爭",
-"斗牛" => "鬥牛",
-"斗狠" => "鬥狠",
-"斗眼" => "鬥眼",
-"斗私批修" => "鬥私批修",
-"斗笠" => "鬥笠",
-"斗草" => "鬥草",
-"斗着" => "鬥著",
-"斗蟋蟀" => "鬥蟋蟀",
-"斗起" => "鬥起",
-"斗鸡" => "鬥雞",
-"斗斗" => "鬥鬥",
-"斗鱼" => "鬥魚",
-"斗鹌鹑" => "鬥鵪鶉",
-"闹着玩儿" => "鬧著玩兒",
-"闹钟" => "鬧鐘",
-"哄动" => "鬨動",
-"哄堂" => "鬨堂",
-"哄笑" => "鬨笑",
-"郁伊" => "鬱伊",
-"郁勃" => "鬱勃",
-"郁卒" => "鬱卒",
-"郁堙不偶" => "鬱堙不偶",
-"郁塞" => "鬱塞",
-"郁垒" => "鬱壘",
-"郁律" => "鬱律",
-"郁悒" => "鬱悒",
-"郁闷" => "鬱悶",
-"郁愤" => "鬱憤",
-"郁抑" => "鬱抑",
-"郁挹" => "鬱挹",
-"郁江" => "鬱江",
-"郁沉沉" => "鬱沉沉",
-"郁泱" => "鬱泱",
-"郁火" => "鬱火",
-"郁热" => "鬱熱",
-"郁燠" => "鬱燠",
-"郁症" => "鬱症",
-"郁积" => "鬱積",
-"郁纡" => "鬱紆",
-"郁结" => "鬱結",
-"郁蒸" => "鬱蒸",
-"郁蓊" => "鬱蓊",
-"郁血" => "鬱血",
-"郁邑" => "鬱邑",
-"郁郁" => "鬱郁",
-"郁金" => "鬱金",
-"郁金香" => "鬱金香",
-"郁闭" => "鬱閉",
-"郁陶" => "鬱陶",
-"郁郁不平" => "鬱鬱不平",
-"郁郁不乐" => "鬱鬱不樂",
-"郁郁寡欢" => "鬱鬱寡歡",
-"郁郁而终" => "鬱鬱而終",
-"郁郁葱葱" => "鬱鬱蔥蔥",
-"郁黑" => "鬱黑",
-"魏征" => "魏徵",
-"鱼干" => "魚乾",
-"鱼松" => "魚鬆",
-"鲸须" => "鯨鬚",
-"鲇鱼" => "鯰魚",
-"鸠占鹊巢" => "鳩佔鵲巢",
-"凤梨干" => "鳳梨乾",
-"鸣钟" => "鳴鐘",
-"鸿范" => "鴻範",
-"鹄发" => "鵠髮",
-"雕悍" => "鵰悍",
-"鹤发" => "鶴髮",
-"咸味" => "鹹味",
-"咸嘴淡舌" => "鹹嘴淡舌",
-"咸土" => "鹹土",
-"咸度" => "鹹度",
-"咸得" => "鹹得",
-"咸批" => "鹹批",
-"咸水" => "鹹水",
-"咸派" => "鹹派",
-"咸海" => "鹹海",
-"咸淡" => "鹹淡",
-"咸湖" => "鹹湖",
-"咸汤" => "鹹湯",
-"咸潟" => "鹹潟",
-"咸的" => "鹹的",
-"咸粥" => "鹹粥",
-"咸肉" => "鹹肉",
-"咸菜" => "鹹菜",
-"咸蛋" => "鹹蛋",
-"咸猪肉" => "鹹豬肉",
-"咸类" => "鹹類",
-"咸食" => "鹹食",
-"咸鱼" => "鹹魚",
-"咸鸭蛋" => "鹹鴨蛋",
-"咸卤" => "鹹鹵",
-"咸咸" => "鹹鹹",
-"硷化" => "鹼化",
-"硷土金属" => "鹼土金屬",
-"硷地" => "鹼地",
-"硷度" => "鹼度",
-"硷性" => "鹼性",
-"硷水" => "鹼水",
-"硷液" => "鹼液",
-"硷熔" => "鹼熔",
-"硷石灰" => "鹼石灰",
-"硷纤维素" => "鹼纖維素",
-"硷金属" => "鹼金屬",
-"硷类" => "鹼類",
-"盐打怎么咸" => "鹽打怎麼鹹",
-"盐卤" => "鹽滷",
-"盐余" => "鹽餘",
-"盐硷土" => "鹽鹼土",
-"盐硷滩" => "鹽鹼灘",
-"丽于" => "麗於",
-"麴霉" => "麴黴",
-"面人儿" => "麵人兒",
-"面价" => "麵價",
-"面包" => "麵包",
-"面团" => "麵團",
-"面坊" => "麵坊",
-"面坯儿" => "麵坯兒",
-"面塑" => "麵塑",
-"面店" => "麵店",
-"面厂" => "麵廠",
-"面杖" => "麵杖",
-"面条" => "麵條",
-"面汤" => "麵湯",
-"面浆" => "麵漿",
-"面灰" => "麵灰",
-"面疙瘩" => "麵疙瘩",
-"面皮" => "麵皮",
-"面码儿" => "麵碼兒",
-"面筋" => "麵筋",
-"面粉" => "麵粉",
-"面糊" => "麵糊",
-"面线" => "麵線",
-"面缸" => "麵缸",
-"面茶" => "麵茶",
-"面食" => "麵食",
-"面饺" => "麵餃",
-"面饼" => "麵餅",
-"面馆" => "麵館",
-"麻药" => "麻藥",
-"麻醉药" => "麻醉藥",
-"麻酱面" => "麻醬麵",
-"麻雀在后" => "麻雀在後",
-"黄干黑廋" => "黃乾黑廋",
-"黄历" => "黃曆",
-"黄钟" => "黃鐘",
-"黄雀在后" => "黃雀在後",
-"黄发垂髫" => "黃髮垂髫",
-"黑奴吁天录" => "黑奴籲天錄",
-"黑发" => "黑髮",
-"点钟" => "點鐘",
-"霉毒" => "黴毒",
-"霉菌" => "黴菌",
-"霉黑" => "黴黑",
-"霉黧" => "黴黧",
-"鼓里" => "鼓裡",
-"冬冬" => "鼕鼕",
-"鼠药" => "鼠藥",
-"鼻梁" => "鼻樑",
-"鼻准" => "鼻準",
-"齐王舍牛" => "齊王捨牛",
-"齿危发秀" => "齒危髮秀",
-"齿发" => "齒髮",
-"出剧" => "齣劇",
-"出卡通" => "齣卡通",
-"出戏" => "齣戲",
-"出电影" => "齣電影",
-"龙卷" => "龍捲",
-"龙争虎斗" => "龍爭虎鬥",