aboutsummaryrefslogtreecommitdiffstats
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/Article.php1478
-rw-r--r--includes/DatabaseFunctions.php134
-rw-r--r--includes/DefaultSettings.php66
-rw-r--r--includes/DifferenceEngine.php1138
-rw-r--r--includes/FulltextStoplist.php592
-rw-r--r--includes/GlobalFunctions.php494
-rw-r--r--includes/Interwiki.php256
-rw-r--r--includes/LinkCache.php109
-rw-r--r--includes/LinksUpdate.php110
-rw-r--r--includes/LogPage.php118
-rw-r--r--includes/Namespace.php49
-rw-r--r--includes/OutputPage.php1295
-rw-r--r--includes/SearchEngine.php383
-rw-r--r--includes/SearchUpdate.php78
-rw-r--r--includes/Setup.php37
-rw-r--r--includes/SiteStatsUpdate.php40
-rw-r--r--includes/Skin.php1674
-rw-r--r--includes/SkinCologneBlue.php216
-rw-r--r--includes/SkinFramed.php87
-rw-r--r--includes/SkinNostalgia.php78
-rw-r--r--includes/SkinStandard.php60
-rw-r--r--includes/SpecialAllpages.php116
-rw-r--r--includes/SpecialAsksql.php113
-rw-r--r--includes/SpecialBlockip.php97
-rw-r--r--includes/SpecialBooksources.php57
-rw-r--r--includes/SpecialContributions.php151
-rw-r--r--includes/SpecialDebug.php13
-rw-r--r--includes/SpecialEmailuser.php135
-rw-r--r--includes/SpecialImagelist.php115
-rw-r--r--includes/SpecialIntl.php555
-rw-r--r--includes/SpecialIpblocklist.php128
-rw-r--r--includes/SpecialListusers.php42
-rw-r--r--includes/SpecialLockdb.php96
-rw-r--r--includes/SpecialLonelypages.php46
-rw-r--r--includes/SpecialLongpages.php47
-rw-r--r--includes/SpecialMaintenance.php368
-rw-r--r--includes/SpecialMovepage.php424
-rw-r--r--includes/SpecialNeglectedpages.php13
-rw-r--r--includes/SpecialNewpages.php54
-rw-r--r--includes/SpecialPopularpages.php47
-rw-r--r--includes/SpecialPreferences.php260
-rw-r--r--includes/SpecialRandompage.php29
-rw-r--r--includes/SpecialRecentchanges.php172
-rw-r--r--includes/SpecialRecentchangeslinked.php89
-rw-r--r--includes/SpecialShortpages.php46
-rw-r--r--includes/SpecialSpecialpages.php46
-rw-r--r--includes/SpecialStatistics.php53
-rw-r--r--includes/SpecialUndelete.php161
-rw-r--r--includes/SpecialUnlockdb.php82
-rw-r--r--includes/SpecialUnusedimages.php56
-rw-r--r--includes/SpecialUpload.php245
-rw-r--r--includes/SpecialUserlogin.php248
-rw-r--r--includes/SpecialUserlogout.php13
-rw-r--r--includes/SpecialVote.php10
-rw-r--r--includes/SpecialWantedpages.php74
-rw-r--r--includes/SpecialWatchlist.php90
-rw-r--r--includes/SpecialWhatlinkshere.php101
-rw-r--r--includes/Title.php370
-rw-r--r--includes/UpdateClasses.php11
-rw-r--r--includes/User.php530
-rw-r--r--includes/UserTalkUpdate.php63
-rw-r--r--includes/UserUpdate.php15
-rw-r--r--includes/Utf8Case.php1520
-rw-r--r--includes/ViewCountUpdate.php21
64 files changed, 15414 insertions, 0 deletions
diff --git a/includes/Article.php b/includes/Article.php
new file mode 100644
index 000000000000..1d0df3bb3bc4
--- /dev/null
+++ b/includes/Article.php
@@ -0,0 +1,1478 @@
+<?
+# Class representing a Wikipedia article and history.
+# See design.doc for an overview.
+
+class Article {
+ /* private */ var $mContent, $mContentLoaded;
+ /* private */ var $mUser, $mTimestamp, $mUserText;
+ /* private */ var $mCounter, $mComment, $mCountAdjustment;
+ /* private */ var $mMinorEdit, $mRedirectedFrom;
+ /* private */ var $mTouched;
+
+ function Article() { $this->clear(); }
+
+ /* private */ function clear()
+ {
+ $this->mContentLoaded = false;
+ $this->mUser = $this->mCounter = -1; # Not loaded
+ $this->mRedirectedFrom = $this->mUserText =
+ $this->mTimestamp = $this->mComment = "";
+ $this->mCountAdjustment = 0;
+ $this->mTouched = "19700101000000";
+ }
+
+ /* static */ function newFromID( $newid )
+ {
+ global $wgOut, $wgTitle, $wgArticle;
+ $a = new Article();
+ $n = Article::nameOf( $newid );
+
+ $wgTitle = Title::newFromDBkey( $n );
+ $wgTitle->resetArticleID( $newid );
+
+ return $a;
+ }
+
+ /* static */ function nameOf( $id )
+ {
+ $sql = "SELECT cur_namespace,cur_title FROM cur WHERE " .
+ "cur_id={$id}";
+ $res = wfQuery( $sql, "Article::nameOf" );
+ if ( 0 == wfNumRows( $res ) ) { return NULL; }
+
+ $s = wfFetchObject( $res );
+ $n = Title::makeName( $s->cur_namespace, $s->cur_title );
+ return $n;
+ }
+
+ # Note that getContent/loadContent may follow redirects if
+ # not told otherwise, and so may cause a change to wgTitle.
+
+ function getContent( $noredir = false )
+ {
+ global $action,$wgTitle; # From query string
+ wfProfileIn( "Article::getContent" );
+
+ if ( 0 == $this->getID() ) {
+ if ( "edit" == $action ) {
+
+ global $wgTitle;
+ return ""; # was "newarticletext", now moved above the box)
+
+
+ }
+ wfProfileOut();
+ return wfMsg( "noarticletext" );
+ } else {
+ $this->loadContent( $noredir );
+ wfProfileOut();
+
+ if(
+ # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
+ ( $wgTitle->getNamespace() == Namespace::getTalk( Namespace::getUser()) ) &&
+ preg_match("/^\d{1,3}\.\d{1,3}.\d{1,3}\.\d{1,3}$/",$wgTitle->getText()) &&
+ $action=="view"
+ )
+ {
+ return $this->mContent . "\n" .wfMsg("anontalkpagetext"); }
+ else {
+ return $this->mContent;
+ }
+ }
+ }
+
+ function loadContent( $noredir = false )
+ {
+ global $wgOut, $wgTitle;
+ global $oldid, $redirect; # From query
+
+ if ( $this->mContentLoaded ) return;
+ $fname = "Article::loadContent";
+
+ # Pre-fill content with error message so that if something
+ # fails we'll have something telling us what we intended.
+
+ $t = $wgTitle->getPrefixedText();
+ if ( $oldid ) { $t .= ",oldid={$oldid}"; }
+ if ( $redirect ) { $t .= ",redirect={$redirect}"; }
+ $this->mContent = str_replace( "$1", $t, wfMsg( "missingarticle" ) );
+
+ if ( ! $oldid ) { # Retrieve current version
+ $id = $this->getID();
+ if ( 0 == $id ) return;
+
+ $sql = "SELECT " .
+ "cur_text,cur_timestamp,cur_user,cur_counter,cur_restrictions,cur_touched " .
+ "FROM cur WHERE cur_id={$id}";
+ $res = wfQuery( $sql, $fname );
+ if ( 0 == wfNumRows( $res ) ) { return; }
+
+ $s = wfFetchObject( $res );
+
+ # If we got a redirect, follow it (unless we've been told
+ # not to by either the function parameter or the query
+
+ if ( ( "no" != $redirect ) && ( false == $noredir ) &&
+ ( preg_match( "/^#redirect/i", $s->cur_text ) ) ) {
+ if ( preg_match( "/\\[\\[([^\\]\\|]+)[\\]\\|]/",
+ $s->cur_text, $m ) ) {
+ $rt = Title::newFromText( $m[1] );
+
+ # Gotta hand redirects to special pages differently:
+ # Fill the HTTP response "Location" header and ignore
+ # the rest of the page we're on.
+
+ if ( $rt->getInterwiki() != "" ) {
+ $wgOut->redirect( $rt->getFullURL() ) ;
+ return;
+ }
+ if ( $rt->getNamespace() == Namespace::getSpecial() ) {
+ $wgOut->redirect( wfLocalUrl(
+ $rt->getPrefixedURL() ) );
+ return;
+ }
+ $rid = $rt->getArticleID();
+ if ( 0 != $rid ) {
+ $sql = "SELECT cur_text,cur_timestamp,cur_user," .
+ "cur_counter,cur_touched FROM cur WHERE cur_id={$rid}";
+ $res = wfQuery( $sql, $fname );
+
+ if ( 0 != wfNumRows( $res ) ) {
+ $this->mRedirectedFrom = $wgTitle->getPrefixedText();
+ $wgTitle = $rt;
+ $s = wfFetchObject( $res );
+ }
+ }
+ }
+ }
+ $this->mContent = $s->cur_text;
+ $this->mUser = $s->cur_user;
+ $this->mCounter = $s->cur_counter;
+ $this->mTimestamp = $s->cur_timestamp;
+ $this->mTouched = $s->cur_touched;
+ $wgTitle->mRestrictions = explode( ",", trim( $s->cur_restrictions ) );
+ $wgTitle->mRestrictionsLoaded = true;
+ wfFreeResult( $res );
+ } else { # oldid set, retrieve historical version
+ $sql = "SELECT old_text,old_timestamp,old_user FROM old " .
+ "WHERE old_id={$oldid}";
+ $res = wfQuery( $sql, $fname );
+ if ( 0 == wfNumRows( $res ) ) { return; }
+
+ $s = wfFetchObject( $res );
+ $this->mContent = $s->old_text;
+ $this->mUser = $s->old_user;
+ $this->mCounter = 0;
+ $this->mTimestamp = $s->old_timestamp;
+ wfFreeResult( $res );
+ }
+ $this->mContentLoaded = true;
+ }
+
+ function getID() { global $wgTitle; return $wgTitle->getArticleID(); }
+
+ function getCount()
+ {
+ if ( -1 == $this->mCounter ) {
+ $id = $this->getID();
+ $this->mCounter = wfGetSQL( "cur", "cur_counter", "cur_id={$id}" );
+ }
+ return $this->mCounter;
+ }
+
+ # Would the given text make this article a "good" article (i.e.,
+ # suitable for including in the article count)?
+
+ function isCountable( $text )
+ {
+ global $wgTitle, $wgUseCommaCount;
+
+ if ( 0 != $wgTitle->getNamespace() ) { return 0; }
+ if ( preg_match( "/^#redirect/i", $text ) ) { return 0; }
+ $token = ($wgUseCommaCount ? "," : "[[" );
+ if ( false === strstr( $text, $token ) ) { return 0; }
+ return 1;
+ }
+
+ # Load the field related to the last edit time of the article.
+ # This isn't necessary for all uses, so it's only done if needed.
+
+ /* private */ function loadLastEdit()
+ {
+ global $wgOut;
+ if ( -1 != $this->mUser ) return;
+
+ $sql = "SELECT cur_user,cur_user_text,cur_timestamp," .
+ "cur_comment,cur_minor_edit FROM cur WHERE " .
+ "cur_id=" . $this->getID();
+ $res = wfQuery( $sql, "Article::loadLastEdit" );
+
+ if ( wfNumRows( $res ) > 0 ) {
+ $s = wfFetchObject( $res );
+ $this->mUser = $s->cur_user;
+ $this->mUserText = $s->cur_user_text;
+ $this->mTimestamp = $s->cur_timestamp;
+ $this->mComment = $s->cur_comment;
+ $this->mMinorEdit = $s->cur_minor_edit;
+ }
+ }
+
+ function getTimestamp()
+ {
+ $this->loadLastEdit();
+ return $this->mTimestamp;
+ }
+
+ function getUser()
+ {
+ $this->loadLastEdit();
+ return $this->mUser;
+ }
+
+ function getUserText()
+ {
+ $this->loadLastEdit();
+ return $this->mUserText;
+ }
+
+ function getComment()
+ {
+ $this->loadLastEdit();
+ return $this->mComment;
+ }
+
+ function getMinorEdit()
+ {
+ $this->loadLastEdit();
+ return $this->mMinorEdit;
+ }
+
+ # This is the default action of the script: just view the page of
+ # the given title.
+
+ function view()
+ {
+ global $wgUser, $wgOut, $wgTitle, $wgLang;
+ global $oldid, $diff; # From query
+ global $wgLinkCache;
+ wfProfileIn( "Article::view" );
+
+ $wgOut->setArticleFlag( true );
+ $wgOut->setRobotpolicy( "index,follow" );
+
+ # If we got diff and oldid in the query, we want to see a
+ # diff page instead of the article.
+
+ if ( isset( $diff ) ) {
+ $wgOut->setPageTitle( $wgTitle->getPrefixedText() );
+ $de = new DifferenceEngine( $oldid, $diff );
+ $de->showDiffPage();
+ wfProfileOut();
+ return;
+ }
+ $text = $this->getContent(); # May change wgTitle!
+ $wgOut->setPageTitle( $wgTitle->getPrefixedText() );
+ $wgOut->setHTMLTitle( $wgTitle->getPrefixedText() .
+ " - " . wfMsg( "wikititlesuffix" ) );
+
+ # We're looking at an old revision
+
+ if ( $oldid ) {
+ $this->setOldSubtitle();
+ $wgOut->setRobotpolicy( "noindex,follow" );
+ }
+ if ( "" != $this->mRedirectedFrom ) {
+ $sk = $wgUser->getSkin();
+ $redir = $sk->makeKnownLink( $this->mRedirectedFrom, "",
+ "redirect=no" );
+ $s = str_replace( "$1", $redir, wfMsg( "redirectedfrom" ) );
+ $wgOut->setSubtitle( $s );
+ }
+ $wgOut->checkLastModified( $this->mTouched );
+ $wgLinkCache->preFill( $wgTitle );
+ $wgOut->addWikiText( $text );
+
+ # 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 ( Namespace::getImage() == $wgTitle->getNamespace() ) {
+ $this->imageHistory();
+ $this->imageLinks();
+ }
+ $this->viewUpdates();
+ wfProfileOut();
+ }
+
+ # This is the function that gets called for "action=edit".
+
+ function edit()
+ {
+ global $wgOut, $wgUser, $wgTitle;
+ global $wpTextbox1, $wpSummary, $wpSave, $wpPreview;
+ global $wpMinoredit, $wpEdittime, $wpTextbox2;
+
+ $fields = array( "wpTextbox1", "wpSummary", "wpTextbox2" );
+ wfCleanFormFields( $fields );
+
+ if ( ! $wgTitle->userCanEdit() ) {
+ $this->view();
+ return;
+ }
+ if ( $wgUser->isBlocked() ) {
+ $this->blockedIPpage();
+ return;
+ }
+ if ( wfReadOnly() ) {
+ if( isset( $wpSave ) or isset( $wpPreview ) ) {
+ $this->editForm( "preview" );
+ } else {
+ $wgOut->readOnlyPage();
+ }
+ return;
+ }
+ if ( $_SERVER['REQUEST_METHOD'] != "POST" ) unset( $wpSave );
+ if ( isset( $wpSave ) ) {
+ $this->editForm( "save" );
+ } else if ( isset( $wpPreview ) ) {
+ $this->editForm( "preview" );
+ } else { # First time through
+ $this->editForm( "initial" );
+ }
+ }
+
+ # Since there is only one text field on the edit form,
+ # pressing <enter> will cause the form to be submitted, but
+ # the submit button value won't appear in the query, so we
+ # Fake it here before going back to edit(). This is kind of
+ # ugly, but it helps some old URLs to still work.
+
+ function submit()
+ {
+ global $wpSave, $wpPreview;
+ if ( ! isset( $wpPreview ) ) { $wpSave = 1; }
+
+ $this->edit();
+ }
+
+ # The edit form is self-submitting, so that when things like
+ # preview and edit conflicts occur, we get the same form back
+ # with the extra stuff added. Only when the final submission
+ # is made and all is well do we actually save and redirect to
+ # the newly-edited page.
+
+ function editForm( $formtype )
+ {
+ global $wgOut, $wgUser, $wgTitle;
+ global $wpTextbox1, $wpSummary, $wpWatchthis;
+ global $wpSave, $wpPreview;
+ global $wpMinoredit, $wpEdittime, $wpTextbox2;
+ global $oldid, $redirect;
+ global $wgLang;
+
+ $sk = $wgUser->getSkin();
+ $isConflict = false;
+ $wpTextbox1 = rtrim ( $wpTextbox1 ) ; # To avoid text getting longer on each preview
+
+ if(!$wgTitle->getArticleID()) { # new article
+
+ $wgOut->addWikiText(wfmsg("newarticletext"));
+
+ }
+
+ # Attempt submission here. This will check for edit conflicts,
+ # and redundantly check for locked database, blocked IPs, etc.
+ # that edit() already checked just in case someone tries to sneak
+ # in the back door with a hand-edited submission URL.
+
+ if ( "save" == $formtype ) {
+ if ( $wgUser->isBlocked() ) {
+ $this->blockedIPpage();
+ return;
+ }
+ if ( wfReadOnly() ) {
+ $wgOut->readOnlyPage();
+ return;
+ }
+ # If article is new, insert it.
+
+ $aid = $wgTitle->getArticleID();
+ if ( 0 == $aid ) {
+ # we need to strip Windoze linebreaks because some browsers
+ # append them and the string comparison fails
+ if ( ( "" == $wpTextbox1 ) ||
+ ( wfMsg( "newarticletext" ) == rtrim( preg_replace("/\r/","",$wpTextbox1) ) ) ) {
+ $wgOut->redirect( wfLocalUrl(
+ $wgTitle->getPrefixedURL() ) );
+ return;
+ }
+ $this->mCountAdjustment = $this->isCountable( $wpTextbox1 );
+ $this->insertNewArticle( $wpTextbox1, $wpSummary, $wpMinoredit, $wpWatchthis );
+ return;
+ }
+ # Article exists. Check for edit conflict.
+
+ $this->clear(); # Force reload of dates, etc.
+ if ( $this->getTimestamp() != $wpEdittime ) { $isConflict = true; }
+ $u = $wgUser->getID();
+
+ # Supress edit conflict with self
+
+ if ( ( 0 != $u ) && ( $this->getUser() == $u ) ) {
+ $isConflict = false;
+ }
+ if ( ! $isConflict ) {
+ # All's well: update the article here
+ $this->updateArticle( $wpTextbox1, $wpSummary, $wpMinoredit, $wpWatchthis );
+ return;
+ }
+ }
+ # First time through: get contents, set time for conflict
+ # checking, etc.
+
+ if ( "initial" == $formtype ) {
+ $wpEdittime = $this->getTimestamp();
+ $wpTextbox1 = $this->getContent();
+ $wpSummary = "";
+ }
+ $wgOut->setRobotpolicy( "noindex,nofollow" );
+ $wgOut->setArticleFlag( false );
+
+ if ( $isConflict ) {
+ $s = str_replace( "$1", $wgTitle->getPrefixedText(),
+ wfMsg( "editconflict" ) );
+ $wgOut->setPageTitle( $s );
+ $wgOut->addHTML( wfMsg( "explainconflict" ) );
+
+ $wpTextbox2 = $wpTextbox1;
+ $wpTextbox1 = $this->getContent();
+ $wpEdittime = $this->getTimestamp();
+ } else {
+ $s = str_replace( "$1", $wgTitle->getPrefixedText(),
+ wfMsg( "editing" ) );
+ $wgOut->setPageTitle( $s );
+ if ( $oldid ) {
+ $this->setOldSubtitle();
+ $wgOut->addHTML( wfMsg( "editingold" ) );
+ }
+ }
+
+ if( wfReadOnly() ) {
+ $wgOut->addHTML( "<strong>" .
+ wfMsg( "readonlywarning" ) .
+ "</strong>" );
+ }
+ if( $wgTitle->isProtected() ) {
+ $wgOut->addHTML( "<strong>" . wfMsg( "protectedpagewarning" ) .
+ "</strong><br />\n" );
+ }
+
+ $kblength = (int)(strlen( $wpTextbox1 ) / 1024);
+ if( $kblength > 29 ) {
+ $wgOut->addHTML( "<strong>" .
+ str_replace( '$1', $kblength , wfMsg( "longpagewarning" ) )
+ . "</strong>" );
+ }
+
+ $rows = $wgUser->getOption( "rows" );
+ $cols = $wgUser->getOption( "cols" );
+
+ $ew = $wgUser->getOption( "editwidth" );
+ if ( $ew ) $ew = " style=\"width:100%\"";
+ else $ew = "" ;
+
+ $q = "action=submit";
+ if ( "no" == $redirect ) { $q .= "&redirect=no"; }
+ $action = wfEscapeHTML( wfLocalUrl( $wgTitle->getPrefixedURL(), $q ) );
+
+ $summary = wfMsg( "summary" );
+ $minor = wfMsg( "minoredit" );
+ $watchthis = wfMsg ("watchthis");
+ $save = wfMsg( "savearticle" );
+ $prev = wfMsg( "showpreview" );
+
+ $cancel = $sk->makeKnownLink( $wgTitle->getPrefixedURL(),
+ wfMsg( "cancel" ) );
+ $edithelp = $sk->makeKnownLink( wfMsg( "edithelppage" ),
+ wfMsg( "edithelp" ) );
+ $copywarn = str_replace( "$1", $sk->makeKnownLink(
+ wfMsg( "copyrightpage" ) ), wfMsg( "copyrightwarning" ) );
+
+ $wpTextbox1 = wfEscapeHTML( $wpTextbox1 );
+ $wpTextbox2 = wfEscapeHTML( $wpTextbox2 );
+ $wpSummary = wfEscapeHTML( $wpSummary );
+
+ // activate checkboxes if user wants them to be always active
+ if (!$wpPreview && $wgUser->getOption("watchdefault")) $wpWatchthis=1;
+ if (!$wpPreview && $wgUser->getOption("minordefault")) $wpMinoredit=1;
+
+ // activate checkbox also if user is already watching the page,
+ // require wpWatchthis to be unset so that second condition is not
+ // checked unnecessarily
+ if (!$wpWatchthis && !$wpPreview && $wgTitle->userIsWatching()) $wpWatchthis=1;
+
+ if ( 0 != $wgUser->getID() ) {
+ $checkboxhtml=
+ "<input tabindex=3 type=checkbox value=1 name='wpMinoredit'".($wpMinoredit?" checked":"").">{$minor}".
+ "<input tabindex=4 type=checkbox name='wpWatchthis'".($wpWatchthis?" checked":"").">{$watchthis}<br>";
+
+ } else {
+ $checkboxhtml="";
+ }
+
+
+ if ( "preview" == $formtype) {
+
+ $previewhead="<h2>" . wfMsg( "preview" ) . "</h2>\n<p><large><center><font color=\"#cc0000\">" .
+ wfMsg( "note" ) . wfMsg( "previewnote" ) . "</font></center></large><P>\n";
+ if ( $isConflict ) {
+ $previewhead.="<h2>" . wfMsg( "previewconflict" ) .
+ "</h2>\n";
+ }
+ $previewtext = wfUnescapeHTML( $wpTextbox1 );
+
+ if($wgUser->getOption("previewontop")) {
+ $wgOut->addHTML($previewhead);
+ $wgOut->addWikiText( $this->preSaveTransform( $previewtext ) ."\n\n");
+ }
+ }
+ $wgOut->addHTML( "
+<form id=\"editform\" method=\"post\" action=\"$action\"
+enctype=\"application/x-www-form-urlencoded\">
+<textarea tabindex=1 name=\"wpTextbox1\" rows={$rows}
+cols={$cols}{$ew} wrap=\"virtual\">" .
+$wgLang->recodeForEdit( $wpTextbox1 ) .
+"
+</textarea><br>
+{$summary}: <input tabindex=2 type=text value=\"{$wpSummary}\"
+name=\"wpSummary\" maxlength=200 size=60><br>
+{$checkboxhtml}
+<input tabindex=5 type=submit value=\"{$save}\" name=\"wpSave\">
+<input tabindex=6 type=submit value=\"{$prev}\" name=\"wpPreview\">
+<em>{$cancel}</em> | <em>{$edithelp}</em>
+<br><br>{$copywarn}
+<input type=hidden value=\"{$wpEdittime}\" name=\"wpEdittime\">\n" );
+
+ if ( $isConflict ) {
+ $wgOut->addHTML( "<h2>" . wfMsg( "yourdiff" ) . "</h2>\n" );
+ DifferenceEngine::showDiff( $wpTextbox2, $wpTextbox1,
+ wfMsg( "yourtext" ), wfMsg( "storedversion" ) );
+
+ $wgOut->addHTML( "<h2>" . wfMsg( "yourtext" ) . "</h2>
+<textarea tabindex=6 name=\"wpTextbox2\" rows={$rows} cols={$cols} wrap=virtual>"
+. $wgLang->recodeForEdit( $wpTextbox2 ) .
+"
+</textarea>" );
+ }
+ $wgOut->addHTML( "</form>\n" );
+ if($formtype =="preview" && !$wgUser->getOption("previewontop")) {
+ $wgOut->addHTML($previewhead);
+ $wgOut->addWikiText( $this->preSaveTransform( $previewtext ) );
+ }
+
+ }
+
+ # Theoretically we could defer these whole insert and update
+ # functions for after display, but that's taking a big leap
+ # of faith, and we want to be able to report database
+ # errors at some point.
+
+ /* private */ function insertNewArticle( $text, $summary, $isminor, $watchthis )
+ {
+ global $wgOut, $wgUser, $wgTitle, $wgLinkCache;
+ $fname = "Article::insertNewArticle";
+
+ $ns = $wgTitle->getNamespace();
+ $ttl = $wgTitle->getDBkey();
+ $text = $this->preSaveTransform( $text );
+ if ( preg_match( "/^#redirect/i", $text ) ) { $redir = 1; }
+ else { $redir = 0; }
+
+ $now = wfTimestampNow();
+ $won = wfInvertTimestamp( $now );
+ $sql = "INSERT INTO cur (cur_namespace,cur_title,cur_text," .
+ "cur_comment,cur_user,cur_timestamp,cur_minor_edit,cur_counter," .
+ "cur_restrictions,cur_user_text,cur_is_redirect," .
+ "cur_is_new,cur_random,cur_touched,inverse_timestamp) VALUES ({$ns},'" . wfStrencode( $ttl ) . "', '" .
+ wfStrencode( $text ) . "', '" .
+ wfStrencode( $summary ) . "', '" .
+ $wgUser->getID() . "', '{$now}', " .
+ ( $isminor ? 1 : 0 ) . ", 0, '', '" .
+ wfStrencode( $wgUser->getName() ) . "', $redir, 1, RAND(), '{$now}', '{$won}')";
+ $res = wfQuery( $sql, $fname );
+
+ $newid = wfInsertId();
+ $wgTitle->resetArticleID( $newid );
+
+ $sql = "INSERT INTO recentchanges (rc_timestamp,rc_cur_time," .
+ "rc_namespace,rc_title,rc_new,rc_minor,rc_cur_id,rc_user," .
+ "rc_user_text,rc_comment,rc_this_oldid,rc_last_oldid,rc_bot) VALUES (" .
+ "'{$now}','{$now}',{$ns},'" . wfStrencode( $ttl ) . "',1," .
+ ( $isminor ? 1 : 0 ) . ",{$newid}," . $wgUser->getID() . ",'" .
+ wfStrencode( $wgUser->getName() ) . "','" .
+ wfStrencode( $summary ) . "',0,0," .
+ ( $wgUser->isBot() ? 1 : 0 ) . ")";
+ wfQuery( $sql, $fname );
+ if ($watchthis) {
+ if(!$wgTitle->userIsWatching()) $this->watch();
+ } else {
+ if ( $wgTitle->userIsWatching() ) {
+ $this->unwatch();
+ }
+ }
+
+ $this->showArticle( $text, wfMsg( "newarticle" ) );
+ }
+
+ function updateArticle( $text, $summary, $minor, $watchthis )
+ {
+ global $wgOut, $wgUser, $wgTitle, $wgLinkCache;
+ global $wgDBtransactions;
+ $fname = "Article::updateArticle";
+
+ if ( $this->mMinorEdit ) { $me1 = 1; } else { $me1 = 0; }
+ if ( $minor ) { $me2 = 1; } else { $me2 = 0; }
+ if ( preg_match( "/^(#redirect[^\\n]+)/i", $text, $m ) ) {
+ $redir = 1;
+ $text = $m[1] . "\n"; # Remove all content but redirect
+ }
+ else { $redir = 0; }
+ $this->loadLastEdit();
+
+ $text = $this->preSaveTransform( $text );
+
+ # Update article, but only if changed.
+
+ if( $wgDBtransactions ) {
+ $sql = "BEGIN";
+ wfQuery( $sql );
+ }
+ $oldtext = $this->getContent( true );
+
+ if ( 0 != strcmp( $text, $oldtext ) ) {
+ $this->mCountAdjustment = $this->isCountable( $text )
+ - $this->isCountable( $oldtext );
+
+ $sql = "INSERT INTO old (old_namespace,old_title,old_text," .
+ "old_comment,old_user,old_user_text,old_timestamp," .
+ "old_minor_edit,inverse_timestamp) VALUES (" .
+ $wgTitle->getNamespace() . ", '" .
+ wfStrencode( $wgTitle->getDBkey() ) . "', '" .
+ wfStrencode( $oldtext ) . "', '" .
+ wfStrencode( $this->getComment() ) . "', " .
+ $this->getUser() . ", '" .
+ wfStrencode( $this->getUserText() ) . "', '" .
+ $this->getTimestamp() . "', " . $me1 . ", '" .
+ wfInvertTimestamp( $this->getTimestamp() ) . "')";
+ $res = wfQuery( $sql, $fname );
+ $oldid = wfInsertID( $res );
+
+ $now = wfTimestampNow();
+ $won = wfInvertTimestamp( $now );
+ $sql = "UPDATE cur SET cur_text='" . wfStrencode( $text ) .
+ "',cur_comment='" . wfStrencode( $summary ) .
+ "',cur_minor_edit={$me2}, cur_user=" . $wgUser->getID() .
+ ",cur_timestamp='{$now}',cur_user_text='" .
+ wfStrencode( $wgUser->getName() ) .
+ "',cur_is_redirect={$redir}, cur_is_new=0, cur_touched='{$now}', inverse_timestamp='{$won}' " .
+ "WHERE cur_id=" . $this->getID();
+ wfQuery( $sql, $fname );
+
+ $sql = "INSERT INTO recentchanges (rc_timestamp,rc_cur_time," .
+ "rc_namespace,rc_title,rc_new,rc_minor,rc_bot,rc_cur_id,rc_user," .
+ "rc_user_text,rc_comment,rc_this_oldid,rc_last_oldid) VALUES (" .
+ "'{$now}','{$now}'," . $wgTitle->getNamespace() . ",'" .
+ wfStrencode( $wgTitle->getDBkey() ) . "',0,{$me2}," .
+ ( $wgUser->isBot() ? 1 : 0 ) . "," .
+ $this->getID() . "," . $wgUser->getID() . ",'" .
+ wfStrencode( $wgUser->getName() ) . "','" .
+ wfStrencode( $summary ) . "',0,{$oldid})";
+ wfQuery( $sql, $fname );
+
+ $sql = "UPDATE recentchanges SET rc_this_oldid={$oldid} " .
+ "WHERE rc_namespace=" . $wgTitle->getNamespace() . " AND " .
+ "rc_title='" . wfStrencode( $wgTitle->getDBkey() ) . "' AND " .
+ "rc_timestamp='" . $this->getTimestamp() . "'";
+ wfQuery( $sql, $fname );
+
+ $sql = "UPDATE recentchanges SET rc_cur_time='{$now}' " .
+ "WHERE rc_cur_id=" . $this->getID();
+ wfQuery( $sql, $fname );
+ }
+ if( $wgDBtransactions ) {
+ $sql = "COMMIT";
+ wfQuery( $sql );
+ }
+
+ if ($watchthis) {
+ if (!$wgTitle->userIsWatching()) $this->watch();
+ } else {
+ if ( $wgTitle->userIsWatching() ) {
+ $this->unwatch();
+ }
+ }
+
+ $this->showArticle( $text, wfMsg( "updated" ) );
+ }
+
+ # After we've either updated or inserted the article, update
+ # the link tables and redirect to the new page.
+
+ function showArticle( $text, $subtitle )
+ {
+ global $wgOut, $wgTitle, $wgUser, $wgLinkCache;
+
+ $wgLinkCache = new LinkCache();
+ $wgOut->addWikiText( $text ); # Just to update links
+
+ $this->editUpdates( $text );
+ if( preg_match( "/^#redirect/i", $text ) )
+ $r = "redirect=no";
+ else
+ $r = "";
+ $wgOut->redirect( wfLocalUrl( $wgTitle->getPrefixedURL(), $r ) );
+ }
+
+ # If the page we've just displayed is in the "Image" namespace,
+ # we follow it with an upload history of the image and its usage.
+
+ function imageHistory()
+ {
+ global $wgUser, $wgOut, $wgLang, $wgTitle;
+ $fname = "Article::imageHistory";
+
+ $sql = "SELECT img_size,img_description,img_user," .
+ "img_user_text,img_timestamp FROM image WHERE " .
+ "img_name='" . wfStrencode( $wgTitle->getDBkey() ) . "'";
+ $res = wfQuery( $sql, $fname );
+
+ if ( 0 == wfNumRows( $res ) ) { return; }
+
+ $sk = $wgUser->getSkin();
+ $s = $sk->beginImageHistoryList();
+
+ $line = wfFetchObject( $res );
+ $s .= $sk->imageHistoryLine( true, $line->img_timestamp,
+ $wgTitle->getText(), $line->img_user,
+ $line->img_user_text, $line->img_size, $line->img_description );
+
+ $sql = "SELECT oi_size,oi_description,oi_user," .
+ "oi_user_text,oi_timestamp,oi_archive_name FROM oldimage WHERE " .
+ "oi_name='" . wfStrencode( $wgTitle->getDBkey() ) . "' " .
+ "ORDER BY oi_timestamp DESC";
+ $res = wfQuery( $sql, $fname );
+
+ while ( $line = wfFetchObject( $res ) ) {
+ $s .= $sk->imageHistoryLine( false, $line->oi_timestamp,
+ $line->oi_archive_name, $line->oi_user,
+ $line->oi_user_text, $line->oi_size, $line->oi_description );
+ }
+ $s .= $sk->endImageHistoryList();
+ $wgOut->addHTML( $s );
+ }
+
+ function imageLinks()
+ {
+ global $wgUser, $wgOut, $wgTitle;
+
+ $wgOut->addHTML( "<h2>" . wfMsg( "imagelinks" ) . "</h2>\n" );
+
+ $sql = "SELECT il_from FROM imagelinks WHERE il_to='" .
+ wfStrencode( $wgTitle->getDBkey() ) . "'";
+ $res = wfQuery( $sql, "Article::imageLinks" );
+
+ if ( 0 == wfNumRows( $res ) ) {
+ $wgOut->addHtml( "<p>" . wfMsg( "nolinkstoimage" ) . "\n" );
+ return;
+ }
+ $wgOut->addHTML( "<p>" . wfMsg( "linkstoimage" ) . "\n<ul>" );
+
+ $sk = $wgUser->getSkin();
+ while ( $s = wfFetchObject( $res ) ) {
+ $name = $s->il_from;
+ $link = $sk->makeKnownLink( $name, "" );
+ $wgOut->addHTML( "<li>{$link}</li>\n" );
+ }
+ $wgOut->addHTML( "</ul>\n" );
+ }
+
+ # Add this page to my watchlist
+
+ function watch()
+ {
+ global $wgUser, $wgTitle, $wgOut, $wgLang;
+ global $wgDeferredUpdateList;
+
+ if ( 0 == $wgUser->getID() ) {
+ $wgOut->errorpage( "watchnologin", "watchnologintext" );
+ return;
+ }
+ if ( wfReadOnly() ) {
+ $wgOut->readOnlyPage();
+ return;
+ }
+ $wgUser->addWatch( $wgTitle );
+
+ $wgOut->setPagetitle( wfMsg( "addedwatch" ) );
+ $wgOut->setRobotpolicy( "noindex,follow" );
+
+ $sk = $wgUser->getSkin() ;
+ $link = $sk->makeKnownLink ( $wgTitle->getPrefixedText() ) ;
+
+ $text = str_replace( "$1", $link ,
+ wfMsg( "addedwatchtext" ) );
+ $wgOut->addHTML( $text );
+
+ $up = new UserUpdate();
+ array_push( $wgDeferredUpdateList, $up );
+
+ $wgOut->returnToMain( false );
+ }
+
+ function unwatch()
+ {
+ global $wgUser, $wgTitle, $wgOut, $wgLang;
+ global $wgDeferredUpdateList;
+
+ if ( 0 == $wgUser->getID() ) {
+ $wgOut->errorpage( "watchnologin", "watchnologintext" );
+ return;
+ }
+ if ( wfReadOnly() ) {
+ $wgOut->readOnlyPage();
+ return;
+ }
+ $wgUser->removeWatch( $wgTitle );
+
+ $wgOut->setPagetitle( wfMsg( "removedwatch" ) );
+ $wgOut->setRobotpolicy( "noindex,follow" );
+
+ $sk = $wgUser->getSkin() ;
+ $link = $sk->makeKnownLink ( $wgTitle->getPrefixedText() ) ;
+
+ $text = str_replace( "$1", $link ,
+ wfMsg( "removedwatchtext" ) );
+ $wgOut->addHTML( $text );
+
+ $up = new UserUpdate();
+ array_push( $wgDeferredUpdateList, $up );
+
+ $wgOut->returnToMain( false );
+ }
+
+ # This shares a lot of issues (and code) with Recent Changes
+
+ function history()
+ {
+ global $wgUser, $wgOut, $wgLang, $wgTitle, $offset, $limit;
+
+ # If page hasn't changed, client can cache this
+
+ $wgOut->checkLastModified( $this->getTimestamp() );
+ wfProfileIn( "Article::history" );
+
+ $wgOut->setPageTitle( $wgTitle->getPRefixedText() );
+ $wgOut->setSubtitle( wfMsg( "revhistory" ) );
+ $wgOut->setArticleFlag( false );
+ $wgOut->setRobotpolicy( "noindex,nofollow" );
+
+ if( $wgTitle->getArticleID() == 0 ) {
+ $wgOut->addHTML( wfMsg( "nohistory" ) );
+ wfProfileOut();
+ return;
+ }
+
+ $offset = (int)$offset;
+ $limit = (int)$limit;
+ if( $limit == 0 ) $limit = 50;
+ $namespace = $wgTitle->getNamespace();
+ $title = $wgTitle->getText();
+ $sql = "SELECT old_id,old_user," .
+ "old_comment,old_user_text,old_timestamp,old_minor_edit ".
+ "FROM old USE INDEX (name_title_timestamp) " .
+ "WHERE old_namespace={$namespace} AND " .
+ "old_title='" . wfStrencode( $wgTitle->getDBkey() ) . "' " .
+ "ORDER BY inverse_timestamp LIMIT $offset, $limit";
+ $res = wfQuery( $sql, "Article::history" );
+
+ $revs = wfNumRows( $res );
+ if( $wgTitle->getArticleID() == 0 ) {
+ $wgOut->addHTML( wfMsg( "nohistory" ) );
+ wfProfileOut();
+ return;
+ }
+
+ $sk = $wgUser->getSkin();
+ $numbar = wfViewPrevNext(
+ $offset, $limit,
+ $wgTitle->getPrefixedText(),
+ "action=history" );
+ $s = $numbar;
+ $s .= $sk->beginHistoryList();
+
+ if($offset == 0 )
+ $s .= $sk->historyLine( $this->getTimestamp(), $this->getUser(),
+ $this->getUserText(), $namespace,
+ $title, 0, $this->getComment(),
+ ( $this->getMinorEdit() > 0 ) );
+
+ $revs = wfNumRows( $res );
+ while ( $line = wfFetchObject( $res ) ) {
+ $s .= $sk->historyLine( $line->old_timestamp, $line->old_user,
+ $line->old_user_text, $namespace,
+ $title, $line->old_id,
+ $line->old_comment, ( $line->old_minor_edit > 0 ) );
+ }
+ $s .= $sk->endHistoryList();
+ $s .= $numbar;
+ $wgOut->addHTML( $s );
+ wfProfileOut();
+ }
+
+ function protect()
+ {
+ global $wgUser, $wgOut, $wgTitle;
+
+ if ( ! $wgUser->isSysop() ) {
+ $wgOut->sysopRequired();
+ return;
+ }
+ if ( wfReadOnly() ) {
+ $wgOut->readOnlyPage();
+ return;
+ }
+ $id = $wgTitle->getArticleID();
+ if ( 0 == $id ) {
+ $wgOut->fatalEror( wfMsg( "badarticleerror" ) );
+ return;
+ }
+ $sql = "UPDATE cur SET cur_touched='" . wfTimestampNow() . "'," .
+ "cur_restrictions='sysop' WHERE cur_id={$id}";
+ wfQuery( $sql, "Article::protect" );
+
+ $wgOut->redirect( wfLocalUrl( $wgTitle->getPrefixedURL() ) );
+ }
+
+ function unprotect()
+ {
+ global $wgUser, $wgOut, $wgTitle;
+
+ if ( ! $wgUser->isSysop() ) {
+ $wgOut->sysopRequired();
+ return;
+ }
+ if ( wfReadOnly() ) {
+ $wgOut->readOnlyPage();
+ return;
+ }
+ $id = $wgTitle->getArticleID();
+ if ( 0 == $id ) {
+ $wgOut->fatalEror( wfMsg( "badarticleerror" ) );
+ return;
+ }
+ $sql = "UPDATE cur SET cur_touched='" . wfTimestampNow() . "'," .
+ "cur_restrictions='' WHERE cur_id={$id}";
+ wfQuery( $sql, "Article::unprotect" );
+
+ $wgOut->redirect( wfLocalUrl( $wgTitle->getPrefixedURL() ) );
+ }
+
+ function delete()
+ {
+ global $wgUser, $wgOut, $wgTitle;
+ global $wpConfirm, $wpReason, $image, $oldimage;
+
+ # Anybody can delete old revisions of images; only sysops
+ # can delete articles and current images
+
+ if ( ( ! $oldimage ) && ( ! $wgUser->isSysop() ) ) {
+ $wgOut->sysopRequired();
+ return;
+ }
+ if ( wfReadOnly() ) {
+ $wgOut->readOnlyPage();
+ return;
+ }
+
+ # Better double-check that it hasn't been deleted yet!
+ $wgOut->setPagetitle( wfMsg( "confirmdelete" ) );
+ if ( $image ) {
+ if ( "" == trim( $image ) ) {
+ $wgOut->fatalError( wfMsg( "cannotdelete" ) );
+ return;
+ }
+ $sub = str_replace( "$1", $image, wfMsg( "deletesub" ) );
+ } else {
+ if ( ( "" == trim( $wgTitle->getText() ) )
+ or ( $wgTitle->getArticleId() == 0 ) ) {
+ $wgOut->fatalError( wfMsg( "cannotdelete" ) );
+ return;
+ }
+ $sub = str_replace( "$1", $wgTitle->getPrefixedText(),
+ wfMsg( "deletesub" ) );
+ }
+
+ # Likewise, deleting old images doesn't require confirmation
+ if ( $oldimage || 1 == $wpConfirm ) {
+ $this->doDelete();
+ return;
+ }
+
+ $wgOut->setSubtitle( $sub );
+ $wgOut->setRobotpolicy( "noindex,nofollow" );
+ $wgOut->addWikiText( wfMsg( "confirmdeletetext" ) );
+
+ $t = $wgTitle->getPrefixedURL();
+ $q = "action=delete";
+
+ if ( $image ) {
+ $q .= "&image={$image}";
+ } else if ( $oldimage ) {
+ $q .= "&oldimage={$oldimage}";
+ } else {
+ $q .= "&title={$t}";
+ }
+ $formaction = wfEscapeHTML( wfLocalUrl( "", $q ) );
+ $confirm = wfMsg( "confirm" );
+ $check = wfMsg( "confirmcheck" );
+ $delcom = wfMsg( "deletecomment" );
+
+ $wgOut->addHTML( "
+<form id=\"deleteconfirm\" method=\"post\" action=\"{$formaction}\">
+<table border=0><tr><td align=right>
+{$delcom}:</td><td align=left>
+<input type=text size=20 name=\"wpReason\" value=\"{$wpReason}\">
+</td></tr><tr><td>&nbsp;</td></tr>
+<tr><td align=right>
+<input type=checkbox name=\"wpConfirm\" value='1'>
+</td><td>{$check}</td>
+</tr><tr><td>&nbsp;</td><td>
+<input type=submit name=\"wpConfirmB\" value=\"{$confirm}\">
+</td></tr></table></form>\n" );
+
+ $wgOut->returnToMain( false );
+ }
+
+ function doDelete()
+ {
+ global $wgOut, $wgTitle, $wgUser, $wgLang;
+ global $image, $oldimage, $wpReason;
+ $fname = "Article::doDelete";
+
+ if ( $image ) {
+ $dest = wfImageDir( $image );
+ $archive = wfImageDir( $image );
+ if ( ! unlink( "{$dest}/{$image}" ) ) {
+ $wgOut->fileDeleteError( "{$dest}/{$image}" );
+ return;
+ }
+ $sql = "DELETE FROM image WHERE img_name='" .
+ wfStrencode( $image ) . "'";
+ wfQuery( $sql, $fname );
+
+ $sql = "SELECT oi_archive_name FROM oldimage WHERE oi_name='" .
+ wfStrencode( $image ) . "'";
+ $res = wfQuery( $sql, $fname );
+
+ while ( $s = wfFetchObject( $res ) ) {
+ $this->doDeleteOldImage( $s->oi_archive_name );
+ }
+ $sql = "DELETE FROM oldimage WHERE oi_name='" .
+ wfStrencode( $image ) . "'";
+ wfQuery( $sql, $fname );
+
+ # Image itself is now gone, and database is cleaned.
+ # Now we remove the image description page.
+
+ $nt = Title::newFromText( $wgLang->getNsText( Namespace::getImage() ) . ":" . $image );
+ $this->doDeleteArticle( $nt );
+
+ $deleted = $image;
+ } else if ( $oldimage ) {
+ $this->doDeleteOldImage( $oldimage );
+ $sql = "DELETE FROM oldimage WHERE oi_archive_name='" .
+ wfStrencode( $oldimage ) . "'";
+ wfQuery( $sql, $fname );
+
+ $deleted = $oldimage;
+ } else {
+ $this->doDeleteArticle( $wgTitle );
+ $deleted = $wgTitle->getPrefixedText();
+ }
+ $wgOut->setPagetitle( wfMsg( "actioncomplete" ) );
+ $wgOut->setRobotpolicy( "noindex,nofollow" );
+
+ $sk = $wgUser->getSkin();
+ $loglink = $sk->makeKnownLink( $wgLang->getNsText(
+ Namespace::getWikipedia() ) .
+ ":" . wfMsg( "dellogpage" ), wfMsg( "deletionlog" ) );
+
+ $text = str_replace( "$1" , $deleted, wfMsg( "deletedtext" ) );
+ $text = str_replace( "$2", $loglink, $text );
+
+ $wgOut->addHTML( "<p>" . $text );
+ $wgOut->returnToMain( false );
+ }
+
+ function doDeleteOldImage( $oldimage )
+ {
+ global $wgOut;
+
+ $name = substr( $oldimage, 15 );
+ $archive = wfImageArchiveDir( $name );
+ if ( ! unlink( "{$archive}/{$oldimage}" ) ) {
+ $wgOut->fileDeleteError( "{$archive}/{$oldimage}" );
+ }
+ }
+
+ function doDeleteArticle( $title )
+ {
+ global $wgUser, $wgOut, $wgLang, $wpReason, $wgTitle, $wgDeferredUpdateList;
+
+ $fname = "Article::doDeleteArticle";
+ $ns = $title->getNamespace();
+ $t = wfStrencode( $title->getDBkey() );
+ $id = $title->getArticleID();
+
+ if ( "" == $t ) {
+ $wgOut->fatalError( wfMsg( "cannotdelete" ) );
+ return;
+ }
+
+ $u = new SiteStatsUpdate( 0, 1, -$this->isCountable( $this->getContent( true ) ) );
+ array_push( $wgDeferredUpdateList, $u );
+
+ # Move article and history to the "archive" table
+ $sql = "INSERT INTO archive (ar_namespace,ar_title,ar_text," .
+ "ar_comment,ar_user,ar_user_text,ar_timestamp,ar_minor_edit," .
+ "ar_flags) SELECT cur_namespace,cur_title,cur_text,cur_comment," .
+ "cur_user,cur_user_text,cur_timestamp,cur_minor_edit,0 FROM cur " .
+ "WHERE cur_namespace={$ns} AND cur_title='{$t}'";
+ wfQuery( $sql, $fname );
+
+ $sql = "INSERT INTO archive (ar_namespace,ar_title,ar_text," .
+ "ar_comment,ar_user,ar_user_text,ar_timestamp,ar_minor_edit," .
+ "ar_flags) SELECT old_namespace,old_title,old_text,old_comment," .
+ "old_user,old_user_text,old_timestamp,old_minor_edit,old_flags " .
+ "FROM old WHERE old_namespace={$ns} AND old_title='{$t}'";
+ wfQuery( $sql, $fname );
+
+ # Now that it's safely backed up, delete it
+
+ $sql = "DELETE FROM cur WHERE cur_namespace={$ns} AND " .
+ "cur_title='{$t}'";
+ wfQuery( $sql, $fname );
+
+ $sql = "DELETE FROM old WHERE old_namespace={$ns} AND " .
+ "old_title='{$t}'";
+ wfQuery( $sql, $fname );
+
+ $sql = "DELETE FROM recentchanges WHERE rc_namespace={$ns} AND " .
+ "rc_title='{$t}'";
+ wfQuery( $sql, $fname );
+
+ # Finally, clean up the link tables
+
+ if ( 0 != $id ) {
+ $t = wfStrencode( $title->getPrefixedDBkey() );
+ $sql = "SELECT l_from FROM links WHERE l_to={$id}";
+ $res = wfQuery( $sql, $fname );
+
+ $sql = "INSERT INTO brokenlinks (bl_from,bl_to) VALUES ";
+ $now = wfTimestampNow();
+ $sql2 = "UPDATE cur SET cur_touched='{$now}' WHERE cur_id IN (";
+ $first = true;
+
+ while ( $s = wfFetchObject( $res ) ) {
+ $nt = Title::newFromDBkey( $s->l_from );
+ $lid = $nt->getArticleID();
+
+ if ( ! $first ) { $sql .= ","; $sql2 .= ","; }
+ $first = false;
+ $sql .= "({$lid},'{$t}')";
+ $sql2 .= "{$lid}";
+ }
+ $sql2 .= ")";
+ if ( ! $first ) {
+ wfQuery( $sql, $fname );
+ wfQuery( $sql2, $fname );
+ }
+ wfFreeResult( $res );
+
+ $sql = "DELETE FROM links WHERE l_to={$id}";
+ wfQuery( $sql, $fname );
+
+ $sql = "DELETE FROM links WHERE l_from='{$t}'";
+ wfQuery( $sql, $fname );
+
+ $sql = "DELETE FROM imagelinks WHERE il_from='{$t}'";
+ wfQuery( $sql, $fname );
+
+ $sql = "DELETE FROM brokenlinks WHERE bl_from={$id}";
+ wfQuery( $sql, $fname );
+ }
+
+ $log = new LogPage( wfMsg( "dellogpage" ), wfMsg( "dellogpagetext" ) );
+ $art = $title->getPrefixedText();
+ $wpReason = wfCleanQueryVar( $wpReason );
+ $log->addEntry( str_replace( "$1", $art, wfMsg( "deletedarticle" ) ), $wpReason );
+
+ # Clear the cached article id so the interface doesn't act like we exist
+ $wgTitle->resetArticleID( 0 );
+ $wgTitle->mArticleID = 0;
+ }
+
+ function revert()
+ {
+ global $wgOut;
+ global $oldimage;
+
+ if ( strlen( $oldimage ) < 16 ) {
+ $wgOut->unexpectedValueError( "oldimage", $oldimage );
+ return;
+ }
+ if ( wfReadOnly() ) {
+ $wgOut->readOnlyPage();
+ return;
+ }
+ $name = substr( $oldimage, 15 );
+
+ $dest = wfImageDir( $name );
+ $archive = wfImageArchiveDir( $name );
+ $curfile = "{$dest}/{$name}";
+
+ if ( ! is_file( $curfile ) ) {
+ $wgOut->fileNotFoundError( $curfile );
+ return;
+ }
+ $oldver = wfTimestampNow() . "!{$name}";
+ $size = wfGetSQL( "oldimage", "oi_size", "oi_archive_name='" .
+ wfStrencode( $oldimage ) . "'" );
+
+ if ( ! rename( $curfile, "${archive}/{$oldver}" ) ) {
+ $wgOut->fileRenameError( $curfile, "${archive}/{$oldver}" );
+ return;
+ }
+ if ( ! copy( "{$archive}/{$oldimage}", $curfile ) ) {
+ $wgOut->fileCopyError( "${archive}/{$oldimage}", $curfile );
+ }
+ wfRecordUpload( $name, $oldver, $size, wfMsg( "reverted" ) );
+
+ $wgOut->setPagetitle( wfMsg( "actioncomplete" ) );
+ $wgOut->setRobotpolicy( "noindex,nofollow" );
+ $wgOut->addHTML( wfMsg( "imagereverted" ) );
+ $wgOut->returnToMain( false );
+ }
+
+ function rollback()
+ {
+ global $wgUser, $wgTitle, $wgLang, $wgOut;
+
+ if ( ! $wgUser->isSysop() ) {
+ $wgOut->sysopRequired();
+ return;
+ }
+
+ # Replace all this user's current edits with the next one down
+ $tt = wfStrencode( $wgTitle->getDBKey() );
+ $n = $wgTitle->getNamespace();
+
+ # Get the last editor
+ $sql = "SELECT cur_id,cur_user,cur_user_text FROM cur WHERE cur_title='{$tt}' AND cur_namespace={$n}";
+ $res = wfQuery( $sql );
+ if( ($x = wfNumRows( $res )) != 1 ) {
+ # Something wrong
+ $wgOut->addHTML( wfMsg( "notanarticle" ) );
+ return;
+ }
+ $s = wfFetchObject( $res );
+ $ut = wfStrencode( $s->cur_user_text );
+ $uid = $s->cur_user;
+ $pid = $s->cur_id;
+
+ # Get the last edit not by this guy
+ $sql = "SELECT old_text,old_user,old_user_text
+ FROM old USE INDEX (name_title_timestamp)
+ WHERE old_namespace={$n} AND old_title='{$tt}'
+ AND (old_user <> {$uid} OR old_user_text <> '{$ut}')
+ ORDER BY inverse_timestamp LIMIT 1";
+ $res = wfQuery( $sql );
+ if( wfNumRows( $res ) != 1 ) {
+ # Something wrong
+ $wgOut->addHTML( wfMsg( "cantrollback" ) );
+ return;
+ }
+ $s = wfFetchObject( $res );
+
+ # Save it!
+ $newcomment = str_replace( "$1", $s->old_user_text, wfMsg( "revertpage" ) );
+ $wgOut->setPagetitle( wfMsg( "actioncomplete" ) );
+ $wgOut->setRobotpolicy( "noindex,nofollow" );
+ $wgOut->addHTML( "<h2>" . $newcomment . "</h2>\n<hr>\n" );
+ $this->updateArticle( $s->old_text, $newcomment, 1, $wgTitle->userIsWatching() );
+
+ $wgOut->returnToMain( false );
+ }
+
+
+ # Do standard deferred updates after page view
+
+ /* private */ function viewUpdates()
+ {
+ global $wgDeferredUpdateList, $wgTitle;
+
+ if ( 0 != $this->getID() ) {
+ $u = new ViewCountUpdate( $this->getID() );
+ array_push( $wgDeferredUpdateList, $u );
+ $u = new SiteStatsUpdate( 1, 0, 0 );
+ array_push( $wgDeferredUpdateList, $u );
+
+ $u = new UserTalkUpdate( 0, $wgTitle->getNamespace(),
+ $wgTitle->getDBkey() );
+ array_push( $wgDeferredUpdateList, $u );
+ }
+ }
+
+ # Do standard deferred updates after page edit.
+ # Every 1000th edit, prune the recent changes table.
+
+ /* private */ function editUpdates( $text )
+ {
+ global $wgDeferredUpdateList, $wgTitle;
+
+ wfSeedRandom();
+ if ( 0 == mt_rand( 0, 999 ) ) {
+ $cutoff = wfUnix2Timestamp( time() - ( 7 * 86400 ) );
+ $sql = "DELETE FROM recentchanges WHERE rc_timestamp < '{$cutoff}'";
+ wfQuery( $sql );
+ }
+ $id = $this->getID();
+ $title = $wgTitle->getPrefixedDBkey();
+ $adj = $this->mCountAdjustment;
+
+ if ( 0 != $id ) {
+ $u = new LinksUpdate( $id, $title );
+ array_push( $wgDeferredUpdateList, $u );
+ $u = new SiteStatsUpdate( 0, 1, $adj );
+ array_push( $wgDeferredUpdateList, $u );
+ $u = new SearchUpdate( $id, $title, $text );
+ array_push( $wgDeferredUpdateList, $u );
+
+ $u = new UserTalkUpdate( 1, $wgTitle->getNamespace(),
+ $wgTitle->getDBkey() );
+ array_push( $wgDeferredUpdateList, $u );
+ }
+ }
+
+ /* private */ function setOldSubtitle()
+ {
+ global $wgLang, $wgOut;
+
+ $td = $wgLang->timeanddate( $this->mTimestamp, true );
+ $r = str_replace( "$1", "{$td}", wfMsg( "revisionasof" ) );
+ $wgOut->setSubtitle( "({$r})" );
+ }
+
+ function blockedIPpage()
+ {
+ global $wgOut, $wgUser, $wgLang;
+
+ $wgOut->setPageTitle( wfMsg( "blockedtitle" ) );
+ $wgOut->setRobotpolicy( "noindex,nofollow" );
+ $wgOut->setArticleFlag( false );
+
+ $id = $wgUser->blockedBy();
+ $reason = $wgUser->blockedFor();
+
+ $name = User::whoIs( $id );
+ $link = "[[" . $wgLang->getNsText( Namespace::getUser() ) .
+ ":{$name}|{$name}]]";
+
+ $text = str_replace( "$1", $link, wfMsg( "blockedtext" ) );
+ $text = str_replace( "$2", $reason, $text );
+ $wgOut->addWikiText( $text );
+ $wgOut->returnToMain( false );
+ }
+
+ # This function is called right before saving the wikitext,
+ # so we can do things like signatures and links-in-context.
+
+ function preSaveTransform( $text )
+ {
+ $s = "";
+ while ( "" != $text ) {
+ $p = preg_split( "/<\\s*nowiki\\s*>/i", $text, 2 );
+ $s .= $this->pstPass2( $p[0] );
+
+ if ( ( count( $p ) < 2 ) || ( "" == $p[1] ) ) { $text = ""; }
+ else {
+ $q = preg_split( "/<\\/\\s*nowiki\\s*>/i", $p[1], 2 );
+ $s .= "<nowiki>{$q[0]}</nowiki>";
+ $text = $q[1];
+ }
+ }
+ return rtrim( $s );
+ }
+
+ /* private */ function pstPass2( $text )
+ {
+ global $wgUser, $wgLang, $wgTitle, $wgLocaltimezone;
+
+ # Signatures
+ #
+ $n = $wgUser->getName();
+ $k = $wgUser->getOption( "nickname" );
+ if ( "" == $k ) { $k = $n; }
+ if(isset($wgLocaltimezone)) {
+ $oldtz = getenv("TZ"); putenv("TZ=$wgLocaltimezone");
+ }
+ $d = $wgLang->timeanddate( wfTimestampNow(), false ) .
+ " (" . date( "T" ) . ")";
+ if(isset($wgLocaltimezone)) putenv("TZ=$oldtz");
+
+ $text = preg_replace( "/~~~~/", "[[" . $wgLang->getNsText(
+ Namespace::getUser() ) . ":$n|$k]] $d", $text );
+ $text = preg_replace( "/~~~/", "[[" . $wgLang->getNsText(
+ Namespace::getUser() ) . ":$n|$k]]", $text );
+
+ # Context links: [[|name]] and [[name (context)|]]
+ #
+ $tc = "[&;%\\-,.\\(\\)' _0-9A-Za-z\\/:\\x80-\\xff]";
+ $np = "[&;%\\-,.' _0-9A-Za-z\\/:\\x80-\\xff]"; # No parens
+ $conpat = "/^({$np}+) \\(({$tc}+)\\)$/";
+
+ $p1 = "/\[\[({$np}+) \\(({$np}+)\\)\\|]]/"; # [[page (context)|]]
+ $p2 = "/\[\[\\|({$tc}+)]]/"; # [[|page]]
+ $p3 = "/\[\[([A-Za-z _]+):({$np}+)\\|]]/"; # [[namespace:page|]]
+ $p4 = "/\[\[([A-Aa-z _]+):({$np}+) \\(({$np}+)\\)\\|]]/";
+ # [[ns:page (cont)|]]
+ $context = "";
+ $t = $wgTitle->getText();
+ if ( preg_match( $conpat, $t, $m ) ) {
+ $context = $m[2];
+ }
+ $text = preg_replace( $p4, "[[\\1:\\2 (\\3)|\\2]]", $text );
+ $text = preg_replace( $p1, "[[\\1 (\\2)|\\1]]", $text );
+ $text = preg_replace( $p3, "[[\\1:\\2|\\2]]", $text );
+
+ if ( "" == $context ) {
+ $text = preg_replace( $p2, "[[\\1]]", $text );
+ } else {
+ $text = preg_replace( $p2, "[[\\1 ({$context})|\\1]]", $text );
+ }
+ # Replace local image links with new [[image:]] style
+
+ $text = preg_replace(
+ "/(^|[^[])http:\/\/(www.|)wikipedia.com\/upload\/" .
+ "([a-zA-Z0-9_:.~\%\-]+)\.(png|PNG|jpg|JPG|jpeg|JPEG|gif|GIF)/",
+ "\\1[[image:\\3.\\4]]", $text );
+ $text = preg_replace(
+ "/(^|[^[])http:\/\/(www.|)wikipedia.com\/images\/uploads\/" .
+ "([a-zA-Z0-9_:.~\%\-]+)\.(png|PNG|jpg|JPG|jpeg|JPEG|gif|GIF)/",
+ "\\1[[image:\\3.\\4]]", $text );
+
+ return $text;
+ }
+}
+
+?>
diff --git a/includes/DatabaseFunctions.php b/includes/DatabaseFunctions.php
new file mode 100644
index 000000000000..3d930472e0ff
--- /dev/null
+++ b/includes/DatabaseFunctions.php
@@ -0,0 +1,134 @@
+<?
+global $IP;
+include_once( "$IP/FulltextStoplist.php" );
+
+$wgLastDatabaseQuery = "";
+
+function wfGetDB( $altuser = "", $altpassword = "" )
+{
+ global $wgDBserver, $wgDBuser, $wgDBpassword;
+ global $wgDBname, $wgDBconnection, $wgEmergencyContact;
+
+ $noconn = str_replace( "$1", $wgDBserver, wfMsg( "noconnect" ) );
+ $nodb = str_replace( "$1", $wgDBname, wfMsg( "nodb" ) );
+
+ $helpme = "\n<p>If this error persists after reloading and clearing " .
+ "your browser cache, please notify the <a href=\"mailto:" .
+ $wgEmergencyContact . "\">Wikipedia developers</a>.</p>";
+
+ if ( $altuser != "" ) {
+ $wgDBconnection = mysql_connect( $wgDBserver, $altuser, $altpassword )
+ or die( "bad sql user" );
+ mysql_select_db( $wgDBname, $wgDBconnection ) or die(
+ htmlspecialchars(mysql_error()) );
+ }
+
+ if ( ! $wgDBconnection ) {
+ $wgDBconnection = mysql_pconnect( $wgDBserver, $wgDBuser,
+ $wgDBpassword ) or die( $noconn .
+ "\n<p><b>" . htmlspecialchars(mysql_error()) . "</b></p>\n" . $helpme );
+ if( !mysql_select_db( $wgDBname, $wgDBconnection ) ) {
+ wfDebug( "Persistent connection is broken?\n", true );
+
+ $wgDBconnection = mysql_connect( $wgDBserver, $wgDBuser,
+ $wgDBpassword ) or die( $noconn .
+ "\n<p><b>" . htmlspecialchars(mysql_error()) . "</b> (tried non-p connect)</p>\n" . $helpme );
+ mysql_select_db( $wgDBname, $wgDBconnection ) or die( $nodb .
+ "\n<p><b>" . htmlspecialchars(mysql_error()) . "</b> (tried non-p connect)</p>\n" . $helpme );
+ }
+ }
+ # mysql_ping( $wgDBconnection );
+ return $wgDBconnection;
+}
+
+function wfQuery( $sql, $fname = "" )
+{
+ global $wgLastDatabaseQuery, $wgOut;
+## wfProfileIn( "wfQuery" );
+ $wgLastDatabaseQuery = $sql;
+
+ $conn = wfGetDB();
+ $ret = mysql_query( $sql, $conn );
+
+ if ( "" != $fname ) {
+# wfDebug( "{$fname}:SQL: {$sql}\n", true );
+ } else {
+# wfDebug( "SQL: {$sql}\n", true );
+ }
+ if ( false === $ret ) {
+ $wgOut->databaseError( $fname );
+ exit;
+ }
+## wfProfileOut();
+ return $ret;
+}
+
+function wfFreeResult( $res ) { mysql_free_result( $res ); }
+function wfFetchObject( $res ) { return mysql_fetch_object( $res ); }
+function wfNumRows( $res ) { return mysql_num_rows( $res ); }
+function wfNumFields( $res ) { return mysql_num_fields( $res ); }
+function wfFieldName( $res, $n ) { return mysql_field_name( $res, $n ); }
+function wfInsertId() { return mysql_insert_id( wfGetDB() ); }
+function wfDataSeek( $res, $row ) { return mysql_data_seek( $res, $row ); }
+function wfLastErrno() { return mysql_errno(); }
+function wfLastError() { return mysql_error(); }
+
+function wfLastDBquery()
+{
+ global $wgLastDatabaseQuery;
+ return $wgLastDatabaseQuery;
+}
+
+function wfSetSQL( $table, $var, $value, $cond )
+{
+ $sql = "UPDATE $table SET $var = '" .
+ wfStrencode( $value ) . "' WHERE ($cond)";
+ wfQuery( $sql, "wfSetSQL" );
+}
+
+function wfGetSQL( $table, $var, $cond )
+{
+ $sql = "SELECT $var FROM $table WHERE ($cond)";
+ $result = wfQuery( $sql, "wfGetSQL" );
+
+ $ret = "";
+ if ( mysql_num_rows( $result ) > 0 ) {
+ $s = mysql_fetch_object( $result );
+ $ret = $s->$var;
+ mysql_free_result( $result );
+ }
+ return $ret;
+}
+
+function wfStrencode( $s )
+{
+ return addslashes( $s );
+}
+
+# Ideally we'd be using actual time fields in the db
+function wfTimestamp2Unix( $ts ) {
+ return mktime( ( (int)substr( $ts, 8, 2) ),
+ (int)substr( $ts, 10, 2 ), (int)substr( $ts, 12, 2 ),
+ (int)substr( $ts, 4, 2 ), (int)substr( $ts, 6, 2 ),
+ (int)substr( $ts, 0, 4 ) );
+}
+
+function wfUnix2Timestamp( $unixtime ) {
+ return date( "YmdHis", $unixtime );
+}
+
+function wfTimestampNow() {
+ # return NOW
+ return date( "YmdHis" );
+}
+
+# Sorting hack for MySQL 3, which doesn't use index sorts for DESC
+function wfInvertTimestamp( $ts ) {
+ return strtr(
+ $ts,
+ "0123456789",
+ "9876543210"
+ );
+}
+
+?>
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
new file mode 100644
index 000000000000..77be904f3a1f
--- /dev/null
+++ b/includes/DefaultSettings.php
@@ -0,0 +1,66 @@
+<?
+# DO NOT EDIT THIS FILE!
+# To customize your installation, edit "LocalSettings.php".
+
+$wgServer = "http://" . getenv( "SERVER_NAME" );
+$wgScriptPath = "/wiki";
+$wgScript = "{$wgScriptPath}/wiki.phtml";
+$wgRedirectScript = "{$wgScriptPath}/redirect.phtml";
+$wgStyleSheetPath = "{$wgScriptPath}/style";
+$wgStyleSheetDirectory = "{$IP}/style";
+$wgArticlePath = "{$wgScript}?title=$1";
+$wgUploadPath = "{$wgScriptPath}/upload";
+$wgUploadDirectory = "{$IP}/upload";
+$wgLogo = "{$wgUploadPath}/wiki.png";
+$wgMathPath = "{$wgUploadPath}/math";
+$wgMathDirectory = "{$wgUploadDirectory}/math";
+$wgTmpDirectory = "{$wgUploadDirectory}/tmp";
+$wgEmergencyContact = "wikiadmin@" . getenv( "SERVER_NAME" );
+
+# MySQL settings
+#
+$wgDBserver = "127.0.0.1";
+$wgDBname = "wikidb";
+$wgDBintlname = "intl";
+$wgDBconnection = "";
+$wgDBuser = "wikiuser";
+$wgDBpassword = "userpass";
+$wgDBsqluser = "sqluser";
+$wgDBsqlpassword = "sqlpass";
+$wgDBminWordLen = 4;
+$wgDBtransactions = false; # Set to true if using InnoDB tables
+
+# Language settings
+#
+$wgLanguageCode = "en";
+$wgInterwikiMagic = true; # Treat language links as magic connectors, not inline links
+$wgUseNewInterlanguage = false;
+$wgInputEncoding = "ISO-8859-1";
+$wgOutputEncoding = "ISO-8859-1";
+$wgEditEncoding = "";
+$wgDocType = "-//W3C//DTD HTML 4.01 Transitional//EN";
+$wgAmericanDates = false; # Enable for English module to print dates
+$wgLocalInterwiki = "w";
+
+# Miscellaneous configuration settings
+#
+$wgReadOnlyFile = "{$wgUploadDirectory}/lock_yBgMBwiR";
+$wgDebugLogFile = "{$wgUploadDirectory}/log_dlJbnMZb";
+$wgDebugComments = false;
+$wgReadOnly = false;
+
+$wgCachePages = true; # Allow client-side caching of pages
+$wgCookieExpiration = 2592000;
+
+$wgAllowExternalImages = true;
+$wgMiserMode = false; # Disable database-intensive features
+$wgUseTeX = false;
+$wgProfiling = false; # Enable for more detailed by-function times in debug log
+
+# Which namespaces should support subpages?
+# See Language.php for a list of namespaces.
+#
+$wgNamespacesWithSubpages = array( -1 => 0, 0 => 0, 1 => 1,
+ 2 => 1, 3 => 1, 4 => 0, 5 => 1, 6 => 0, 7 => 1 );
+
+?>
diff --git a/includes/DifferenceEngine.php b/includes/DifferenceEngine.php
new file mode 100644
index 000000000000..835ac74f03c5
--- /dev/null
+++ b/includes/DifferenceEngine.php
@@ -0,0 +1,1138 @@
+<?
+# See diff.doc
+
+class DifferenceEngine {
+ /* private */ var $mOldid, $mNewid;
+ /* private */ var $mOldtitle, $mNewtitle;
+ /* private */ var $mOldtext, $mNewtext;
+
+ function DifferenceEngine( $old, $new )
+ {
+ $this->mOldid = $old;
+ $this->mNewid = $new;
+ }
+
+ function showDiffPage()
+ {
+ global $wgUser, $wgTitle, $wgOut, $wgLang;
+
+ $t = $wgTitle->getPrefixedText() . " (Diff: {$this->mOldid}, " .
+ "{$this->mNewid})";
+ $mtext = str_replace( "$1", $t, wfMsg( "missingarticle" ) );
+
+ $wgOut->setArticleFlag( false );
+ if ( ! $this->loadText() ) {
+ $wgOut->setPagetitle( wfMsg( "errorpagetitle" ) );
+ $wgOut->addHTML( $mtext );
+ return;
+ }
+ $wgOut->supressQuickbar();
+ $wgOut->setSubtitle( wfMsg( "difference" ) );
+ $wgOut->setRobotpolicy( "noindex,follow" );
+
+ DifferenceEngine::showDiff( $this->mOldtext, $this->mNewtext,
+ $this->mOldtitle, $this->mNewtitle );
+ $wgOut->addHTML( "<hr><h2>{$this->mNewtitle}</h2>\n" );
+ $wgOut->addWikiText( $this->mNewtext );
+ }
+
+ function showDiff( $otext, $ntext, $otitle, $ntitle )
+ {
+ global $wgOut;
+
+ $ota = explode( "\n", str_replace( "\r\n", "\n",
+ htmlspecialchars( $otext ) ) );
+ $nta = explode( "\n", str_replace( "\r\n", "\n",
+ htmlspecialchars( $ntext ) ) );
+
+ $wgOut->addHTML( "<table width='98%' border=0
+cellpadding=0 cellspacing='4px'><tr>
+<td colspan=2 width='50%' align=center bgcolor='#cccccc'>
+<strong>{$otitle}</strong></td>
+<td colspan=2 width='50%' align=center bgcolor='#cccccc'>
+<strong>{$ntitle}</strong></td>
+</tr>\n" );
+
+ $diffs = new Diff( $ota, $nta );
+ $formatter = new TableDiffFormatter();
+ $formatter->format( $diffs );
+ $wgOut->addHTML( "</table>\n" );
+ }
+
+ # Load the text of the articles to compare. If newid is 0, then compare
+ # the old article in oldid to the current article; if oldid is 0, then
+ # compare the current article to the immediately previous one (ignoring
+ # the value of newid).
+ #
+ function loadText()
+ {
+ global $wgTitle, $wgOut, $wgLang;
+ $fname = "DifferenceEngine::loadText";
+
+ if ( 0 == $this->mNewid || 0 == $this->mOldid ) {
+ $wgOut->setArticleFlag( true );
+ $this->mNewtitle = wfMsg( "currentrev" );
+ $id = $wgTitle->getArticleID();
+
+ $sql = "SELECT cur_text FROM cur WHERE cur_id={$id}";
+ $res = wfQuery( $sql, $fname );
+ if ( 0 == wfNumRows( $res ) ) { return false; }
+
+ $s = wfFetchObject( $res );
+ $this->mNewtext = $s->cur_text;
+ } else {
+ $sql = "SELECT old_timestamp,old_text FROM old WHERE " .
+ "old_id={$this->mNewid}";
+
+ $res = wfQuery( $sql, $fname );
+ if ( 0 == wfNumRows( $res ) ) { return false; }
+
+ $s = wfFetchObject( $res );
+ $this->mNewtext = $s->old_text;
+
+ $t = $wgLang->timeanddate( $s->old_timestamp, true );
+ $this->mNewtitle = str_replace( "$1", "{$t}",
+ wfMsg( "revisionasof" ) );
+ }
+ if ( 0 == $this->mOldid ) {
+ $sql = "SELECT old_timestamp,old_text FROM old USE INDEX (name_title_timestamp) WHERE " .
+ "old_namespace=" . $wgTitle->getNamespace() . " AND " .
+ "old_title='" . wfStrencode( $wgTitle->getDBkey() ) .
+ "' ORDER BY inverse_timestamp LIMIT 1";
+ $res = wfQuery( $sql, $fname );
+ } else {
+ $sql = "SELECT old_timestamp,old_text FROM old WHERE " .
+ "old_id={$this->mOldid}";
+ $res = wfQuery( $sql, $fname );
+ }
+ if ( 0 == wfNumRows( $res ) ) { return false; }
+
+ $s = wfFetchObject( $res );
+ $this->mOldtext = $s->old_text;
+
+ $t = $wgLang->timeanddate( $s->old_timestamp, true );
+ $this->mOldtitle = str_replace( "$1", "{$t}",
+ wfMsg( "revisionasof" ) );
+
+ return true;
+ }
+}
+
+// A PHP diff engine for phpwiki. (Taken from phpwiki-1.3.3)
+//
+// Copyright (C) 2000, 2001 Geoffrey T. Dairiki <dairiki@dairiki.org>
+// You may copy this code freely under the conditions of the GPL.
+//
+
+define('USE_ASSERTS', function_exists('assert'));
+
+class _DiffOp {
+ var $type;
+ var $orig;
+ var $final;
+
+ function reverse() {
+ trigger_error("pure virtual", E_USER_ERROR);
+ }
+
+ function norig() {
+ return $this->orig ? sizeof($this->orig) : 0;
+ }
+
+ function nfinal() {
+ return $this->final ? sizeof($this->final) : 0;
+ }
+}
+
+class _DiffOp_Copy extends _DiffOp {
+ var $type = 'copy';
+
+ function _DiffOp_Copy ($orig, $final = false) {
+ if (!is_array($final))
+ $final = $orig;
+ $this->orig = $orig;
+ $this->final = $final;
+ }
+
+ function reverse() {
+ return new _DiffOp_Copy($this->final, $this->orig);
+ }
+}
+
+class _DiffOp_Delete extends _DiffOp {
+ var $type = 'delete';
+
+ function _DiffOp_Delete ($lines) {
+ $this->orig = $lines;
+ $this->final = false;
+ }
+
+ function reverse() {
+ return new _DiffOp_Add($this->orig);
+ }
+}
+
+class _DiffOp_Add extends _DiffOp {
+ var $type = 'add';
+
+ function _DiffOp_Add ($lines) {
+ $this->final = $lines;
+ $this->orig = false;
+ }
+
+ function reverse() {
+ return new _DiffOp_Delete($this->final);
+ }
+}
+
+class _DiffOp_Change extends _DiffOp {
+ var $type = 'change';
+
+ function _DiffOp_Change ($orig, $final) {
+ $this->orig = $orig;
+ $this->final = $final;
+ }
+
+ function reverse() {
+ return new _DiffOp_Change($this->final, $this->orig);
+ }
+}
+
+
+/**
+ * Class used internally by Diff to actually compute the diffs.
+ *
+ * The algorithm used here is mostly lifted from the perl module
+ * Algorithm::Diff (version 1.06) by Ned Konz, which is available at:
+ * http://www.perl.com/CPAN/authors/id/N/NE/NEDKONZ/Algorithm-Diff-1.06.zip
+ *
+ * More ideas are taken from:
+ * http://www.ics.uci.edu/~eppstein/161/960229.html
+ *
+ * Some ideas are (and a bit of code) are from from analyze.c, from GNU
+ * diffutils-2.7, which can be found at:
+ * ftp://gnudist.gnu.org/pub/gnu/diffutils/diffutils-2.7.tar.gz
+ *
+ * Finally, some ideas (subdivision by NCHUNKS > 2, and some optimizations)
+ * are my own.
+ *
+ * @author Geoffrey T. Dairiki
+ * @access private
+ */
+class _DiffEngine
+{
+ function diff ($from_lines, $to_lines) {
+ $n_from = sizeof($from_lines);
+ $n_to = sizeof($to_lines);
+
+ $this->xchanged = $this->ychanged = array();
+ $this->xv = $this->yv = array();
+ $this->xind = $this->yind = array();
+ unset($this->seq);
+ unset($this->in_seq);
+ unset($this->lcs);
+
+ // Skip leading common lines.
+ for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) {
+ if ($from_lines[$skip] != $to_lines[$skip])
+ break;
+ $this->xchanged[$skip] = $this->ychanged[$skip] = false;
+ }
+ // Skip trailing common lines.
+ $xi = $n_from; $yi = $n_to;
+ for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) {
+ if ($from_lines[$xi] != $to_lines[$yi])
+ break;
+ $this->xchanged[$xi] = $this->ychanged[$yi] = false;
+ }
+
+ // Ignore lines which do not exist in both files.
+ for ($xi = $skip; $xi < $n_from - $endskip; $xi++)
+ $xhash[$from_lines[$xi]] = 1;
+ for ($yi = $skip; $yi < $n_to - $endskip; $yi++) {
+ $line = $to_lines[$yi];
+ if ( ($this->ychanged[$yi] = empty($xhash[$line])) )
+ continue;
+ $yhash[$line] = 1;
+ $this->yv[] = $line;
+ $this->yind[] = $yi;
+ }
+ for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
+ $line = $from_lines[$xi];
+ if ( ($this->xchanged[$xi] = empty($yhash[$line])) )
+ continue;
+ $this->xv[] = $line;
+ $this->xind[] = $xi;
+ }
+
+ // Find the LCS.
+ $this->_compareseq(0, sizeof($this->xv), 0, sizeof($this->yv));
+
+ // Merge edits when possible
+ $this->_shift_boundaries($from_lines, $this->xchanged, $this->ychanged);
+ $this->_shift_boundaries($to_lines, $this->ychanged, $this->xchanged);
+
+ // Compute the edit operations.
+ $edits = array();
+ $xi = $yi = 0;
+ while ($xi < $n_from || $yi < $n_to) {
+ USE_ASSERTS && assert($yi < $n_to || $this->xchanged[$xi]);
+ USE_ASSERTS && assert($xi < $n_from || $this->ychanged[$yi]);
+
+ // Skip matching "snake".
+ $copy = array();
+ while ( $xi < $n_from && $yi < $n_to
+ && !$this->xchanged[$xi] && !$this->ychanged[$yi]) {
+ $copy[] = $from_lines[$xi++];
+ ++$yi;
+ }
+ if ($copy)
+ $edits[] = new _DiffOp_Copy($copy);
+
+ // Find deletes & adds.
+ $delete = array();
+ while ($xi < $n_from && $this->xchanged[$xi])
+ $delete[] = $from_lines[$xi++];
+
+ $add = array();
+ while ($yi < $n_to && $this->ychanged[$yi])
+ $add[] = $to_lines[$yi++];
+
+ if ($delete && $add)
+ $edits[] = new _DiffOp_Change($delete, $add);
+ elseif ($delete)
+ $edits[] = new _DiffOp_Delete($delete);
+ elseif ($add)
+ $edits[] = new _DiffOp_Add($add);
+ }
+ return $edits;
+ }
+
+
+ /* Divide the Largest Common Subsequence (LCS) of the sequences
+ * [XOFF, XLIM) and [YOFF, YLIM) into NCHUNKS approximately equally
+ * sized segments.
+ *
+ * Returns (LCS, PTS). LCS is the length of the LCS. PTS is an
+ * array of NCHUNKS+1 (X, Y) indexes giving the diving points between
+ * sub sequences. The first sub-sequence is contained in [X0, X1),
+ * [Y0, Y1), the second in [X1, X2), [Y1, Y2) and so on. Note
+ * that (X0, Y0) == (XOFF, YOFF) and
+ * (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM).
+ *
+ * This function assumes that the first lines of the specified portions
+ * of the two files do not match, and likewise that the last lines do not
+ * match. The caller must trim matching lines from the beginning and end
+ * of the portions it is going to specify.
+ */
+ function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks) {
+ $flip = false;
+
+ if ($xlim - $xoff > $ylim - $yoff) {
+ // Things seems faster (I'm not sure I understand why)
+ // when the shortest sequence in X.
+ $flip = true;
+ list ($xoff, $xlim, $yoff, $ylim)
+ = array( $yoff, $ylim, $xoff, $xlim);
+ }
+
+ if ($flip)
+ for ($i = $ylim - 1; $i >= $yoff; $i--)
+ $ymatches[$this->xv[$i]][] = $i;
+ else
+ for ($i = $ylim - 1; $i >= $yoff; $i--)
+ $ymatches[$this->yv[$i]][] = $i;
+
+ $this->lcs = 0;
+ $this->seq[0]= $yoff - 1;
+ $this->in_seq = array();
+ $ymids[0] = array();
+
+ $numer = $xlim - $xoff + $nchunks - 1;
+ $x = $xoff;
+ for ($chunk = 0; $chunk < $nchunks; $chunk++) {
+ if ($chunk > 0)
+ for ($i = 0; $i <= $this->lcs; $i++)
+ $ymids[$i][$chunk-1] = $this->seq[$i];
+
+ $x1 = $xoff + (int)(($numer + ($xlim-$xoff)*$chunk) / $nchunks);
+ for ( ; $x < $x1; $x++) {
+ $line = $flip ? $this->yv[$x] : $this->xv[$x];
+ if (empty($ymatches[$line]))
+ continue;
+ $matches = $ymatches[$line];
+ reset($matches);
+ while (list ($junk, $y) = each($matches))
+ if (empty($this->in_seq[$y])) {
+ $k = $this->_lcs_pos($y);
+ USE_ASSERTS && assert($k > 0);
+ $ymids[$k] = $ymids[$k-1];
+ break;
+ }
+ while (list ($junk, $y) = each($matches)) {
+ if ($y > $this->seq[$k-1]) {
+ USE_ASSERTS && assert($y < $this->seq[$k]);
+ // Optimization: this is a common case:
+ // next match is just replacing previous match.
+ $this->in_seq[$this->seq[$k]] = false;
+ $this->seq[$k] = $y;
+ $this->in_seq[$y] = 1;
+ }
+ else if (empty($this->in_seq[$y])) {
+ $k = $this->_lcs_pos($y);
+ USE_ASSERTS && assert($k > 0);
+ $ymids[$k] = $ymids[$k-1];
+ }
+ }
+ }
+ }
+
+ $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff);
+ $ymid = $ymids[$this->lcs];
+ for ($n = 0; $n < $nchunks - 1; $n++) {
+ $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks);
+ $y1 = $ymid[$n] + 1;
+ $seps[] = $flip ? array($y1, $x1) : array($x1, $y1);
+ }
+ $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim);
+
+ return array($this->lcs, $seps);
+ }
+
+ function _lcs_pos ($ypos) {
+ $end = $this->lcs;
+ if ($end == 0 || $ypos > $this->seq[$end]) {
+ $this->seq[++$this->lcs] = $ypos;
+ $this->in_seq[$ypos] = 1;
+ return $this->lcs;
+ }
+
+ $beg = 1;
+ while ($beg < $end) {
+ $mid = (int)(($beg + $end) / 2);
+ if ( $ypos > $this->seq[$mid] )
+ $beg = $mid + 1;
+ else
+ $end = $mid;
+ }
+
+ USE_ASSERTS && assert($ypos != $this->seq[$end]);
+
+ $this->in_seq[$this->seq[$end]] = false;
+ $this->seq[$end] = $ypos;
+ $this->in_seq[$ypos] = 1;
+ return $end;
+ }
+
+ /* Find LCS of two sequences.
+ *
+ * The results are recorded in the vectors $this->{x,y}changed[], by
+ * storing a 1 in the element for each line that is an insertion
+ * or deletion (ie. is not in the LCS).
+ *
+ * The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1.
+ *
+ * Note that XLIM, YLIM are exclusive bounds.
+ * All line numbers are origin-0 and discarded lines are not counted.
+ */
+ function _compareseq ($xoff, $xlim, $yoff, $ylim) {
+ // Slide down the bottom initial diagonal.
+ while ($xoff < $xlim && $yoff < $ylim
+ && $this->xv[$xoff] == $this->yv[$yoff]) {
+ ++$xoff;
+ ++$yoff;
+ }
+
+ // Slide up the top initial diagonal.
+ while ($xlim > $xoff && $ylim > $yoff
+ && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) {
+ --$xlim;
+ --$ylim;
+ }
+
+ if ($xoff == $xlim || $yoff == $ylim)
+ $lcs = 0;
+ else {
+ // This is ad hoc but seems to work well.
+ //$nchunks = sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5);
+ //$nchunks = max(2,min(8,(int)$nchunks));
+ $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1;
+ list ($lcs, $seps)
+ = $this->_diag($xoff,$xlim,$yoff, $ylim,$nchunks);
+ }
+
+ if ($lcs == 0) {
+ // X and Y sequences have no common subsequence:
+ // mark all changed.
+ while ($yoff < $ylim)
+ $this->ychanged[$this->yind[$yoff++]] = 1;
+ while ($xoff < $xlim)
+ $this->xchanged[$this->xind[$xoff++]] = 1;
+ }
+ else {
+ // Use the partitions to split this problem into subproblems.
+ reset($seps);
+ $pt1 = $seps[0];
+ while ($pt2 = next($seps)) {
+ $this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]);
+ $pt1 = $pt2;
+ }
+ }
+ }
+
+ /* Adjust inserts/deletes of identical lines to join changes
+ * as much as possible.
+ *
+ * We do something when a run of changed lines include a
+ * line at one end and has an excluded, identical line at the other.
+ * We are free to choose which identical line is included.
+ * `compareseq' usually chooses the one at the beginning,
+ * but usually it is cleaner to consider the following identical line
+ * to be the "change".
+ *
+ * This is extracted verbatim from analyze.c (GNU diffutils-2.7).
+ */
+ function _shift_boundaries ($lines, &$changed, $other_changed) {
+ $i = 0;
+ $j = 0;
+
+ USE_ASSERTS && assert('sizeof($lines) == sizeof($changed)');
+ $len = sizeof($lines);
+ $other_len = sizeof($other_changed);
+
+ while (1) {
+ /*
+ * Scan forwards to find beginning of another run of changes.
+ * Also keep track of the corresponding point in the other file.
+ *
+ * Throughout this code, $i and $j are adjusted together so that
+ * the first $i elements of $changed and the first $j elements
+ * of $other_changed both contain the same number of zeros
+ * (unchanged lines).
+ * Furthermore, $j is always kept so that $j == $other_len or
+ * $other_changed[$j] == false.
+ */
+ while ($j < $other_len && $other_changed[$j])
+ $j++;
+
+ while ($i < $len && ! $changed[$i]) {
+ USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]');
+ $i++; $j++;
+ while ($j < $other_len && $other_changed[$j])
+ $j++;
+ }
+
+ if ($i == $len)
+ break;
+
+ $start = $i;
+
+ // Find the end of this run of changes.
+ while (++$i < $len && $changed[$i])
+ continue;
+
+ do {
+ /*
+ * Record the length of this run of changes, so that
+ * we can later determine whether the run has grown.
+ */
+ $runlength = $i - $start;
+
+ /*
+ * Move the changed region back, so long as the
+ * previous unchanged line matches the last changed one.
+ * This merges with previous changed regions.
+ */
+ while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) {
+ $changed[--$start] = 1;
+ $changed[--$i] = false;
+ while ($start > 0 && $changed[$start - 1])
+ $start--;
+ USE_ASSERTS && assert('$j > 0');
+ while ($other_changed[--$j])
+ continue;
+ USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]');
+ }
+
+ /*
+ * Set CORRESPONDING to the end of the changed run, at the last
+ * point where it corresponds to a changed run in the other file.
+ * CORRESPONDING == LEN means no such point has been found.
+ */
+ $corresponding = $j < $other_len ? $i : $len;
+
+ /*
+ * Move the changed region forward, so long as the
+ * first changed line matches the following unchanged one.
+ * This merges with following changed regions.
+ * Do this second, so that if there are no merges,
+ * the changed region is moved forward as far as possible.
+ */
+ while ($i < $len && $lines[$start] == $lines[$i]) {
+ $changed[$start++] = false;
+ $changed[$i++] = 1;
+ while ($i < $len && $changed[$i])
+ $i++;
+
+ USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]');
+ $j++;
+ if ($j < $other_len && $other_changed[$j]) {
+ $corresponding = $i;
+ while ($j < $other_len && $other_changed[$j])
+ $j++;
+ }
+ }
+ } while ($runlength != $i - $start);
+
+ /*
+ * If possible, move the fully-merged run of changes
+ * back to a corresponding run in the other file.
+ */
+ while ($corresponding < $i) {
+ $changed[--$start] = 1;
+ $changed[--$i] = 0;
+ USE_ASSERTS && assert('$j > 0');
+ while ($other_changed[--$j])
+ continue;
+ USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]');
+ }
+ }
+ }
+}
+
+/**
+ * Class representing a 'diff' between two sequences of strings.
+ */
+class Diff
+{
+ var $edits;
+
+ /**
+ * Constructor.
+ * Computes diff between sequences of strings.
+ *
+ * @param $from_lines array An array of strings.
+ * (Typically these are lines from a file.)
+ * @param $to_lines array An array of strings.
+ */
+ function Diff($from_lines, $to_lines) {
+ $eng = new _DiffEngine;
+ $this->edits = $eng->diff($from_lines, $to_lines);
+ //$this->_check($from_lines, $to_lines);
+ }
+
+ /**
+ * Compute reversed Diff.
+ *
+ * SYNOPSIS:
+ *
+ * $diff = new Diff($lines1, $lines2);
+ * $rev = $diff->reverse();
+ * @return object A Diff object representing the inverse of the
+ * original diff.
+ */
+ function reverse () {
+ $rev = $this;
+ $rev->edits = array();
+ foreach ($this->edits as $edit) {
+ $rev->edits[] = $edit->reverse();
+ }
+ return $rev;
+ }
+
+ /**
+ * Check for empty diff.
+ *
+ * @return bool True iff two sequences were identical.
+ */
+ function isEmpty () {
+ foreach ($this->edits as $edit) {
+ if ($edit->type != 'copy')
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Compute the length of the Longest Common Subsequence (LCS).
+ *
+ * This is mostly for diagnostic purposed.
+ *
+ * @return int The length of the LCS.
+ */
+ function lcs () {
+ $lcs = 0;
+ foreach ($this->edits as $edit) {
+ if ($edit->type == 'copy')
+ $lcs += sizeof($edit->orig);
+ }
+ return $lcs;
+ }
+
+ /**
+ * Get the original set of lines.
+ *
+ * This reconstructs the $from_lines parameter passed to the
+ * constructor.
+ *
+ * @return array The original sequence of strings.
+ */
+ function orig() {
+ $lines = array();
+
+ foreach ($this->edits as $edit) {
+ if ($edit->orig)
+ array_splice($lines, sizeof($lines), 0, $edit->orig);
+ }
+ return $lines;
+ }
+
+ /**
+ * Get the final set of lines.
+ *
+ * This reconstructs the $to_lines parameter passed to the
+ * constructor.
+ *
+ * @return array The sequence of strings.
+ */
+ function final() {
+ $lines = array();
+
+ foreach ($this->edits as $edit) {
+ if ($edit->final)
+ array_splice($lines, sizeof($lines), 0, $edit->final);
+ }
+ return $lines;
+ }
+
+ /**
+ * Check a Diff for validity.
+ *
+ * This is here only for debugging purposes.
+ */
+ function _check ($from_lines, $to_lines) {
+ if (serialize($from_lines) != serialize($this->orig()))
+ trigger_error("Reconstructed original doesn't match", E_USER_ERROR);
+ if (serialize($to_lines) != serialize($this->final()))
+ trigger_error("Reconstructed final doesn't match", E_USER_ERROR);
+
+ $rev = $this->reverse();
+ if (serialize($to_lines) != serialize($rev->orig()))
+ trigger_error("Reversed original doesn't match", E_USER_ERROR);
+ if (serialize($from_lines) != serialize($rev->final()))
+ trigger_error("Reversed final doesn't match", E_USER_ERROR);
+
+
+ $prevtype = 'none';
+ foreach ($this->edits as $edit) {
+ if ( $prevtype == $edit->type )
+ trigger_error("Edit sequence is non-optimal", E_USER_ERROR);
+ $prevtype = $edit->type;
+ }
+
+ $lcs = $this->lcs();
+ trigger_error("Diff okay: LCS = $lcs", E_USER_NOTICE);
+ }
+}
+
+/**
+ * FIXME: bad name.
+ */
+class MappedDiff
+extends Diff
+{
+ /**
+ * Constructor.
+ *
+ * Computes diff between sequences of strings.
+ *
+ * This can be used to compute things like
+ * case-insensitve diffs, or diffs which ignore
+ * changes in white-space.
+ *
+ * @param $from_lines array An array of strings.
+ * (Typically these are lines from a file.)
+ *
+ * @param $to_lines array An array of strings.
+ *
+ * @param $mapped_from_lines array This array should
+ * have the same size number of elements as $from_lines.
+ * The elements in $mapped_from_lines and
+ * $mapped_to_lines are what is actually compared
+ * when computing the diff.
+ *
+ * @param $mapped_to_lines array This array should
+ * have the same number of elements as $to_lines.
+ */
+ function MappedDiff($from_lines, $to_lines,
+ $mapped_from_lines, $mapped_to_lines) {
+
+ assert(sizeof($from_lines) == sizeof($mapped_from_lines));
+ assert(sizeof($to_lines) == sizeof($mapped_to_lines));
+
+ $this->Diff($mapped_from_lines, $mapped_to_lines);
+
+ $xi = $yi = 0;
+ for ($i = 0; $i < sizeof($this->edits); $i++) {
+ $orig = &$this->edits[$i]->orig;
+ if (is_array($orig)) {
+ $orig = array_slice($from_lines, $xi, sizeof($orig));
+ $xi += sizeof($orig);
+ }
+
+ $final = &$this->edits[$i]->final;
+ if (is_array($final)) {
+ $final = array_slice($to_lines, $yi, sizeof($final));
+ $yi += sizeof($final);
+ }
+ }
+ }
+}
+
+/**
+ * A class to format Diffs
+ *
+ * This class formats the diff in classic diff format.
+ * It is intended that this class be customized via inheritance,
+ * to obtain fancier outputs.
+ */
+class DiffFormatter
+{
+ /**
+ * Number of leading context "lines" to preserve.
+ *
+ * This should be left at zero for this class, but subclasses
+ * may want to set this to other values.
+ */
+ var $leading_context_lines = 0;
+
+ /**
+ * Number of trailing context "lines" to preserve.
+ *
+ * This should be left at zero for this class, but subclasses
+ * may want to set this to other values.
+ */
+ var $trailing_context_lines = 0;
+
+ /**
+ * Format a diff.
+ *
+ * @param $diff object A Diff object.
+ * @return string The formatted output.
+ */
+ function format($diff) {
+
+ $xi = $yi = 1;
+ $block = false;
+ $context = array();
+
+ $nlead = $this->leading_context_lines;
+ $ntrail = $this->trailing_context_lines;
+
+ $this->_start_diff();
+
+ foreach ($diff->edits as $edit) {
+ if ($edit->type == 'copy') {
+ if (is_array($block)) {
+ if (sizeof($edit->orig) <= $nlead + $ntrail) {
+ $block[] = $edit;
+ }
+ else{
+ if ($ntrail) {
+ $context = array_slice($edit->orig, 0, $ntrail);
+ $block[] = new _DiffOp_Copy($context);
+ }
+ $this->_block($x0, $ntrail + $xi - $x0,
+ $y0, $ntrail + $yi - $y0,
+ $block);
+ $block = false;
+ }
+ }
+ $context = $edit->orig;
+ }
+ else {
+ if (! is_array($block)) {
+ $context = array_slice($context, sizeof($context) - $nlead);
+ $x0 = $xi - sizeof($context);
+ $y0 = $yi - sizeof($context);
+ $block = array();
+ if ($context)
+ $block[] = new _DiffOp_Copy($context);
+ }
+ $block[] = $edit;
+ }
+
+ if ($edit->orig)
+ $xi += sizeof($edit->orig);
+ if ($edit->final)
+ $yi += sizeof($edit->final);
+ }
+
+ if (is_array($block))
+ $this->_block($x0, $xi - $x0,
+ $y0, $yi - $y0,
+ $block);
+
+ return $this->_end_diff();
+ }
+
+ function _block($xbeg, $xlen, $ybeg, $ylen, &$edits) {
+ $this->_start_block($this->_block_header($xbeg, $xlen, $ybeg, $ylen));
+ foreach ($edits as $edit) {
+ if ($edit->type == 'copy')
+ $this->_context($edit->orig);
+ elseif ($edit->type == 'add')
+ $this->_added($edit->final);
+ elseif ($edit->type == 'delete')
+ $this->_deleted($edit->orig);
+ elseif ($edit->type == 'change')
+ $this->_changed($edit->orig, $edit->final);
+ else
+ trigger_error("Unknown edit type", E_USER_ERROR);
+ }
+ $this->_end_block();
+ }
+
+ function _start_diff() {
+ ob_start();
+ }
+
+ function _end_diff() {
+ $val = ob_get_contents();
+ ob_end_clean();
+ return $val;
+ }
+
+ function _block_header($xbeg, $xlen, $ybeg, $ylen) {
+ if ($xlen > 1)
+ $xbeg .= "," . ($xbeg + $xlen - 1);
+ if ($ylen > 1)
+ $ybeg .= "," . ($ybeg + $ylen - 1);
+
+ return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg;
+ }
+
+ function _start_block($header) {
+ echo $header;
+ }
+
+ function _end_block() {
+ }
+
+ function _lines($lines, $prefix = ' ') {
+ foreach ($lines as $line)
+ echo "$prefix $line\n";
+ }
+
+ function _context($lines) {
+ $this->_lines($lines);
+ }
+
+ function _added($lines) {
+ $this->_lines($lines, ">");
+ }
+ function _deleted($lines) {
+ $this->_lines($lines, "<");
+ }
+
+ function _changed($orig, $final) {
+ $this->_deleted($orig);
+ echo "---\n";
+ $this->_added($final);
+ }
+}
+
+
+/**
+ * Additions by Axel Boldt follow, partly taken from diff.php, phpwiki-1.3.3
+ *
+ */
+
+define('NBSP', "\xA0"); // iso-8859-x non-breaking space.
+
+class _HWLDF_WordAccumulator {
+ function _HWLDF_WordAccumulator () {
+ $this->_lines = array();
+ $this->_line = '';
+ $this->_group = '';
+ $this->_tag = '';
+ }
+
+ function _flushGroup ($new_tag) {
+ if ($this->_group !== '') {
+ if ($this->_tag == 'mark')
+ $this->_line .= "<font color=\"red\">$this->_group</font>";
+ else
+ $this->_line .= $this->_group;
+ }
+ $this->_group = '';
+ $this->_tag = $new_tag;
+ }
+
+ function _flushLine ($new_tag) {
+ $this->_flushGroup($new_tag);
+ if ($this->_line != '')
+ $this->_lines[] = $this->_line;
+ $this->_line = '';
+ }
+
+ function addWords ($words, $tag = '') {
+ if ($tag != $this->_tag)
+ $this->_flushGroup($tag);
+
+ foreach ($words as $word) {
+ // new-line should only come as first char of word.
+ if ($word == '')
+ continue;
+ if ($word[0] == "\n") {
+ $this->_group .= NBSP;
+ $this->_flushLine($tag);
+ $word = substr($word, 1);
+ }
+ assert(!strstr($word, "\n"));
+ $this->_group .= $word;
+ }
+ }
+
+ function getLines() {
+ $this->_flushLine('~done');
+ return $this->_lines;
+ }
+}
+
+class WordLevelDiff extends MappedDiff
+{
+ function WordLevelDiff ($orig_lines, $final_lines) {
+ list ($orig_words, $orig_stripped) = $this->_split($orig_lines);
+ list ($final_words, $final_stripped) = $this->_split($final_lines);
+
+
+ $this->MappedDiff($orig_words, $final_words,
+ $orig_stripped, $final_stripped);
+ }
+
+ function _split($lines) {
+ // FIXME: fix POSIX char class.
+# if (!preg_match_all('/ ( [^\S\n]+ | [[:alnum:]]+ | . ) (?: (?!< \n) [^\S\n])? /xs',
+ if (!preg_match_all('/ ( [^\S\n]+ | [0-9_A-Za-z\x80-\xff]+ | . ) (?: (?!< \n) [^\S\n])? /xs',
+ implode("\n", $lines),
+ $m)) {
+ return array(array(''), array(''));
+ }
+ return array($m[0], $m[1]);
+ }
+
+ function orig () {
+ $orig = new _HWLDF_WordAccumulator;
+
+ foreach ($this->edits as $edit) {
+ if ($edit->type == 'copy')
+ $orig->addWords($edit->orig);
+ elseif ($edit->orig)
+ $orig->addWords($edit->orig, 'mark');
+ }
+ return $orig->getLines();
+ }
+
+ function final () {
+ $final = new _HWLDF_WordAccumulator;
+
+ foreach ($this->edits as $edit) {
+ if ($edit->type == 'copy')
+ $final->addWords($edit->final);
+ elseif ($edit->final)
+ $final->addWords($edit->final, 'mark');
+ }
+ return $final->getLines();
+ }
+}
+
+/**
+ * Wikipedia Table style diff formatter.
+ *
+ */
+class TableDiffFormatter extends DiffFormatter
+{
+ function TableDiffFormatter() {
+ $this->leading_context_lines = 2;
+ $this->trailing_context_lines = 2;
+ }
+
+ function _block_header( $xbeg, $xlen, $ybeg, $ylen ) {
+ $l1 = str_replace( "$1", $xbeg, wfMsg( "lineno" ) );
+ $l2 = str_replace( "$1", $ybeg, wfMsg( "lineno" ) );
+
+ $r = "<tr><td colspan=2 align=left><strong>{$l1}</strong></td>\n" .
+ "<td colspan=2 align=left><strong>{$l2}</strong></td></tr>\n";
+ return $r;
+ }
+
+ function _start_block( $header ) {
+ global $wgOut;
+ $wgOut->addHTML( $header );
+ }
+
+ function _end_block() {
+ }
+
+ function _lines( $lines, $prefix=' ', $color="white" ) {
+ }
+
+ function addedLine( $line ) {
+ return "<td>+</td><td bgcolor='#ccffcc'>" .
+ "<small>{$line}</small></td>";
+ }
+
+ function deletedLine( $line ) {
+ return "<td>-</td><td bgcolor='#ffffaa'>" .
+ "<small>{$line}</small></td>";
+ }
+
+ function emptyLine() {
+ return "<td colspan=2>&nbsp;</td>";
+ }
+
+ function contextLine( $line ) {
+ return "<td> </td><td bgcolor='white'><small>{$line}</small></td>";
+ }
+
+ function _added($lines) {
+ global $wgOut;
+ foreach ($lines as $line) {
+ $wgOut->addHTML( "<tr>" . $this->emptyLine() .
+ $this->addedLine( $line ) . "</tr>\n" );
+ }
+ }
+
+ function _deleted($lines) {
+ global $wgOut;
+ foreach ($lines as $line) {
+ $wgOut->addHTML( "<tr>" . $this->deletedLine( $line ) .
+ $this->emptyLine() . "</tr>\n" );
+ }
+ }
+
+ function _context( $lines ) {
+ global $wgOut;
+ foreach ($lines as $line) {
+ $wgOut->addHTML( "<tr>" . $this->contextLine( $line ) .
+ $this->contextLine( $line ) . "</tr>\n" );
+ }
+ }
+
+ function _changed( $orig, $final ) {
+ global $wgOut;
+ $diff = new WordLevelDiff( $orig, $final );
+ $del = $diff->orig();
+ $add = $diff->final();
+
+ while ( $line = array_shift( $del ) ) {
+ $aline = array_shift( $add );
+ $wgOut->addHTML( "<tr>" . $this->deletedLine( $line ) .
+ $this->addedLine( $aline ) . "</tr>\n" );
+ }
+ $this->_added( $add ); # If any leftovers
+ }
+}
+
+?>
diff --git a/includes/FulltextStoplist.php b/includes/FulltextStoplist.php
new file mode 100644
index 000000000000..ce0756f057b4
--- /dev/null
+++ b/includes/FulltextStoplist.php
@@ -0,0 +1,592 @@
+<?
+
+# This is the MySQL fulltext search stoplist, copied from the
+# source file "myisam/ft_static.c" in the MySQL source distribution.
+# If you use a new version of MySQL, this might have to be changed.
+
+/* private */ $wgFulltextStoplist = array(
+
+ "a",
+ "a's",
+ "able",
+ "about",
+ "above",
+ "according",
+ "accordingly",
+ "across",
+ "actually",
+ "after",
+ "afterwards",
+ "again",
+ "against",
+ "ain't",
+ "all",
+ "allow",
+ "allows",
+ "almost",
+ "alone",
+ "along",
+ "already",
+ "also",
+ "although",
+ "always",
+ "am",
+ "among",
+ "amongst",
+ "an",
+ "and",
+ "another",
+ "any",
+ "anybody",
+ "anyhow",
+ "anyone",
+ "anything",
+ "anyway",
+ "anyways",
+ "anywhere",
+ "apart",
+ "appear",
+ "appreciate",
+ "appropriate",
+ "are",
+ "aren't",
+ "around",
+ "as",
+ "aside",
+ "ask",
+ "asking",
+ "associated",
+ "at",
+ "available",
+ "away",
+ "awfully",
+ "b",
+ "be",
+ "became",
+ "because",
+ "become",
+ "becomes",
+ "becoming",
+ "been",
+ "before",
+ "beforehand",
+ "behind",
+ "being",
+ "believe",
+ "below",
+ "beside",
+ "besides",
+ "best",
+ "better",
+ "between",
+ "beyond",
+ "both",
+ "brief",
+ "but",
+ "by",
+ "c",
+ "c'mon",
+ "c's",
+ "came",
+ "can",
+ "can't",
+ "cannot",
+ "cant",
+ "cause",
+ "causes",
+ "certain",
+ "certainly",
+ "changes",
+ "clearly",
+ "co",
+ "com",
+ "come",
+ "comes",
+ "concerning",
+ "consequently",
+ "consider",
+ "considering",
+ "contain",
+ "containing",
+ "contains",
+ "corresponding",
+ "could",
+ "couldn't",
+ "course",
+ "currently",
+ "d",
+ "definitely",
+ "described",
+ "despite",
+ "did",
+ "didn't",
+ "different",
+ "do",
+ "does",
+ "doesn't",
+ "doing",
+ "don't",
+ "done",
+ "down",
+ "downwards",
+ "during",
+ "e",
+ "each",
+ "edu",
+ "eg",
+ "eight",
+ "either",
+ "else",
+ "elsewhere",
+ "enough",
+ "entirely",
+ "especially",
+ "et",
+ "etc",
+ "even",
+ "ever",
+ "every",
+ "everybody",
+ "everyone",
+ "everything",
+ "everywhere",
+ "ex",
+ "exactly",
+ "example",
+ "except",
+ "f",
+ "far",
+ "few",
+ "fifth",
+ "first",
+ "five",
+ "followed",
+ "following",
+ "follows",
+ "for",
+ "former",
+ "formerly",
+ "forth",
+ "four",
+ "from",
+ "further",
+ "furthermore",
+ "g",
+ "get",
+ "gets",
+ "getting",
+ "given",
+ "gives",
+ "go",
+ "goes",
+ "going",
+ "gone",
+ "got",
+ "gotten",
+ "greetings",
+ "h",
+ "had",
+ "hadn't",
+ "happens",
+ "hardly",
+ "has",
+ "hasn't",
+ "have",
+ "haven't",
+ "having",
+ "he",
+ "he's",
+ "hello",
+ "help",
+ "hence",
+ "her",
+ "here",
+ "here's",
+ "hereafter",
+ "hereby",
+ "herein",
+ "hereupon",
+ "hers",
+ "herself",
+ "hi",
+ "him",
+ "himself",
+ "his",
+ "hither",
+ "hopefully",
+ "how",
+ "howbeit",
+ "however",
+ "i",
+ "i'd",
+ "i'll",
+ "i'm",
+ "i've",
+ "ie",
+ "if",
+ "ignored",
+ "immediate",
+ "in",
+ "inasmuch",
+ "inc",
+ "indeed",
+ "indicate",
+ "indicated",
+ "indicates",
+ "inner",
+ "insofar",
+ "instead",
+ "into",
+ "inward",
+ "is",
+ "isn't",
+ "it",
+ "it'd",
+ "it'll",
+ "it's",
+ "its",
+ "itself",
+ "j",
+ "just",
+ "k",
+ "keep",
+ "keeps",
+ "kept",
+ "know",
+ "knows",
+ "known",
+ "l",
+ "last",
+ "lately",
+ "later",
+ "latter",
+ "latterly",
+ "least",
+ "less",
+ "lest",
+ "let",
+ "let's",
+ "like",
+ "liked",
+ "likely",
+ "little",
+ "look",
+ "looking",
+ "looks",
+ "ltd",
+ "m",
+ "mainly",
+ "many",
+ "may",
+ "maybe",
+ "me",
+ "mean",
+ "meanwhile",
+ "merely",
+ "might",
+ "more",
+ "moreover",
+ "most",
+ "mostly",
+ "much",
+ "must",
+ "my",
+ "myself",
+ "n",
+ "name",
+ "namely",
+ "nd",
+ "near",
+ "nearly",
+ "necessary",
+ "need",
+ "needs",
+ "neither",
+ "never",
+ "nevertheless",
+ "new",
+ "next",
+ "nine",
+ "no",
+ "nobody",
+ "non",
+ "none",
+ "noone",
+ "nor",
+ "normally",
+ "not",
+ "nothing",
+ "novel",
+ "now",
+ "nowhere",
+ "o",
+ "obviously",
+ "of",
+ "off",
+ "often",
+ "oh",
+ "ok",
+ "okay",
+ "old",
+ "on",
+ "once",
+ "one",
+ "ones",
+ "only",
+ "onto",
+ "or",
+ "other",
+ "others",
+ "otherwise",
+ "ought",
+ "our",
+ "ours",
+ "ourselves",
+ "out",
+ "outside",
+ "over",
+ "overall",
+ "own",
+ "p",
+ "particular",
+ "particularly",
+ "per",
+ "perhaps",
+ "placed",
+ "please",
+ "plus",
+ "possible",
+ "presumably",
+ "probably",
+ "provides",
+ "q",
+ "que",
+ "quite",
+ "qv",
+ "r",
+ "rather",
+ "rd",
+ "re",
+ "really",
+ "reasonably",
+ "regarding",
+ "regardless",
+ "regards",
+ "relatively",
+ "respectively",
+ "right",
+ "s",
+ "said",
+ "same",
+ "saw",
+ "say",
+ "saying",
+ "says",
+ "second",
+ "secondly",
+ "see",
+ "seeing",
+ "seem",
+ "seemed",
+ "seeming",
+ "seems",
+ "seen",
+ "self",
+ "selves",
+ "sensible",
+ "sent",
+ "serious",
+ "seriously",
+ "seven",
+ "several",
+ "shall",
+ "she",
+ "should",
+ "shouldn't",
+ "since",
+ "six",
+ "so",
+ "some",
+ "somebody",
+ "somehow",
+ "someone",
+ "something",
+ "sometime",
+ "sometimes",
+ "somewhat",
+ "somewhere",
+ "soon",
+ "sorry",
+ "specified",
+ "specify",
+ "specifying",
+ "still",
+ "sub",
+ "such",
+ "sup",
+ "sure",
+ "t",
+ "t's",
+ "take",
+ "taken",
+ "tell",
+ "tends",
+ "th",
+ "than",
+ "thank",
+ "thanks",
+ "thanx",
+ "that",
+ "that's",
+ "thats",
+ "the",
+ "their",
+ "theirs",
+ "them",
+ "themselves",
+ "then",
+ "thence",
+ "there",
+ "there's",
+ "thereafter",
+ "thereby",
+ "therefore",
+ "therein",
+ "theres",
+ "thereupon",
+ "these",
+ "they",
+ "they'd",
+ "they'll",
+ "they're",
+ "they've",
+ "think",
+ "third",
+ "this",
+ "thorough",
+ "thoroughly",
+ "those",
+ "though",
+ "three",
+ "through",
+ "throughout",
+ "thru",
+ "thus",
+ "to",
+ "together",
+ "too",
+ "took",
+ "toward",
+ "towards",
+ "tried",
+ "tries",
+ "truly",
+ "try",
+ "trying",
+ "twice",
+ "two",
+ "u",
+ "un",
+ "under",
+ "unfortunately",
+ "unless",
+ "unlikely",
+ "until",
+ "unto",
+ "up",
+ "upon",
+ "us",
+ "use",
+ "used",
+ "useful",
+ "uses",
+ "using",
+ "usually",
+ "v",
+ "value",
+ "various",
+ "very",
+ "via",
+ "viz",
+ "vs",
+ "w",
+ "want",
+ "wants",
+ "was",
+ "wasn't",
+ "way",
+ "we",
+ "we'd",
+ "we'll",
+ "we're",
+ "we've",
+ "welcome",
+ "well",
+ "went",
+ "were",
+ "weren't",
+ "what",
+ "what's",
+ "whatever",
+ "when",
+ "whence",
+ "whenever",
+ "where",
+ "where's",
+ "whereafter",
+ "whereas",
+ "whereby",
+ "wherein",
+ "whereupon",
+ "wherever",
+ "whether",
+ "which",
+ "while",
+ "whither",
+ "who",
+ "who's",
+ "whoever",
+ "whole",
+ "whom",
+ "whose",
+ "why",
+ "will",
+ "willing",
+ "wish",
+ "with",
+ "within",
+ "without",
+ "won't",
+ "wonder",
+ "would",
+ "would",
+ "wouldn't",
+ "x",
+ "y",
+ "yes",
+ "yet",
+ "you",
+ "you'd",
+ "you'll",
+ "you're",
+ "you've",
+ "your",
+ "yours",
+ "yourself",
+ "yourselves",
+ "z",
+ "zero" );
+
+class FulltextStoplist {
+
+ /* static */ function inList( $word )
+ {
+ global $wgFulltextStoplist;
+
+ $w = strtolower( $word );
+ $w = preg_replace( "/[^a-z']+/", "", $w );
+ return in_array( $w, $wgFulltextStoplist );
+ }
+}
+
+?>
diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php
new file mode 100644
index 000000000000..a60007bea4fe
--- /dev/null
+++ b/includes/GlobalFunctions.php
@@ -0,0 +1,494 @@
+<?
+# Global functions used everywhere
+
+$wgNumberOfArticles = -1; # Unset
+$wgTotalViews = -1;
+$wgTotalEdits = -1;
+
+global $IP;
+include_once( "$IP/DatabaseFunctions.php" );
+include_once( "$IP/UpdateClasses.php" );
+include_once( "$IP/LogPage.php" );
+
+# PHP 4.1+ has array_key_exists, PHP 4.0.6 has key_exists instead, and earlier
+# versions of PHP have neither. So we roll our own. Note that this
+# function will return false even for keys that exist but whose associated
+# value is NULL.
+#
+if ( phpversion() == "4.0.6" ) {
+ function array_key_exists( $k, $a ) {
+ return key_exists( $k, $a );
+ }
+} else if (phpversion() < "4.1") {
+ function array_key_exists( $k, $a ) {
+ return isset($a[$k]);
+ }
+}
+
+$wgRandomSeeded = false;
+
+function wfSeedRandom()
+{
+ global $wgRandomSeeded;
+
+ if ( ! $wgRandomSeeded ) {
+ mt_srand( (double)microtime() * 1000000 );
+ $wgRandomSeeded = true;
+ }
+}
+
+function wfLocalUrl( $a, $q = "" )
+{
+ global $wgServer, $wgScript, $wgArticlePath;
+
+ $a = str_replace( " ", "_", $a );
+ #$a = wfUrlencode( $a ); # This stuff is _already_ URL-encoded.
+
+ if ( "" == $a ) {
+ if( "" == $q ) {
+ $a = $wgServer . $wgScript;
+ } else {
+ $a = "{$wgServer}{$wgScript}?{$q}";
+ }
+ } else if ( "" == $q ) {
+ $a = str_replace( "$1", $a, $wgArticlePath );
+ } else {
+ $a = "{$wgServer}{$wgScript}?title={$a}&{$q}";
+ }
+ return $a;
+}
+
+function wfLocalUrlE( $a, $q = "" )
+{
+ return wfEscapeHTML( wfLocalUrl( $a, $q ) );
+}
+
+function wfImageUrl( $img )
+{
+ global $wgUploadPath;
+
+ $nt = Title::newFromText( $img );
+ $name = $nt->getDBkey();
+ $hash = md5( $name );
+
+ $url = "{$wgUploadPath}/" . $hash{0} . "/" .
+ substr( $hash, 0, 2 ) . "/{$name}";
+ return wfUrlencode( $url );
+}
+
+function wfImageArchiveUrl( $name )
+{
+ global $wgUploadPath;
+
+ $hash = md5( substr( $name, 15) );
+ $url = "{$wgUploadPath}/archive/" . $hash{0} . "/" .
+ substr( $hash, 0, 2 ) . "/{$name}";
+ return $url;
+}
+
+function wfUrlencode ( $s )
+{
+ $ulink = urlencode( $s );
+ $ulink = preg_replace( "/%3[Aa]/", ":", $ulink );
+ $ulink = preg_replace( "/%2[Ff]/", "/", $ulink );
+ return $ulink;
+}
+
+function wfUtf8Sequence($codepoint) {
+ if($codepoint < 0x80) return chr($codepoint);
+ if($codepoint < 0x800) return chr($codepoint >> 6 & 0x3f | 0xc0) .
+ chr($codepoint & 0x3f | 0x80);
+ if($codepoint < 0x10000) return chr($codepoint >> 12 & 0x0f | 0xe0) .
+ chr($codepoint >> 6 & 0x3f | 0x80) .
+ chr($codepoint & 0x3f | 0x80);
+ if($codepoint < 0x100000) return chr($codepoint >> 18 & 0x07 | 0xf0) . # Double-check this
+ chr($codepoint >> 12 & 0x3f | 0x80) .
+ chr($codepoint >> 6 & 0x3f | 0x80) .
+ chr($codepoint & 0x3f | 0x80);
+ # Doesn't yet handle outside the BMP
+ return "&#$codepoint;";
+}
+
+function wfMungeToUtf8($string) {
+ global $wgInputEncoding; # This is debatable
+ #$string = iconv($wgInputEncoding, "UTF-8", $string);
+ $string = preg_replace ( '/&#([0-9]+);/e', 'wfUtf8Sequence($1)', $string );
+ $string = preg_replace ( '/&#x([0-9a-f]+);/ie', 'wfUtf8Sequence(0x$1)', $string );
+ # Should also do named entities here
+ return $string;
+}
+
+function wfDebug( $text, $logonly = false )
+{
+ global $wgOut, $wgDebugLogFile;
+
+ if ( ! $logonly ) {
+ $wgOut->debug( $text );
+ }
+ if ( "" != $wgDebugLogFile ) {
+ error_log( $text, 3, $wgDebugLogFile );
+ }
+}
+
+if( !isset( $wgProfiling ) )
+ $wgProfiling = false;
+$wgProfileStack = array();
+$wgProfileWorkStack = array();
+
+if( $wgProfiling ) {
+ function wfProfileIn( $functionname )
+ {
+ global $wgProfileStack, $wgProfileWorkStack;
+ array_push( $wgProfileWorkStack, "$functionname " .
+ count( $wgProfileWorkStack ) . " " . microtime() );
+ }
+
+ function wfProfileOut() {
+ global $wgProfileStack, $wgProfileWorkStack;
+ $bit = array_pop( $wgProfileWorkStack );
+ $bit .= " " . microtime();
+ array_push( $wgProfileStack, $bit );
+ }
+} else {
+ function wfProfileIn( $functionname ) { }
+ function wfProfileOut( ) { }
+}
+
+function wfReadOnly()
+{
+ global $wgReadOnlyFile;
+
+ if ( "" == $wgReadOnlyFile ) { return false; }
+ return is_file( $wgReadOnlyFile );
+}
+
+$wgReplacementKeys = array( "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9" );
+function wfMsg( $key )
+{
+ global $wgLang, $wgReplacementKeys;
+ $ret = $wgLang->getMessage( $key );
+
+ if( func_num_args() > 1 ) {
+ $reps = func_get_args();
+ array_shift( $reps );
+ $ret = str_replace( $wgReplacementKeys, $reps, $ret );
+ }
+
+ if ( "" == $ret ) {
+ user_error( "Couldn't find text for message \"{$key}\"." );
+ }
+ return $ret;
+}
+
+function wfCleanFormFields( $fields )
+{
+ global $HTTP_POST_VARS;
+ global $wgInputEncoding, $wgOutputEncoding, $wgEditEncoding, $wgLang;
+
+ if ( get_magic_quotes_gpc() ) {
+ foreach ( $fields as $fname ) {
+ if ( isset( $HTTP_POST_VARS[$fname] ) ) {
+ $HTTP_POST_VARS[$fname] = stripslashes(
+ $HTTP_POST_VARS[$fname] );
+ }
+ global ${$fname};
+ if ( isset( ${$fname} ) ) {
+ ${$fname} = stripslashes( ${$fname} );
+ }
+ }
+ }
+ $enc = $wgOutputEncoding;
+ if( $wgEditEncoding != "") $enc = $wgEditEncoding;
+ if ( $enc != $wgInputEncoding ) {
+ foreach ( $fields as $fname ) {
+ if ( isset( $HTTP_POST_VARS[$fname] ) ) {
+ $HTTP_POST_VARS[$fname] = $wgLang->iconv(
+ $wgOutputEncoding, $wgInputEncoding,
+ $HTTP_POST_VARS[$fname] );
+ }
+ global ${$fname};
+ if ( isset( ${$fname} ) ) {
+ ${$fname} = $wgLang->iconv(
+ $enc, $wgInputEncoding, ${$fname} );
+ }
+ }
+ }
+}
+
+function wfMungeQuotes( $in )
+{
+ $out = str_replace( "%", "%25", $in );
+ $out = str_replace( "'", "%27", $out );
+ $out = str_replace( "\"", "%22", $out );
+ return $out;
+}
+
+function wfDemungeQuotes( $in )
+{
+ $out = str_replace( "%22", "\"", $in );
+ $out = str_replace( "%27", "'", $out );
+ $out = str_replace( "%25", "%", $out );
+ return $out;
+}
+
+function wfCleanQueryVar( $var )
+{
+ global $wgLang;
+ if ( get_magic_quotes_gpc() ) {
+ $var = stripslashes( $var );
+ }
+ return $wgLang->recodeInput( $var );
+}
+
+function wfSpecialPage()
+{
+ global $wgUser, $wgOut, $wgTitle, $wgLang;
+
+ $validSP = $wgLang->getValidSpecialPages();
+ $sysopSP = $wgLang->getSysopSpecialPages();
+ $devSP = $wgLang->getDeveloperSpecialPages();
+
+ $wgOut->setArticleFlag( false );
+ $wgOut->setRobotpolicy( "noindex,follow" );
+
+ $t = $wgTitle->getDBkey();
+ if ( array_key_exists( $t, $validSP ) ||
+ ( $wgUser->isSysop() && array_key_exists( $t, $sysopSP ) ) ||
+ ( $wgUser->isDeveloper() && array_key_exists( $t, $devSP ) ) ) {
+ $wgOut->setPageTitle( wfMsg( strtolower( $wgTitle->getText() ) ) );
+
+ $inc = "Special" . $t . ".php";
+ include_once( $inc );
+ $call = "wfSpecial" . $t;
+ $call();
+ } else if ( array_key_exists( $t, $sysopSP ) ) {
+ $wgOut->sysopRequired();
+ } else if ( array_key_exists( $t, $devSP ) ) {
+ $wgOut->developerRequired();
+ } else {
+ $wgOut->errorpage( "nosuchspecialpage", "nospecialpagetext" );
+ }
+}
+
+function wfSearch( $s )
+{
+ $se = new SearchEngine( wfCleanQueryVar( $s ) );
+ $se->showResults();
+}
+
+function wfGo( $s )
+{ # pick the nearest match
+ $se = new SearchEngine( wfCleanQueryVar( $s ) );
+ $se->goResult();
+}
+
+function wfNumberOfArticles()
+{
+ global $wgNumberOfArticles;
+
+ wfLoadSiteStats();
+ return $wgNumberOfArticles;
+}
+
+/* private */ function wfLoadSiteStats()
+{
+ global $wgNumberOfArticles, $wgTotalViews, $wgTotalEdits;
+ if ( -1 != $wgNumberOfArticles ) return;
+
+ $sql = "SELECT ss_total_views, ss_total_edits, ss_good_articles " .
+ "FROM site_stats WHERE ss_row_id=1";
+ $res = wfQuery( $sql, "wfLoadSiteStats" );
+
+ if ( 0 == wfNumRows( $res ) ) { return; }
+ else {
+ $s = wfFetchObject( $res );
+ $wgTotalViews = $s->ss_total_views;
+ $wgTotalEdits = $s->ss_total_edits;
+ $wgNumberOfArticles = $s->ss_good_articles;
+ }
+}
+
+function wfEscapeHTML( $in )
+{
+ return str_replace(
+ array( "&", "\"", ">", "<" ),
+ array( "&amp;", "&quot;", "&gt;", "&lt;" ),
+ $in );
+}
+
+function wfEscapeHTMLTagsOnly( $in ) {
+ return str_replace(
+ array( "\"", ">", "<" ),
+ array( "&quot;", "&gt;", "&lt;" ),
+ $in );
+}
+
+function wfUnescapeHTML( $in )
+{
+ $in = str_replace( "&lt;", "<", $in );
+ $in = str_replace( "&gt;", ">", $in );
+ $in = str_replace( "&quot;", "\"", $in );
+ $in = str_replace( "&amp;", "&", $in );
+ return $in;
+}
+
+function wfImageDir( $fname )
+{
+ global $wgUploadDirectory;
+
+ $hash = md5( $fname );
+ $oldumask = umask(0);
+ $dest = $wgUploadDirectory . "/" . $hash{0};
+ if ( ! is_dir( $dest ) ) { mkdir( $dest, 0777 ); }
+ $dest .= "/" . substr( $hash, 0, 2 );
+ if ( ! is_dir( $dest ) ) { mkdir( $dest, 0777 ); }
+
+ umask( $oldumask );
+ return $dest;
+}
+
+function wfImageArchiveDir( $fname )
+{
+ global $wgUploadDirectory;
+
+ $hash = md5( $fname );
+ $oldumask = umask(0);
+ $archive = "{$wgUploadDirectory}/archive";
+ if ( ! is_dir( $archive ) ) { mkdir( $archive, 0777 ); }
+ $archive .= "/" . $hash{0};
+ if ( ! is_dir( $archive ) ) { mkdir( $archive, 0777 ); }
+ $archive .= "/" . substr( $hash, 0, 2 );
+ if ( ! is_dir( $archive ) ) { mkdir( $archive, 0777 ); }
+
+ umask( $oldumask );
+ return $archive;
+}
+
+function wfRecordUpload( $name, $oldver, $size, $desc )
+{
+ global $wgUser, $wgLang, $wgTitle, $wgOut, $wgDeferredUpdateList;
+ $fname = "wfRecordUpload";
+
+ $sql = "SELECT img_name,img_size,img_timestamp,img_description,img_user," .
+ "img_user_text FROM image WHERE img_name='" . wfStrencode( $name ) . "'";
+ $res = wfQuery( $sql, $fname );
+
+ if ( 0 == wfNumRows( $res ) ) {
+ $sql = "INSERT INTO image (img_name,img_size,img_timestamp," .
+ "img_description,img_user,img_user_text) VALUES ('" .
+ wfStrencode( $name ) . "',{$size},'" . date( "YmdHis" ) . "','" .
+ wfStrencode( $desc ) . "', '" . $wgUser->getID() .
+ "', '" . wfStrencode( $wgUser->getName() ) . "')";
+ wfQuery( $sql, $fname );
+
+ $sql = "SELECT cur_id,cur_text FROM cur WHERE cur_namespace=" .
+ Namespace::getImage() . " AND cur_title='" .
+ wfStrencode( $name ) . "'";
+ $res = wfQuery( $sql, $fname );
+ if ( 0 == wfNumRows( $res ) ) {
+ $now = wfTimestampNow();
+ $won = wfInvertTimestamp( $now );
+ $common =
+ Namespace::getImage() . ",'" .
+ wfStrencode( $name ) . "','" .
+ wfStrencode( $desc ) . "','" . $wgUser->getID() . "','" .
+ wfStrencode( $wgUser->getName() ) . "','" . $now .
+ "',1";
+ $sql = "INSERT INTO cur (cur_namespace,cur_title," .
+ "cur_comment,cur_user,cur_user_text,cur_timestamp,cur_is_new," .
+ "cur_text,inverse_timestamp) VALUES (" .
+ $common .
+ ",'" . wfStrencode( $desc ) . "','{$won}')";
+ wfQuery( $sql, $fname );
+ $id = wfInsertId() or 0; # We should throw an error instead
+ $sql = "INSERT INTO recentchanges (rc_namespace,rc_title,
+ rc_comment,rc_user,rc_user_text,rc_timestamp,rc_new,
+ rc_cur_id,rc_cur_time) VALUES ({$common},{$id},'{$now}')";
+ wfQuery( $sql, $fname );
+ $u = new SearchUpdate( $id, $name, $desc );
+ $u->doUpdate();
+ }
+ } else {
+ $s = wfFetchObject( $res );
+
+ $sql = "INSERT INTO oldimage (oi_name,oi_archive_name,oi_size," .
+ "oi_timestamp,oi_description,oi_user,oi_user_text) VALUES ('" .
+ wfStrencode( $s->img_name ) . "','" .
+ wfStrencode( $oldver ) .
+ "',{$s->img_size},'{$s->img_timestamp}','" .
+ wfStrencode( $s->img_description ) . "','" .
+ wfStrencode( $s->img_user ) . "','" .
+ wfStrencode( $s->img_user_text) . "')";
+ wfQuery( $sql, $fname );
+
+ $sql = "UPDATE image SET img_size={$size}," .
+ "img_timestamp='" . date( "YmdHis" ) . "',img_user='" .
+ $wgUser->getID() . "',img_user_text='" .
+ wfStrencode( $wgUser->getName() ) . "', img_description='" .
+ wfStrencode( $desc ) . "' WHERE img_name='" .
+ wfStrencode( $name ) . "'";
+ wfQuery( $sql, $fname );
+ }
+
+ $log = new LogPage( wfMsg( "uploadlogpage" ), wfMsg( "uploadlogpagetext" ) );
+ $da = str_replace( "$1", "[[:" . $wgLang->getNsText(
+ Namespace::getImage() ) . ":{$name}|{$name}]]",
+ wfMsg( "uploadedimage" ) );
+ $ta = str_replace( "$1", $name, wfMsg( "uploadedimage" ) );
+ $log->addEntry( $da, $desc, $ta );
+}
+
+
+/* Some generic result counters, pulled out of SearchEngine */
+
+function wfShowingResults( $offset, $limit )
+{
+ $top = str_replace( "$1", $limit, wfMsg( "showingresults" ) );
+ $top = str_replace( "$2", $offset+1, $top );
+ return $top;
+}
+
+function wfViewPrevNext( $offset, $limit, $link, $query = "" )
+{
+ global $wgUser;
+ $prev = str_replace( "$1", $limit, wfMsg( "prevn" ) );
+ $next = str_replace( "$1", $limit, wfMsg( "nextn" ) );
+
+ $sk = $wgUser->getSkin();
+ if ( 0 != $offset ) {
+ $po = $offset - $limit;
+ if ( $po < 0 ) { $po = 0; }
+ $q = "limit={$limit}&offset={$po}";
+ if ( "" != $query ) { $q .= "&{$query}"; }
+ $plink = "<a href=\"" . wfLocalUrlE( $link, $q ) . "\">{$prev}</a>";
+ } else { $plink = $prev; }
+
+ $no = $offset + $limit;
+ $q = "limit={$limit}&offset={$no}";
+ if ( "" != $query ) { $q .= "&{$query}"; }
+
+ $nlink = "<a href=\"" . wfLocalUrlE( $link, $q ) . "\">{$next}</a>";
+ $nums = wfNumLink( $offset, 20, $link , $query ) . " | " .
+ wfNumLink( $offset, 50, $link, $query ) . " | " .
+ wfNumLink( $offset, 100, $link, $query ) . " | " .
+ wfNumLink( $offset, 250, $link, $query ) . " | " .
+ wfNumLink( $offset, 500, $link, $query );
+
+ $sl = str_replace( "$1", $plink, wfMsg( "viewprevnext" ) );
+ $sl = str_replace( "$2", $nlink, $sl );
+ $sl = str_replace( "$3", $nums, $sl );
+ return $sl;
+}
+
+function wfNumLink( $offset, $limit, $link, $query = "" )
+{
+ global $wgUser;
+ if ( "" == $query ) { $q = ""; }
+ else { $q = "{$query}&"; }
+ $q .= "limit={$limit}&offset={$offset}";
+
+ $s = "<a href=\"" . wfLocalUrlE( $link, $q ) . "\">{$limit}</a>";
+ return $s;
+}
+
+?>
diff --git a/includes/Interwiki.php b/includes/Interwiki.php
new file mode 100644
index 000000000000..417159e38f1a
--- /dev/null
+++ b/includes/Interwiki.php
@@ -0,0 +1,256 @@
+<?
+# Note -- this file is generated by maintenance/fetchInterwiki.pl
+# Edit and rerun that script rather than modifying this directly.
+
+/* private */ $wgValidInterwikis = array(
+ # General interwiki links from the InterMap
+ "AbbeNormal" => "http://www.ourpla.net/cgi-bin/pikie.cgi?$1",
+ "AcadWiki" => "http://xarch.tu-graz.ac.at/autocad/wiki/$1",
+ "Acronym" => "http://www.acronymfinder.com/af-query.asp?String=exact&Acronym=$1",
+ "Advogato" => "http://www.advogato.org/$1",
+ "AIWiki" => "http://www.ifi.unizh.ch/ailab/aiwiki/aiw.cgi?$1",
+ "ALife" => "http://news.alife.org/wiki/index.php?$1",
+ "AndStuff" => "http://andstuff.org/wiki.php?$1",
+ "Annotation" => "http://bayle.stanford.edu/crit/nph-med.cgi/$1",
+ "AnnotationWiki" => "http://www.seedwiki.com/page.cfm?wikiid=368&doc=$1",
+ "AwarenessWiki" => "http://taoriver.net/aware/$1",
+ "BenefitsWiki" => "http://www.benefitslink.com/cgi-bin/wiki.cgi?$1",
+ "BridgesWiki" => "http://c2.com/w2/bridges/$1",
+ "C2find" => "http://c2.com/cgi/wiki?FindPage&value=$1",
+ "Cache" => "http://www.google.com/search?q=cache:$1",
+ "CLiki" => "http://ww.telent.net/cliki/$1",
+ "CmWiki" => "http://www.ourpla.net/cgi-bin/wiki.pl?$1",
+ "CreationMatters" => "http://www.ourpla.net/cgi-bin/wiki.pl?$1",
+ "DejaNews" => "http://www.deja.com/=dnc/getdoc.xp?AN=$1",
+ "DeWikiPedia" => "http://www.wikipedia.de/wiki.cgi?$1",
+ "Dictionary" => "http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query=$1",
+ "DiveIntoOsx" => "http://diveintoosx.org/$1",
+ "DocBook" => "http://docbook.org/wiki/moin.cgi/$1",
+ "DolphinWiki" => "http://www.object-arts.com/wiki/html/Dolphin/$1",
+ "EfnetCeeWiki" => "http://purl.net/wiki/c/$1",
+ "EfnetCppWiki" => "http://purl.net/wiki/cpp/$1",
+ "EfnetPythonWiki" => "http://purl.net/wiki/python/$1",
+ "EfnetXmlWiki" => "http://purl.net/wiki/xml/$1",
+ "EljWiki" => "http://elj.sourceforge.net/phpwiki/index.php/$1",
+ "EmacsWiki" => "http://www.emacswiki.org/cgi-bin/wiki.pl?$1",
+ "FinalEmpire" => "http://final-empire.sourceforge.net/cgi-bin/wiki.pl?$1",
+ "Foldoc" => "http://www.foldoc.org/foldoc/foldoc.cgi?$1",
+ "FoxWiki" => "http://fox.wikis.com/wc.dll?Wiki~$1",
+ "FreeBSDman" => "http://www.FreeBSD.org/cgi/man.cgi?apropos=1&query=$1",
+ "Google" => "http://www.google.com/search?q=$1",
+ "GoogleGroups" => "http://groups.google.com/groups?q=$1",
+ "GreenCheese" => "http://www.greencheese.org/$1",
+ "HammondWiki" => "http://www.dairiki.org/HammondWiki/index.php3?$1",
+ "Haribeau" => "http://wiki.haribeau.de/cgi-bin/wiki.pl?$1",
+ "IAWiki" => "http://www.IAwiki.net/$1",
+ "IMDB" => "http://us.imdb.com/Title?$1",
+ "JargonFile" => "http://sunir.org/apps/meta.pl?wiki=JargonFile&redirect=$1",
+ "JiniWiki" => "http://www.cdegroot.com/cgi-bin/jini?$1",
+ "JspWiki" => "http://www.ecyrd.com/JSPWiki/Wiki.jsp?page=$1",
+ "KmWiki" => "http://www.voght.com/cgi-bin/pywiki?$1",
+ "KnowHow" => "http://www2.iro.umontreal.ca/~paquetse/cgi-bin/wiki.cgi?$1",
+ "LanifexWiki" => "http://opt.lanifex.com/cgi-bin/wiki.pl?$1",
+ "LegoWiki" => "http://www.object-arts.com/wiki/html/Lego-Robotics/$1",
+ "LinuxWiki" => "http://www.linuxwiki.de/$1",
+ "LugKR" => "http://lug-kr.sourceforge.net/cgi-bin/lugwiki.pl?$1",
+ "MathSongsWiki" => "http://SeedWiki.com/page.cfm?wikiid=237&doc=$1",
+ "MbTest" => "http://www.usemod.com/cgi-bin/mbtest.pl?$1",
+ "MeatBall" => "http://www.usemod.com/cgi-bin/mb.pl?$1",
+ "MetaWiki" => "http://sunir.org/apps/meta.pl?$1",
+ "MetaWikiPedia" => "http://meta.wikipedia.com/wiki/$1",
+ "MoinMoin" => "http://purl.net/wiki/moin/$1",
+ "MuWeb" => "http://www.dunstable.com/scripts/MuWebWeb?$1",
+ "NetVillage" => "http://www.netbros.com/?$1",
+ "OpenWiki" => "http://openwiki.com/?$1",
+ "OrgPatterns" => "http://www.bell-labs.com/cgi-user/OrgPatterns/OrgPatterns?$1",
+ "PangalacticOrg" => "http://www.pangalactic.org/Wiki/$1",
+ "PersonalTelco" => "http://www.personaltelco.net/index.cgi/$1",
+ "PhpWiki" => "http://phpwiki.sourceforge.net/phpwiki/index.php?$1",
+ "Pikie" => "http://pikie.darktech.org/cgi/pikie?$1",
+ "PPR" => "http://c2.com/cgi/wiki?$1",
+ "PurlNet" => "http://purl.oclc.org/NET/$1",
+ "PythonInfo" => "http://www.python.org/cgi-bin/moinmoin/$1",
+ "PythonWiki" => "http://www.pythonwiki.de/$1",
+ "PyWiki" => "http://www.voght.com/cgi-bin/pywiki?$1",
+ "SeaPig" => "http://www.seapig.org/ $1",
+ "SeattleWireless" => "http://seattlewireless.net/?$1",
+ "SenseisLibrary" => "http://senseis.xmp.net/?$1",
+ "Shakti" => "http://cgi.algonet.se/htbin/cgiwrap/pgd/ShaktiWiki/$1",
+ "SourceForge" => "http://sourceforge.net/$1",
+ "Squeak" => "http://minnow.cc.gatech.edu/squeak/$1",
+ "StrikiWiki" => "http://ch.twi.tudelft.nl/~mostert/striki/teststriki.pl?$1",
+ "SVGWiki" => "http://www.protocol7.com/svg-wiki/default.asp?$1",
+ "Tavi" => "http://tavi.sourceforge.net/index.php?$1",
+ "TmNet" => "http://www.technomanifestos.net/?$1",
+ "TMwiki" => "http://www.EasyTopicMaps.com/?page=$1",
+ "TWiki" => "http://twiki.org/cgi-bin/view/$1",
+ "TwistedWiki" => "http://purl.net/wiki/twisted/$1",
+ "Unreal" => "http://wiki.beyondunreal.com/wiki/$1",
+ "UseMod" => "http://www.usemod.com/cgi-bin/wiki.pl?$1",
+ "VisualWorks" => "http://wiki.cs.uiuc.edu/VisualWorks/$1",
+ "WebDevWikiNL" => "http://www.promo-it.nl/WebDevWiki/index.php?page=$1",
+ "WebSeitzWiki" => "http://webseitz.fluxent.com/wiki/$1",
+ "Why" => "http://clublet.com/c/c/why?$1",
+ "Wiki" => "http://c2.com/cgi/wiki?$1",
+ "WikiPedia" => "http://www.wikipedia.com/wiki/$1",
+ "WikiWorld" => "http://WikiWorld.com/wiki/index.php/$1",
+ "YpsiEyeball" => "http://sknkwrks.dyndns.org:1957/writewiki/wiki.pl?$1",
+ "ZWiki" => "http://www.zwiki.org/$1",
+
+ # Some custom additions:
+ "ReVo" => "http://purl.org/NET/voko/revo/art/$1.html",
+ # eg [[ReVo:cerami]], [[ReVo:astero]] - note X-sensitive!
+ "EcheI" => "http://www.ikso.net/cgi-bin/wiki.pl?$1",
+ "E\xc4\x89eI" => "http://www.ikso.net/cgi-bin/wiki.pl?$1",
+ "UnuMondo" => "http://unumondo.com/cgi-bin/wiki.pl?$1", # X-sensitive!
+ "JEFO" => "http://esperanto.jeunes.free.fr/vikio/index.php?$1",
+ "PMEG" => "http://www.bertilow.com/pmeg/$1.php",
+ # ekz [[PMEG:gramatiko/kunligaj vortetoj/au]]
+ "EnciclopediaLibre" => "http://enciclopedia.us.es/wiki.phtml?title=$1",
+
+ # Wikipedia-specific stuff:
+ # Special cases
+ "w" => "http://www.wikipedia.org/wiki/$1",
+ "m" => "http://meta.wikipedia.org/wiki/$1",
+ "meta" => "http://meta.wikipedia.org/wiki/$1",
+ "sep11" => "http://sep11.wikipedia.org/wiki/$1",
+ "simple"=> "http://simple.wikipedia.com/wiki.cgi?$1",
+ "wiktionary" => "http://wiktionary.wikipedia.org/wiki/$1",
+
+ # ISO 639 2-letter language codes
+ "aa" => "http://aa.wikipedia.com/wiki.cgi?$1",
+ "ab" => "http://ab.wikipedia.com/wiki.cgi?$1",
+ "af" => "http://af.wikipedia.com/wiki.cgi?$1",
+ "am" => "http://am.wikipedia.com/wiki.cgi?$1",
+ "ar" => "http://ar.wikipedia.com/wiki.cgi?$1",
+ "as" => "http://as.wikipedia.com/wiki.cgi?$1",
+ "ay" => "http://ay.wikipedia.com/wiki.cgi?$1",
+ "az" => "http://az.wikipedia.com/wiki.cgi?$1",
+ "ba" => "http://ba.wikipedia.com/wiki.cgi?$1",
+ "be" => "http://be.wikipedia.com/wiki.cgi?$1",
+ "bh" => "http://bh.wikipedia.com/wiki.cgi?$1",
+ "bi" => "http://bi.wikipedia.com/wiki.cgi?$1",
+ "bn" => "http://bn.wikipedia.com/wiki.cgi?$1",
+ "bs" => "http://bs.wikipedia.org/wiki/$1",
+ "bo" => "http://bo.wikipedia.com/wiki.cgi?$1",
+ "ca" => "http://ca.wikipedia.com/wiki.cgi?$1",
+ "co" => "http://co.wikipedia.com/wiki.cgi?$1",
+ "cs" => "http://cs.wikipedia.org/wiki/$1",
+ "cy" => "http://cy.wikipedia.com/wiki.cgi?$1",
+ "da" => "http://da.wikipedia.org/wiki/$1",
+ "de" => "http://de.wikipedia.org/wiki/$1",
+ "dk" => "http://da.wikipedia.org/wiki/$1",
+ "dz" => "http://dz.wikipedia.com/wiki.cgi?$1",
+ "el" => "http://el.wikipedia.org/wiki/$1",
+ "en" => "http://www.wikipedia.org/wiki/$1", # May in future be renamed to en.wikipedia.org; should work as alternate
+ "eo" => "http://eo.wikipedia.org/wiki/$1",
+ "es" => "http://es.wikipedia.org/wiki/$1",
+ "et" => "http://et.wikipedia.com/wiki.cgi?$1",
+ "eu" => "http://eu.wikipedia.com/wiki.cgi?$1",
+ "fa" => "http://fa.wikipedia.com/wiki.cgi?$1",
+ "fi" => "http://fi.wikipedia.com/wiki.cgi?$1",
+ "fj" => "http://fj.wikipedia.com/wiki.cgi?$1",
+ "fo" => "http://fo.wikipedia.com/wiki.cgi?$1",
+ "fr" => "http://fr.wikipedia.org/wiki/$1",
+ "fy" => "http://fy.wikipedia.com/wiki.cgi?$1",
+ "ga" => "http://ga.wikipedia.com/wiki.cgi?$1",
+ "gl" => "http://gl.wikipedia.com/wiki.cgi?$1",
+ "gn" => "http://gn.wikipedia.com/wiki.cgi?$1",
+ "gu" => "http://gu.wikipedia.com/wiki.cgi?$1",
+ "ha" => "http://ha.wikipedia.com/wiki.cgi?$1",
+ "he" => "http://he.wikipedia.com/wiki.cgi?$1",
+ "hi" => "http://hi.wikipedia.com/wiki.cgi?$1",
+ "hr" => "http://hr.wikipedia.org/wiki/$1",
+ "hu" => "http://hu.wikipedia.com/wiki.cgi?$1",
+ "hy" => "http://hy.wikipedia.com/wiki.cgi?$1",
+ "ia" => "http://ia.wikipedia.com/wiki.cgi?$1",
+ "id" => "http://id.wikipedia.com/wiki.cgi?$1",
+ "ik" => "http://ik.wikipedia.com/wiki.cgi?$1",
+ "is" => "http://is.wikipedia.com/wiki.cgi?$1",
+ "it" => "http://it.wikipedia.com/wiki.cgi?$1",
+ "iu" => "http://iu.wikipedia.com/wiki.cgi?$1",
+ "ja" => "http://ja.wikipedia.org/wiki/$1",
+ "jv" => "http://jv.wikipedia.com/wiki.cgi?$1",
+ "ka" => "http://ka.wikipedia.com/wiki.cgi?$1",
+ "kk" => "http://kk.wikipedia.com/wiki.cgi?$1",
+ "kl" => "http://kl.wikipedia.com/wiki.cgi?$1",
+ "km" => "http://km.wikipedia.com/wiki.cgi?$1",
+ "kn" => "http://kn.wikipedia.com/wiki.cgi?$1",
+ "ko" => "http://ko.wikipedia.org/wiki/$1",
+ "ks" => "http://ks.wikipedia.com/wiki.cgi?$1",
+ "ku" => "http://ku.wikipedia.com/wiki.cgi?$1",
+ "ky" => "http://ky.wikipedia.com/wiki.cgi?$1",
+ "la" => "http://la.wikipedia.com/wiki.cgi?$1",
+ "lo" => "http://lo.wikipedia.com/wiki.cgi?$1",
+ "lv" => "http://lv.wikipedia.com/wiki.cgi?$1",
+ "mg" => "http://mg.wikipedia.com/wiki.cgi?$1",
+ "mi" => "http://mi.wikipedia.com/wiki.cgi?$1",
+ "mk" => "http://mk.wikipedia.com/wiki.cgi?$1",
+ "ml" => "http://ml.wikipedia.org/wiki/$1",
+ "mn" => "http://mn.wikipedia.com/wiki.cgi?$1",
+ "mo" => "http://mo.wikipedia.com/wiki.cgi?$1",
+ "mr" => "http://mr.wikipedia.com/wiki.cgi?$1",
+ "ms" => "http://ms.wikipedia.org/wiki/$1",
+ "my" => "http://my.wikipedia.com/wiki.cgi?$1",
+ "na" => "http://na.wikipedia.com/wiki.cgi?$1",
+ "ne" => "http://ne.wikipedia.com/wiki.cgi?$1",
+ "nl" => "http://nl.wikipedia.org/wiki/$1",
+ "no" => "http://no.wikipedia.com/wiki.cgi?$1",
+ "oc" => "http://oc.wikipedia.com/wiki.cgi?$1",
+ "om" => "http://om.wikipedia.com/wiki.cgi?$1",
+ "or" => "http://or.wikipedia.com/wiki.cgi?$1",
+ "pa" => "http://pa.wikipedia.com/wiki.cgi?$1",
+ "pl" => "http://pl.wikipedia.org/wiki/$1",
+ "ps" => "http://ps.wikipedia.com/wiki.cgi?$1",
+ "pt" => "http://pt.wikipedia.com/wiki.cgi?$1",
+ "qu" => "http://qu.wikipedia.com/wiki.cgi?$1",
+ "rm" => "http://rm.wikipedia.com/wiki.cgi?$1",
+ "rn" => "http://rn.wikipedia.com/wiki.cgi?$1",
+ "ro" => "http://ro.wikipedia.com/wiki.cgi?$1",
+ "ru" => "http://ru.wikipedia.org/wiki/$1",
+ "rw" => "http://rw.wikipedia.com/wiki.cgi?$1",
+ "sa" => "http://sa.wikipedia.com/wiki.cgi?$1",
+ "sd" => "http://sd.wikipedia.com/wiki.cgi?$1",
+ "sg" => "http://sg.wikipedia.com/wiki.cgi?$1",
+ "sh" => "http://sh.wikipedia.org/wiki/$1",
+ "si" => "http://si.wikipedia.com/wiki.cgi?$1",
+ "sk" => "http://sk.wikipedia.com/wiki.cgi?$1",
+ "sl" => "http://sl.wikipedia.com/wiki.cgi?$1",
+ "sm" => "http://sm.wikipedia.com/wiki.cgi?$1",
+ "sn" => "http://sn.wikipedia.com/wiki.cgi?$1",
+ "so" => "http://so.wikipedia.com/wiki.cgi?$1",
+ "sq" => "http://sq.wikipedia.com/wiki.cgi?$1",
+ "sr" => "http://sr.wikipedia.org/wiki/$1",
+ "ss" => "http://ss.wikipedia.com/wiki.cgi?$1",
+ "st" => "http://st.wikipedia.com/wiki.cgi?$1",
+ "su" => "http://su.wikipedia.com/wiki.cgi?$1",
+ "sv" => "http://sv.wikipedia.org/wiki/$1",
+ "sw" => "http://sw.wikipedia.com/wiki.cgi?$1",
+ "ta" => "http://ta.wikipedia.com/wiki.cgi?$1",
+ "te" => "http://te.wikipedia.com/wiki.cgi?$1",
+ "tg" => "http://tg.wikipedia.com/wiki.cgi?$1",
+ "th" => "http://th.wikipedia.com/wiki.cgi?$1",
+ "ti" => "http://ti.wikipedia.com/wiki.cgi?$1",
+ "tk" => "http://tk.wikipedia.com/wiki.cgi?$1",
+ "tl" => "http://tl.wikipedia.com/wiki.cgi?$1",
+ "tn" => "http://tn.wikipedia.com/wiki.cgi?$1",
+ "to" => "http://to.wikipedia.com/wiki.cgi?$1",
+ "tr" => "http://tr.wikipedia.org/wiki/$1",
+ "ts" => "http://ts.wikipedia.com/wiki.cgi?$1",
+ "tt" => "http://tt.wikipedia.com/wiki.cgi?$1",
+ "tw" => "http://tw.wikipedia.com/wiki.cgi?$1",
+ "ug" => "http://ug.wikipedia.com/wiki.cgi?$1",
+ "uk" => "http://uk.wikipedia.com/wiki.cgi?$1",
+ "ur" => "http://ur.wikipedia.com/wiki.cgi?$1",
+ "uz" => "http://uz.wikipedia.com/wiki.cgi?$1",
+ "vi" => "http://vi.wikipedia.com/wiki.cgi?$1",
+ "vo" => "http://vo.wikipedia.com/wiki.cgi?$1",
+ "wo" => "http://wo.wikipedia.com/wiki.cgi?$1",
+ "xh" => "http://xh.wikipedia.com/wiki.cgi?$1",
+ "yi" => "http://yi.wikipedia.com/wiki.cgi?$1",
+ "yo" => "http://yo.wikipedia.com/wiki.cgi?$1",
+ "za" => "http://za.wikipedia.com/wiki.cgi?$1",
+ "zh" => "http://zh.wikipedia.org/wiki/$1",
+ "zu" => "http://zu.wikipedia.com/wiki.cgi?$1"
+);
+?>
diff --git a/includes/LinkCache.php b/includes/LinkCache.php
new file mode 100644
index 000000000000..af8505236a8a
--- /dev/null
+++ b/includes/LinkCache.php
@@ -0,0 +1,109 @@
+<?
+# Cache for article titles and ids linked from one source
+
+class LinkCache {
+
+ /* private */ var $mGoodLinks, $mBadLinks, $mActive;
+ /* private */ var $mImageLinks;
+
+ function LinkCache()
+ {
+ $this->mActive = true;
+ $this->mGoodLinks = array();
+ $this->mBadLinks = array();
+ $this->mImageLinks = array();
+ }
+
+ function getGoodLinkID( $title )
+ {
+ if ( array_key_exists( $title, $this->mGoodLinks ) ) {
+ return $this->mGoodLinks[$title];
+ } else {
+ return 0;
+ }
+ }
+
+ function isBadLink( $title )
+ {
+ return in_array( $title, $this->mBadLinks );
+ }
+
+ function addGoodLink( $id, $title )
+ {
+ if ( $this->mActive ) {
+ $this->mGoodLinks[$title] = $id;
+ }
+ }
+
+ function addBadLink( $title )
+ {
+ if ( $this->mActive && ( ! $this->isBadLink( $title ) ) ) {
+ array_push( $this->mBadLinks, $title );
+ }
+ }
+
+ function addImageLink( $title )
+ {
+ if ( $this->mActive ) { $this->mImageLinks[$title] = 1; }
+ }
+
+ function clearBadLink( $title )
+ {
+ $index = array_search( $title, $this->mBadLinks );
+ if ( isset( $index ) ) {
+ unset( $this->mBadLinks[$index] );
+ }
+ }
+
+ function suspend() { $this->mActive = false; }
+ function resume() { $this->mActive = true; }
+ function getGoodLinks() { return $this->mGoodLinks; }
+ function getBadLinks() { return $this->mBadLinks; }
+ function getImageLinks() { return $this->mImageLinks; }
+
+ function addLink( $title )
+ {
+ if ( $this->isBadLink( $title ) ) { return 0; }
+ $id = $this->getGoodLinkID( $title );
+ if ( 0 != $id ) { return $id; }
+
+ $nt = Title::newFromDBkey( $title );
+ $ns = $nt->getNamespace();
+ $t = $nt->getDBkey();
+
+ if ( "" == $t ) { return 0; }
+ $sql = "SELECT HIGH_PRIORITY cur_id FROM cur WHERE cur_namespace=" .
+ "{$ns} AND cur_title='" . wfStrencode( $t ) . "'";
+ $res = wfQuery( $sql, "LinkCache::addLink" );
+
+ if ( 0 == wfNumRows( $res ) ) {
+ $id = 0;
+ } else {
+ $s = wfFetchObject( $res );
+ $id = $s->cur_id;
+ }
+ if ( 0 == $id ) { $this->addBadLink( $title ); }
+ else { $this->addGoodLink( $id, $title ); }
+ return $id;
+ }
+
+ function preFill( $fromtitle )
+ {
+ wfProfileIn( "LinkCache::preFill" );
+ # Note -- $fromtitle is a Title *object*
+ $dbkeyfrom = wfStrencode( $fromtitle->getPrefixedDBKey() );
+ $sql = "SELECT HIGH_PRIORITY cur_id,cur_namespace,cur_title
+ FROM cur,links
+ WHERE cur_id=l_to AND l_from='{$dbkeyfrom}'";
+ $res = wfQuery( $sql, "LinkCache::preFill" );
+ while( $s = wfFetchObject( $res ) ) {
+ $this->addGoodLink( $s->cur_id,
+ Title::makeName( $s->cur_namespace, $s->cur_title )
+ );
+ }
+ wfProfileOut();
+ }
+
+}
+
+?>
diff --git a/includes/LinksUpdate.php b/includes/LinksUpdate.php
new file mode 100644
index 000000000000..bf181ad6a5e4
--- /dev/null
+++ b/includes/LinksUpdate.php
@@ -0,0 +1,110 @@
+<?
+# See deferred.doc
+
+class LinksUpdate {
+
+ /* private */ var $mId, $mTitle;
+
+ function LinksUpdate( $id, $title )
+ {
+ $this->mId = $id;
+ $this->mTitle = $title;
+ }
+
+ function doUpdate()
+ {
+ global $wgLinkCache, $wgDBtransactions;
+ $fname = "LinksUpdate::doUpdate";
+ wfProfileIn( $fname );
+ $t = wfStrencode( $this->mTitle );
+
+ if( $wgDBtransactions ) {
+ $sql = "BEGIN";
+ wfQuery( $sql, $fname );
+ }
+
+ $sql = "DELETE FROM links WHERE l_from='{$t}'";
+ wfQuery( $sql, $fname );
+
+ $a = $wgLinkCache->getGoodLinks();
+ $sql = "";
+ if ( 0 != count( $a ) ) {
+ $sql = "INSERT INTO links (l_from,l_to) VALUES ";
+ $first = true;
+ foreach( $a as $lt => $lid ) {
+ if ( ! $first ) { $sql .= ","; }
+ $first = false;
+
+ $sql .= "('{$t}',{$lid})";
+ }
+ }
+ if ( "" != $sql ) { wfQuery( $sql, $fname ); }
+
+ $sql = "DELETE FROM brokenlinks WHERE bl_from={$this->mId}";
+ wfQuery( $sql, $fname );
+
+ $a = $wgLinkCache->getBadLinks();
+ $sql = "";
+ if ( 0 != count ( $a ) ) {
+ $sql = "INSERT INTO brokenlinks (bl_from,bl_to) VALUES ";
+ $first = true;
+ foreach( $a as $blt ) {
+ $blt = wfStrencode( $blt );
+ if ( ! $first ) { $sql .= ","; }
+ $first = false;
+
+ $sql .= "({$this->mId},'{$blt}')";
+ }
+ }
+ if ( "" != $sql ) { wfQuery( $sql, $fname ); }
+
+ $sql = "DELETE FROM imagelinks WHERE il_from='{$t}'";
+ wfQuery( $sql, $fname );
+
+ $a = $wgLinkCache->getImageLinks();
+ $sql = "";
+ if ( 0 != count ( $a ) ) {
+ $sql = "INSERT INTO imagelinks (il_from,il_to) VALUES ";
+ $first = true;
+ foreach( $a as $iname => $val ) {
+ $iname = wfStrencode( $iname );
+ if ( ! $first ) { $sql .= ","; }
+ $first = false;
+
+ $sql .= "('{$t}','{$iname}')";
+ }
+ }
+ if ( "" != $sql ) { wfQuery( $sql, $fname ); }
+
+ $sql = "SELECT bl_from FROM brokenlinks WHERE bl_to='{$t}'";
+ $res = wfQuery( $sql, $fname );
+ if ( 0 == wfNumRows( $res ) ) { return; }
+
+ $sql = "INSERT INTO links (l_from,l_to) VALUES ";
+ $now = wfTimestampNow();
+ $sql2 = "UPDATE cur SET cur_touched='{$now}' WHERE cur_id IN (";
+ $first = true;
+ while ( $row = wfFetchObject( $res ) ) {
+ if ( ! $first ) { $sql .= ","; $sql2 .= ","; }
+ $first = false;
+ $nl = wfStrencode( Article::nameOf( $row->bl_from ) );
+
+ $sql .= "('{$nl}',{$this->mId})";
+ $sql2 .= $row->bl_from;
+ }
+ $sql2 .= ")";
+ wfQuery( $sql, $fname );
+ wfQuery( $sql2, $fname );
+
+ $sql = "DELETE FROM brokenlinks WHERE bl_to='{$t}'";
+ wfQuery( $sql, $fname );
+
+ if( $wgDBtransactions ) {
+ $sql = "COMMIT";
+ wfQuery( $sql, $fname );
+ }
+ wfProfileOut();
+ }
+}
+
+?>
diff --git a/includes/LogPage.php b/includes/LogPage.php
new file mode 100644
index 000000000000..eca14cb48f03
--- /dev/null
+++ b/includes/LogPage.php
@@ -0,0 +1,118 @@
+<?
+# Class to simplify the use of log pages
+
+class LogPage {
+ /* private */ var $mTitle, $mContent, $mContentLoaded, $mId, $mComment;
+ var $mUpdateRecentChanges ;
+
+ function LogPage( $title, $defaulttext = "<ul>\n</ul>" )
+ {
+ # For now, assume title is correct dbkey
+ # and log pages always go in Wikipedia namespace
+ $this->mTitle = $title;
+ $this->mId = 0;
+ $this->mUpdateRecentChanges = true ;
+ $this->mContentLoaded = false;
+ $this->getContent( $defaulttext );
+ }
+
+ function getContent( $defaulttext = "<ul>\n</ul>" )
+ {
+ $sql = "SELECT cur_id,cur_text FROM cur " .
+ "WHERE cur_namespace=" . Namespace::getWikipedia() . " AND " .
+ "cur_title='" . wfStrencode($this->mTitle ) . "'";
+ $res = wfQuery( $sql, "LogPage::getContent" );
+
+ if( wfNumRows( $res ) > 0 ) {
+ $s = wfFetchObject( $res );
+ $this->mId = $s->cur_id;
+ $this->mContent = $s->cur_text;
+ } else {
+ $this->mId = 0;
+ $this->mContent = $defaulttext;
+ }
+ $this->mContentLoaded = true; # Well, sort of
+
+ return $this->mContent;
+ }
+
+ function saveContent()
+ {
+ global $wgUser;
+ $fname = "LogPage::saveContent";
+ $uid = $wgUser->getID();
+ $ut = wfStrencode( $wgUser->getName() );
+
+ if( !$this->mContentLoaded ) return false;
+ $now = date( "YmdHis" );
+ $won = wfInvertTimestamp( $now );
+ if($this->mId == 0) {
+ $sql = "INSERT INTO cur (cur_timestamp,cur_user,cur_user_text,
+ cur_namespace,cur_title,cur_text,cur_comment,cur_restrictions,inverse_timestamp)
+ VALUES ('{$now}', {$uid}, '{$ut}', " .
+ Namespace::getWikipedia() . ", '" .
+ wfStrencode( $this->mTitle ) . "', '" .
+ wfStrencode( $this->mContent ) . "', '" .
+ wfStrencode( $this->mComment ) . "', 'sysop', '{$won}')";
+ wfQuery( $sql, $fname );
+ $this->mId = wfInsertId();
+ } else {
+ $sql = "UPDATE cur SET cur_timestamp='{$now}', " .
+ "cur_user={$uid}, cur_user_text='{$ut}', " .
+ "cur_text='" . wfStrencode( $this->mContent ) . "', " .
+ "cur_comment='" . wfStrencode( $this->mComment ) . "', " .
+ "cur_restrictions='sysop', inverse_timestamp='{$won}' " .
+ "WHERE cur_id={$this->mId}";
+ wfQuery( $sql, $fname );
+ }
+
+ # And update recentchanges
+ if ( $this->mUpdateRecentChanges ) {
+ $sql = "INSERT INTO recentchanges (rc_timestamp,rc_cur_time,
+ rc_user,rc_user_text,rc_namespace,rc_title,rc_comment,
+ rc_cur_id) VALUES ('{$now}','{$now}',{$uid},'{$ut}',4,'" .
+ wfStrencode( $this->mTitle ) . "','" .
+ wfStrencode( $this->mComment ) . "',{$this->mId})";
+ wfQuery( $sql, $fname );
+ }
+ return true;
+ }
+
+ function addEntry( $action, $comment, $textaction = "" )
+ {
+ global $wgLang, $wgUser;
+ $this->getContent();
+
+ $ut = $wgUser->getName();
+ $uid = $wgUser->getID();
+ if( $uid ) {
+ $ul = "[[" .
+ $wgLang->getNsText( Namespace::getUser() ) .
+ ":{$ut}|{$ut}]]";
+ } else {
+ $ul = $ut;
+ }
+ $d = $wgLang->timeanddate( date( "YmdHis" ), false );
+
+ preg_match( "/^(.*?)<ul>(.*)$/sD", $this->mContent, $m );
+
+ if($textaction)
+ $this->mComment = $textaction;
+ else
+ $this->mComment = $action;
+
+ if ( "" == $comment ) {
+ $inline = "";
+ } else {
+ $inline = " <em>({$comment})</em>";
+ $this->mComment .= ": {$comment}";
+ }
+ $this->mContent = "{$m[1]}<ul><li>{$d} {$ul} {$action}{$inline}</li>\n{$m[2]}";
+
+ # TODO: automatic log rotation...
+
+ return $this->saveContent();
+ }
+}
+
+?>
diff --git a/includes/Namespace.php b/includes/Namespace.php
new file mode 100644
index 000000000000..03b351f9b17f
--- /dev/null
+++ b/includes/Namespace.php
@@ -0,0 +1,49 @@
+<?
+# This is a utility class with only static functions
+# for dealing with namespaces that encodes all the
+# "magic" behaviors of them based on index. The textual
+# names of the namespaces are handled by Language.php.
+
+class Namespace {
+
+ function getSpecial() { return -1; }
+ function getUser() { return 2; }
+ function getWikipedia() { return 4; }
+ function getImage() { return 6; }
+
+ function isMovable( $index )
+ {
+ if ( $index < 0 || $index > 5 ) { return false; }
+ return true;
+ }
+
+ function isTalk( $index )
+ {
+ if ( 1 == $index || 3 == $index || 5 == $index || 7 == $index ) {
+ return true;
+ }
+ return false;
+ }
+
+ # Get the talk namespace corresponding to the given index
+ #
+ function getTalk( $index )
+ {
+ if ( Namespace::isTalk( $index ) ) {
+ return $index;
+ } else {
+ return $index + 1;
+ }
+ }
+
+ function getSubject( $index )
+ {
+ if ( Namespace::isTalk( $index ) ) {
+ return $index - 1;
+ } else {
+ return $index;
+ }
+ }
+}
+
+?>
diff --git a/includes/OutputPage.php b/includes/OutputPage.php
new file mode 100644
index 000000000000..b1c97a1f813d
--- /dev/null
+++ b/includes/OutputPage.php
@@ -0,0 +1,1295 @@
+<?
+# See design.doc
+
+function linkToMathImage ( $tex, $outputhash )
+{
+ global $wgMathPath;
+ return "<img src=\"".$wgMathPath."/".$outputhash.".png\" alt=\"".wfEscapeHTML($tex)."\">";
+}
+
+function renderMath( $tex )
+{
+ global $wgUser, $wgMathDirectory, $wgTmpDirectory, $wgInputEncoding;
+ $mf = wfMsg( "math_failure" );
+ $munk = wfMsg( "math_unknown_error" );
+
+ $fname = "renderMath";
+
+ $math = $wgUser->getOption("math");
+ if ($math == 3)
+ return ('$ '.wfEscapeHTML($tex).' $');
+
+ $md5 = md5($tex);
+ $md5_sql = mysql_escape_string(pack("H32", $md5));
+ if ($math == 0)
+ $sql = "SELECT math_outputhash FROM math WHERE math_inputhash = '".$md5_sql."'";
+ else
+ $sql = "SELECT math_outputhash,math_html_conservativeness,math_html FROM math WHERE math_inputhash = '".$md5_sql."'";
+
+ $res = wfQuery( $sql, $fname );
+ if ( wfNumRows( $res ) == 0 )
+ {
+ $cmd = "./math/texvc ".escapeshellarg($wgTmpDirectory)." ".
+ escapeshellarg($wgMathDirectory)." ".escapeshellarg($tex)." ".escapeshellarg($wgInputEncoding);
+ $contents = `$cmd`;
+
+ if (strlen($contents) == 0)
+ return "<b>".$mf." (".$munk."): ".wfEscapeHTML($tex)."</b>";
+ $retval = substr ($contents, 0, 1);
+ if (($retval == "C") || ($retval == "M") || ($retval == "L")) {
+ if ($retval == "C")
+ $conservativeness = 2;
+ else if ($retval == "M")
+ $conservativeness = 1;
+ else
+ $conservativeness = 0;
+ $outdata = substr ($contents, 33);
+
+ $i = strpos($outdata, "\000");
+
+ $outhtml = substr($outdata, 0, $i);
+ $mathml = substr($outdata, $i+1);
+
+ $sql_html = "'".mysql_escape_string($outhtml)."'";
+ $sql_mathml = "'".mysql_escape_string($mathml)."'";
+ } else if (($retval == "c") || ($retval == "m") || ($retval == "l")) {
+ $outhtml = substr ($contents, 33);
+ if ($retval == "c")
+ $conservativeness = 2;
+ else if ($retval == "m")
+ $conservativeness = 1;
+ else
+ $conservativeness = 0;
+ $sql_html = "'".mysql_escape_string($outhtml)."'";
+ $mathml = '';
+ $sql_mathml = 'NULL';
+ } else if ($retval == "X") {
+ $outhtml = '';
+ $mathml = substr ($contents, 33);
+ $sql_html = 'NULL';
+ $sql_mathml = "'".mysql_escape_string($mathml)."'";
+ $conservativeness = 0;
+ } else if ($retval == "+") {
+ $outhtml = '';
+ $mathml = '';
+ $sql_html = 'NULL';
+ $sql_mathml = 'NULL';
+ $conservativeness = 0;
+ } else {
+ if ($retval == "E")
+ $errmsg = wfMsg( "math_lexing_error" );
+ else if ($retval == "S")
+ $errmsg = wfMsg( "math_syntax_error" );
+ else if ($retval == "F")
+ $errmsg = wfMsg( "math_unknown_function" );
+ else
+ $errmsg = $munk;
+ return "<h3>".$mf." (".$errmsg.substr($contents, 1)."): ".wfEscapeHTML($tex)."</h3>";
+ }
+
+ $outmd5 = substr ($contents, 1, 32);
+ if (!preg_match("/^[a-f0-9]{32}$/", $outmd5))
+ return "<b>".$mf." (".$munk."): ".wfEscapeHTML($tex)."</b>";
+
+ $outmd5_sql = mysql_escape_string(pack("H32", $outmd5));
+
+ $sql = "INSERT INTO math VALUES ('".$md5_sql."', '".$outmd5_sql."', ".$conservativeness.", ".$sql_html.", ".$sql_mathml.")";
+
+ $res = wfQuery( $sql, $fname );
+ # we don't really care if it fails
+
+ if (($math == 0) || ($rpage->math_html == '') || (($math == 1) && ($conservativeness != 2)) || (($math == 4) && ($conservativeness == 0)))
+ return linkToMathImage($tex, $outmd5);
+ else
+ return $outhtml;
+ } else {
+ $rpage = wfFetchObject ( $res );
+ $outputhash = unpack( "H32md5", $rpage->math_outputhash . " " );
+ $outputhash = $outputhash ['md5'];
+
+ if (($math == 0) || ($rpage->math_html == '') || (($math == 1) && ($rpage->math_html_conservativeness != 2)) || (($math == 4) && ($rpage->math_html_conservativeness == 0)))
+ return linkToMathImage ( $tex, $outputhash );
+ else
+ return $rpage->math_html;
+ }
+}
+
+class OutputPage {
+ var $mHeaders, $mCookies, $mMetatags, $mKeywords;
+ var $mLinktags, $mPagetitle, $mBodytext, $mDebugtext;
+ var $mHTMLtitle, $mRobotpolicy, $mIsarticle, $mPrintable;
+ var $mSubtitle, $mRedirect, $mAutonumber, $mHeadtext;
+ var $mLastModified;
+
+ var $mDTopen, $mLastSection; # Used for processing DL, PRE
+ var $mLanguageLinks, $mSupressQuickbar;
+
+ function OutputPage()
+ {
+ $this->mHeaders = $this->mCookies = $this->mMetatags =
+ $this->mKeywords = $this->mLinktags = array();
+ $this->mHTMLtitle = $this->mPagetitle = $this->mBodytext =
+ $this->mLastSection = $this->mRedirect = $this->mLastModified =
+ $this->mSubtitle = $this->mDebugtext = $this->mRobotpolicy = "";
+ $this->mIsarticle = $this->mPrintable = true;
+ $this->mSupressQuickbar = $this->mDTopen = $this->mPrintable = false;
+ $this->mLanguageLinks = array();
+ $this->mAutonumber = 0;
+ }
+
+ function addHeader( $name, $val ) { array_push( $this->mHeaders, "$name: $val" ) ; }
+ function addCookie( $name, $val ) { array_push( $this->mCookies, array( $name, $val ) ); }
+ function redirect( $url ) { $this->mRedirect = $url; }
+
+ # To add an http-equiv meta tag, precede the name with "http:"
+ function addMeta( $name, $val ) { array_push( $this->mMetatags, array( $name, $val ) ); }
+ function addKeyword( $text ) { array_push( $this->mKeywords, $text ); }
+ function addLink( $rel, $rev, $target ) { array_push( $this->mLinktags, array( $rel, $rev, $target ) ); }
+
+ function checkLastModified ( $timestamp )
+ {
+ global $wgLang, $wgCachePages, $wgUser;
+ if( !$wgCachePages ) return;
+ if( preg_match( '/MSIE ([1-4]|5\.0)/', $_SERVER["HTTP_USER_AGENT"] ) ) {
+ # IE 5.0 has probs with our caching
+ return;
+ }
+
+ if( $_SERVER["HTTP_IF_MODIFIED_SINCE"] != "" ) {
+ $ismodsince = wfUnix2Timestamp( strtotime( $_SERVER["HTTP_IF_MODIFIED_SINCE"] ) );
+ $lastmod = gmdate( "D, j M Y H:i:s", wfTimestamp2Unix(
+ max( $timestamp, $wgUser->mTouched ) ) ) . " GMT";
+
+ if( ($ismodsince >= $timestamp ) and $wgUser->validateCache( $ismodsince ) ) {
+ # Make sure you're in a place you can leave when you call us!
+ header( "HTTP/1.0 304 Not Modified" );
+ header( "Expires: Mon, 15 Jan 2001 00:00:00 GMT" ); # Cachers always validate the page!
+ header( "Cache-Control: private, must-revalidate, max-age=0" );
+ header( "Last-Modified: {$lastmod}" );
+ #wfDebug( "CACHED client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp\n", false );
+ exit;
+ } else {
+ #wfDebug( "READY client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp\n", false );
+ $this->mLastModified = $lastmod;
+ }
+ }
+ }
+
+ function setRobotpolicy( $str ) { $this->mRobotpolicy = $str; }
+ function setHTMLtitle( $name ) { $this->mHTMLtitle = $name; }
+ function setPageTitle( $name ) { $this->mPagetitle = $name; }
+ function getPageTitle() { return $this->mPagetitle; }
+ function setSubtitle( $str ) { $this->mSubtitle = $str; }
+ function getSubtitle() { return $this->mSubtitle; }
+ function setArticleFlag( $v ) { $this->mIsarticle = $v; }
+ function isArticle() { return $this->mIsarticle; }
+ function setPrintable() { $this->mPrintable = true; }
+ function isPrintable() { return $this->mPrintable; }
+
+ function getLanguageLinks() {
+ global $wgUseNewInterlanguage, $wgTitle, $wgLanguageCode;
+ global $wgDBconnection, $wgDBname, $wgDBintlname;
+
+ if ( ! $wgUseNewInterlanguage )
+ return $this->mLanguageLinks;
+
+ mysql_select_db( $wgDBintlname, $wgDBconnection ) or die(
+ htmlspecialchars(mysql_error()) );
+
+ $list = array();
+ $sql = "SELECT * FROM ilinks WHERE lang_from=\"" .
+ "{$wgLanguageCode}\" AND title_from=\"" . $wgTitle->getDBkey() . "\"";
+ $res = mysql_query( $sql, $wgDBconnection );
+
+ while ( $q = mysql_fetch_object ( $res ) ) {
+ $list[] = $q->lang_to . ":" . $q->title_to;
+ }
+ mysql_free_result( $res );
+ mysql_select_db( $wgDBname, $wgDBconnection ) or die(
+ htmlspecialchars(mysql_error()) );
+
+ return $list;
+ }
+
+ function supressQuickbar() { $this->mSupressQuickbar = true; }
+ function isQuickbarSupressed() { return $this->mSupressQuickbar; }
+
+ function addHTML( $text ) { $this->mBodytext .= $text; }
+ function addHeadtext( $text ) { $this->mHeadtext .= $text; }
+ function debug( $text ) { $this->mDebugtext .= $text; }
+
+ # First pass--just handle <nowiki> sections, pass the rest off
+ # to doWikiPass2() which does all the real work.
+ #
+
+ function addWikiText( $text, $linestart = true )
+ {
+ global $wgUseTeX;
+ wfProfileIn( "OutputPage::addWikiText" );
+ $unique = "3iyZiyA7iMwg5rhxP0Dcc9oTnj8qD1jm1Sfv4";
+ $unique2 = "4LIQ9nXtiYFPCSfitVwDw7EYwQlL4GeeQ7qSO";
+ $unique3 = "fPaA8gDfdLBqzj68Yjg9Hil3qEF8JGO0uszIp";
+ $nwlist = array();
+ $nwsecs = 0;
+ $mathlist = array();
+ $mathsecs = 0;
+ $prelist = array ();
+ $presecs = 0;
+ $stripped = "";
+ $stripped2 = "";
+ $stripped3 = "";
+
+ while ( "" != $text ) {
+ $p = preg_split( "/<\\s*nowiki\\s*>/i", $text, 2 );
+ $stripped .= $p[0];
+ if ( ( count( $p ) < 2 ) || ( "" == $p[1] ) ) { $text = ""; }
+ else {
+ $q = preg_split( "/<\\/\\s*nowiki\\s*>/i", $p[1], 2 );
+ ++$nwsecs;
+ $nwlist[$nwsecs] = wfEscapeHTMLTagsOnly($q[0]);
+ $stripped .= $unique;
+ $text = $q[1];
+ }
+ }
+
+ if( $wgUseTeX ) {
+ while ( "" != $stripped ) {
+ $p = preg_split( "/<\\s*math\\s*>/i", $stripped, 2 );
+ $stripped2 .= $p[0];
+ if ( ( count( $p ) < 2 ) || ( "" == $p[1] ) ) { $stripped = ""; }
+ else {
+ $q = preg_split( "/<\\/\\s*math\\s*>/i", $p[1], 2 );
+ ++$mathsecs;
+ $mathlist[$mathsecs] = renderMath($q[0]);
+ $stripped2 .= $unique2;
+ $stripped = $q[1];
+ }
+ }
+ } else {
+ $stripped2 = $stripped;
+ }
+
+ while ( "" != $stripped2 ) {
+ $p = preg_split( "/<\\s*pre\\s*>/i", $stripped2, 2 );
+ $stripped3 .= $p[0];
+ if ( ( count( $p ) < 2 ) || ( "" == $p[1] ) ) { $stripped2 = ""; }
+ else {
+ $q = preg_split( "/<\\/\\s*pre\\s*>/i", $p[1], 2 );
+ ++$presecs;
+ $prelist[$presecs] = "<pre>". wfEscapeHTMLTagsOnly($q[0]). "</pre>";
+ $stripped3 .= $unique3;
+ $stripped2 = $q[1];
+ }
+ }
+
+ $text = $this->doWikiPass2( $stripped3, $linestart );
+
+ for ( $i = 1; $i <= $presecs; ++$i ) {
+ $text = preg_replace( "/{$unique3}/", str_replace( '$', '\$', $prelist[$i] ), $text, 1 );
+ }
+
+ for ( $i = 1; $i <= $mathsecs; ++$i ) {
+ $text = preg_replace( "/{$unique2}/", str_replace( '$', '\$', $mathlist[$i] ), $text, 1 );
+ }
+
+ for ( $i = 1; $i <= $nwsecs; ++$i ) {
+ $text = preg_replace( "/{$unique}/", str_replace( '$', '\$', $nwlist[$i] ), $text, 1 );
+ }
+ $this->addHTML( $text );
+ wfProfileOut();
+ }
+
+ # Finally, all the text has been munged and accumulated into
+ # the object, let's actually output it:
+ #
+ function output()
+ {
+ global $wgUser, $wgLang, $wgDebugComments, $wgCookieExpiration;
+ global $wgInputEncoding, $wgOutputEncoding, $wgLanguageCode;
+ wfProfileIn( "OutputPage::output" );
+ $sk = $wgUser->getSkin();
+
+ wfProfileIn( "OutputPage::output-headers" );
+ if( $this->mLastModified != "" ) {
+ header( "Cache-Control: private, must-revalidate, max-age=0" );
+ header( "Last-modified: {$this->mLastModified}" );
+ } else {
+ header( "Cache-Control: no-cache" ); # Experimental - see below
+ header( "Pragma: no-cache" );
+ header( "Last-modified: " . gmdate( "D, j M Y H:i:s" ) . " GMT" );
+ }
+ header( "Expires: Mon, 15 Jan 2001 00:00:00 GMT" ); # Cachers always validate the page!
+
+ header( "Content-type: text/html; charset={$wgOutputEncoding}" );
+ header( "Content-language: {$wgLanguageCode}" );
+
+ if ( "" != $this->mRedirect ) {
+ header( "Location: {$this->mRedirect}" );
+ wfProfileOut();
+ return;
+ }
+
+ $exp = time() + $wgCookieExpiration;
+ foreach( $this->mCookies as $name => $val ) {
+ setcookie( $name, $val, $exp, "/" );
+ }
+ wfProfileOut();
+
+ wfProfileIn( "OutputPage::output-middle" );
+ $sk->initPage();
+ $this->out( $this->headElement() );
+
+ $this->out( "\n<body" );
+ $ops = $sk->getBodyOptions();
+ foreach ( $ops as $name => $val ) {
+ $this->out( " $name='$val'" );
+ }
+ $this->out( ">\n" );
+ if ( $wgDebugComments ) {
+ $this->out( "<!-- Wiki debugging output:\n" .
+ $this->mDebugtext . "-->\n" );
+ }
+ $this->out( $sk->beforeContent() );
+ wfProfileOut();
+
+ wfProfileIn( "OutputPage::output-bodytext" );
+ $this->out( $this->mBodytext );
+ wfProfileOut();
+ wfProfileIn( "OutputPage::output-after" );
+ $this->out( $sk->afterContent() );
+ wfProfileOut();
+
+ wfProfileOut(); # A hack - we can't report after here
+ $this->out( $this->reportTime() );
+
+ $this->out( "\n</body></html>" );
+ flush();
+ }
+
+ function out( $ins )
+ {
+ global $wgInputEncoding, $wgOutputEncoding, $wgLang;
+ if ( 0 == strcmp( $wgInputEncoding, $wgOutputEncoding ) ) {
+ $outs = $ins;
+ } else {
+ $outs = $wgLang->iconv( $wgInputEncoding, $wgOutputEncoding, $ins );
+ if ( false === $outs ) { $outs = $ins; }
+ }
+ print $outs;
+ }
+
+ function setEncodings()
+ {
+ global $HTTP_SERVER_VARS, $wgInputEncoding, $wgOutputEncoding;
+ global $wgUser, $wgLang;
+
+ $wgInputEncoding = strtolower( $wgInputEncoding );
+ $s = $HTTP_SERVER_VARS['HTTP_ACCEPT_CHARSET'];
+
+ if( $wgUser->getOption( 'altencoding' ) ) {
+ $wgLang->setAltEncoding();
+ return;
+ }
+
+ if ( "" == $s ) {
+ $wgOutputEncoding = strtolower( $wgOutputEncoding );
+ return;
+ }
+ $a = explode( ",", $s );
+ $best = 0.0;
+ $bestset = "*";
+
+ foreach ( $a as $s ) {
+ if ( preg_match( "/(.*);q=(.*)/", $s, $m ) ) {
+ $set = $m[1];
+ $q = (float)($m[2]);
+ } else {
+ $set = $s;
+ $q = 1.0;
+ }
+ if ( $q > $best ) {
+ $bestset = $set;
+ $best = $q;
+ }
+ }
+ #if ( "*" == $bestset ) { $bestset = "iso-8859-1"; }
+ if ( "*" == $bestset ) { $bestset = $wgOutputEncoding; }
+ $wgOutputEncoding = strtolower( $bestset );
+
+# Disable for now
+#
+ $wgOutputEncoding = $wgInputEncoding;
+ }
+
+ function reportTime()
+ {
+ global $wgRequestTime, $wgDebugLogFile, $HTTP_SERVER_VARS;
+ global $wgProfiling, $wgProfileStack;
+
+ list( $usec, $sec ) = explode( " ", microtime() );
+ $now = (float)$sec + (float)$usec;
+
+ list( $usec, $sec ) = explode( " ", $wgRequestTime );
+ $start = (float)$sec + (float)$usec;
+ $elapsed = $now - $start;
+
+ if ( "" != $wgDebugLogFile ) {
+ $prof = "";
+ if( $wgProfiling and count( $wgProfileStack ) ) {
+ $lasttime = $start;
+ foreach( $wgProfileStack as $ile ) {
+ # "foo::bar 99 0.12345 1 0.23456 2"
+ if( preg_match( '/^(\S+)\s+([0-9]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)/', $ile, $m ) ) {
+ $thisstart = (float)$m[3] + (float)$m[4] - $start;
+ $thisend = (float)$m[5] + (float)$m[6] - $start;
+ $thiselapsed = $thisend - $thisstart;
+ $thispercent = $thiselapsed / $elapsed * 100.0;
+
+ $prof .= sprintf( "\tat %04.3f in %04.3f (%2.1f%%) - %s %s\n",
+ $thisstart, $thiselapsed, $thispercent,
+ str_repeat( "*", $m[2] ), $m[1] );
+ $lasttime = $thistime;
+ #$prof .= "\t(^ $ile)\n";
+ } else {
+ $prof .= "\t?broken? $ile\n";
+ }
+ }
+ }
+
+ if( $forward = $HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR'] )
+ $forward = " forwarded for $forward";
+ if( $client = $HTTP_SERVER_VARS['HTTP_CLIENT_IP'] )
+ $forward .= " client IP $client";
+ if( $from = $HTTP_SERVER_VARS['HTTP_FROM'] )
+ $forward .= " from $from";
+ if( $forward )
+ $forward = "\t(proxied via {$HTTP_SERVER_VARS['REMOTE_ADDR']}{$forward})";
+ $log = sprintf( "%s\t%04.3f\t%s\n",
+ date( "YmdHis" ), $elapsed,
+ urldecode( $HTTP_SERVER_VARS['REQUEST_URI'] . $forward ) );
+ error_log( $log . $prof, 3, $wgDebugLogFile );
+ }
+ $com = sprintf( "<!-- Time since request: %01.2f secs. -->",
+ $elapsed );
+ return $com;
+ }
+
+ # Note: these arguments are keys into wfMsg(), not text!
+ #
+ function errorpage( $title, $msg )
+ {
+ global $wgTitle;
+
+ $this->mDebugtext .= "Original title: " .
+ $wgTitle->getPrefixedText() . "\n";
+ $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
+ $this->setPageTitle( wfMsg( $title ) );
+ $this->setRobotpolicy( "noindex,nofollow" );
+ $this->setArticleFlag( false );
+
+ $this->mBodytext = "";
+ $this->addHTML( "<p>" . wfMsg( $msg ) . "\n" );
+ $this->returnToMain( false );
+
+ $this->output();
+ exit;
+ }
+
+ function sysopRequired()
+ {
+ global $wgUser;
+
+ $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
+ $this->setPageTitle( wfMsg( "sysoptitle" ) );
+ $this->setRobotpolicy( "noindex,nofollow" );
+ $this->setArticleFlag( false );
+ $this->mBodytext = "";
+
+ $sk = $wgUser->getSkin();
+ $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" );
+ $text = str_replace( "$1", $ap, wfMsg( "sysoptext" ) );
+ $this->addHTML( $text );
+ $this->returnToMain();
+ }
+
+ function developerRequired()
+ {
+ global $wgUser;
+
+ $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
+ $this->setPageTitle( wfMsg( "developertitle" ) );
+ $this->setRobotpolicy( "noindex,nofollow" );
+ $this->setArticleFlag( false );
+ $this->mBodytext = "";
+
+ $sk = $wgUser->getSkin();
+ $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" );
+ $text = str_replace( "$1", $ap, wfMsg( "developertext" ) );
+ $this->addHTML( $text );
+ $this->returnToMain();
+ }
+
+ function databaseError( $fname )
+ {
+ global $wgUser;
+
+ $this->setPageTitle( wfMsg( "databaseerror" ) );
+ $this->setRobotpolicy( "noindex,nofollow" );
+ $this->setArticleFlag( false );
+
+ $msg = str_replace( "$1", htmlspecialchars( wfLastDBquery() ), wfMsg( "dberrortext" ) );
+ $msg = str_replace( "$2", htmlspecialchars( $fname ), $msg );
+ $msg = str_replace( "$3", wfLastErrno(), $msg );
+ $msg = str_replace( "$4", htmlspecialchars( wfLastError() ), $msg );
+
+ $sk = $wgUser->getSkin();
+ $shlink = $sk->makeKnownLink( wfMsg( "searchhelppage" ),
+ wfMsg( "searchingwikipedia" ) );
+ $msg = str_replace( "$5", $shlink, $msg );
+
+ $this->mBodytext = $msg;
+ $this->output();
+ exit();
+ }
+
+ function readOnlyPage()
+ {
+ global $wgUser, $wgReadOnlyFile;
+
+ $this->setPageTitle( wfMsg( "readonly" ) );
+ $this->setRobotpolicy( "noindex,nofollow" );
+ $this->setArticleFlag( false );
+
+ $reason = implode( "", file( $wgReadOnlyFile ) );
+ $text = str_replace( "$1", $reason, wfMsg( "readonlytext" ) );
+ $this->addHTML( $text );
+ $this->returnToMain( false );
+ }
+
+ function fatalError( $message )
+ {
+ $this->setPageTitle( wfMsg( "internalerror" ) );
+ $this->setRobotpolicy( "noindex,nofollow" );
+ $this->setArticleFlag( false );
+
+ $this->mBodytext = $message;
+ $this->output();
+ exit;
+ }
+
+ function unexpectedValueError( $name, $val )
+ {
+ $msg = str_replace( "$1", $name, wfMsg( "unexpected" ) );
+ $msg = str_replace( "$2", $val, $msg );
+ $this->fatalError( $msg );
+ }
+
+ function fileCopyError( $old, $new )
+ {
+ $msg = str_replace( "$1", $old, wfMsg( "filecopyerror" ) );
+ $msg = str_replace( "$2", $new, $msg );
+ $this->fatalError( $msg );
+ }
+
+ function fileRenameError( $old, $new )
+ {
+ $msg = str_replace( "$1", $old, wfMsg( "filerenameerror" ) );
+ $msg = str_replace( "$2", $new, $msg );
+ $this->fatalError( $msg );
+ }
+
+ function fileDeleteError( $name )
+ {
+ $msg = str_replace( "$1", $name, wfMsg( "filedeleteerror" ) );
+ $this->fatalError( $msg );
+ }
+
+ function fileNotFoundError( $name )
+ {
+ $msg = str_replace( "$1", $name, wfMsg( "filenotfound" ) );
+ $this->fatalError( $msg );
+ }
+
+ function returnToMain( $auto = true )
+ {
+ global $wgUser, $wgOut, $returnto;
+
+ $sk = $wgUser->getSkin();
+ if ( "" == $returnto ) {
+ $returnto = wfMsg( "mainpage" );
+ }
+ $link = $sk->makeKnownLink( $returnto, "" );
+
+ $r = str_replace( "$1", $link, wfMsg( "returnto" ) );
+ if ( $auto ) {
+ $wgOut->addMeta( "http:Refresh", "10;url=" .
+ wfLocalUrlE( wfUrlencode( $returnto ) ) );
+ }
+ $wgOut->addHTML( "\n<p>$r\n" );
+ }
+
+ # Well, OK, it's actually about 14 passes. But since all the
+ # hard lifting is done inside PHP's regex code, it probably
+ # wouldn't speed things up much to add a real parser.
+ #
+ function doWikiPass2( $text, $linestart )
+ {
+ global $wgUser;
+ wfProfileIn( "OutputPage::doWikiPass2" );
+
+ $text = $this->removeHTMLtags( $text );
+ $text = $this->replaceVariables( $text );
+
+ $text = preg_replace( "/(^|\n)-----*/", "\\1<hr>", $text );
+ $text = str_replace ( "<HR>", "<hr>", $text );
+
+ $text = $this->doQuotes( $text );
+ $text = $this->doHeadings( $text );
+ $text = $this->doBlockLevels( $text, $linestart );
+
+ $text = $this->replaceExternalLinks( $text );
+ $text = $this->replaceInternalLinks ( $text );
+
+ $text = $this->magicISBN( $text );
+ $text = $this->magicRFC( $text );
+ $text = $this->autoNumberHeadings( $text );
+
+ $sk = $wgUser->getSkin();
+ $text = $sk->transformContent( $text );
+
+ wfProfileOut();
+ return $text;
+ }
+
+ /* private */ function doQuotes( $text )
+ {
+ $text = preg_replace( "/'''(.+)'''/mU", "<strong>\$1</strong>", $text );
+ $text = preg_replace( "/''(.+)''/mU", "<em>\$1</em>", $text );
+ return $text;
+ }
+
+ /* private */ function doHeadings( $text )
+ {
+ for ( $i = 6; $i >= 1; --$i ) {
+ $h = substr( "======", 0, $i );
+ $text = preg_replace( "/^{$h}([^=]+){$h}(\\s|$)/m",
+ "<h{$i}>\\1</h{$i}>\\2", $text );
+ }
+ return $text;
+ }
+
+ # Note: we have to do external links before the internal ones,
+ # and otherwise take great care in the order of things here, so
+ # that we don't end up interpreting some URLs twice.
+
+ /* private */ function replaceExternalLinks( $text )
+ {
+ wfProfileIn( "OutputPage::replaceExternalLinks" );
+ $text = $this->subReplaceExternalLinks( $text, "http", true );
+ $text = $this->subReplaceExternalLinks( $text, "https", true );
+ $text = $this->subReplaceExternalLinks( $text, "ftp", false );
+ $text = $this->subReplaceExternalLinks( $text, "gopher", false );
+ $text = $this->subReplaceExternalLinks( $text, "news", false );
+ $text = $this->subReplaceExternalLinks( $text, "mailto", false );
+ wfProfileOut();
+ return $text;
+ }
+
+ /* private */ function subReplaceExternalLinks( $s, $protocol, $autonumber )
+ {
+ global $wgUser, $printable;
+ global $wgAllowExternalImages;
+
+
+ $unique = "4jzAfzB8hNvf4sqyO9Edd8pSmk9rE2in0Tgw3";
+ $uc = "A-Za-z0-9_\\/~%\\-+&*#?!=()@\\x80-\\xFF";
+
+ # this is the list of separators that should be ignored if they
+ # are the last character of an URL but that should be included
+ # if they occur within the URL, e.g. "go to www.foo.com, where .."
+ # in this case, the last comma should not become part of the URL,
+ # but in "www.foo.com/123,2342,32.htm" it should.
+ $sep = ",;\.:";
+ $fnc = "A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF";
+ $images = "gif|png|jpg|jpeg";
+
+ # PLEASE NOTE: The curly braces { } are not part of the regex,
+ # they are interpreted as part of the string (used to tell PHP
+ # that the content of the string should be inserted there).
+ $e1 = "/(^|[^\\[])({$protocol}:)([{$uc}{$sep}]+)\\/([{$fnc}]+)\\." .
+ "((?i){$images})([^{$uc}]|$)/";
+
+ $e2 = "/(^|[^\\[])({$protocol}:)(([".$uc."]|[".$sep."][".$uc."])+)([^". $uc . $sep. "]|[".$sep."]|$)/";
+ $sk = $wgUser->getSkin();
+
+ if ( $autonumber and $wgAllowExternalImages) { # Use img tags only for HTTP urls
+ $s = preg_replace( $e1, "\\1" . $sk->makeImage( "{$unique}:\\3" .
+ "/\\4.\\5", "\\4.\\5" ) . "\\6", $s );
+ }
+ $s = preg_replace( $e2, "\\1" . "<a href=\"{$unique}:\\3\"" .
+ $sk->getExternalLinkAttributes( "{$unique}:\\3", wfEscapeHTML(
+ "{$unique}:\\3" ) ) . ">" . wfEscapeHTML( "{$unique}:\\3" ) .
+ "</a>\\5", $s );
+ $s = str_replace( $unique, $protocol, $s );
+
+ $a = explode( "[{$protocol}:", " " . $s );
+ $s = array_shift( $a );
+ $s = substr( $s, 1 );
+
+ $e1 = "/^([{$uc}"."{$sep}]+)](.*)\$/sD";
+ $e2 = "/^([{$uc}"."{$sep}]+)\\s+([^\\]]+)](.*)\$/sD";
+
+ foreach ( $a as $line ) {
+ if ( preg_match( $e1, $line, $m ) ) {
+ $link = "{$protocol}:{$m[1]}";
+ $trail = $m[2];
+ if ( $autonumber ) { $text = "[" . ++$this->mAutonumber . "]"; }
+ else { $text = wfEscapeHTML( $link ); }
+ } else if ( preg_match( $e2, $line, $m ) ) {
+ $link = "{$protocol}:{$m[1]}";
+ $text = $m[2];
+ $trail = $m[3];
+ } else {
+ $s .= "[{$protocol}:" . $line;
+ continue;
+ }
+ if ( $printable == "yes") $paren = " (<i>" . htmlspecialchars ( $link ) . "</i>)";
+ else $paren = "";
+ $la = $sk->getExternalLinkAttributes( $link, $text );
+ $s .= "<a href='{$link}'{$la}>{$text}</a>{$paren}{$trail}";
+
+ }
+ return $s;
+ }
+
+ /* private */ function replaceInternalLinks( $s )
+ {
+ global $wgTitle, $wgUser, $wgLang;
+ global $wgLinkCache, $wgInterwikiMagic;
+ global $wgNamespacesWithSubpages;
+ wfProfileIn( "OutputPage::replaceInternalLinks" );
+
+ $tc = Title::legalChars() . "#";
+ $sk = $wgUser->getSkin();
+
+ $a = explode( "[[", " " . $s );
+ $s = array_shift( $a );
+ $s = substr( $s, 1 );
+
+ $e1 = "/^([{$tc}]+)\\|([^]]+)]](.*)\$/sD";
+ $e2 = "/^([{$tc}]+)]](.*)\$/sD";
+
+ foreach ( $a as $line ) {
+ if ( preg_match( $e1, $line, $m ) ) { # page with alternate text
+
+ $text = $m[2];
+ $trail = $m[3];
+
+ } else if ( preg_match( $e2, $line, $m ) ) { # page with normal text
+
+ $text = "";
+ $trail = $m[2];
+ }
+
+ else { # Invalid form; output directly
+ $s .= "[[" . $line ;
+ continue;
+ }
+ if(substr($m[1],0,1)=="/") { # subpage
+ if(substr($m[1],-1,1)=="/") { # / at end means we don't want the slash to be shown
+ $m[1]=substr($m[1],1,strlen($m[1])-2);
+ $noslash=$m[1];
+
+ } else {
+ $noslash=substr($m[1],1);
+ }
+ if($wgNamespacesWithSubpages[$wgTitle->getNamespace()]) { # subpages allowed here
+ $link = $wgTitle->getPrefixedText(). "/" . trim($noslash);
+ if(!$text) {
+ $text= $m[1];
+ } # this might be changed for ugliness reasons
+ } else {
+ $link = $noslash; # no subpage allowed, use standard link
+ }
+ } else { # no subpage
+ $link = $m[1];
+ }
+
+ if ( preg_match( "/^([A-Za-z\\x80-\\xff]+):(.*)\$/", $link, $m ) ) {
+ $pre = strtolower( $m[1] );
+ $suf = $m[2];
+ if ( $wgLang->getNsIndex( $pre ) ==
+ Namespace::getImage() ) {
+ $nt = Title::newFromText( $suf );
+ $name = $nt->getDBkey();
+ if ( "" == $text ) { $text = $nt->GetText(); }
+
+ $wgLinkCache->addImageLink( $name );
+ $s .= $sk->makeImageLink( $name,
+ wfImageUrl( $name ), $text );
+ $s .= $trail;
+ } else if ( "media" == $pre ) {
+ $nt = Title::newFromText( $suf );
+ $name = $nt->getDBkey();
+ if ( "" == $text ) { $text = $nt->GetText(); }
+
+ $wgLinkCache->addImageLink( $name );
+ $s .= $sk->makeMediaLink( $name,
+ wfImageUrl( $name ), $text );
+ $s .= $trail;
+ } else {
+ $l = $wgLang->getLanguageName( $pre );
+ if ( "" == $l or !$wgInterwikiMagic or
+ Namespace::isTalk( $wgTitle->getNamespace() ) ) {
+ if ( "" == $text ) { $text = $link; }
+ $s .= $sk->makeLink( $link, $text, "", $trail );
+ } else {
+ array_push( $this->mLanguageLinks, "$pre:$suf" );
+ $s .= $trail;
+ }
+ }
+# } else if ( 0 == strcmp( "##", substr( $link, 0, 2 ) ) ) {
+# $link = substr( $link, 2 );
+# $s .= "<a name=\"{$link}\">{$text}</a>{$trail}";
+ } else {
+ if ( "" == $text ) { $text = $link; }
+ $s .= $sk->makeLink( $link, $text, "", $trail );
+ }
+ }
+ wfProfileOut();
+ return $s;
+ }
+
+ # Some functions here used by doBlockLevels()
+ #
+ /* private */ function closeParagraph()
+ {
+ $result = "";
+ if ( 0 != strcmp( "p", $this->mLastSection ) &&
+ 0 != strcmp( "", $this->mLastSection ) ) {
+ $result = "</" . $this->mLastSection . ">";
+ }
+ $this->mLastSection = "";
+ return $result;
+ }
+ # getCommon() returns the length of the longest common substring
+ # of both arguments, starting at the beginning of both.
+ #
+ /* private */ function getCommon( $st1, $st2 )
+ {
+ $fl = strlen( $st1 );
+ $shorter = strlen( $st2 );
+ if ( $fl < $shorter ) { $shorter = $fl; }
+
+ for ( $i = 0; $i < $shorter; ++$i ) {
+ if ( $st1{$i} != $st2{$i} ) { break; }
+ }
+ return $i;
+ }
+ # These next three functions open, continue, and close the list
+ # element appropriate to the prefix character passed into them.
+ #
+ /* private */ function openList( $char )
+ {
+ $result = $this->closeParagraph();
+
+ if ( "*" == $char ) { $result .= "<ul><li>"; }
+ else if ( "#" == $char ) { $result .= "<ol><li>"; }
+ else if ( ":" == $char ) { $result .= "<dl><dd>"; }
+ else if ( ";" == $char ) {
+ $result .= "<dl><dt>";
+ $this->mDTopen = true;
+ }
+ else { $result = "<!-- ERR 1 -->"; }
+
+ return $result;
+ }
+
+ /* private */ function nextItem( $char )
+ {
+ if ( "*" == $char || "#" == $char ) { return "</li><li>"; }
+ else if ( ":" == $char || ";" == $char ) {
+ $close = "</dd>";
+ if ( $this->mDTopen ) { $close = "</dt>"; }
+ if ( ";" == $char ) {
+ $this->mDTopen = true;
+ return $close . "<dt>";
+ } else {
+ $this->mDTopen = false;
+ return $close . "<dd>";
+ }
+ }
+ return "<!-- ERR 2 -->";
+ }
+
+ /* private */function closeList( $char )
+ {
+ if ( "*" == $char ) { return "</li></ul>"; }
+ else if ( "#" == $char ) { return "</li></ol>"; }
+ else if ( ":" == $char ) {
+ if ( $this->mDTopen ) {
+ $this->mDTopen = false;
+ return "</dt></dl>";
+ } else {
+ return "</dd></dl>";
+ }
+ }
+ return "<!-- ERR 3 -->";
+ }
+
+ /* private */ function doBlockLevels( $text, $linestart )
+ {
+ wfProfileIn( "OutputPage::doBlockLevels" );
+ # Parsing through the text line by line. The main thing
+ # happening here is handling of block-level elements p, pre,
+ # and making lists from lines starting with * # : etc.
+ #
+ $a = explode( "\n", $text );
+ $text = $lastPref = "";
+ $this->mDTopen = $inBlockElem = false;
+
+ if ( ! $linestart ) { $text .= array_shift( $a ); }
+ foreach ( $a as $t ) {
+ if ( "" != $text ) { $text .= "\n"; }
+
+ $oLine = $t;
+ $opl = strlen( $lastPref );
+ $npl = strspn( $t, "*#:;" );
+ $pref = substr( $t, 0, $npl );
+ $pref2 = str_replace( ";", ":", $pref );
+ $t = substr( $t, $npl );
+
+ if ( 0 != $npl && 0 == strcmp( $lastPref, $pref2 ) ) {
+ $text .= $this->nextItem( substr( $pref, -1 ) );
+
+ if ( ";" == substr( $pref, -1 ) ) {
+ $cpos = strpos( $t, ":" );
+ if ( ! ( false === $cpos ) ) {
+ $term = substr( $t, 0, $cpos );
+ $text .= $term . $this->nextItem( ":" );
+ $t = substr( $t, $cpos + 1 );
+ }
+ }
+ } else if (0 != $npl || 0 != $opl) {
+ $cpl = $this->getCommon( $pref, $lastPref );
+
+ while ( $cpl < $opl ) {
+ $text .= $this->closeList( $lastPref{$opl-1} );
+ --$opl;
+ }
+ if ( $npl <= $cpl && $cpl > 0 ) {
+ $text .= $this->nextItem( $pref{$cpl-1} );
+ }
+ while ( $npl > $cpl ) {
+ $char = substr( $pref, $cpl, 1 );
+ $text .= $this->openList( $char );
+
+ if ( ";" == $char ) {
+ $cpos = strpos( $t, ":" );
+ if ( ! ( false === $cpos ) ) {
+ $term = substr( $t, 0, $cpos );
+ $text .= $term . $this->nextItem( ":" );
+ $t = substr( $t, $cpos + 1 );
+ }
+ }
+ ++$cpl;
+ }
+ $lastPref = $pref2;
+ }
+ if ( 0 == $npl ) { # No prefix--go to paragraph mode
+ if ( preg_match(
+ "/(<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6)/i", $t ) ) {
+ $text .= $this->closeParagraph();
+ $inBlockElem = true;
+ }
+ if ( ! $inBlockElem ) {
+ if ( " " == $t{0} ) {
+ $newSection = "pre";
+ # $t = wfEscapeHTML( $t );
+ }
+ else { $newSection = "p"; }
+
+ if ( 0 == strcmp( "", trim( $oLine ) ) ) {
+ $text .= $this->closeParagraph();
+ $text .= "<" . $newSection . ">";
+ } else if ( 0 != strcmp( $this->mLastSection,
+ $newSection ) ) {
+ $text .= $this->closeParagraph();
+ if ( 0 != strcmp( "p", $newSection ) ) {
+ $text .= "<" . $newSection . ">";
+ }
+ }
+ $this->mLastSection = $newSection;
+ }
+ if ( $inBlockElem &&
+ preg_match( "/(<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6)/i", $t ) ) {
+ $inBlockElem = false;
+ }
+ }
+ $text .= $t;
+ }
+ while ( $npl ) {
+ $text .= $this->closeList( $pref2{$npl-1} );
+ --$npl;
+ }
+ if ( "" != $this->mLastSection ) {
+ if ( "p" != $this->mLastSection ) {
+ $text .= "</" . $this->mLastSection . ">";
+ }
+ $this->mLastSection = "";
+ }
+ wfProfileOut();
+ return $text;
+ }
+
+ /* private */ function replaceVariables( $text )
+ {
+ global $wgLang;
+ wfProfileIn( "OutputPage:replaceVariables" );
+
+ $v = date( "m" );
+ $text = str_replace( "{{CURRENTMONTH}}", $v, $text );
+ $v = $wgLang->getMonthName( date( "n" ) );
+ $text = str_replace( "{{CURRENTMONTHNAME}}", $v, $text );
+ $v = $wgLang->getMonthNameGen( date( "n" ) );
+ $text = str_replace( "{{CURRENTMONTHNAMEGEN}}", $v, $text );
+ $v = date( "j" );
+ $text = str_replace( "{{CURRENTDAY}}", $v, $text );
+ $v = $wgLang->getWeekdayName( date( "w" )+1 );
+ $text = str_replace( "{{CURRENTDAYNAME}}", $v, $text );
+ $v = date( "Y" );
+ $text = str_replace( "{{CURRENTYEAR}}", $v, $text );
+ $v = $wgLang->time( date( "YmdHis" ), false );
+ $text = str_replace( "{{CURRENTTIME}}", $v, $text );
+
+ if ( false !== strstr( $text, "{{NUMBEROFARTICLES}}" ) ) {
+ $v = wfNumberOfArticles();
+ $text = str_replace( "{{NUMBEROFARTICLES}}", $v, $text );
+ }
+ wfProfileOut();
+ return $text;
+ }
+
+ /* private */ function removeHTMLtags( $text )
+ {
+ wfProfileIn( "OutputPage::removeHTMLtags" );
+ $htmlpairs = array( # Tags that must be closed
+ "b", "i", "u", "font", "big", "small", "sub", "sup", "h1",
+ "h2", "h3", "h4", "h5", "h6", "cite", "code", "em", "s",
+ "strike", "strong", "tt", "var", "div", "center",
+ "blockquote", "ol", "ul", "dl", "table", "caption", "pre",
+ "ruby", "rt" , "rb" , "rp"
+ );
+ $htmlsingle = array(
+ "br", "p", "hr", "li", "dt", "dd"
+ );
+ $htmlnest = array( # Tags that can be nested--??
+ "table", "tr", "td", "th", "div", "blockquote", "ol", "ul",
+ "dl", "font", "big", "small", "sub", "sup"
+ );
+ $tabletags = array( # Can only appear inside table
+ "td", "th", "tr"
+ );
+
+ $htmlsingle = array_merge( $tabletags, $htmlsingle );
+ $htmlelements = array_merge( $htmlsingle, $htmlpairs );
+
+ $htmlattrs = array( # Allowed attributes--no scripting, etc.
+ "title", "align", "lang", "dir", "width", "height",
+ "bgcolor", "clear", /* BR */ "noshade", /* HR */
+ "cite", /* BLOCKQUOTE, Q */ "size", "face", "color",
+ /* FONT */ "type", "start", "value", "compact",
+ /* For various lists, mostly deprecated but safe */
+ "summary", "width", "border", "frame", "rules",
+ "cellspacing", "cellpadding", "valign", "char",
+ "charoff", "colgroup", "col", "span", "abbr", "axis",
+ "headers", "scope", "rowspan", "colspan", /* Tables */
+ "id", "class", "name", "style" /* For CSS */
+ );
+
+ # Remove HTML comments
+ $text = preg_replace( "/<!--.*-->/sU", "", $text );
+
+ $bits = explode( "<", $text );
+ $text = array_shift( $bits );
+ $tagstack = array(); $tablestack = array();
+
+ foreach ( $bits as $x ) {
+ $prev = error_reporting( E_ALL & ~( E_NOTICE | E_WARNING ) );
+ preg_match( "/^(\\/?)(\\w+)([^>]*)(\\/{0,1}>)([^<]*)$/",
+ $x, $regs );
+ list( $qbar, $slash, $t, $params, $brace, $rest ) = $regs;
+ error_reporting( $prev );
+
+ $badtag = 0 ;
+ if ( in_array( $t = strtolower( $t ), $htmlelements ) ) {
+ # Check our stack
+ if ( $slash ) {
+ # Closing a tag...
+ if ( ! in_array( $t, $htmlsingle ) &&
+ ( $ot = array_pop( $tagstack ) ) != $t ) {
+ array_push( $tagstack, $ot );
+ $badtag = 1;
+ } else {
+ if ( $t == "table" ) {
+ $tagstack = array_pop( $tablestack );
+ }
+ $newparams = "";
+ }
+ } else {
+ # Keep track for later
+ if ( in_array( $t, $tabletags ) &&
+ ! in_array( "table", $tagstack ) ) {
+ $badtag = 1;
+ } else if ( in_array( $t, $tagstack ) &&
+ ! in_array ( $t , $htmlnest ) ) {
+ $badtag = 1 ;
+ } else if ( ! in_array( $t, $htmlsingle ) ) {
+ if ( $t == "table" ) {
+ array_push( $tablestack, $tagstack );
+ $tagstack = array();
+ }
+ array_push( $tagstack, $t );
+ }
+ # Strip non-approved attributes from the tag
+ $newparams = preg_replace(
+ "/(\\w+)(\\s*=\\s*([^\\s\">]+|\"[^\">]*\"))?/e",
+ "(in_array(strtolower(\"\$1\"),\$htmlattrs)?(\"\$1\".((\"x\$3\" != \"x\")?\"=\$3\":'')):'')",
+ $params);
+ }
+ if ( ! $badtag ) {
+ $rest = str_replace( ">", "&gt;", $rest );
+ $text .= "<$slash$t$newparams$brace$rest";
+ continue;
+ }
+ }
+ $text .= "&lt;" . str_replace( ">", "&gt;", $x);
+ }
+ # Close off any remaining tags
+ while ( $t = array_pop( $tagstack ) ) {
+ $text .= "</$t>\n";
+ if ( $t == "table" ) { $tagstack = array_pop( $tablestack ); }
+ }
+ wfProfileOut();
+ return $text;
+ }
+
+ /* private */ function autoNumberHeadings( $text )
+ {
+ global $wgUser;
+ if ( 1 != $wgUser->getOption( "numberheadings" ) ) {
+ return $text;
+ }
+ $j = 0;
+ $n = -1;
+ for ( $i = 0; $i < 9; ++$i ) {
+ if ( stristr( $text, "<h$i>" ) != false ) {
+ ++$j;
+ if ( $n == -1 ) $n = $i;
+ }
+ }
+ if ( $j < 2 ) return $text;
+ $i = $n;
+ $v = array( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
+ $t = "";
+ while ( count( spliti( "<h", $text, 2 ) ) == 2 ) {
+ $a = spliti( "<h", $text, 2 );
+ $j = substr( $a[1], 0, 1 );
+ if ( strtolower( $j ) != "r" ) {
+ $t .= $a[0] . "<h" . $j . ">";
+ ++$v[$j];
+ $b = array();
+ for ( $k = $i; $k <= $j; $k++ ) array_push( $b, $v[$k] );
+ for ( $k = $j+1; $k < 9; $k++ ) $v[$k] = 0;
+ $t .= implode( ".", $b ) . " ";
+ $text = substr( $a[1] , 2 ) ;
+ } else { # <HR> tag, not a heading!
+ $t .= $a[0] . "<hr>";
+ $text = substr( $a[1], 2 );
+ }
+ }
+ return $t . $text;
+ }
+
+ /* private */ function magicISBN( $text )
+ {
+ global $wgLang;
+
+ $a = split( "ISBN ", " $text" );
+ if ( count ( $a ) < 2 ) return $text;
+ $text = substr( array_shift( $a ), 1);
+ $valid = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ foreach ( $a as $x ) {
+ $isbn = $blank = "" ;
+ while ( " " == $x{0} ) {
+ $blank .= " ";
+ $x = substr( $x, 1 );
+ }
+ while ( strstr( $valid, $x{0} ) != false ) {
+ $isbn .= $x{0};
+ $x = substr( $x, 1 );
+ }
+ $num = str_replace( "-", "", $isbn );
+ $num = str_replace( " ", "", $num );
+
+ if ( "" == $num ) {
+ $text .= "ISBN $blank$x";
+ } else {
+ $text .= "<a href=\"" . wfLocalUrlE( $wgLang->specialPage(
+ "Booksources"), "isbn={$num}" ) . "\">ISBN $isbn</a>";
+ $text .= $x;
+ }
+ }
+ return $text;
+ }
+
+ /* private */ function magicRFC( $text )
+ {
+ return $text;
+ }
+
+ /* private */ function headElement()
+ {
+ global $wgDocType, $wgUser, $wgLanguageCode, $wgOutputEncoding;
+
+ $ret = "<!DOCTYPE HTML PUBLIC \"$wgDocType\">\n";
+
+ if ( "" == $this->mHTMLtitle ) {
+ $this->mHTMLtitle = $this->mPagetitle;
+ }
+ $ret .= "<html lang=\"$wgLanguageCode\"><head><title>{$this->mHTMLtitle}</title>\n";
+ array_push( $this->mMetatags, array( "http:Content-type", "text/html; charset={$wgOutputEncoding}" ) );
+ foreach ( $this->mMetatags as $tag ) {
+ if ( 0 == strcasecmp( "http:", substr( $tag[0], 0, 5 ) ) ) {
+ $a = "http-equiv";
+ $tag[0] = substr( $tag[0], 5 );
+ } else {
+ $a = "name";
+ }
+ $ret .= "<meta $a=\"{$tag[0]}\" content=\"{$tag[1]}\">\n";
+ }
+ $p = $this->mRobotpolicy;
+ if ( "" == $p ) { $p = "index,follow"; }
+ $ret .= "<meta name=\"robots\" content=\"$p\">\n";
+
+ if ( count( $this->mKeywords ) > 0 ) {
+ $ret .= "<meta name=\"keywords\" content=\"" .
+ implode( ",", $this->mKeywords ) . "\">\n";
+ }
+ foreach ( $this->mLinktags as $tag ) {
+ $ret .= "<link ";
+ if ( "" != $tag[0] ) { $ret .= "rel=\"{$tag[0]}\" "; }
+ if ( "" != $tag[1] ) { $ret .= "rev=\"{$tag[1]}\" "; }
+ $ret .= "href=\"{$tag[2]}\">\n";
+ }
+ $sk = $wgUser->getSkin();
+ $ret .= $sk->getHeadScripts();
+ $ret .= $sk->getUserStyles();
+
+ $ret .= "</head>\n";
+ return $ret;
+ }
+}
+
+?>
diff --git a/includes/SearchEngine.php b/includes/SearchEngine.php
new file mode 100644
index 000000000000..53be8f7eaf58
--- /dev/null
+++ b/includes/SearchEngine.php
@@ -0,0 +1,383 @@
+<?
+# See search.doc
+
+class SearchEngine {
+ /* private */ var $mUsertext, $mSearchterms;
+ /* private */ var $mTitlecond, $mTextcond;
+
+ var $doSearchRedirects = true;
+ var $addtoquery = array();
+ var $namespacesToSearch = array();
+ var $alternateTitle;
+
+ function SearchEngine( $text )
+ {
+ # We display the query, so let's strip it for safety
+ #
+ $lc = SearchEngine::legalSearchChars() . "()";
+ $this->mUsertext = trim( preg_replace( "/[^{$lc}]/", " ", $text ) );
+ $this->mSearchterms = array();
+ }
+
+ function queryNamespaces()
+ {
+ return "cur_namespace IN (" . implode( ",", $this->namespacesToSearch ) . ")";
+ #return "1";
+ }
+
+ function searchRedirects()
+ {
+ if ( $this->doSearchRedirects ) return "";
+ return "AND cur_is_redirect=0 ";
+ }
+
+ function powersearch()
+ {
+ global $wgUser, $wgOut, $wgLang, $wgTitle;
+ $nscb = array();
+
+ $search = $_REQUEST['search'];
+ $searchx = $_REQUEST['searchx'];
+ $listredirs = $_REQUEST['redirs'];
+ $nscb[0] = $_REQUEST['ns0'];
+ $nscb[1] = $_REQUEST['ns1'];
+ $nscb[2] = $_REQUEST['ns2'];
+ $nscb[3] = $_REQUEST['ns3'];
+ $nscb[4] = $_REQUEST['ns4'];
+ $nscb[5] = $_REQUEST['ns5'];
+ $nscb[6] = $_REQUEST['ns6'];
+ $nscb[7] = $_REQUEST['ns7'];
+
+ if ( ! isset ( $searchx ) ) { /* First time here */
+ $nscb[0] = $listredirs = 1; /* All others should be unset */
+ }
+ $this->checkboxes["searchx"] = 1;
+ $ret = wfMsg("powersearchtext");
+
+ # Determine namespace checkboxes
+
+ $ns = $wgLang->getNamespaces();
+ array_shift( $ns ); /* Skip "Special" */
+
+ $r1 = "";
+ for ( $i = 0; $i < count( $ns ); ++$i ) {
+ $checked = "";
+ if ( $nscb[$i] == 1 ) {
+ $checked = " checked";
+ $this->addtoquery["ns{$i}"] = 1;
+ array_push( $this->namespacesToSearch, $i );
+ }
+ $name = str_replace( "_", " ", $ns[$i] );
+ if ( "" == $name ) { $name = "(Main)"; }
+
+ if ( 0 != $i ) { $r1 .= " "; }
+ $r1 .= "<input type=checkbox value=\"1\" name=\"" .
+ "ns{$i}\"{$checked}>{$name}\n";
+ }
+ $ret = str_replace ( "$1", $r1, $ret );
+
+ # List redirects checkbox
+
+ $checked = "";
+ if ( $listredirs == 1 ) {
+ $this->addtoquery["redirs"] = 1;
+ $checked = " checked";
+ }
+ $r2 = "<input type=checkbox value=1 name=\"redirs\"{$checked}>\n";
+ $ret = str_replace( "$2", $r2, $ret );
+
+ # Search field
+
+ $r3 = "<input type=text name=\"search\" value=\"" .
+ htmlspecialchars( $search ) ."\" width=80>\n";
+ $ret = str_replace( "$3", $r3, $ret );
+
+ # Searchx button
+
+ $r9 = "<input type=submit name=\"searchx\" value=\"" .
+ wfMsg("powersearch") . "\">\n";
+ $ret = str_replace( "$9", $r9, $ret );
+
+ $ret = "<br><br>\n<form id=\"powersearch\" method=\"get\" " .
+ "action=\"" . wfLocalUrl( "" ) . "\">\n{$ret}\n</form>\n";
+
+ if ( isset ( $searchx ) ) {
+ if ( ! $listredirs ) { $this->doSearchRedirects = false; }
+ }
+ return $ret;
+ }
+
+ function showResults()
+ {
+ global $wgUser, $wgTitle, $wgOut, $wgLang, $wgDisableTextSearch;
+ $fname = "SearchEngine::showResults";
+
+ $offset = $_REQUEST['offset'];
+ $limit = $_REQUEST['limit'];
+ $search = $_REQUEST['search'];
+
+ $powersearch = $this->powersearch(); /* Need side-effects here? */
+
+ $wgOut->setPageTitle( wfMsg( "searchresults" ) );
+ $q = str_replace( "$1", $this->mUsertext,
+ wfMsg( "searchquery" ) );
+ $wgOut->setSubtitle( $q );
+ $wgOut->setArticleFlag( false );
+ $wgOut->setRobotpolicy( "noindex,nofollow" );
+
+ $sk = $wgUser->getSkin();
+ $text = str_replace( "$1", $sk->makeKnownLink(
+ wfMsg( "searchhelppage" ), wfMsg( "searchingwikipedia" ) ),
+ wfMsg( "searchresulttext" ) );
+ $wgOut->addHTML( $text );
+
+ $this->parseQuery();
+ if ( "" == $this->mTitlecond || "" == $this->mTextcond ) {
+ $wgOut->addHTML( "<h2>" . wfMsg( "badquery" ) . "</h2>\n" .
+ "<p>" . wfMsg( "badquerytext" ) );
+ return;
+ }
+ if ( ! isset( $limit ) ) {
+ $limit = $wgUser->getOption( "searchlimit" );
+ if ( ! $limit ) { $limit = 20; }
+ }
+ if ( ! $offset ) { $offset = 0; }
+
+ $searchnamespaces = $this->queryNamespaces();
+ $redircond = $this->searchRedirects();
+
+ $sql = "SELECT cur_id,cur_namespace,cur_title," .
+ "cur_text FROM cur,searchindex " .
+ "WHERE cur_id=si_page AND {$this->mTitlecond} " .
+ "AND {$searchnamespaces} {$redircond}" .
+ "LIMIT {$offset}, {$limit}";
+ $res1 = wfQuery( $sql, $fname );
+
+ if ( $wgDisableTextSearch ) {
+ $res2 = 0;
+ } else {
+ $sql = "SELECT cur_id,cur_namespace,cur_title," .
+ "cur_text FROM cur,searchindex " .
+ "WHERE cur_id=si_page AND {$this->mTextcond} " .
+ "AND {$searchnamespaces} {$redircond} " .
+ "LIMIT {$offset}, {$limit}";
+ $res2 = wfQuery( $sql, $fname );
+ }
+
+ $top = wfShowingResults( $offset, $limit );
+ $wgOut->addHTML( "<p>{$top}\n" );
+
+ # For powersearch
+
+ $a2l = "" ;
+ $akk = array_keys( $this->addtoquery ) ;
+ foreach ( $akk AS $ak ) {
+ $a2l .= "&{$ak}={$this->addtoquery[$ak]}" ;
+ }
+
+ $sl = wfViewPrevNext( $offset, $limit, "",
+ "search=" . wfUrlencode( $this->mUsertext ) . $a2l );
+ $wgOut->addHTML( "<br>{$sl}\n" );
+
+ $foundsome = false;
+
+ if ( 0 == wfNumRows( $res1 ) ) {
+ $wgOut->addHTML( "<h2>" . wfMsg( "notitlematches" ) .
+ "</h2>\n" );
+ } else {
+ $foundsome = true;
+ $off = $offset + 1;
+ $wgOut->addHTML( "<h2>" . wfMsg( "titlematches" ) .
+ "</h2>\n<ol start='{$off}'>" );
+
+ while ( $row = wfFetchObject( $res1 ) ) {
+ $this->showHit( $row );
+ }
+ wfFreeResult( $res1 );
+ $wgOut->addHTML( "</ol>\n" );
+ }
+
+ if ( $wgDisableTextSearch ) {
+ $wgOut->addHTML( str_replace( "$1",
+ htmlspecialchars( $search ), wfMsg( "searchdisabled" ) ) );
+ } else {
+ if ( 0 == wfNumRows( $res2 ) ) {
+ $wgOut->addHTML( "<h2>" . wfMsg( "notextmatches" ) .
+ "</h2>\n" );
+ } else {
+ $foundsome = true;
+ $off = $offset + 1;
+ $wgOut->addHTML( "<h2>" . wfMsg( "textmatches" ) . "</h2>\n" .
+ "<ol start='{$off}'>" );
+ while ( $row = wfFetchObject( $res2 ) ) {
+ $this->showHit( $row );
+ }
+ wfFreeResult( $res2 );
+ $wgOut->addHTML( "</ol>\n" );
+ }
+ }
+ if ( ! $foundsome ) {
+ $wgOut->addHTML( "<p>" . wfMsg( "nonefound" ) . "\n" );
+ }
+ $wgOut->addHTML( "<p>{$sl}\n" );
+ $wgOut->addHTML( $powersearch );
+ }
+
+ function legalSearchChars()
+ {
+ $lc = "A-Za-z_'0-9\\x80-\\xFF\\-";
+ return $lc;
+ }
+
+ function parseQuery()
+ {
+ global $wgDBminWordLen, $wgLang;
+
+ $lc = SearchEngine::legalSearchChars() . "()";
+ $q = preg_replace( "/([()])/", " \\1 ", $this->mUsertext );
+ $q = preg_replace( "/\\s+/", " ", $q );
+ $w = explode( " ", strtolower( trim( $q ) ) );
+
+ $last = $cond = "";
+ foreach ( $w as $word ) {
+ $word = $wgLang->stripForSearch( $word );
+ if ( "and" == $word || "or" == $word || "not" == $word
+ || "(" == $word || ")" == $word ) {
+ $cond .= " " . strtoupper( $word );
+ $last = "";
+ } else if ( strlen( $word ) < $wgDBminWordLen ) {
+ continue;
+ } else if ( FulltextStoplist::inList( $word ) ) {
+ continue;
+ } else {
+ if ( "" != $last ) { $cond .= " AND"; }
+ $cond .= " (MATCH (##field##) AGAINST ('" .
+ wfStrencode( $word ). "'))";
+ $last = $word;
+ array_push( $this->mSearchterms, "\\b" . $word . "\\b" );
+ }
+ }
+ if ( 0 == count( $this->mSearchterms ) ) { return; }
+
+ # To disable boolean:
+ # $cond = "MATCH (##field##) AGAINST('" . wfStrencode( $q ) . "')";
+
+ $this->mTitlecond = "(" . str_replace( "##field##",
+ "si_title", $cond ) . " )";
+
+ $this->mTextcond = "(" . str_replace( "##field##",
+ "si_text", $cond ) . " AND (cur_is_redirect=0) )";
+ }
+
+ function showHit( $row )
+ {
+ global $wgUser, $wgOut;
+
+ $t = Title::makeName( $row->cur_namespace, $row->cur_title );
+ $sk = $wgUser->getSkin();
+
+ $contextlines = $wgUser->getOption( "contextlines" );
+ if ( "" == $contextlines ) { $contextlines = 5; }
+ $contextchars = $wgUser->getOption( "contextchars" );
+ if ( "" == $contextchars ) { $contextchars = 50; }
+
+ $link = $sk->makeKnownLink( $t, "" );
+ $size = str_replace( "$1", strlen( $row->cur_text ), WfMsg( "nbytes" ) );
+ $wgOut->addHTML( "<li>{$link} ({$size})" );
+
+ $lines = explode( "\n", $row->cur_text );
+ $pat1 = "/(.*)(" . implode( "|", $this->mSearchterms ) . ")(.*)/i";
+ $lineno = 0;
+
+ foreach ( $lines as $line ) {
+ if ( 0 == $contextlines ) { break; }
+ --$contextlines;
+ ++$lineno;
+ if ( ! preg_match( $pat1, $line, $m ) ) { continue; }
+
+ $pre = $m[1];
+ if ( 0 == $contextchars ) { $pre = "..."; }
+ else {
+ if ( strlen( $pre ) > $contextchars ) {
+ $pre = "..." . substr( $pre, -$contextchars );
+ }
+ }
+ $pre = wfEscapeHTML( $pre );
+
+ if ( count( $m ) < 3 ) { $post = ""; }
+ else { $post = $m[3]; }
+
+ if ( 0 == $contextchars ) { $post = "..."; }
+ else {
+ if ( strlen( $post ) > $contextchars ) {
+ $post = substr( $post, 0, $contextchars ) . "...";
+ }
+ }
+ $post = wfEscapeHTML( $post );
+ $found = wfEscapeHTML( $m[2] );
+
+ $line = "{$pre}{$found}{$post}";
+ $pat2 = "/(" . implode( "|", $this->mSearchterms ) . ")/i";
+ $line = preg_replace( $pat2,
+ "<font color='red'>\\1</font>", $line );
+
+ $wgOut->addHTML( "<br><small>{$lineno}: {$line}</small>\n" );
+ }
+ $wgOut->addHTML( "</li>\n" );
+ }
+
+ function goResult()
+ {
+ global $wgOut, $wgArticle, $wgTitle;
+ $fname = "SearchEngine::goResult";
+
+ $search = $_REQUEST['search'];
+
+ # First try to go to page as entered
+ #
+ $wgArticle = new Article();
+ $wgTitle = Title::newFromText( $search );
+
+ if ( 0 != $wgArticle->getID() ) {
+ $wgArticle->view();
+ return;
+ }
+
+ # Now try all lower case (i.e. first letter capitalized)
+ #
+ $wgTitle = Title::newFromText( strtolower( $search ) );
+ if ( 0 != $wgArticle->getID() ) {
+ $wgArticle->view();
+ return;
+ }
+
+ # Now try capitalized string
+ #
+ $wgTitle=Title::newFromText( ucwords( strtolower( $search ) ) );
+ if ( 0 != $wgArticle->getID() ) {
+ $wgArticle->view();
+ return;
+ }
+
+ # Try a near match
+ #
+ $this->parseQuery();
+ $sql = "SELECT cur_id,cur_title,cur_namespace,si_page FROM cur,searchindex " .
+ "WHERE cur_id=si_page AND {$this->mTitlecond} LIMIT 1";
+
+ if ( "" != $this->mTitlecond ) {
+ $res = wfQuery( $sql, $fname );
+ }
+ if ( isset( $res ) && 0 != wfNumRows( $res ) ) {
+ $s = wfFetchObject( $res );
+
+ $wgTitle = Title::newFromDBkey( $s->cur_title );
+ $wgTitle->setNamespace( $s->cur_namespace );
+ $wgArticle->view();
+ return;
+ }
+ $wgOut->addHTML( wfMsg("nogomatch") . "\n<p>" );
+ $this->showResults();
+ }
+}
+
diff --git a/includes/SearchUpdate.php b/includes/SearchUpdate.php
new file mode 100644
index 000000000000..9e9ff2af1ff5
--- /dev/null
+++ b/includes/SearchUpdate.php
@@ -0,0 +1,78 @@
+<?
+# See deferred.doc
+
+class SearchUpdate {
+
+ /* private */ var $mId, $mNamespace, $mTitle, $mText;
+ /* private */ var $mTitleWords;
+
+ function SearchUpdate( $id, $title, $text = false )
+ {
+ $this->mId = $id;
+ $this->mText = $text;
+
+ $nt = Title::newFromText( $title );
+ $this->mNamespace = $nt->getNamespace();
+ $this->mTitle = $nt->getText(); # Discard namespace
+
+ $this->mTitleWords = $this->mTextWords = array();
+ }
+
+ function doUpdate()
+ {
+ global $wgDBminWordLen, $wgLang;
+ $lc = SearchEngine::legalSearchChars() . "&#;";
+
+ if( $this->mText == false ) {
+ # Just update the title
+ $sql = "UPDATE LOW_PRIORITY searchindex SET si_title='" .
+ wfStrencode( Title::indexTitle( $this->mNamespace, $this->mTitle ) ) .
+ "' WHERE si_page={$this->mId}";
+ wfQuery( $sql, "SearchUpdate::doUpdate" );
+ return;
+ }
+
+ # Language-specific strip/conversion
+ $text = $wgLang->stripForSearch( $this->mText );
+
+ $text = preg_replace( "/<\\/?\\s*[A-Za-z][A-Za-z0-9]*\\s*([^>]*?)>/",
+ " ", strtolower( " " . $text /*$this->mText*/ . " " ) ); # Strip HTML markup
+ $text = preg_replace( "/(^|\\n)\\s*==\\s+([^\\n]+)\\s+==\\s/sD",
+ "\\2 \\2 \\2 ", $text ); # Emphasize headings
+
+ # Strip external URLs
+ $uc = "A-Za-z0-9_\\/:.,~%\\-+&;#?!=()@\\xA0-\\xFF";
+ $protos = "http|https|ftp|mailto|news|gopher";
+ $pat = "/(^|[^\\[])({$protos}):[{$uc}]+([^{$uc}]|$)/";
+ $text = preg_replace( $pat, "\\1 \\3", $text );
+
+ $p1 = "/([^\\[])\\[({$protos}):[{$uc}]+]/";
+ $p2 = "/([^\\[])\\[({$protos}):[{$uc}]+\\s+([^\\]]+)]/";
+ $text = preg_replace( $p1, "\\1 ", $text );
+ $text = preg_replace( $p2, "\\1 \\3 ", $text );
+
+ # Internal image links
+ $pat2 = "/\\[\\[image:([{$uc}]+)\\.(gif|png|jpg|jpeg)([^{$uc}])/i";
+ $text = preg_replace( $pat2, " \\1 \\3", $text );
+
+ $text = preg_replace( "/([^{$lc}])([{$lc}]+)]]([a-z]+)/",
+ "\\1\\2 \\2\\3", $text ); # Handle [[game]]s
+
+ # Strip all remaining non-search characters
+ $text = preg_replace( "/[^{$lc}]+/", " ", $text );
+
+ # Handle 's, s'
+ $text = preg_replace( "/([{$lc}]+)'s /", "\\1 \\1's ", $text );
+ $text = preg_replace( "/([{$lc}]+)s' /", "\\1s ", $text );
+
+ # Strip wiki '' and '''
+ $text = preg_replace( "/''[']*/", " ", $text );
+
+ $sql = "REPLACE DELAYED INTO searchindex (si_page,si_title,si_text) VALUES ({$this->mId},'" .
+ wfStrencode( Title::indexTitle( $this->mNamespace, $this->mTitle ) ) . "','" .
+ wfStrencode( $text ) . "')";
+ wfQuery( $sql, "SearchUpdate::doUpdate" );
+ }
+}
+
+?>
diff --git a/includes/Setup.php b/includes/Setup.php
new file mode 100644
index 000000000000..7b0c64670cb2
--- /dev/null
+++ b/includes/Setup.php
@@ -0,0 +1,37 @@
+<?
+# The main wiki script and things like database
+# conversion and maintenance scripts all share a
+# common setup of including lots of classes and
+# setting up a few globals.
+#
+
+global $IP;
+include_once( "$IP/GlobalFunctions.php" );
+include_once( "$IP/Language.php" );
+include_once( "$IP/Namespace.php" );
+include_once( "$IP/Skin.php" );
+include_once( "$IP/OutputPage.php" );
+include_once( "$IP/DifferenceEngine.php" );
+include_once( "$IP/SearchEngine.php" );
+include_once( "$IP/User.php" );
+include_once( "$IP/LinkCache.php" );
+include_once( "$IP/Title.php" );
+include_once( "$IP/Article.php" );
+
+global $wgUser, $wgLang, $wgOut, $wgTitle;
+global $wgArticle, $wgDeferredUpdateList, $wgLinkCache;
+
+$wgOut = new OutputPage();
+$wgLangClass = "Language" . ucfirst( $wgLanguageCode );
+if( ! class_exists( $wgLangClass ) ) {
+ include_once( "$IP/Utf8Case.php" );
+ $wgLangClass = "LanguageUtf8";
+}
+$wgLang = new $wgLangClass();
+
+$wgUser = new User();
+$wgUser->loadFromSession();
+$wgDeferredUpdateList = array();
+$wgLinkCache = new LinkCache();
+
+?>
diff --git a/includes/SiteStatsUpdate.php b/includes/SiteStatsUpdate.php
new file mode 100644
index 000000000000..61ca8464c3b0
--- /dev/null
+++ b/includes/SiteStatsUpdate.php
@@ -0,0 +1,40 @@
+<?
+# See deferred.doc
+
+class SiteStatsUpdate {
+
+ var $mViews, $mEdits, $mGood;
+
+ function SiteStatsUpdate( $views, $edits, $good )
+ {
+ $this->mViews = $views;
+ $this->mEdits = $edits;
+ $this->mGood = $good;
+ }
+
+ function doUpdate()
+ {
+ $a = array();
+
+ if ( $this->mViews < 0 ) { $m = "-1"; }
+ else if ( $this->mViews > 0 ) { $m = "+1"; }
+ else $m = "";
+ array_push( $a, "ss_total_views=(ss_total_views$m)" );
+
+ if ( $this->mEdits < 0 ) { $m = "-1"; }
+ else if ( $this->mEdits > 0 ) { $m = "+1"; }
+ else $m = "";
+ array_push( $a, "ss_total_edits=(ss_total_edits$m)" );
+
+ if ( $this->mGood < 0 ) { $m = "-1"; }
+ else if ( $this->mGood > 0 ) { $m = "+1"; }
+ else $m = "";
+ array_push( $a, "ss_good_articles=(ss_good_articles$m)" );
+
+ $sql = "UPDATE LOW_PRIORITY site_stats SET " . implode ( ",", $a ) .
+ " WHERE ss_row_id=1";
+ wfQuery( $sql, "SiteStatsUpdate::doUpdate" );
+ }
+}
+
+?>
diff --git a/includes/Skin.php b/includes/Skin.php
new file mode 100644
index 000000000000..303ad176fc06
--- /dev/null
+++ b/includes/Skin.php
@@ -0,0 +1,1674 @@
+<?
+# See skin.doc
+
+# These are the INTERNAL names, which get mapped
+# directly to class names. For display purposes, the
+# Language class has internationalized names
+#
+/* private */ $wgValidSkinNames = array(
+ "Standard", "Nostalgia", "CologneBlue"
+);
+
+class RecentChangesClass {
+ var $secureName , $displayName , $link , $namespace ;
+ var $oldid , $diffid , $timestamp , $curlink , $lastlink , $usertalklink , $versionlink ;
+ var $usercomment , $userlink ;
+ var $isminor , $isnew , $watched , $islog ;
+ } ;
+
+class Skin {
+
+ /* private */ var $lastdate, $lastline;
+
+ var $rc_cache ; # Cache for Enhanced Recent Changes
+ var $rccc ; # Recent Changes Cache Counter for visibility toggle
+
+
+ function Skin()
+ {
+ }
+
+ function getSkinNames()
+ {
+ global $wgValidSkinNames;
+ return $wgValidSkinNames;
+ }
+
+ function getStylesheet()
+ {
+ return "wikistandard.css";
+ }
+
+ function qbSetting()
+ {
+ global $wgOut, $wgUser;
+
+ if ( $wgOut->isQuickbarSupressed() ) { return 0; }
+ $q = $wgUser->getOption( "quickbar" );
+ if ( "" == $q ) { $q = 0; }
+ return $q;
+ }
+
+ function initPage()
+ {
+ global $wgOut, $wgStyleSheetPath;
+ wfProfileIn( "Skin::initPage" );
+
+ $wgOut->addLink( "shortcut icon", "", "/favicon.ico" );
+ if ( $wgOut->isPrintable() ) { $ss = "wikiprintable.css"; }
+ else { $ss = $this->getStylesheet(); }
+ $wgOut->addLink( "stylesheet", "", "{$wgStyleSheetPath}/{$ss}" );
+ wfProfileOut();
+ }
+
+ function getHeadScripts() {
+ $r = "
+<SCRIPT TYPE=\"text/javascript\">
+function toggleVisibility( _levelId, _otherId, _linkId) {
+ var thisLevel = document.getElementById( _levelId );
+ var otherLevel = document.getElementById( _otherId );
+ var linkLevel = document.getElementById( _linkId );
+ if ( thisLevel.style.display == 'none' ) {
+ thisLevel.style.display = 'block';
+ otherLevel.style.display = 'none';
+ linkLevel.style.display = 'inline';
+ } else {
+ thisLevel.style.display = 'none';
+ otherLevel.style.display = 'inline';
+ linkLevel.style.display = 'none';
+ }
+ }
+</SCRIPT>
+ " ;
+ return $r;
+ }
+
+ function getUserStyles()
+ {
+ $s = "<style type='text/css' media='screen'><!--\n";
+ $s .= $this->doGetUserStyles();
+ $s .= "//--></style>\n";
+ return $s;
+ }
+
+ function doGetUserStyles()
+ {
+ global $wgUser;
+
+ $s = "";
+ if ( 1 == $wgUser->getOption( "underline" ) ) {
+ $s .= "a.stub, a.new, a.internal, a.external { " .
+ "text-decoration: underline; }\n";
+ } else {
+ $s .= "a.stub, a.new, a.internal, a.external { " .
+ "text-decoration: none; }\n";
+ }
+ if ( 1 == $wgUser->getOption( "highlightbroken" ) ) {
+ $s .= "a.new { color: #CC2200; }\n" .
+ "#quickbar a.new { color: CC2200; }\n";
+ }
+ if ( 1 == $wgUser->getOption( "justify" ) ) {
+ $s .= "#article { text-align: justify; }\n";
+ }
+ return $s;
+ }
+
+ function getBodyOptions()
+ {
+ global $wgUser, $wgTitle, $wgNamespaceBackgrounds, $wgOut, $oldid, $redirect, $diff,$action;
+
+ if ( 0 != $wgTitle->getNamespace() ) {
+ $a = array( "bgcolor" => "#FFFFDD" );
+ }
+ else $a = array( "bgcolor" => "#FFFFFF" );
+ if($wgOut->isArticle() && $wgUser->getOption("editondblclick")
+ &&
+ (!$wgTitle->isProtected() || $wgUser->isSysop())
+
+ ) {
+ $n = $wgTitle->getPrefixedURL();
+ $t = wfMsg( "editthispage" );
+ $oid = $red = "";
+ if ( $redirect ) { $red = "&redirect={$redirect}"; }
+ if ( $oldid && ! isset( $diff ) ) {
+ $oid = "&oldid={$oldid}";
+ }
+ $s = wfLocalUrlE($n,"action=edit{$oid}{$red}");
+ $s = "document.location = \"" .$s ."\";";
+ $a += array ("ondblclick" => $s);
+
+ }
+ if($action=="edit") { # set focus in edit box
+ $a += array("onLoad"=>"document.editform.wpTextbox1.focus()");
+ }
+ return $a;
+ }
+
+ function getExternalLinkAttributes( $link, $text )
+ {
+ global $wgUser, $wgOut, $wgLang;
+
+ $link = urldecode( $link );
+ $link = $wgLang->checkTitleEncoding( $link );
+ $link = str_replace( "_", " ", $link );
+ $link = wfEscapeHTML( $link );
+
+ if ( $wgOut->isPrintable() ) { $r = " class='printable'"; }
+ else { $r = " class='external'"; }
+
+ if ( 1 == $wgUser->getOption( "hover" ) ) {
+ $r .= " title=\"{$link}\"";
+ }
+ return $r;
+ }
+
+ function getInternalLinkAttributes( $link, $text, $broken = false )
+ {
+ global $wgUser, $wgOut;
+
+ $link = urldecode( $link );
+ $link = str_replace( "_", " ", $link );
+ $link = wfEscapeHTML( $link );
+
+ if ( $wgOut->isPrintable() ) { $r = " class='printable'"; }
+ else if ( $broken == "stub" ) { $r = " class='stub'"; }
+ else if ( $broken == "yes" ) { $r = " class='new'"; }
+ else { $r = " class='internal'"; }
+
+ if ( 1 == $wgUser->getOption( "hover" ) ) {
+ $r .= " title=\"{$link}\"";
+ }
+ return $r;
+ }
+
+ function getLogo()
+ {
+ global $wgLogo;
+ return $wgLogo;
+ }
+
+ # This will be called immediately after the <body> tag. Split into
+ # two functions to make it easier to subclass.
+ #
+ function beforeContent()
+ {
+ global $wgUser, $wgOut;
+
+ if ( $wgOut->isPrintable() ) {
+ $s = $this->pageTitle() . $this->pageSubtitle() . "\n";
+ $s .= "\n<div class='bodytext'>";
+ return $s;
+ }
+ return $this->doBeforeContent();
+ }
+
+ function doBeforeContent()
+ {
+ global $wgUser, $wgOut, $wgTitle;
+ wfProfileIn( "Skin::doBeforeContent" );
+
+ $s = "";
+ $qb = $this->qbSetting();
+
+ if( $langlinks = $this->otherLanguages() ) {
+ $rows = 2;
+ $borderhack = "";
+ } else {
+ $rows = 1;
+ $langlinks = false;
+ $borderhack = "class='top'";
+ }
+
+ $s .= "\n<div id='content'>\n<div id='topbar'>" .
+ "<table width='98%' border=0 cellspacing=0><tr>";
+
+ if ( 0 == $qb ) {
+ $s .= "<td class='top' align=left valign=top rowspan='{$rows}'>" .
+ $this->logoText() . "</td>";
+ } else if ( 1 == $qb || 3 == $qb ) { # Left
+ $s .= $this->getQuickbarCompensator( $rows );
+ }
+ $s .= "<td {$borderhack} align=left valign=top>";
+
+ $s .= $this->topLinks() ;
+ $s .= "<p class='subtitle'>" . $this->pageTitleLinks();
+
+ $s .= "</td>\n<td {$borderhack} valign=top align=right nowrap>";
+ $s .= $this->nameAndLogin();
+ $s .= "\n<br>" . $this->searchForm() . "</td>";
+
+ if ( $langlinks ) {
+ $s .= "</tr>\n<tr><td class='top' colspan=\"2\">$langlinks</td>";
+ }
+
+ if ( 2 == $qb ) { # Right
+ $s .= $this->getQuickbarCompensator( $rows );
+ }
+ $s .= "</tr></table>\n</div>\n";
+ $s .= "\n<div id='article'>";
+
+ $s .= $this->pageTitle();
+ $s .= $this->pageSubtitle() . "\n<p>";
+ wfProfileOut();
+ return $s;
+ }
+
+ function getQuickbarCompensator( $rows = 1 )
+ {
+ return "<td width='152' rowspan='{$rows}'>&nbsp;</td>";
+ }
+
+ # This gets called immediately before the </body> tag.
+ #
+ function afterContent()
+ {
+ global $wgUser, $wgOut, $wgServer, $HTTP_SERVER_VARS;
+
+ if ( $wgOut->isPrintable() ) {
+ $s = "\n</div>\n";
+
+ $u = $wgServer . $HTTP_SERVER_VARS['REQUEST_URI'];
+ $u = preg_replace( "/[?&]printable=yes/", "", $u );
+ $rf = str_replace( "$1", $u, wfMsg( "retrievedfrom" ) );
+
+ if ( $wgOut->isArticle() ) {
+ $lm = "<br>" . $this->lastModified();
+ } else { $lm = ""; }
+
+ $s .= "<p><em>{$rf}{$lm}</em>\n";
+ return $s;
+ }
+ return $this->doAfterContent();
+ }
+
+ function doAfterContent()
+ {
+ global $wgUser, $wgOut;
+ wfProfileIn( "Skin::doAfterContent" );
+
+ $s = "\n</div><br clear=all>\n";
+
+ $s .= "\n<div id='footer'>";
+ $s .= "<table width='98%' border=0 cellspacing=0><tr>";
+
+ $qb = $this->qbSetting();
+ if ( 1 == $qb || 3 == $qb ) { # Left
+ $s .= $this->getQuickbarCompensator();
+ }
+ $s .= "<td class='bottom' align=left valign=top>";
+
+ $s .= $this->bottomLinks();
+ $s .= "\n<br>" . $this->mainPageLink()
+ . " | " . $this->aboutLink()
+ . " | " . $this->specialLink( "recentchanges" )
+ . " | " . $this->searchForm()
+ . "<br>" . $this->pageStats();
+
+ $s .= "</td>";
+ if ( 2 == $qb ) { # Right
+ $s .= $this->getQuickbarCompensator();
+ }
+ $s .= "</tr></table>\n</div>\n</div>\n";
+
+ if ( 0 != $qb ) { $s .= $this->quickBar(); }
+ wfProfileOut();
+ return $s;
+ }
+
+ function pageTitleLinks()
+ {
+ global $wgOut, $wgTitle, $oldid, $action, $diff, $wgUser, $wgLang;
+
+ $s = $this->printableLink();
+
+ if ( $wgOut->isArticle() ) {
+ if ( $wgTitle->getNamespace() == Namespace::getImage() ) {
+ $name = $wgTitle->getDBkey();
+ $link = wfEscapeHTML( wfImageUrl( $name ) );
+ $style = $this->getInternalLinkAttributes( $link, $name );
+ $s .= " | <a href=\"{$link}\"{$style}>{$name}</a>";
+ }
+ }
+ if ( "history" == $action || isset( $diff ) || isset( $oldid ) ) {
+ $s .= " | " . $this->makeKnownLink( $wgTitle->getPrefixedText(),
+ wfMsg( "currentrev" ) );
+ }
+
+ if ( $wgUser->getNewtalk() ) {
+ # do not show "You have new messages" text when we are viewing our
+ # own talk page
+
+ if(!(strcmp($wgTitle->getText(),$wgUser->getName()) == 0 &&
+ $wgTitle->getNamespace()==Namespace::getTalk(Namespace::getUser()))) {
+ $n =$wgUser->getName();
+ $tl = $this->makeKnownLink( $wgLang->getNsText(
+ Namespace::getTalk( Namespace::getUser() ) ) . ":{$n}",
+ wfMsg("newmessageslink") );
+ $s.=" | <strong>". str_replace( "$1", $tl, wfMsg("newmessages") ) . "</strong>";
+ }
+ }
+ return $s;
+ }
+
+ function printableLink()
+ {
+ global $wgOut, $wgTitle, $oldid, $action;
+
+ if ( "history" == $action ) { $q = "action=history&"; }
+ else { $q = ""; }
+
+ $s = $this->makeKnownLink( $wgTitle->getPrefixedText(),
+ WfMsg( "printableversion" ), "{$q}printable=yes" );
+ return $s;
+ }
+
+ function pageTitle()
+ {
+ global $wgOut, $wgTitle;
+
+ $s = "<h1 class='pagetitle'>" . $wgOut->getPageTitle() . "</h1>";
+ return $s;
+ }
+
+ function pageSubtitle()
+ {
+ global $wgOut,$wgTitle,$wgNamespacesWithSubpages;
+
+ $sub = $wgOut->getSubtitle();
+ if ( "" == $sub ) { $sub = wfMsg( "fromwikipedia" ); }
+ if($wgOut->isArticle() && $wgNamespacesWithSubpages[$wgTitle->getNamespace()]) {
+ $ptext=$wgTitle->getPrefixedText();
+ if(preg_match("/\//",$ptext)) {
+ $sub.="</p><p class='subpages'>";
+ $links=explode("/",$ptext);
+ $c=0;
+ $growinglink="";
+ foreach($links as $link) {
+ $c++;
+ if ($c<count($links)) {
+ $growinglink.=$link;
+ $getlink=$this->makeLink($growinglink,$link);
+ if(preg_match("/class='new'/i",$getlink)) { break; } # this is a hack, but it saves time
+ if ($c>1) {
+ $sub .= " | ";
+ } else {
+ $sub .="&lt; ";
+ }
+ $sub .= $getlink;
+ $growinglink.="/";
+ }
+
+ }
+ }
+ }
+ $s = "<p class='subtitle'>{$sub}\n";
+ return $s;
+ }
+
+ function nameAndLogin()
+ {
+ global $wgUser, $wgTitle, $wgLang;
+
+ $li = $wgLang->specialPage( "Userlogin" );
+ $lo = $wgLang->specialPage( "Userlogout" );
+
+ $s = "";
+ if ( 0 == $wgUser->getID() ) {
+ $n = getenv( "REMOTE_ADDR" );
+ $rt = $wgTitle->getPrefixedURL();
+ if ( 0 == strcasecmp( urlencode( $lo ), $rt ) ) {
+ $q = "";
+ } else { $q = "returnto={$rt}"; }
+
+
+ $tl = $this->makeKnownLink( $wgLang->getNsText(
+ Namespace::getTalk( Namespace::getUser() ) ) . ":{$n}",
+ $wgLang->getNsText( Namespace::getTalk( 0 ) ) );
+
+ $s .= $n . " (".$tl.")" . "\n<br>" . $this->makeKnownLink( $li,
+ wfMsg( "login" ), $q );
+
+ $tl = " ({$tl})";
+
+ } else {
+ $n = $wgUser->getName();
+ $rt = $wgTitle->getPrefixedURL();
+ $tl = $this->makeKnownLink( $wgLang->getNsText(
+ Namespace::getTalk( Namespace::getUser() ) ) . ":{$n}",
+ $wgLang->getNsText( Namespace::getTalk( 0 ) ) );
+
+ $tl = " ({$tl})";
+
+ $s .= $this->makeKnownLink( $wgLang->getNsText(
+ Namespace::getUser() ) . ":{$n}", $n ) . "{$tl}<br>" .
+ $this->makeKnownLink( $lo, wfMsg( "logout" ),
+ "returnto={$rt}" ) . " | " .
+ $this->specialLink( "preferences" );
+ }
+ $s .= " | " . $this->makeKnownLink( wfMsg( "helppage" ),
+ wfMsg( "help" ) );
+
+ return $s;
+ }
+
+ function searchForm()
+ {
+ global $search;
+ $s = "<form id=\"search\" class=\"inline\" method=\"get\" action=\""
+ . wfLocalUrl( "" ) . "\">"
+ . "<input type=text name=\"search\" size=19 value=\""
+ . htmlspecialchars(substr($search,0,256)) . "\">\n"
+ . "<input type=submit value=\"" . wfMsg( "search" )
+ . "\">&nbsp;<input type=submit name=\"go\" value=\""
+ . wfMsg ("go") . "\"></form>";
+
+ return $s;
+ }
+
+ function topLinks()
+ {
+ global $wgOut;
+ $sep = " |\n";
+
+ $s = $this->mainPageLink() . $sep
+ . $this->specialLink( "recentchanges" );
+
+ if ( $wgOut->isArticle() ) {
+ $s .= $sep . $this->editThisPage()
+ . $sep . $this->historyLink();
+ }
+ $s .= $sep . $this->specialPagesList();
+
+ return $s;
+ }
+
+ function bottomLinks()
+ {
+ global $wgOut, $wgUser, $wgTitle;
+ $sep = " |\n";
+
+ $s = "";
+ if ( $wgOut->isArticle() ) {
+ $s .= "<strong>" . $this->editThisPage() . "</strong>";
+ if ( 0 != $wgUser->getID() ) {
+ $s .= $sep . $this->watchThisPage();
+ }
+ $s .= $sep . $this->talkLink()
+ . $sep . $this->historyLink()
+ . $sep . $this->whatLinksHere()
+ . $sep . $this->watchPageLinksLink();
+
+ if ( $wgTitle->getNamespace() == Namespace::getUser()
+ || $wgTitle->getNamespace() == Namespace::getTalk(Namespace::getUser()) )
+
+ {
+ $id=User::idFromName($wgTitle->getText());
+ $ip=User::isIP($wgTitle->getText());
+
+ if($id || $ip) { # both anons and non-anons have contri list
+ $s .= $sep . $this->userContribsLink();
+ }
+ if ( 0 != $wgUser->getID() ) { # show only to signed in users
+ if($id) { # can only email non-anons
+ $s .= $sep . $this->emailUserLink();
+ }
+ }
+ }
+ if ( $wgUser->isSysop() && $wgTitle->getArticleId() ) {
+ $s .= "\n<br>" . $this->deleteThisPage() .
+ $sep . $this->protectThisPage() .
+ $sep . $this->moveThisPage();
+ }
+ $s .= "<br>\n" . $this->otherLanguages();
+ }
+ return $s;
+ }
+
+ function pageStats()
+ {
+ global $wgOut, $wgLang, $wgArticle;
+ global $oldid, $diff;
+
+ if ( ! $wgOut->isArticle() ) { return ""; }
+ if ( isset( $oldid ) || isset( $diff ) ) { return ""; }
+ if ( 0 == $wgArticle->getID() ) { return ""; }
+
+ $count = $wgArticle->getCount();
+ $s = str_replace( "$1", $count, wfMsg( "viewcount" ) );
+
+ $s .= $this->lastModified();
+ $s .= " ".wfMsg( "gnunote" ) ;
+ return "<span id='pagestats'>{$s}</span>";
+ }
+
+ function lastModified()
+ {
+ global $wgLang, $wgArticle;
+
+ $d = $wgLang->timeanddate( $wgArticle->getTimestamp(), true );
+ $s = " " . str_replace( "$1", $d, wfMsg( "lastmodified" ) );
+ return $s;
+ }
+
+ function logoText( $align = "" )
+ {
+ if ( "" != $align ) { $a = " align='{$align}'"; }
+ else { $a = ""; }
+
+ $mp = wfMsg( "mainpage" );
+ $s = "<a href=\"" . wfLocalUrlE( $mp ) . "\"><img{$a} border=0 src=\""
+ . $this->getLogo() . "\" alt=\"" . "[{$mp}]\"></a>";
+ return $s;
+ }
+
+ function quickBar()
+ {
+ global $wgOut, $wgTitle, $wgUser, $action, $wgLang;
+ global $wpPreview;
+ wfProfileIn( "Skin::quickBar" );
+
+ $s = "\n<div id='quickbar'>";
+ $s .= "\n" . $this->logoText() . "\n<hr>";
+
+ $sep = "\n<br>";
+ $s .= $this->mainPageLink()
+ . $sep . $this->specialLink( "recentchanges" )
+ . $sep . $this->specialLink( "randompage" );
+ if ($wgUser->getID()) {
+ $s.= $sep . $this->specialLink( "watchlist" ) ;
+ $s .= $sep .$this->makeKnownLink( $wgLang->specialPage( "Contributions" ),
+ wfMsg( "mycontris" ), "target=" . wfUrlencode($wgUser->getName() ) );
+
+ }
+ // only show watchlist link if logged in
+ if ( wfMsg ( "currentevents" ) != "-" ) $s .= $sep . $this->makeKnownLink( wfMsg( "currentevents" ), "" ) ;
+ $s .= "\n<hr>";
+ $articleExists = $wgTitle->getArticleId();
+ if ( $wgOut->isArticle() || $action =="edit" || $action =="history" || $wpPreview) {
+
+ if($wgOut->isArticle()) {
+ $s .= "<strong>" . $this->editThisPage() . "</strong>";
+ } else { # backlink to the article in edit or history mode
+
+ if($articleExists){ # no backlink if no article
+ $tns=$wgTitle->getNamespace();
+ switch($tns) {
+ case 0:
+ $text = wfMsg("articlepage");
+ break;
+ case 1:
+ $text = wfMsg("viewtalkpage");
+ break;
+ case 2:
+ $text = wfMsg("userpage");
+ break;
+ case 3:
+ $text = wfMsg("viewtalkpage");
+ break;
+ case 4:
+ $text = wfMsg("wikipediapage");
+ break;
+ case 5:
+ $text = wfMsg("viewtalkpage");
+ break;
+ case 6:
+ $text = wfMsg("imagepage");
+ break;
+ case 7:
+ $text = wfMsg("viewtalkpage");
+ break;
+ default:
+ $text= wfMsg("articlepage");
+ }
+
+ $link = $wgTitle->getText();
+ if ($nstext = $wgLang->getNsText($tns) ) { # add namespace if necessary
+ $link = $nstext . ":" . $link ;
+ }
+ $s .= $this->makeLink($link, $text );
+ } elseif( $wgTitle->getNamespace() != Namespace::getSpecial() ) {
+ # we just throw in a "New page" text to tell the user that he's in edit mode,
+ # and to avoid messing with the separator that is prepended to the next item
+ $s .= "<strong>" . wfMsg("newpage") . "</strong>";
+ }
+
+ }
+
+ /*
+ watching could cause problems in edit mode:
+ if user edits article, then loads "watch this article" in background and then saves
+ article with "Watch this article" checkbox disabled, the article is transparently
+ unwatched. Therefore we do not show the "Watch this page" link in edit mode
+ */
+ if ( 0 != $wgUser->getID() && $articleExists) {
+ if($action!="edit" && $action!="history" &&
+ $action != "submit" )
+ {$s .= $sep . $this->watchThisPage(); }
+ if ( $wgTitle->userCanEdit() ) $s .= $sep . $this->moveThisPage();
+ }
+ if ( $wgUser->isSysop() and $articleExists ) {
+ $s .= $sep . $this->deleteThisPage() .
+ $sep . $this->protectThisPage();
+ }
+ $s .= $sep . $this->talkLink();
+ if ($articleExists && $action !="history") { $s .= $sep . $this->historyLink();}
+ $s.=$sep . $this->whatLinksHere();
+
+ if($wgOut->isArticle()) {
+ $s .= $sep . $this->watchPageLinksLink();
+ }
+
+ if ( Namespace::getUser() == $wgTitle->getNamespace()
+ || $wgTitle->getNamespace() == Namespace::getTalk(Namespace::getUser())
+ ) {
+
+ $id=User::idFromName($wgTitle->getText());
+ $ip=User::isIP($wgTitle->getText());
+
+ if($id||$ip) {
+ $s .= $sep . $this->userContribsLink();
+ }
+ if ( 0 != $wgUser->getID() ) {
+ if($id) { # can only email real users
+ $s .= $sep . $this->emailUserLink();
+ }
+ }
+ }
+ $s .= "\n<hr>";
+ }
+
+ if ( 0 != $wgUser->getID() ) {
+ $s .= $this->specialLink( "upload" ) . $sep;
+ }
+ $s .= $this->specialLink( "specialpages" )
+ . $sep . $this->bugReportsLink();
+
+ $s .= "\n</div>\n";
+ wfProfileOut();
+ return $s;
+ }
+
+ function specialPagesList()
+ {
+ global $wgUser, $wgOut, $wgLang, $wgServer, $wgRedirectScript;
+ $a = array();
+
+ $validSP = $wgLang->getValidSpecialPages();
+
+ foreach ( $validSP as $name => $desc ) {
+ if ( "" == $desc ) { continue; }
+ $a[$name] = $desc;
+ }
+ if ( $wgUser->isSysop() )
+ {
+ $sysopSP = $wgLang->getSysopSpecialPages();
+
+ foreach ( $sysopSP as $name => $desc ) {
+ if ( "" == $desc ) { continue; }
+ $a[$name] = $desc ;
+ }
+ }
+ if ( $wgUser->isDeveloper() )
+ {
+ $devSP = $wgLang->getDeveloperSpecialPages();
+
+ foreach ( $devSP as $name => $desc ) {
+ if ( "" == $desc ) { continue; }
+ $a[$name] = $desc ;
+ }
+ }
+ $go = wfMsg( "go" );
+ $sp = wfMsg( "specialpages" );
+ $spp = $wgLang->specialPage( "Specialpages" );
+
+ $s = "<form id=\"specialpages\" method=\"get\" class=\"inline\" " .
+ "action=\"{$wgServer}{$wgRedirectScript}\">\n";
+ $s .= "<select name=\"wpDropdown\">\n";
+ $s .= "<option value=\"{$spp}\">{$sp}</option>\n";
+
+ foreach ( $a as $name => $desc ) {
+ $p = $wgLang->specialPage( $name );
+ $s .= "<option value=\"{$p}\">{$desc}</option>\n";
+ }
+ $s .= "</select>\n";
+ $s .= "<input type=submit value=\"{$go}\" name=redirect>\n";
+ $s .= "</form>\n";
+ return $s;
+ }
+
+ function mainPageLink()
+ {
+ $mp = wfMsg( "mainpage" );
+ $s = $this->makeKnownLink( $mp, $mp );
+ return $s;
+ }
+
+ function copyrightLink()
+ {
+ $s = $this->makeKnownLink( wfMsg( "copyrightpage" ),
+ wfMsg( "copyrightpagename" ) );
+ return $s;
+ }
+
+ function aboutLink()
+ {
+ $s = $this->makeKnownLink( wfMsg( "aboutpage" ),
+ wfMsg( "aboutwikipedia" ) );
+ return $s;
+ }
+
+ function editThisPage()
+ {
+ global $wgOut, $wgTitle, $oldid, $redirect, $diff;
+
+ if ( ! $wgOut->isArticle() || $diff ) {
+ $s = wfMsg( "protectedpage" );
+ } else if ( $wgTitle->userCanEdit() ) {
+ $n = $wgTitle->getPrefixedText();
+ $t = wfMsg( "editthispage" );
+ $oid = $red = "";
+
+ if ( $redirect ) { $red = "&redirect={$redirect}"; }
+ if ( $oldid && ! isset( $diff ) ) {
+ $oid = "&oldid={$oldid}";
+ }
+ $s = $this->makeKnownLink( $n, $t, "action=edit{$oid}{$red}" );
+ } else {
+ $s = wfMsg( "protectedpage" );
+ }
+ return $s;
+ }
+
+ function deleteThisPage()
+ {
+ global $wgUser, $wgOut, $wgTitle, $diff;
+
+ if ( $wgTitle->getArticleId() && ( ! $diff ) && $wgUser->isSysop() ) {
+ $n = $wgTitle->getPrefixedText();
+ $t = wfMsg( "deletethispage" );
+
+ $s = $this->makeKnownLink( $n, $t, "action=delete" );
+ } else {
+ $s = wfMsg( "error" );
+ }
+ return $s;
+ }
+
+ function protectThisPage()
+ {
+ global $wgUser, $wgOut, $wgTitle, $diff;
+
+ if ( $wgTitle->getArticleId() && ( ! $diff ) && $wgUser->isSysop() ) {
+ $n = $wgTitle->getPrefixedText();
+
+ if ( $wgTitle->isProtected() ) {
+ $t = wfMsg( "unprotectthispage" );
+ $q = "action=unprotect";
+ } else {
+ $t = wfMsg( "protectthispage" );
+ $q = "action=protect";
+ }
+ $s = $this->makeKnownLink( $n, $t, $q );
+ } else {
+ $s = wfMsg( "error" );
+ }
+ return $s;
+ }
+
+ function watchThisPage()
+ {
+ global $wgUser, $wgOut, $wgTitle, $diff;
+
+ if ( $wgOut->isArticle() && ( ! $diff ) ) {
+ $n = $wgTitle->getPrefixedText();
+
+ if ( $wgTitle->userIsWatching() ) {
+ $t = wfMsg( "unwatchthispage" );
+ $q = "action=unwatch";
+ } else {
+ $t = wfMsg( "watchthispage" );
+ $q = "action=watch";
+ }
+ $s = $this->makeKnownLink( $n, $t, $q );
+ } else {
+ $s = wfMsg( "notanarticle" );
+ }
+ return $s;
+ }
+
+ function moveThisPage()
+ {
+ global $wgTitle, $wgLang;
+
+ if ( $wgTitle->userCanEdit() ) {
+ $s = $this->makeKnownLink( $wgLang->specialPage( "Movepage" ),
+ wfMsg( "movethispage" ), "target=" . $wgTitle->getPrefixedURL() );
+ } // no message if page is protected - would be redundant
+ return $s;
+ }
+
+ function historyLink()
+ {
+ global $wgTitle;
+
+ $s = $this->makeKnownLink( $wgTitle->getPrefixedText(),
+ wfMsg( "history" ), "action=history" );
+ return $s;
+ }
+
+ function whatLinksHere()
+ {
+ global $wgTitle, $wgLang;
+
+ $s = $this->makeKnownLink( $wgLang->specialPage( "Whatlinkshere" ),
+ wfMsg( "whatlinkshere" ), "target=" . $wgTitle->getPrefixedURL() );
+ return $s;
+ }
+
+ function userContribsLink()
+ {
+ global $wgTitle, $wgLang;
+
+ $s = $this->makeKnownLink( $wgLang->specialPage( "Contributions" ),
+ wfMsg( "contributions" ), "target=" . $wgTitle->getURL() );
+ return $s;
+ }
+
+ function emailUserLink()
+ {
+ global $wgTitle, $wgLang;
+
+ $s = $this->makeKnownLink( $wgLang->specialPage( "Emailuser" ),
+ wfMsg( "emailuser" ), "target=" . $wgTitle->getURL() );
+ return $s;
+ }
+
+ function watchPageLinksLink()
+ {
+ global $wgOut, $wgTitle, $wgLang;
+
+ if ( ! $wgOut->isArticle() ) {
+ $s = "(" . wfMsg( "notanarticle" ) . ")";
+ } else {
+ $s = $this->makeKnownLink( $wgLang->specialPage(
+ "Recentchangeslinked" ), wfMsg( "recentchangeslinked" ),
+ "target=" . $wgTitle->getPrefixedURL() );
+ }
+ return $s;
+ }
+
+ function otherLanguages()
+ {
+ global $wgOut, $wgLang, $wgTitle , $wgUseNewInterlanguage ;
+
+ $a = $wgOut->getLanguageLinks();
+ if ( 0 == count( $a ) ) {
+ if ( !$wgUseNewInterlanguage ) return "";
+ $ns = $wgLang->getNsIndex ( $wgTitle->getNamespace () ) ;
+ if ( $ns != 0 AND $ns != 1 ) return "" ;
+ $pn = "Intl" ;
+ $x = "mode=addlink&xt=".$wgTitle->getDBkey() ;
+ return $this->makeKnownLink( $wgLang->specialPage( $pn ),
+ wfMsg( "intl" ) , $x );
+ }
+
+ if ( !$wgUseNewInterlanguage ) {
+ $s = wfMsg( "otherlanguages" ) . ": ";
+ } else {
+ global $wgLanguageCode ;
+ $x = "mode=zoom&xt=".$wgTitle->getDBkey() ;
+ $x .= "&xl=".$wgLanguageCode ;
+ $s = $this->makeKnownLink( $wgLang->specialPage( "Intl" ),
+ wfMsg( "otherlanguages" ) , $x ) . ": " ;
+ }
+
+ $first = true;
+ foreach( $a as $l ) {
+ if ( ! $first ) { $s .= " | "; }
+ $first = false;
+
+ $nt = Title::newFromText( $l );
+ $url = $nt->getFullURL();
+ $text = $wgLang->getLanguageName( $nt->getInterwiki() );
+
+ if ( "" == $text ) { $text = $l; }
+ $style = $this->getExternalLinkAttributes( $l, $text );
+ $s .= "<a href=\"{$url}\"{$style}>{$text}</a>";
+ }
+ return $s;
+ }
+
+ function bugReportsLink()
+ {
+ $s = $this->makeKnownLink( wfMsg( "bugreportspage" ),
+ wfMsg( "bugreports" ) );
+ return $s;
+ }
+
+ function dateLink()
+ {
+ global $wgLinkCache;
+ $t1 = Title::newFromText( date( "F j" ) );
+ $t2 = Title::newFromText( date( "Y" ) );
+
+ $wgLinkCache->suspend();
+ $id = $t1->getArticleID();
+ $wgLinkCache->resume();
+
+ if ( 0 == $id ) {
+ $s = $this->makeBrokenLink( $t1->getText() );
+ } else {
+ $s = $this->makeKnownLink( $t1->getText() );
+ }
+ $s .= ", ";
+
+ $wgLinkCache->suspend();
+ $id = $t2->getArticleID();
+ $wgLinkCache->resume();
+
+ if ( 0 == $id ) {
+ $s .= $this->makeBrokenLink( $t2->getText() );
+ } else {
+ $s .= $this->makeKnownLink( $t2->getText() );
+ }
+ return $s;
+ }
+
+ function talkLink()
+ {
+ global $wgLang, $wgTitle, $wgLinkCache;
+
+ $tns = $wgTitle->getNamespace();
+ if ( -1 == $tns ) { return ""; }
+
+ $pn = $wgTitle->getText();
+ $tp = wfMsg( "talkpage" );
+ if ( Namespace::isTalk( $tns ) ) {
+ $lns = Namespace::getSubject( $tns );
+ switch($tns) {
+ case 1:
+ $text = wfMsg("articlepage");
+ break;
+ case 3:
+ $text = wfMsg("userpage");
+ break;
+ case 5:
+ $text = wfMsg("wikipediapage");
+ break;
+ case 7:
+ $text = wfMsg("imagepage");
+ break;
+ default:
+ $text= wfMsg("articlepage");
+ }
+ } else {
+
+ $lns = Namespace::getTalk( $tns );
+ $text=$tp;
+ }
+ $n = $wgLang->getNsText( $lns );
+ if ( "" == $n ) { $link = $pn; }
+ else { $link = "{$n}:{$pn}"; }
+
+ $wgLinkCache->suspend();
+ $s = $this->makeLink( $link, $text );
+ $wgLinkCache->resume();
+
+ return $s;
+ }
+
+ # After all the page content is transformed into HTML, it makes
+ # a final pass through here for things like table backgrounds.
+ #
+ function transformContent( $text )
+ {
+ return $text;
+ }
+
+ # Note: This function MUST call getArticleID() on the link,
+ # otherwise the cache won't get updated properly. See LINKCACHE.DOC.
+ #
+ function makeLink( $title, $text= "", $query = "", $trail = "" )
+ {
+ global $wgOut, $wgUser;
+
+ $nt = Title::newFromText( $title );
+
+ if ( $nt->isExternal() ) {
+ $u = $nt->getFullURL();
+ if ( "" == $text ) { $text = $nt->getPrefixedText(); }
+ $style = $this->getExternalLinkAttributes( $link, $text );
+
+ $inside = "";
+ if ( "" != $trail ) {
+ if ( preg_match( "/^([a-z]+)(.*)$$/sD", $trail, $m ) ) {
+ $inside = $m[1];
+ $trail = $m[2];
+ }
+ }
+ return "<a href=\"{$u}\"{$style}>{$text}{$inside}</a>{$trail}";
+ }
+ if ( 0 == $nt->getNamespace() && "" == $nt->getText() ) {
+ return $this->makeKnownLink( $title, $text, $query, $trail );
+ }
+ if ( ( -1 == $nt->getNamespace() ) ||
+ ( Namespace::getImage() == $nt->getNamespace() ) ) {
+ return $this->makeKnownLink( $title, $text, $query, $trail );
+ }
+ $aid = $nt->getArticleID() ;
+ if ( 0 == $aid ) {
+ return $this->makeBrokenLink( $title, $text, $query, $trail );
+ } else {
+ $threshold = $wgUser->getOption("stubthreshold") ;
+ if ( $threshold > 0 ) {
+ $res = wfQuery ( "SELECT HIGH_PRIORITY length(cur_text) AS x, cur_namespace, cur_is_redirect FROM cur WHERE cur_id='{$aid}'" ) ;
+
+ if ( wfNumRows( $res ) > 0 ) {
+ $s = wfFetchObject( $res );
+ $size = $s->x;
+ if ( $s->cur_is_redirect OR $s->cur_namespace != 0 )
+ $size = $threshold*2 ; # Really big
+ wfFreeResult( $res );
+ } else $size = $threshold*2 ; # Really big
+ } else $size = 1 ;
+
+ if ( $size < $threshold )
+ return $this->makeStubLink( $title, $text, $query, $trail );
+ return $this->makeKnownLink( $title, $text, $query, $trail );
+ }
+ }
+
+ function makeKnownLink( $title, $text = "", $query = "", $trail = "" )
+ {
+ global $wgOut, $wgTitle;
+
+ $nt = Title::newFromText( $title );
+ $link = $nt->getPrefixedURL();
+
+ if ( "" == $link ) {
+ $u = "";
+ if ( "" == $text ) { $text = $nt->getFragment(); }
+ } else {
+ $u = wfLocalUrlE( $link, $query );
+ }
+ if ( "" != $nt->getFragment() ) {
+ $u .= "#" . wfEscapeHTML( $nt->getFragment() );
+ }
+ if ( "" == $text ) { $text = $nt->getPrefixedText(); }
+ $style = $this->getInternalLinkAttributes( $link, $text );
+
+ $inside = "";
+ if ( "" != $trail ) {
+ if ( preg_match( wfMsg("linktrail"), $trail, $m ) ) {
+ $inside = $m[1];
+ $trail = $m[2];
+ }
+ }
+ $r = "<a href=\"{$u}\"{$style}>{$text}{$inside}</a>{$trail}";
+ return $r;
+ }
+
+ function makeBrokenLink( $title, $text = "", $query = "", $trail = "" )
+ {
+ global $wgOut, $wgUser;
+
+ $nt = Title::newFromText( $title );
+ $link = $nt->getPrefixedURL();
+
+ if ( "" == $query ) { $q = "action=edit"; }
+ else { $q = "action=edit&{$query}"; }
+ $u = wfLocalUrlE( $link, $q );
+
+ if ( "" == $text ) { $text = $nt->getPrefixedText(); }
+ $style = $this->getInternalLinkAttributes( $link, $text, "yes" );
+
+ $inside = "";
+ if ( "" != $trail ) {
+ if ( preg_match( wfMsg("linktrail"), $trail, $m ) ) {
+ $inside = $m[1];
+ $trail = $m[2];
+ }
+ }
+ if ( $wgOut->isPrintable() ||
+ ( 1 == $wgUser->getOption( "highlightbroken" ) ) ) {
+ $s = "<a href=\"{$u}\"{$style}>{$text}{$inside}</a>{$trail}";
+ } else {
+ $s = "{$text}{$inside}<a href=\"{$u}\"{$style}>?</a>{$trail}";
+ }
+ return $s;
+ }
+
+ function makeStubLink( $title, $text = "", $query = "", $trail = "" )
+ {
+ global $wgOut, $wgUser;
+
+ $nt = Title::newFromText( $title );
+ $link = $nt->getPrefixedURL();
+
+ $u = wfLocalUrlE( $link, $query );
+
+ if ( "" == $text ) { $text = $nt->getPrefixedText(); }
+ $style = $this->getInternalLinkAttributes( $link, $text, "stub" );
+
+ $inside = "";
+ if ( "" != $trail ) {
+ if ( preg_match( wfMsg("linktrail"), $trail, $m ) ) {
+ $inside = $m[1];
+ $trail = $m[2];
+ }
+ }
+ if ( $wgOut->isPrintable() ||
+ ( 1 == $wgUser->getOption( "highlightbroken" ) ) ) {
+ $s = "<a href=\"{$u}\"{$style}>{$text}{$inside}</a>{$trail}";
+ } else {
+ $s = "{$text}{$inside}<a href=\"{$u}\"{$style}>!</a>{$trail}";
+ }
+ return $s;
+ }
+
+ function fnamePart( $url )
+ {
+ $basename = strrchr( $url, "/" );
+ if ( false === $basename ) { $basename = $url; }
+ else { $basename = substr( $basename, 1 ); }
+ return wfEscapeHTML( $basename );
+ }
+
+ function makeImage( $url, $alt = "" )
+ {
+ global $wgOut;
+
+ if ( "" == $alt ) { $alt = $this->fnamePart( $url ); }
+ $s = "<img src=\"{$url}\" alt=\"{$alt}\">";
+ return $s;
+ }
+
+ function makeImageLink( $name, $url, $alt = "" )
+ {
+ global $wgOut, $wgTitle, $wgLang;
+
+ $nt = Title::newFromText( $wgLang->getNsText(
+ Namespace::getImage() ) . ":{$name}" );
+ $link = $nt->getPrefixedURL();
+ if ( "" == $alt ) { $alt = $name; }
+
+ $u = wfLocalUrlE( $link );
+ $s = "<a href=\"{$u}\" class='image' title=\"{$alt}\">" .
+ "<img border=0 src=\"{$url}\" alt=\"{$alt}\"></a>";
+ return $s;
+ }
+
+ function makeMediaLink( $name, $url, $alt = "" )
+ {
+ global $wgOut, $wgTitle;
+
+ if ( "" == $alt ) { $alt = $name; }
+ $u = wfEscapeHTML( $url );
+ $s = "<a href=\"{$u}\" class='media' title=\"{$alt}\">{$alt}</a>";
+ return $s;
+ }
+
+ function specialLink( $name, $key = "" )
+ {
+ global $wgLang;
+
+ if ( "" == $key ) { $key = strtolower( $name ); }
+ $pn = $wgLang->ucfirst( $name );
+ return $this->makeKnownLink( $wgLang->specialPage( $pn ),
+ wfMsg( $key ) );
+ }
+
+ # Called by history lists and recent changes
+ #
+
+ function beginRecentChangesList()
+ {
+ $rc_cache = array() ;
+ $rccc = 0 ;
+ $this->lastdate = "";
+ return "";
+ }
+
+ function beginHistoryList()
+ {
+ $this->lastdate = $this->lastline = "";
+ $s = "\n<p>" . wfMsg( "histlegend" ) . "\n<ul>";
+ return $s;
+ }
+
+ function beginImageHistoryList()
+ {
+ $s = "\n<h2>" . wfMsg( "imghistory" ) . "</h2>\n" .
+ "<p>" . wfMsg( "imghistlegend" ) . "\n<ul>";
+ return $s;
+ }
+
+ function endRecentChangesList()
+ {
+ $s = $this->recentChangesBlock() ;
+ $s .= "</ul>\n";
+ return $s;
+ }
+
+ function endHistoryList()
+ {
+ $last = wfMsg( "last" );
+
+ $s = preg_replace( "/!OLDID![0-9]+!/", $last, $this->lastline );
+ $s .= "</ul>\n";
+ return $s;
+ }
+
+ function endImageHistoryList()
+ {
+ $s = "</ul>\n";
+ return $s;
+ }
+
+ function historyLine( $ts, $u, $ut, $ns, $ttl, $oid, $c, $isminor )
+ {
+ global $wgLang;
+
+ $artname = Title::makeName( $ns, $ttl );
+ $last = wfMsg( "last" );
+ $cur = wfMsg( "cur" );
+ $cr = wfMsg( "currentrev" );
+
+ if ( $oid && $this->lastline ) {
+ $ret = preg_replace( "/!OLDID!([0-9]+)!/", $this->makeKnownLink(
+ $artname, $last, "diff=\\1&oldid={$oid}" ), $this->lastline );
+ } else {
+ $ret = "";
+ }
+ $dt = $wgLang->timeanddate( $ts, true );
+
+ if ( $oid ) { $q = "oldid={$oid}"; }
+ else { $q = ""; }
+ $link = $this->makeKnownLink( $artname, $dt, $q );
+
+ if ( 0 == $u ) {
+ $ul = $this->makeKnownLink( $wgLang->specialPage( "Contributions" ),
+ $ut, "target=" . $ut );
+ } else { $ul = $this->makeLink( $wgLang->getNsText(
+ Namespace::getUser() ) . ":{$ut}", $ut ); }
+
+ $s = "<li>";
+ if ( $oid ) {
+ $curlink = $this->makeKnownLink( $artname, $cur,
+ "diff=0&oldid={$oid}" );
+ } else {
+ $curlink = $cur;
+ }
+ $s .= "({$curlink}) (!OLDID!{$oid}!) . .";
+
+ $M = wfMsg( "minoreditletter" );
+ if ( $isminor ) { $s .= " <strong>{$M}</strong>"; }
+ $s .= " {$link} . . {$ul}";
+
+ if ( "" != $c && "*" != $c ) { $s .= " <em>(" . wfEscapeHTML($c) . ")</em>"; }
+ $s .= "</li>\n";
+
+ $this->lastline = $s;
+ return $ret;
+ }
+
+ function recentChangesBlockLine ( $y ) {
+ global $wgUploadPath ;
+
+ $M = wfMsg( "minoreditletter" );
+ $N = wfMsg( "newpageletter" );
+ $r = "" ;
+ $r .= "<img src='{$wgUploadPath}/Arr_.png' width=12 height=12 border=0>" ;
+ $r .= "<tt>" ;
+ if ( $y->isnew ) $r .= $N ;
+ else $r .= "&nbsp;" ;
+ if ( $y->isminor ) $r .= $M ;
+ else $r .= "&nbsp;" ;
+ $r .= " ".$y->timestamp." " ;
+ $r .= "</tt>" ;
+ $link = $y->link ;
+ if ( $y->watched ) $link = "<strong>{$link}</strong>" ;
+ $r .= $link ;
+
+ $r .= " (" ;
+ $r .= $y->curlink ;
+ $r .= "; " ;
+ $r .= $this->makeKnownLink( $y->secureName, wfMsg( "hist" ), "action=history" );
+
+ $r .= ") . . ".$y->userlink ;
+ $r .= $y->usertalklink ;
+ if ( $y->usercomment != "" )
+ $r .= " <em>(".wfEscapeHTML($y->usercomment).")</em>" ;
+ $r .= "<br>\n" ;
+ return $r ;
+ }
+
+ function recentChangesBlockGroup ( $y ) {
+ global $wgUploadPath ;
+
+ $r = "" ;
+ $M = wfMsg( "minoreditletter" );
+ $N = wfMsg( "newpageletter" );
+ $isnew = false ;
+ $userlinks = array () ;
+ foreach ( $y AS $x ) {
+ $oldid = $x->diffid ;
+ if ( $x->isnew ) $isnew = true ;
+ $u = $x->userlink ;
+ if ( !isset ( $userlinks[$u] ) ) $userlinks[$u] = 0 ;
+ $userlinks[$u]++ ;
+ }
+
+ krsort ( $userlinks ) ;
+ asort ( $userlinks ) ;
+ $users = array () ;
+ $u = array_keys ( $userlinks ) ;
+ foreach ( $u as $x ) {
+ $z = $x ;
+ if ( $userlinks[$x] > 1 ) $z .= " ({$userlinks[$x]}&times;)" ;
+ array_push ( $users , $z ) ;
+ }
+ $users = " <font size='-1'>[".implode("; ",$users)."]</font>" ;
+
+ $e = $y ;
+ $e = array_shift ( $e ) ;
+
+ # Arrow
+ $rci = "RCI{$this->rccc}" ;
+ $rcl = "RCL{$this->rccc}" ;
+ $rcm = "RCM{$this->rccc}" ;
+ $tl = "<a href='javascript:toggleVisibility(\"{$rci}\",\"{$rcm}\",\"{$rcl}\")'>" ;
+ $tl .= "<span id='{$rcm}'><img src='{$wgUploadPath}/Arr_r.png' width=12 height=12 border=0></span>" ;
+ $tl .= "<span id='{$rcl}' style='display:none'><img src='{$wgUploadPath}/Arr_d.png' width=12 height=12 border=0></span>" ;
+ $tl .= "</a>" ;
+ $r .= $tl ;
+
+ # Main line
+ $r .= "<tt>" ;
+ if ( $isnew ) $r .= $N ;
+ else $r .= "&nbsp;" ;
+ $r .= "&nbsp;" ; # Minor
+ $r .= " ".$e->timestamp." " ;
+ $r .= "</tt>" ;
+
+ $link = $e->link ;
+ if ( $e->watched ) $link = "<strong>{$link}</strong>" ;
+ $r .= $link ;
+
+ if ( !$e->islog ) {
+ $r .= " (".count($y)." " ;
+ if ( $isnew ) $r .= wfMsg("changes");
+ else $r .= $this->makeKnownLink( $e->secureName , wfMsg("changes") , "diff=0&oldid=".$oldid ) ;
+ $r .= "; " ;
+ $r .= $this->makeKnownLink( $e->secureName, wfMsg( "history" ), "action=history" );
+ $r .= ")" ;
+ }
+
+ $r .= $users ;
+ $r .= "<br>\n" ;
+
+ # Sub-entries
+ $r .= "<div id='{$rci}' style='display:none'>" ;
+ foreach ( $y AS $x )
+ {
+ $r .= "<img src='{$wgUploadPath}/Arr_.png' width=12 height=12 border=0>";
+ $r .= "<tt>&nbsp; &nbsp; &nbsp; &nbsp;" ;
+ if ( $x->isnew ) $r .= $N ;
+ else $r .= "&nbsp;" ;
+ if ( $x->isminor ) $r .= $M ;
+ else $r .= "&nbsp;" ;
+ $r .= "</tt>" ;
+
+ $o = "" ;
+ if ( $x->oldid != 0 ) $o = "oldid=".$x->oldid ;
+ if ( $x->islog ) $link = $x->timestamp ;
+ else $link = $this->makeKnownLink( $x->secureName, $x->timestamp , $o ) ;
+ $link = "<tt>{$link}</tt>" ;
+
+
+ $r .= $link ;
+ $r .= " (" ;
+ $r .= $x->curlink ;
+ $r .= "; " ;
+ $r .= $x->lastlink ;
+ $r .= ") . . ".$x->userlink ;
+ $r .= $x->usertalklink ;
+ if ( $x->usercomment != "" )
+ $r .= " <em>(".wfEscapeHTML($x->usercomment).")</em>" ;
+ $r .= "<br>\n" ;
+ }
+ $r .= "</div>\n" ;
+
+ $this->rccc++ ;
+ return $r ;
+ }
+
+ function recentChangesBlock ()
+ {
+ global $wgUploadPath ;
+ if ( count ( $this->rc_cache ) == 0 ) return "" ;
+ $k = array_keys ( $this->rc_cache ) ;
+ foreach ( $k AS $x )
+ {
+ $y = $this->rc_cache[$x] ;
+ if ( count ( $y ) < 2 ) {
+ $r .= $this->recentChangesBlockLine ( array_shift ( $y ) ) ;
+ } else {
+ $r .= $this->recentChangesBlockGroup ( $y ) ;
+ }
+ }
+
+ return "<div align=left>{$r}</div>" ;
+ }
+
+ function recentChangesLine( $ts, $u, $ut, $ns, $ttl, $c, $isminor, $isnew, $watched = false, $oldid = 0 , $diffid = 0 )
+ {
+ global $wgUser ;
+ $usenew = $wgUser->getOption( "usenewrc" );
+ if ( $usenew )
+ $r = $this->recentChangesLineNew ( $ts, $u, $ut, $ns, $ttl, $c, $isminor, $isnew, $watched , $oldid , $diffid ) ;
+ else
+ $r = $this->recentChangesLineOld ( $ts, $u, $ut, $ns, $ttl, $c, $isminor, $isnew, $watched , $oldid , $diffid ) ;
+ return $r ;
+ }
+
+ function recentChangesLineOld( $ts, $u, $ut, $ns, $ttl, $c, $isminor, $isnew, $watched = false, $oldid = 0, $diffid = 0 )
+ {
+ global $wgTitle, $wgLang, $wgUser;
+
+ $d = $wgLang->date( $ts, true);
+ $s = "";
+ if ( $d != $this->lastdate ) {
+ if ( "" != $this->lastdate ) { $s .= "</ul>\n"; }
+ $s .= "<h4>{$d}</h4>\n<ul>";
+ $this->lastdate = $d;
+ }
+ $h = $wgLang->time( $ts, true );
+ $t = Title::makeName( $ns, $ttl );
+ $clink = $this->makeKnownLink( $t , "" );
+ $nt = Title::newFromText( $t );
+
+ if ( $watched ) {
+ $clink = "<strong>{$clink}</strong>";
+ }
+ $hlink = $this->makeKnownLink( $t, wfMsg( "hist" ), "action=history" );
+ if ( $isnew || $nt->isLog() ) {
+ $dlink = wfMsg( "diff" );
+ } else {
+ $dlink = $this->makeKnownLink( $t, wfMsg( "diff" ),
+ "diff={$oldid}&oldid={$diffid}" ); # Finagle's law
+ }
+ if ( 0 == $u ) {
+ $ul = $this->makeKnownLink( $wgLang->specialPage( "Contributions" ),
+ $ut, "target=" . $ut );
+ } else { $ul = $this->makeLink( $wgLang->getNsText(
+ Namespace::getUser() ) . ":{$ut}", $ut ); }
+
+ $utns=$wgLang->getNsText(Namespace::getTalk(Namespace::getUser()));
+ $talkname=$wgLang->getNsText(Namespace::getTalk(0)); # use the shorter name
+ $utl= $this->makeLink($utns . ":{$ut}", $talkname );
+ $cr = wfMsg( "currentrev" );
+
+ $s .= "<li> ({$dlink}) ({$hlink}) . .";
+ $M = wfMsg( "minoreditletter" );
+ $N = wfMsg( "newpageletter" );
+ if ( $isminor ) { $s .= " <strong>{$M}</strong>"; }
+ if ( $isnew ) { $s .= "<strong>{$N}</strong>"; }
+ $s .= " {$clink}; {$h} . . {$ul}";
+
+ $blink="";
+ if ( ( 0 == $u ) && $wgUser->isSysop() ) {
+ $blink = $this->makeKnownLink( $wgLang->specialPage(
+ "Blockip" ), wfMsg( "blocklink" ), "ip={$ut}" );
+
+ }
+ if(!$blink) {
+ $utl = "({$utl})";
+ } else {
+ $utl = "({$utl} | {$blink})";
+ }
+ $s.=" {$utl}";
+
+ if ( "" != $c && "*" != $c ) {
+ $s .= " <em>(" . wfEscapeHTML( $c ) . ")</em>";
+ }
+ $s .= "</li>\n";
+
+ return $s;
+ }
+
+ function recentChangesLineNew( $ts, $u, $ut, $ns, $ttl, $c, $isminor, $isnew, $watched = false, $oldid = 0 , $diffid = 0 )
+ {
+ global $wgTitle, $wgLang, $wgUser;
+
+ $rc = new RecentChangesClass ;
+
+ $d = $wgLang->date( $ts, true);
+ $s = "";
+ $ret = "" ;
+ if ( $d != $this->lastdate ) {
+ $ret = $this->recentChangesBlock () ;
+ $this->rc_cache = array() ;
+ $ret .= "<h4>{$d}</h4>\n";
+ $this->lastdate = $d;
+ }
+ $h = $wgLang->time( $ts, true );
+ $t = Title::makeName( $ns, $ttl );
+ $clink = $this->makeKnownLink( $t, "" ) ;
+ if ( $oldid == 0 ) $c2link = $clink ;
+ else $c2link = $this->makeKnownLink( $t, "" , "oldid={$oldid}" );
+ $nt = Title::newFromText( $t );
+
+ $rc->timestamp = $h ;
+ $rc->oldid = $oldid ;
+ $rc->diffid = $diffid ;
+ $rc->watched = $watched ;
+ $rc->isnew = $isnew ;
+ $rc->isminor = $isminor ;
+ $rc->secureName = $t ;
+ $rc->displayName = $nt ;
+ $rc->link = $clink ;
+ $rc->usercomment = $c ;
+ $rc->islog = $nt->isLog() ;
+
+ if ( ( $isnew && $oldid == 0 ) || $nt->isLog() ) {
+ $dlink = wfMsg( "cur" );
+ } else {
+ $dlink = $this->makeKnownLink( $t, wfMsg( "cur" ),
+ "diff=0&oldid={$oldid}" );
+ }
+
+ if ( $diffid == 0 || $nt->isLog() ) {
+ $plink = wfMsg( "last" );
+ } else {
+ $plink = $this->makeKnownLink( $t, wfMsg( "last" ),
+ "diff={$oldid}&oldid={$diffid}" );
+ }
+
+ if ( 0 == $u ) {
+ $ul = $this->makeKnownLink( $wgLang->specialPage( "Contributions" ),
+ $ut, "target=" . $ut );
+ } else { $ul = $this->makeLink( $wgLang->getNsText(
+ Namespace::getUser() ) . ":{$ut}", $ut ); }
+
+ $rc->userlink = $ul ;
+ $rc->lastlink = $plink ;
+ $rc->curlink = $dlink ;
+
+ $utns=$wgLang->getNsText(Namespace::getTalk(Namespace::getUser()));
+ $talkname=$wgLang->getNsText(Namespace::getTalk(0)); # use the shorter name
+ $utl= $this->makeLink($utns . ":{$ut}", $talkname );
+
+ if ( ( 0 == $u ) && $wgUser->isSysop() ) {
+ $blink = $this->makeKnownLink( $wgLang->specialPage(
+ "Blockip" ), wfMsg( "blocklink" ), "ip={$ut}" );
+ $rc->usertalklink= " ({$utl} | {$blink})";
+ } else {
+ $rc->usertalklink=" ({$utl})";
+ }
+
+ if ( !isset ( $this->rc_cache[$t] ) ) $this->rc_cache[$t] = array() ;
+ array_push ( $this->rc_cache[$t] , $rc ) ;
+ return $ret;
+ }
+
+
+ function imageHistoryLine( $iscur, $ts, $img, $u, $ut, $size, $c )
+ {
+ global $wgUser, $wgLang, $wgTitle;
+
+ $dt = $wgLang->timeanddate( $ts, true );
+ $del = wfMsg( "deleteimg" );
+ $cur = wfMsg( "cur" );
+
+ if ( $iscur ) {
+ $url = wfImageUrl( $img );
+ $rlink = $cur;
+ if ( $wgUser->isSysop() ) {
+ $link = wfLocalUrlE( "", "image=" . $wgTitle->getURL() .
+ "&action=delete" );
+ $style = $this->getInternalLinkAttributes( $link, $del );
+
+ $dlink = "<a href=\"{$link}\"{$style}>{$del}</a>";
+ } else {
+ $dlink = $del;
+ }
+ } else {
+ $url = wfEscapeHTML( wfImageArchiveUrl( $img ) );
+ if( $wgUser->getID() != 0 ) {
+ $rlink = $this->makeKnownLink( $wgTitle->getPrefixedText(),
+ wfMsg( "revertimg" ), "action=revert&oldimage=" .
+ urlencode( $img ) );
+ $dlink = $this->makeKnownLink( $wgTitle->getPrefixedText(),
+ $del, "action=delete&oldimage=" . urlencode( $img ) );
+ } else {
+ # Having live active links for non-logged in users
+ # means that bots and spiders crawling our site can
+ # inadvertently change content. Baaaad idea.
+ $rlink = wfMsg( "revertimg" );
+ $dlink = $del;
+ }
+ }
+ if ( 0 == $u ) { $ul = $ut; }
+ else { $ul = $this->makeLink( $wgLang->getNsText(
+ Namespace::getUser() ) . ":{$ut}", $ut ); }
+
+ $nb = str_replace( "$1", $size, wfMsg( "nbytes" ) );
+ $style = $this->getInternalLinkAttributes( $url, $dt );
+
+ $s = "<li> ({$dlink}) ({$rlink}) <a href=\"{$url}\"{$style}>{$dt}</a>"
+ . " . . {$ul} ({$nb})";
+
+ if ( "" != $c && "*" != $c ) {
+ $s .= " <em>(" . wfEscapeHTML( $c ) . ")</em>";
+ }
+ $s .= "</li>\n";
+ return $s;
+ }
+}
+
+include_once( "SkinStandard.php" );
+include_once( "SkinNostalgia.php" );
+include_once( "SkinCologneBlue.php" );
+
+?>
diff --git a/includes/SkinCologneBlue.php b/includes/SkinCologneBlue.php
new file mode 100644
index 000000000000..e0b4aba50f46
--- /dev/null
+++ b/includes/SkinCologneBlue.php
@@ -0,0 +1,216 @@
+<?
+# See skin.doc
+
+class SkinCologneBlue extends Skin {
+
+ function initPage()
+ {
+ global $wgOut, $wgStyleSheetPath;
+
+ $wgOut->addLink( "stylesheet", "",
+ "{$wgStyleSheetPath}/cologneblue.css" );
+ }
+
+ function doBeforeContent()
+ {
+ global $wgUser, $wgOut, $wgTitle;
+
+ $s = "";
+ $qb = $this->qbSetting();
+
+ $s .= "\n<div id='content'>\n<div id='topbar'>" .
+ "<table width='98%' border=0 cellspacing=0 cellpadding=8><tr>";
+
+ $s .= "<td class='top' align=left valign=center nowrap>";
+ $s .= "<a href=\"" . wfLocalUrlE( wfMsg( "mainpage" ) ) . "\">";
+ $s .= "<span id='sitetitle'>" . wfMsg( "sitetitle" ) . "</span></a>";
+
+ $s .= "</td><td class='top' align=right valign=bottom width='100%'>";
+ $s .= $this->sysLinks();
+ $s .= "</td></tr><tr><td valign=top>";
+
+ $s .= "<font size='-1'><span id='sitesub'>";
+ $s .= wfMsg( "sitesubtitle" ) . "</span></font>";
+ $s .= "</td><td align=right>" ;
+
+ $s .= "<font size='-1'><span id='langlinks'>" ;
+ $s .= str_replace ( "<br>" , "" , $this->otherLanguages() ) ;
+ $s .= "<br>" . $this->pageTitleLinks();
+ $s .= "</span></font>";
+
+ $s .= "</td></tr></table>\n";
+
+ $s .= "\n</div>\n<div id='article'>";
+
+ $s .= $this->pageTitle();
+ $s .= $this->pageSubtitle() . "\n<p>";
+ return $s;
+ }
+
+ function doAfterContent()
+ {
+ global $wgUser, $wgOut;
+
+ $s = "\n</div><br clear=all>\n";
+
+ $s .= "\n<div id='footer'>";
+ $s .= "<table width='98%' border=0 cellspacing=0><tr>";
+
+ $qb = $this->qbSetting();
+ if ( 1 == $qb || 3 == $qb ) { # Left
+ $s .= $this->getQuickbarCompensator();
+ }
+ $s .= "<td class='bottom' align=center valign=top>";
+
+ $s .= $this->bottomLinks();
+ $s .= "\n<br>" . $this->makeKnownLink( wfMsg( "mainpage" ),
+ wfMsg( "mainpage" ) ) . " | "
+ . $this->aboutLink() . " | "
+ . $this->searchForm( wfMsg( "qbfind" ) );
+
+ $s .= "\n<br>" . $this->pageStats();
+
+ $s .= "</td>";
+ if ( 2 == $qb ) { # Right
+ $s .= $this->getQuickbarCompensator();
+ }
+ $s .= "</tr></table>\n</div>\n</div>\n";
+
+ if ( 0 != $qb ) { $s .= $this->quickBar(); }
+ return $s;
+ }
+ function doGetUserStyles()
+ {
+ global $wgUser, $wgOut, $wgStyleSheetPath;
+
+ $s = parent::doGetUserStyles();
+ $qb = $this->qbSetting();
+
+ if ( 2 == $qb ) { # Right
+ $s .= "#quickbar { position: absolute; right: 4px; }\n" .
+ "#article { margin-left: 4px; margin-right: 148px; }\n";
+ } else if ( 1 == $qb || 3 == $qb ) {
+ $s .= "#quickbar { position: absolute; left: 4px; }\n" .
+ "#article { margin-left: 148px; margin-right: 4px; }\n";
+ }
+ return $s;
+ }
+ function sysLinks()
+ {
+ global $wgUser;
+ $s = "" .
+ $this->makeKnownLink( wfMsg( "mainpage" ), wfMsg( "mainpage" ) )
+ . " | " .
+ $this->makeKnownLink( wfMsg( "aboutpage" ), wfMsg( "about" ) )
+ . " | " .
+ $this->makeKnownLink( wfMsg( "helppage" ), wfMsg( "help" ) )
+ . " | " .
+ $this->makeKnownLink( wfMsg( "faqpage" ), wfMsg("faq") )
+ . " | " .
+ $this->specialLink( "specialpages" ) . " | " .
+ $this->specialLink( $wgUser->getID() ? "userlogout" : "userlogin" ) ;
+ return $s;
+ }
+
+ function quickBar()
+ {
+ global $wgOut, $wgTitle, $wgUser, $wgLang;
+
+ $s = "\n<div id='quickbar'>";
+
+ $sep = "<br>";
+ $s .= $this->menuHead( "qbfind" );
+ $s .= $this->searchForm();
+
+ $s .= $this->menuHead( "qbbrowse" )
+ . $this->mainPageLink()
+ . $sep . $this->specialLink( "recentchanges" )
+ . $sep . $this->specialLink( "randompage" )
+ . $sep . $this->specialLink( "newpages" )
+ . $sep . $this->specialLink( "imagelist" )
+ . $sep . $this->specialLink( "statistics" )
+ . $sep . $this->specialLink( "specialpages" )
+ . $sep . $this->bugReportsLink() ;
+ if ( wfMsg ( "currentevents" ) != "-" ) $s .= $sep . $this->makeKnownLink( wfMsg( "currentevents" ), "" ) ;
+ $s .= "\n";
+
+ if ( $wgOut->isArticle() ) {
+ $s .= $this->menuHead( "qbedit" );
+ $s .= "<strong>" . $this->editThisPage() . "</strong>"
+ . $sep . $this->makeKnownLink( wfMsg( "edithelppage" ),
+ wfMsg( "edithelp" ) );
+
+ if ( 0 != $wgUser->getID() ) {
+ $s .= $sep . $this->specialLink( "upload" )
+ . $sep . $this->moveThisPage();
+ }
+ if ( $wgUser->isSysop() ) {
+ $s .= $sep . $this->deleteThisPage() .
+ $sep . $this->protectThisPage();
+ }
+ $s .= $sep;
+
+ $s .= $this->menuHead( "qbpageoptions" );
+ $s .= $this->talkLink()
+ . $sep . $this->printableLink();
+ if ( 0 != $wgUser->getID() ) {
+ $s .= $sep . $this->watchThisPage();
+ }
+ $s .= $sep;
+
+ $s .= $this->menuHead( "qbpageinfo" )
+ . $this->historyLink()
+ . $sep . $this->whatLinksHere()
+ . $sep . $this->watchPageLinksLink();
+
+ if ( Namespace::getUser() == $wgTitle->getNamespace() ) {
+ $s .= $sep . $this->userContribsLink();
+ if ( 0 != $wgUser->getID() ) {
+ $s .= $sep . $this->emailUserLink();
+ }
+ }
+ $s .= $sep;
+ }
+ $s .= $this->menuHead( "qbmyoptions" );
+ if ( 0 != $wgUser->getID() ) {
+ $name = $wgUser->getName();
+ $tl = $this->makeKnownLink( $wgLang->getNsText(
+ Namespace::getTalk( Namespace::getUser() ) ) . ":{$name}",
+ wfMsg( "mytalk" ) );
+ if ( 0 != $wgUser->getNewtalk() ) { $tl .= " *"; }
+
+ $s .= $this->makeKnownLink( $wgLang->getNsText(
+ Namespace::getUser() ) . ":{$name}", wfMsg( "mypage" ) )
+ . $sep . $tl
+ . $sep . $this->specialLink( "watchlist" )
+ . $sep . $this->specialLink( "preferences" )
+ . $sep . $this->specialLink( "userlogout" );
+ } else {
+ $s .= $this->specialLink( "userlogin" );
+ }
+ $s .= $sep . "\n</div>\n";
+ return $s;
+ }
+
+ function menuHead( $key )
+ {
+ $s = "\n<h6>" . wfMsg( $key ) . "</h6>";
+ return $s;
+ }
+
+ function searchForm( $label = "" )
+ {
+ global $search;
+ $s = "<form id=\"search\" method=\"get\" class=\"inline\" action=\"" .
+ wfLocalUrlE( "" ) . "\">";
+ if ( "" != $label ) { $s .= "{$label}: "; }
+
+ $s .= "<input type=text name=\"search\" size=10 value=\""
+ . htmlspecialchars(substr($search,0,256)) . "\">"
+ . "<input type=submit value=\"" . wfMsg( "ok" ) . "\"></form>";
+
+ return $s;
+ }
+}
+
+?>
diff --git a/includes/SkinFramed.php b/includes/SkinFramed.php
new file mode 100644
index 000000000000..710ea64aca58
--- /dev/null
+++ b/includes/SkinFramed.php
@@ -0,0 +1,87 @@
+<?
+# See skin.doc
+
+class SkinFramed extends Skin {
+
+ function useBodyTag()
+ {
+ global $frame;
+ return ( "set" != $frame );
+ }
+
+ function qbSetting() { return 0; }
+ function beforeContent() { return ""; }
+ function afterContent() { return ""; }
+ function qbLogo() { return ""; }
+ function isFramed() { return true; }
+
+ function getBaseTag()
+ {
+ global $wgServer, $wgScript;
+
+ $s = "<base href=\"{$wgServer}{$wgScript}\" target=\"_top\">\n";
+ return $s;
+ }
+
+ function getTopFrame()
+ {
+ $t = $this->pageTitleLinks();
+ $t = explode ( "<br>" , $t );
+ while ( count ( $t ) < 4 ) { array_push ( $t, "" ); }
+ $t = implode ( "<br>", $t );
+
+ $s = "";
+ $s .= "<table width='100%' border=0><tr height=152>\n";
+ $s .= "<td class='top' valign=top>" . $this->logoText() . "</td>\n";
+ $s .= "<td class='top'>&nbsp;&nbsp;</td>\n";
+ $s .= "<td class='top' valign=top width='100%'>\n";
+ $s .= $this->topLinks();
+ $s .= $t;
+
+ $s .= $this->pageTitle();
+ $s .= $this->pageSubtitle();
+
+ $s .= "</td>\n<td class='top' valign=top align=right width=200 nowrap>";
+ $s .= $this->nameAndLogin();
+ $s .= "\n<br>" . $this->searchForm() . "</td>";
+ $s .= "</tr></table>";
+ return $s;
+ }
+
+ function transformContent( $text )
+ {
+ global $frame, $HTTP_SERVER_VARS;
+ global $wgOut, $wgServer, $wgScript;
+
+ $qs = $HTTP_SERVER_VARS["QUERY_STRING"];
+ $qs = wfEscapeHTML( $qs );
+
+ if ( "" == $qs ) { $qs = "?frame="; }
+ else { $qs = "?{$qs}&amp;frame="; }
+ $url = "{$wgServer}{$wgScript}{$qs}";
+
+ if ( "set" == $frame ) {
+ $s = "<frameset rows='152,*' border=0>\n" .
+ "<frame marginwidth=0 marginheight=0 frameborder=1 " .
+ "src=\"{$url}top\" noresize scrolling=no>\n" .
+ "<frameset cols='152,*' border=0>\n" .
+ "<frame marginwidth=0 marginheight=0 frameborder=1 " .
+ "src=\"{$url}side\">\n" .
+ "<frame marginwidth=0 marginheight=0 frameborder=1 " .
+ "src=\"{$url}body\">\n" .
+ "</frameset>\n</frameset>\n";
+ } else if ( "top" == $frame ) {
+ $s = $this->getTopFrame();
+ } else if ( "side" == $frame ) {
+ $s = $this->quickBar();
+ # $s = spliti( "<hr>", $s, 2 );
+ # $s = $s[1] ;
+ } else if ( "body" == $frame ) {
+ $s = $text;
+ # Bottom links?
+ }
+ return $s;
+ }
+}
+
+?>
diff --git a/includes/SkinNostalgia.php b/includes/SkinNostalgia.php
new file mode 100644
index 000000000000..d2e97cfc56e2
--- /dev/null
+++ b/includes/SkinNostalgia.php
@@ -0,0 +1,78 @@
+<?
+# See skin.doc
+
+class SkinNostalgia extends Skin {
+
+ function initPage()
+ {
+ global $wgOut, $wgStyleSheetPath;
+
+ $wgOut->addLink( "stylesheet", "",
+ "$wgStyleSheetPath/nostalgia.css" );
+ }
+
+ function doBeforeContent()
+ {
+ global $wgUser, $wgOut, $wgTitle;
+
+ $s = "\n<div id='content'>\n<div id='topbar'>";
+ $s .= $this->logoText( "right" );
+
+ $s .= $this->pageTitle();
+ $s .= $this->pageSubtitle() . "\n<p>";
+
+ $s .= $this->topLinks() . "\n<br>";
+ $s .= $this->pageTitleLinks();
+
+ if ( $wgOut->isArticle() ) {
+ $s .= "<br>" . $this->otherLanguages();
+ }
+ $s .= "<br clear=all><hr>\n</div>\n";
+ $s .= "\n<div id='article'>";
+
+ return $s;
+ }
+
+ function topLinks()
+ {
+ global $wgOut, $wgUser;
+ $sep = " |\n";
+
+ $s = $this->mainPageLink() . $sep
+ . $this->specialLink( "recentchanges" );
+
+ if ( $wgOut->isArticle() ) {
+ $s .= $sep . $this->editThisPage()
+ . $sep . $this->historyLink();
+ }
+ if ( 0 == $wgUser->getID() ) {
+ $s .= $sep . $this->specialLink( "userlogin" );
+ } else {
+ $s .= $sep . $this->specialLink( "userlogout" );
+ }
+ $s .= $sep . $this->specialPagesList();
+
+ return $s;
+ }
+
+ function doAfterContent()
+ {
+ global $wgUser, $wgOut;
+
+ $s = "\n</div><br clear=all>\n";
+
+ $s .= "\n<div id='footer'><hr>";
+
+ $s .= $this->bottomLinks();
+ $s .= "\n<br>" . $this->pageStats();
+ $s .= "\n<br>" . $this->mainPageLink()
+ . " | " . $this->aboutLink()
+ . " | " . $this->searchForm();
+
+ $s .= "\n</div>\n</div>\n";
+
+ return $s;
+ }
+}
+
+?>
diff --git a/includes/SkinStandard.php b/includes/SkinStandard.php
new file mode 100644
index 000000000000..df96a00166fe
--- /dev/null
+++ b/includes/SkinStandard.php
@@ -0,0 +1,60 @@
+<?
+# See skin.doc
+
+class SkinStandard extends Skin {
+
+ function getHeadScripts()
+ {
+ global $wgStyleSheetPath;
+
+ $s = parent::getHeadScripts();
+ if ( 3 == $this->qbSetting() ) { # Floating left
+ $s .= "<script language='javascript' type='text/javascript' " .
+ "src='{$wgStyleSheetPath}/sticky.js'></script>\n";
+ }
+ return $s;
+ }
+
+ function getUserStyles()
+ {
+ global $wgStyleSheetPath;
+
+ $s = parent::getUserStyles();
+ if ( 3 == $this->qbSetting() ) { # Floating left
+ $s .= "<style type='text/css' media='screen'>\n" .
+ "@import '{$wgStyleSheetPath}/quickbar.css';\n</style>\n";
+ }
+ return $s;
+ }
+
+ function doGetUserStyles()
+ {
+ global $wgUser, $wgOut, $wgStyleSheetPath;
+
+ $s = parent::doGetUserStyles();
+ $qb = $this->qbSetting();
+
+ if ( 2 == $qb ) { # Right
+ $s .= "#quickbar { position: absolute; top: 4px; right: 4px; " .
+ "border-left: 2px solid #000000; }\n" .
+ "#article { margin-left: 4px; margin-right: 152px; }\n";
+ } else if ( 1 == $qb || 3 == $qb ) {
+ $s .= "#quickbar { position: absolute; top: 4px; left: 4px; " .
+ "border-right: 2px solid #000000; }\n" .
+ "#article { margin-left: 152px; margin-right: 4px; }\n";
+ }
+ return $s;
+ }
+
+ function getBodyOptions()
+ {
+ $a = parent::getBodyOptions();
+
+ if ( 3 == $this->qbSetting() ) { # Floating left
+ $a["onload"] = "setup(\"quickbar\")";
+ }
+ return $a;
+ }
+}
+
+?>
diff --git a/includes/SpecialAllpages.php b/includes/SpecialAllpages.php
new file mode 100644
index 000000000000..a3b7aafb5d71
--- /dev/null
+++ b/includes/SpecialAllpages.php
@@ -0,0 +1,116 @@
+<?
+
+function wfSpecialAllpages()
+{
+ global $from, $indexMaxperpage;
+ $indexMaxperpage = 480;
+
+ if( isset( $from ) ) {
+ indexShowChunk( $from );
+ } else {
+ indexShowToplevel();
+ }
+}
+
+function indexShowToplevel()
+{
+ global $wgOut, $indexMaxperpage;
+ $fname = "indexShowToplevel";
+ # FIXME: This may be slow; we may need to cache it
+
+# $fromwhere = "FROM cur WHERE cur_namespace=0 AND cur_is_redirect=0";
+ $fromwhere = "FROM cur WHERE cur_namespace=0";
+ $order = "ORDER BY cur_title";
+ $out = "";
+
+ $sql = "SELECT COUNT(*) AS count $fromwhere";
+ $res = wfQuery( $sql, $fname );
+ $s = wfFetchObject( $res );
+ $count = $s->count;
+ $sections = ceil( $count / $indexMaxperpage );
+
+ $sql = "SELECT cur_title $fromwhere $order LIMIT 1";
+ $res = wfQuery( $sql, $fname );
+ $s = wfFetchObject( $res );
+ $inpoint = $s->cur_title;
+
+ $out .= "<table>\n";
+ # There's got to be a cleaner way to do this!
+ for( $i = 1; $i < $sections; $i++ ) {
+ $from = $i * $indexMaxperpage;
+ $sql = "SELECT cur_title $fromwhere $order LIMIT $from,2";
+ $res = wfQuery( $sql, $fname );
+
+ $s = wfFetchObject( $res );
+ $outpoint = $s->cur_title;
+ $out .= indexShowline( $inpoint, $outpoint );
+
+ $s = wfFetchObject( $res );
+ $inpoint = $s->cur_title;
+
+ wfFreeResult( $res );
+ }
+
+ $from = $i * $indexMaxperpage;
+ $sql = "SELECT cur_title $fromwhere $order LIMIT " . ($count-1) . ",1";
+ $res = wfQuery( $sql, $fname );
+ $s = wfFetchObject( $res );
+ $outpoint = $s->cur_title;
+ $out .= indexShowline( $inpoint, $outpoint );
+ $out .= "</table>\n";
+
+ $wgOut->addHtml( $out );
+}
+
+function indexShowline( $inpoint, $outpoint )
+{
+ global $wgOut, $wgLang, $wgUser;
+ $sk = $wgUser->getSkin();
+
+ # Fixme: this is ugly
+ $out = wfMsg(
+ "alphaindexline",
+ $sk->makeKnownLink( $wgLang->specialPage( "Allpages" ),
+ str_replace( "_", " ", $inpoint ),
+ "from=" . wfStrencode( $inpoint ) ) . "</td><td>",
+ "</td><td align=\"left\">" .
+ str_replace( "_", " ", $outpoint )
+ );
+ return "<tr><td align=\"right\">{$out}</td></tr>\n";
+}
+
+function indexShowChunk( $from )
+{
+ global $wgOut, $wgUser, $indexMaxperpage;
+ $sk = $wgUser->getSkin();
+
+ $out = "";
+ $sql = "SELECT cur_title
+FROM cur
+WHERE cur_namespace=0 AND cur_title >= '" . wfStrencode( $from ) . "'
+ORDER BY cur_title
+LIMIT {$indexMaxperpage}";
+ $res = wfQuery( $sql, "indexShowChunk" );
+
+ # FIXME: Dynamic column widths, backlink to main list,
+ # side links to next and previous
+ $n = 0;
+ $out = "<table border=\"0\">\n";
+ while( $s = wfFetchObject( $res ) ) {
+ $out .= "<td width=\"33%\">" .
+ $sk->makeKnownLink( $s->cur_title ) .
+ "</td>";
+ $n = ++$n % 3;
+ if( $n == 0 ) {
+ $out .= "</tr>\n<tr>";
+ }
+ }
+ if( $n != 0 ) {
+ $out .= "</tr>\n";
+ }
+ $out .= "</table>";
+ #return $out;
+ $wgOut->addHtml( $out );
+}
+
+?>
diff --git a/includes/SpecialAsksql.php b/includes/SpecialAsksql.php
new file mode 100644
index 000000000000..edddd062979a
--- /dev/null
+++ b/includes/SpecialAsksql.php
@@ -0,0 +1,113 @@
+<?
+
+function wfSpecialAsksql()
+{
+ global $wgUser, $wgOut, $action;
+
+ if ( ! $wgUser->isSysop() ) {
+ $wgOut->sysopRequired();
+ return;
+ }
+ $fields = array( "wpSqlQuery" );
+ wfCleanFormFields( $fields );
+ $f = new SqlQueryForm();
+
+ if ( "submit" == $action ) { $f->doSubmit(); }
+ else { $f->showForm( "" ); }
+}
+
+class SqlQueryForm {
+
+ function showForm( $err )
+ {
+ global $wgOut, $wgUser, $wgLang;
+ global $wpSqlQuery;
+
+ $wgOut->setPagetitle( wfMsg( "asksql" ) );
+ $wgOut->addWikiText( wfMsg( "asksqltext" ) );
+
+ if ( "" != $err ) {
+ $wgOut->addHTML( "<p><font color='red' size='+1'>" . htmlspecialchars($err) . "</font>\n" );
+ }
+ if ( ! $wpSqlQuery ) { $wpSqlQuery = "SELECT ... FROM ... WHERE ..."; }
+ $q = wfMsg( "sqlquery" );
+ $qb = wfMsg( "querybtn" );
+ $action = wfLocalUrlE( $wgLang->specialPage( "Asksql" ),
+ "action=submit" );
+
+ $wgOut->addHTML( "<p>
+<form id=\"asksql\" method=\"post\" action=\"{$action}\">
+<table border=0><tr>
+<td align=right>{$q}:</td>
+<td align=left>
+<textarea name=\"wpSqlQuery\" cols=80 rows=4 wrap=\"virtual\">"
+. htmlspecialchars($wpSqlQuery) ."
+</textarea>
+</td>
+</tr><tr>
+<td>&nbsp;</td><td align=\"left\">
+<input type=submit name=\"wpQueryBtn\" value=\"{$qb}\">
+</td></tr></table>
+</form>\n" );
+
+ }
+
+ function doSubmit()
+ {
+ global $wgOut, $wgUser, $wgServer, $wgScript, $wgArticlePath;
+ global $wpSqlQuery;
+ global $wgDBsqluser, $wgDBsqlpassword;
+
+ # Use a limit, folks!
+ $wpSqlQuery = trim( $wpSqlQuery );
+ if( preg_match( "/^SELECT/i", $wpSqlQuery )
+ and !preg_match( "/LIMIT/i", $wpSqlQuery ) ) {
+ $wpSqlQuery .= " LIMIT 100";
+ }
+ if ( ! $wgUser->isDeveloper() ) {
+ $connection = wfGetDB( $wgDBsqluser, $wgDBsqlpassword );
+ }
+ $res = wfQuery( $wpSqlQuery, "SpecialAsksql::doSubmit" );
+
+ $n = 0;
+ @$n = wfNumFields( $res );
+ if ( $n ) {
+ $k = array();
+ for ( $x = 0; $x < $n; ++$x ) {
+ array_push( $k, wfFieldName( $res, $x ) );
+ }
+ $a = array();
+ while ( $s = wfFetchObject( $res ) ) {
+ array_push( $a, $s );
+ }
+ wfFreeResult( $res );
+
+ $r = "<table border=1 bordercolor=black cellspacing=0 " .
+ "cellpadding=2><tr>\n";
+ foreach ( $k as $x ) $r .= "<th>" . htmlspecialchars( $x ) . "</th>";
+ $r .= "</tr>\n";
+
+ foreach ( $a as $y ) {
+ $r .= "<tr>";
+ foreach ( $k as $x ) {
+ $o = $y->$x ;
+ if ( $x == "cur_title" or $x == "old_title" ) {
+ $o = str_replace ( "$1" , rawurlencode( $o ) , $wgArticlePath ) ;
+ $o = "<a href=\"{$o}\" class='internal'>" .
+ htmlspecialchars( $y->$x ) . "</a>" ;
+ } else {
+ $o = htmlspecialchars( $o );
+ }
+ $r .= "<td>" . $o . "</td>\n";
+ }
+ $r .= "</tr>\n";
+ }
+ $r .= "</table>\n";
+ }
+ $this->showForm( wfMsg( "querysuccessful" ) );
+ $wgOut->addHTML( "<hr>{$r}\n" );
+ }
+
+}
+
+?>
diff --git a/includes/SpecialBlockip.php b/includes/SpecialBlockip.php
new file mode 100644
index 000000000000..bc24f4d9d137
--- /dev/null
+++ b/includes/SpecialBlockip.php
@@ -0,0 +1,97 @@
+<?
+
+function wfSpecialBlockip()
+{
+ global $wgUser, $wgOut, $action;
+
+ if ( ! $wgUser->isSysop() ) {
+ $wgOut->sysopRequired();
+ return;
+ }
+ $fields = array( "wpBlockAddress", "wpBlockReason" );
+ wfCleanFormFields( $fields );
+ $ipb = new IPBlockForm();
+
+ if ( "success" == $action ) { $ipb->showSuccess(); }
+ else if ( "submit" == $action ) { $ipb->doSubmit(); }
+ else { $ipb->showForm( "" ); }
+}
+
+class IPBlockForm {
+
+ function showForm( $err )
+ {
+ global $wgOut, $wgUser, $wgLang;
+ global $ip, $wpBlockAddress, $wpBlockReason;
+
+ $wgOut->setPagetitle( wfMsg( "blockip" ) );
+ $wgOut->addWikiText( wfMsg( "blockiptext" ) );
+
+ if ( ! $wpBlockAddress ) { $wpBlockAddress = $ip; }
+ $ipa = wfMsg( "ipaddress" );
+ $reason = wfMsg( "ipbreason" );
+ $ipbs = wfMsg( "ipbsubmit" );
+ $action = wfLocalUrlE( $wgLang->specialPage( "Blockip" ),
+ "action=submit" );
+
+ if ( "" != $err ) {
+ $wgOut->setSubtitle( wfMsg( "formerror" ) );
+ $wgOut->addHTML( "<p><font color='red' size='+1'>{$err}</font>\n" );
+ }
+ $wgOut->addHTML( "<p>
+<form id=\"blockip\" method=\"post\" action=\"{$action}\">
+<table border=0><tr>
+<td align=\"right\">{$ipa}:</td>
+<td align=\"left\">
+<input tabindex=1 type=text size=20 name=\"wpBlockAddress\" value=\"{$wpBlockAddress}\">
+</td></tr><tr>
+<td align=\"right\">{$reason}:</td>
+<td align=\"left\">
+<input tabindex=2 type=text size=40 name=\"wpBlockReason\" value=\"{$wpBlockReason}\">
+</td></tr><tr>
+<td>&nbsp;</td><td align=\"left\">
+<input tabindex=3 type=submit name=\"wpBlock\" value=\"{$ipbs}\">
+</td></tr></table>
+</form>\n" );
+
+ }
+
+ function doSubmit()
+ {
+ global $wgOut, $wgUser, $wgLang;
+ global $ip, $wpBlockAddress, $wpBlockReason;
+ $fname = "IPBlockForm::doSubmit";
+
+ if ( ! preg_match( "/\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/",
+ $wpBlockAddress ) ) {
+ $this->showForm( wfMsg( "badipaddress" ) );
+ return;
+ }
+ if ( "" == $wpBlockReason ) {
+ $this->showForm( wfMsg( "noblockreason" ) );
+ return;
+ }
+ $sql = "INSERT INTO ipblocks (ipb_address, ipb_user, ipb_by, " .
+ "ipb_reason, ipb_timestamp ) VALUES ('{$wpBlockAddress}', 0, " .
+ $wgUser->getID() . ", '" . wfStrencode( $wpBlockReason ) . "','" .
+ date( "YmdHis" ) . "')";
+ wfQuery( $sql, $fname );
+
+ $success = wfLocalUrl( $wgLang->specialPage( "Blockip" ),
+ "action=success&ip={$wpBlockAddress}" );
+ $wgOut->redirect( $success );
+ }
+
+ function showSuccess()
+ {
+ global $wgOut, $wgUser;
+ global $ip;
+
+ $wgOut->setPagetitle( wfMsg( "blockip" ) );
+ $wgOut->setSubtitle( wfMsg( "blockipsuccesssub" ) );
+ $text = str_replace( "$1", $ip, wfMsg( "blockipsuccesstext" ) );
+ $wgOut->addWikiText( $text );
+ }
+}
+
+?>
diff --git a/includes/SpecialBooksources.php b/includes/SpecialBooksources.php
new file mode 100644
index 000000000000..a3722cd8dbc0
--- /dev/null
+++ b/includes/SpecialBooksources.php
@@ -0,0 +1,57 @@
+<?
+
+# ISBNs in wiki pages will create links to this page, with
+# the ISBN passed in via the query string.
+
+function wfSpecialBooksources()
+{
+ $isbn = $_REQUEST["isbn"];
+
+ $bsl = new BookSourceList( $isbn );
+ $bsl->show();
+}
+
+class BookSourceList {
+
+ var $mIsbn;
+
+ function BookSourceList( $isbn )
+ {
+ $this->mIsbn = $isbn;
+ }
+
+ function show()
+ {
+ global $wgOut, $wgUser, $wgLang;
+ global $ip, $wpBlockAddress, $wpBlockReason;
+
+ $wgOut->setPagetitle( wfMsg( "booksources" ) );
+ $wgOut->addWikiText( wfMsg( "booksourcetext" ) );
+
+ # If ISBN is blank, just show a list of links to the
+ # home page of the various book sites. Otherwise, show
+ # a list of links directly to the book.
+
+ $s = "<ul>\n";
+ $bs = $wgLang->getBookstoreList() ;
+ $bsn = array_keys ( $bs ) ;
+ foreach ( $bsn as $name ) {
+ $adr = $bs[$name] ;
+ if ( ! $this->mIsbn ) {
+ $adr = explode ( ":" , $adr , 2 ) ;
+ $adr = explode ( "/" , $adr[1] ) ;
+ $a = "" ;
+ while ( $a == "" ) $a = array_shift ( $adr ) ;
+ $adr = "http://".$a ;
+ } else {
+ $adr = str_replace ( "$1" , $this->mIsbn , $adr ) ;
+ }
+ $s .= "<li><a href=\"{$adr}\">{$name}</a></li>\n" ;
+ }
+ $s .= "</ul>\n";
+
+ $wgOut->addHTML( $s );
+ }
+}
+
+?>
diff --git a/includes/SpecialContributions.php b/includes/SpecialContributions.php
new file mode 100644
index 000000000000..d9e86a79e501
--- /dev/null
+++ b/includes/SpecialContributions.php
@@ -0,0 +1,151 @@
+<?
+
+function wfSpecialContributions()
+{
+ global $wgUser, $wgOut, $wgLang, $target, $offset, $limit, $hideminor;
+ $fname = "wfSpecialContributions";
+ $sysop = $wgUser->isSysop();
+
+ if ( "" == $target ) {
+ $wgOut->errorpage( "notargettitle", "notargettext" );
+ return;
+ }
+ $offset = (int)$offset;
+ $limit = (int)$limit;
+ if( $offset < 0 ) { $offset = 0; }
+ if( $limit < 1 ) { $limit = 50; }
+
+ $target = wfCleanQueryVar( $target );
+ $nt = Title::newFromURL( $target );
+ $nt->setNamespace( Namespace::getUser() );
+
+ $sk = $wgUser->getSkin();
+ $id = User::idFromName( $nt->getText() );
+
+ if ( 0 == $id ) { $ul = $nt->getText(); }
+ else {
+ $ul = $sk->makeKnownLink( $nt->getPrefixedText(), $nt->getText() );
+ }
+ $sub = str_replace( "$1", $ul, wfMsg( "contribsub" ) );
+ $wgOut->setSubtitle( $sub );
+
+ if ( ! isset( $hideminor ) ) {
+ $hideminor = $wgUser->getOption( "hideminor" );
+ }
+ if ( $hideminor ) {
+ $cmq = "AND cur_minor_edit=0";
+ $omq = "AND old_minor_edit=0";
+ } else { $cmq = $omq = ""; }
+
+ $top = wfShowingResults( $offset, $limit );
+ $wgOut->addHTML( "<p>{$top}\n" );
+
+ $sl = wfViewPrevNext( $offset, $limit,
+ $wgLang->specialpage( "Contributions" ), "target=" . wfUrlEncode( $target ) );
+ $wgOut->addHTML( "<br>{$sl}\n" );
+
+ # Sorting slowness on cur and especially old
+ # forces us to check RC table first
+ if ( 0 == $id ) {
+ $sql = "SELECT cur_namespace,cur_title,cur_timestamp,cur_comment FROM cur " .
+ "WHERE cur_user_text='" . wfStrencode( $nt->getText() ) . "' {$cmq} " .
+ "ORDER BY inverse_timestamp LIMIT {$offset}, {$limit}";
+ $res1 = wfQuery( $sql, $fname );
+
+ $sql = "SELECT old_namespace,old_title,old_timestamp,old_comment FROM old " .
+ "WHERE old_user_text='" . wfStrencode( $nt->getText() ) . "' {$omq} " .
+ "ORDER BY inverse_timestamp LIMIT {$offset}, {$limit}";
+ $res2 = wfQuery( $sql, $fname );
+ } else {
+ $sql = "SELECT cur_namespace,cur_title,cur_timestamp,cur_comment FROM cur " .
+ "WHERE cur_user={$id} {$cmq} ORDER BY inverse_timestamp LIMIT {$offset}, {$limit}";
+ $res1 = wfQuery( $sql, $fname );
+
+ $sql = "SELECT old_namespace,old_title,old_timestamp,old_comment FROM old " .
+ "WHERE old_user={$id} {$omq} ORDER BY inverse_timestamp LIMIT {$offset}, {$limit}";
+ $res2 = wfQuery( $sql, $fname );
+ }
+ $nCur = wfNumRows( $res1 );
+ $nOld = wfNumRows( $res2 );
+
+
+ if ( 0 == $nCur && 0 == $nOld && 0 == $rcrows ) {
+ $wgOut->addHTML( "\n<p>" . wfMsg( "nocontribs" ) . "</p>\n" );
+ return;
+ }
+ if ( 0 != $nCur ) { $obj1 = wfFetchObject( $res1 ); }
+ if ( 0 != $nOld ) { $obj2 = wfFetchObject( $res2 ); }
+
+ $wgOut->addHTML( "<ul>\n" );
+ while ( $limit ) {
+ if ( 0 == $nCur && 0 == $nOld ) { break; }
+
+ if ( ( 0 == $nOld ) ||
+ ( ( 0 != $nCur ) &&
+ ( $obj1->cur_timestamp >= $obj2->old_timestamp ) ) ) {
+ $ns = $obj1->cur_namespace;
+ $t = $obj1->cur_title;
+ $ts = $obj1->cur_timestamp;
+ $comment =$obj1->cur_comment;
+
+ $obj1 = wfFetchObject( $res1 );
+ $topmark = true;
+ --$nCur;
+ } else {
+ $ns = $obj2->old_namespace;
+ $t = $obj2->old_title;
+ $ts = $obj2->old_timestamp;
+ $comment =$obj2->old_comment;
+
+ $obj2 = wfFetchObject( $res2 );
+ $topmark = false;
+ --$nOld;
+ }
+ ucListEdit( $sk, $ns, $t, $ts, $topmark, $comment );
+
+ --$limit;
+ }
+ $wgOut->addHTML( "</ul>\n" );
+}
+
+function ucListEdit( $sk, $ns, $t, $ts, $topmark, $comment )
+{
+ global $wgLang, $wgOut, $wgUser;
+ $page = Title::makeName( $ns, $t );
+ $link = $sk->makeKnownLink( $page, "" );
+ $topmarktext = $topmark ? wfMsg ( "uctop" ) : "";
+ $sysop = $wgUser->isSysop();
+ if($sysop && $topmark ) {
+ $topmarktext .= " [". $sk->makeKnownLink( $page,
+ wfMsg( "rollbacklink" ), "action=rollback" ) ."]";
+ }
+ if($comment) {
+
+ $comment="<em>(". htmlspecialchars( $comment ) .")</em> ";
+
+ }
+ $d = $wgLang->timeanddate( $ts, true );
+
+ $wgOut->addHTML( "<li>{$d} {$link} {$comment}{$topmarktext}</li>\n" );
+}
+
+function ucCountLink( $lim, $d )
+{
+ global $wgUser, $wgLang, $target;
+
+ $sk = $wgUser->getSkin();
+ $s = $sk->makeKnownLink( $wgLang->specialPage( "Contributions" ),
+ "{$lim}", "target={$target}&days={$d}&limit={$lim}" );
+ return $s;
+}
+
+function ucDaysLink( $lim, $d )
+{
+ global $wgUser, $wgLang, $target;
+
+ $sk = $wgUser->getSkin();
+ $s = $sk->makeKnownLink( $wgLang->specialPage( "Contributions" ),
+ "{$d}", "target={$target}&days={$d}&limit={$lim}" );
+ return $s;
+}
+?>
diff --git a/includes/SpecialDebug.php b/includes/SpecialDebug.php
new file mode 100644
index 000000000000..f11c075d18e8
--- /dev/null
+++ b/includes/SpecialDebug.php
@@ -0,0 +1,13 @@
+<?
+
+function wfSpecialDebug()
+{
+ global $wgUser, $wgOut;
+
+ if ( ! $wgUser->isDeveloper() ) {
+ $wgOut->developerRequired();
+ return;
+ }
+ phpinfo();
+}
+
diff --git a/includes/SpecialEmailuser.php b/includes/SpecialEmailuser.php
new file mode 100644
index 000000000000..8ae02b2800cb
--- /dev/null
+++ b/includes/SpecialEmailuser.php
@@ -0,0 +1,135 @@
+<?
+
+function wfSpecialEmailuser()
+{
+ global $wgUser, $wgOut, $action, $target;
+
+ if ( 0 == $wgUser->getID() ||
+ ( false === strpos( $wgUser->getEmail(), "@" ) ) ) {
+ $wgOut->errorpage( "mailnologin", "mailnologintext" );
+ return;
+ }
+ $target = wfCleanQueryVar( $target );
+ if ( "" == $target ) {
+ $wgOut->errorpage( "notargettitle", "notargettext" );
+ return;
+ }
+ $nt = Title::newFromURL( $target );
+ $nu = User::newFromName( $nt->getText() );
+ $id = $nu->idForName();
+
+ if ( 0 == $id ) {
+ $wgOut->errorpage( "noemailtitle", "noemailtext" );
+ return;
+ }
+ $nu->setID( $id );
+ $address = $nu->getEmail();
+
+ if ( ( false === strpos( $address, "@" ) ) ||
+ ( 1 == $nu->getOption( "disablemail" ) ) ) {
+ $wgOut->errorpage( "noemailtitle", "noemailtext" );
+ return;
+ }
+ $fields = array( "wpSubject", "wpText" );
+ wfCleanFormFields( $fields );
+
+ $f = new EmailUserForm( $nu->getName() . " <{$address}>" );
+
+ if ( "success" == $action ) { $f->showSuccess(); }
+ else if ( "submit" == $action ) { $f->doSubmit(); }
+ else { $f->showForm( "" ); }
+}
+
+class EmailUserForm {
+
+ var $mAddress;
+
+ function EmailUserForm( $addr )
+ {
+ $this->mAddress = $addr;
+ }
+
+ function showForm( $err )
+ {
+ global $wgOut, $wgUser, $wgLang;
+ global $wpSubject, $wpText, $target;
+
+ $wgOut->setPagetitle( wfMsg( "emailpage" ) );
+ $wgOut->addWikiText( wfMsg( "emailpagetext" ) );
+
+ if ( ! $wpSubject ) { $wpSubject = "Wikipedia e-mail"; }
+
+ $emf = wfMsg( "emailfrom" );
+ $sender = $wgUser->getName();
+ $emt = wfMsg( "emailto" );
+ $rcpt = str_replace( "_", " ", urldecode( $target ) );
+ $emr = wfMsg( "emailsubject" );
+ $emm = wfMsg( "emailmessage" );
+ $ems = wfMsg( "emailsend" );
+
+ $action = wfLocalUrlE( $wgLang->specialPage( "Emailuser" ),
+ "target={$target}&action=submit" );
+
+ if ( "" != $err ) {
+ $wgOut->setSubtitle( wfMsg( "formerror" ) );
+ $wgOut->addHTML( "<p><font color='red' size='+1'>{$err}</font>\n" );
+ }
+ $wgOut->addHTML( "<p>
+<form id=\"emailuser\" method=\"post\" action=\"{$action}\">
+<table border=0><tr>
+<td align=right>{$emf}:</td>
+<td align=left><strong>{$sender}</strong></td>
+</tr><tr>
+<td align=right>{$emt}:</td>
+<td align=left><strong>{$rcpt}</strong></td>
+</tr><tr>
+<td align=right>{$emr}:</td>
+<td align=left>
+<input type=text name=\"wpSubject\" value=\"{$wpSubject}\">
+</td>
+</tr><tr>
+<td align=right>{$emm}:</td>
+<td align=left>
+<textarea name=\"wpText\" rows=10 cols=60 wrap=virtual>
+{$wpText}
+</textarea>
+</td></tr><tr>
+<td>&nbsp;</td><td align=left>
+<input type=submit name=\"wpSend\" value=\"{$ems}\">
+</td></tr></table>
+</form>\n" );
+
+ }
+
+ function doSubmit()
+ {
+ global $wgOut, $wgUser, $wgLang, $wgOutputEncoding;
+ global $wpSubject, $wpText, $target;
+
+ $from = $wgUser->getName() . " <" . $wgUser->getEmail() . ">";
+ $headers =
+ "MIME-Version: 1.0\r\n" .
+ "Content-type: text/plain; charset={$wgOutputEncoding}\r\n" .
+ "Content-transfer-encoding: 8bit\r\n" .
+ "From: {$from}\r\n" .
+ "Reply-To: {$from}\r\n" .
+ "To: {$this->mAddress}\r\n" .
+ "X-Mailer: Pediawiki interuser e-mailer";
+ mail( $this->mAddress, $wpSubject, $wpText, $headers );
+
+ $success = wfLocalUrl( $wgLang->specialPage( "Emailuser" ),
+ "target={$target}&action=success" );
+ $wgOut->redirect( $success );
+ }
+
+ function showSuccess()
+ {
+ global $wgOut, $wgUser;
+
+ $wgOut->setPagetitle( wfMsg( "emailsent" ) );
+ $wgOut->addHTML( wfMsg( "emailsenttext" ) );
+
+ $wgOut->returnToMain( false );
+ }
+}
+?>
diff --git a/includes/SpecialImagelist.php b/includes/SpecialImagelist.php
new file mode 100644
index 000000000000..459b362d61f7
--- /dev/null
+++ b/includes/SpecialImagelist.php
@@ -0,0 +1,115 @@
+<?
+
+function wfSpecialImagelist()
+{
+ global $wgUser, $wgOut, $wgLang, $sort, $limit;
+ global $wpIlMatch, $wpIlSubmit;
+
+ $fields = array( 'wpIlMatch' );
+ wfCleanFormFields( $fields );
+
+ $sql = "SELECT img_size,img_name,img_user,img_user_text," .
+ "img_description,img_timestamp FROM image";
+
+ $byname = wfMsg( "byname" );
+ $bydate = wfMsg( "bydate" );
+ $bysize = wfMsg( "bysize" );
+
+ if ( "bysize" == $sort ) {
+ $sql .= " ORDER BY img_size DESC";
+ $st = $bysize;
+ } else if ( "byname" == $sort ) {
+ if ( $wpIlMatch ) {
+ $nt = Title::newFromUrl( $wpIlMatch );
+ $m = wfStrencode( strtolower( $nt->getDBkey() ) );
+ $m = str_replace( "%", "\\%", $m );
+ $m = str_replace( "_", "\\_", $m );
+ $sql .= " WHERE LCASE(img_name) LIKE '%{$m}%'";
+ }
+ $sql .= " ORDER BY img_name";
+ $st = $byname;
+ } else {
+ $sql .= " ORDER BY img_timestamp DESC";
+ $st = $bydate;
+ }
+ if ( ! isset( $limit ) ) { $limit = 50; }
+ if ( 0 == $limit ) {
+ $lt = wfMsg( "all" );
+ } else {
+ $lt = "${limit}";
+ $sql .= " LIMIT {$limit}";
+ }
+ $wgOut->addHTML( "<p>" . wfMsg( "imglegend" ) . "\n" );
+
+ $text = str_replace( "$1", "<strong>{$lt}</strong>",
+ wfMsg( "imagelisttext" ) );
+ $text = str_replace( "$2", "<strong>{$st}</strong>", $text );
+ $wgOut->addHTML( "<p>{$text}\n<p>" );
+
+ $sk = $wgUser->getSkin();
+ $cap = wfMsg( "ilshowmatch" );
+ $sub = wfMsg( "ilsubmit" );
+ $action = wfLocalUrlE( $wgLang->specialPage( "Imagelist" ),
+ "sort=byname&limit={$limit}" );
+
+ $wgOut->addHTML( "<form id=\"imagesearch\" method=\"post\" action=\"" .
+ "{$action}\">" .
+ "{$cap}: <input type=text size=8 name=\"wpIlMatch\" value=\"\"> " .
+ "<input type=submit name=\"wpIlSubmit\" value=\"{$sub}\"></form>" );
+
+ $nums = array( 50, 100, 250, 500 );
+ $here = $wgLang->specialPage( "Imagelist" );
+
+ $fill = "";
+ $first = true;
+ foreach ( $nums as $num ) {
+ if ( ! $first ) { $fill .= " | "; }
+ $first = false;
+
+ $fill .= $sk->makeKnownLink( $here, "{$num}",
+ "sort=bysize&limit={$num}" );
+ }
+ $text = str_replace( "$1", $fill, wfMsg( "showlast" ) );
+ $text = str_replace( "$2", $bysize, $text );
+ $wgOut->addHTML( "{$text}<br>\n" );
+
+ $fill = "";
+ $first = true;
+ foreach ( $nums as $num ) {
+ if ( ! $first ) { $fill .= " | "; }
+ $first = false;
+
+ $fill .= $sk->makeKnownLink( $here, $num,
+ "sort=bydate&limit={$num}" );
+ }
+ $text = str_replace( "$1", $fill, wfMsg( "showlast" ) );
+ $text = str_replace( "$2", $bydate, $text );
+ $wgOut->addHTML( "{$text}<br>\n<p>" );
+
+ $res = wfQuery( $sql, "wfSpecialImagelist" );
+ while ( $s = wfFetchObject( $res ) ) {
+ $name = $s->img_name;
+ $ut = $s->img_user_text;
+ if ( 0 == $s->img_user ) { $ul = $ut; }
+ else { $ul = $sk->makeLink( $wgLang->getNsText(
+ Namespace::getUser() ) . ":{$ut}", $ut ); }
+
+ $ilink = "<a href=\"" . wfImageUrl( $name ) .
+ "\">{$name}</a>";
+
+ $nb = str_replace( "$1", $s->img_size, wfMsg( "nbytes" ) );
+ $l = "(" .
+ $sk->makeKnownLink( $wgLang->getNsText(
+ Namespace::getImage() ) . ":{$name}", wfMsg( "imgdesc" ) ) .
+ ") {$ilink} . . {$nb} . . {$ul} . . " .
+ $wgLang->timeanddate( $s->img_timestamp, true );
+
+ if ( "" != $s->img_description ) {
+ $l .= " <em>({$s->img_description})</em>";
+ }
+ $wgOut->addHTML( "{$l}<br>\n" );
+ }
+ wfFreeResult( $res );
+}
+
+?>
diff --git a/includes/SpecialIntl.php b/includes/SpecialIntl.php
new file mode 100644
index 000000000000..8e366057e322
--- /dev/null
+++ b/includes/SpecialIntl.php
@@ -0,0 +1,555 @@
+<?
+
+function wfSpecialIntl()
+{
+ global $wgUser, $wgOut, $wgLang, $wgTitle;
+ global $limit, $offset; # From query string
+ global $wgDBconnection ;
+ $fname = "wfSpecialIntl";
+ $s = "" ;
+
+ if ( ! $limit ) {
+ $limit = $wgUser->getOption( "rclimit" );
+ if ( ! $limit ) { $limit = 50; }
+ }
+ if ( ! $offset ) { $offset = 0; }
+
+ # Connecting to the wiki-intl database
+ $c = $wgDBconnection ;
+ if ( !mysql_select_db ( $wgDBIntlName, $c ) ) {
+ $wgOut->addHTML( htmlspecialchars(mysql_error()) );
+ return ;
+ }
+
+ global $mode ;
+ $mode = strtolower ( trim ( $mode ) ) ;
+ if ( $mode == "" ) $mode = "main" ;
+
+ if ( $mode == "main" ) $s .= intl_main ( $c ) ;
+ else if ( $mode == "addlink" ) $s .= intl_add ( $c ) ;
+ else if ( $mode == "zoom" ) $s .= intl_zoom ( $c ) ;
+ else if ( $mode == "incominglinks" ) $s .= intl_incoming ( $c ) ;
+ else if ( $mode == "outgoinglinks" ) $s .= intl_outgoing ( $c ) ;
+ else if ( $mode == "alllinks" ) $s .= intl_all ( $c ) ;
+ else if ( $mode == "delete" ) $s .= intl_delete ( $c ) ;
+ else if ( $mode == "recentchanges" ) $s .= intl_recentchanges ( $c ) ;
+
+ $si = "Special:Intl" ;
+ $sk = $wgUser->getSkin();
+ if ( $mode != "" && $mode != "main" )
+ $s .= $sk->makeKnownLink($si,"International issues main menu") ;
+
+ $wgOut->addHTML( $s );
+}
+
+function appendRecentChanges ( $message ) {
+ global $wgDBconnection , $wgUser , $wgLanguageCode , $wgLang ;
+ $user_name = $wgLang->getNSText(Namespace::getUser()).":".$wgUser->getName() ;
+ $user_lang = $wgLanguageCode ;
+ $message = str_replace ( '"' , '\"' , $message ) ;
+ $sql = "INSERT INTO recentchanges (user_name,user_lang,message) VALUES (
+ \"{$user_name}\",
+ \"{$user_lang}\",
+ \"{$message}\")" ;
+ $res = mysql_query ( $sql , $wgDBconnection ) ;
+ }
+
+function intl_recentchanges ( $c ) {
+ global $wgLang ;
+ $r = "<h2>Recent Link Changes</h2>\n" ;
+
+ $rc = array () ;
+ $sql = "SELECT * FROM recentchanges ORDER BY date DESC LIMIT 250" ;
+ $res = mysql_query ( $sql , $c ) ;
+ while ( $q = mysql_fetch_object ( $res ) ) $rc[] = $q ;
+ mysql_free_result ( $res ) ;
+
+ $r .= "<UL>\n" ;
+ foreach ( $rc AS $x ) {
+ $r .= "<li>" ;
+ $r .= getArticleLink ( $x->user_name , $x->user_lang ) ;
+ $h = $wgLang->time( $x->date, true );
+ $r .= " ({$h}) " ;
+ $r .= $x->message ;
+ $r .= "</li>\n" ;
+ }
+ $r .= "</UL>\n" ;
+
+ return $r ;
+ }
+
+function getArticleLink ( $title , $lang = "" ) {
+ global $wgLanguageCode ;
+ $cl = "external" ;
+ if ( $lang == "" ) $lang = $wgLanguageCode ;
+ if ( $lang == $wgLanguageCode ) $cl = "internal" ;
+ $nt = Title::newFromText ( $title ) ;
+ $link = "http://".$lang.".wikipedia.org/wiki/".$title ;
+ $link = "<a class='{$cl}' href=\"{$link}\">".$nt->getText()."</a>" ;
+ return $link ;
+ }
+
+function intl_main ( $c ) {
+ global $wgUser ;
+ $sk = $wgUser->getSkin();
+ $si = "Special:Intl" ;
+
+ $r = "<h2>International issues main menu</h2>" ;
+ $r .= "<UL>" ;
+ $r .= "<li>".$sk->makeKnownLink($si,"Add a link","mode=addlink")."</li>" ;
+ $r .= "<li>".$sk->makeKnownLink($si,"View incoming links","mode=incominglinks")."</li>" ;
+ $r .= "<li>".$sk->makeKnownLink($si,"View outgoing links","mode=outgoinglinks")."</li>" ;
+ $r .= "<li>".$sk->makeKnownLink($si,"View all links","mode=alllinks")."</li>" ;
+ $r .= "<li>".$sk->makeKnownLink($si,"Recent Link Changes","mode=recentchanges")."</li>" ;
+ $r .= "</UL>" ;
+ return $r ;
+ }
+
+function intl_add_doit ( $c ) {
+ global $wgUser , $wgLang , $wgLanguageCode , $doit ;
+ global $l_f , $l_t , $t_f , $t_t , $backlink ;
+ $sk = $wgUser->getSkin();
+ $si = "Special:Intl" ;
+
+ # checking for language link
+ $q = explode ( ":" , $t_t , 2 ) ;
+ $ln = $wgLang->getLanguageNames();
+ if ( count ( $q ) == 2 ) {
+ $nl_t = trim ( array_shift ( $q ) ) ;
+ $nt_t = trim ( array_shift ( $q ) ) ;
+ if ( $nl_t != "" AND isset ( $ln[$nl_t] ) ) {
+ $l_t = $nl_t ;
+ $t_t = $nt_t ;
+ }
+ }
+
+ $nt = Title::newFromText ( $t_f ) ;
+ $t_f = $nt->getDBkey() ;
+ $nt = Title::newFromText ( $t_t ) ;
+ $t_t = $nt->getDBkey() ;
+
+ $r = "<h2>Creating/updating language links</h2>" ;
+
+ # Deleting forward link
+ $sql = "DELETE FROM ilinks WHERE
+lang_from='{$l_f}' AND
+lang_to='{$l_t}' AND
+title_from='{$t_f}'
+" ;
+ $res = mysql_query ( $sql , $c ) ;
+
+ $r .= "Executed {$sql}" ;
+ $r .= "<br>Result {$res}" ;
+ $r .= "<br>Error ". htmlspecialchars(mysql_error()) ;
+ $r .= "<br><br>" ;
+
+ # Adding link
+ $sql = "INSERT INTO ilinks (lang_from,lang_to,title_from,title_to) VALUES
+('{$l_f}','{$l_t}','{$t_f}','{$t_t}')" ;
+ $res = mysql_query ( $sql , $c ) ;
+
+ $r .= "Executed {$sql}" ;
+ $r .= "<br>Result {$res}" ;
+ $r .= "<br>Error ". htmlspecialchars(mysql_error()) ;
+
+ appendRecentChanges ( $ln[$l_f].":".getArticleLink($t_f,$l_f)." &rarr; ".
+ $ln[$l_t].":".getArticleLink($t_t,$l_t) ) ;
+
+ if ( $backlink == "on" ) {
+ $backlink = "" ;
+ $x = $l_f ; $l_f = $l_t ; $l_t = $x ;
+ $x = $t_f ; $t_f = $t_t ; $t_t = $x ;
+ intl_add_doit ( $c ) ; # Ugly recursion
+ }
+
+ return $r ;
+ }
+
+function intl_add ( $c ) {
+ global $wgUser , $wgLang , $wgLanguageCode , $doit , $mode ;
+ global $xl , $xt , $yl , $yt ;
+ $r = "" ;
+ if ( isset ( $doit ) ) {
+ global $al_t , $at_t , $l_t , $t_t ;
+ for ( $x = 0 ; $x < 10 ; $x++ ) {
+ if ( trim($at_t[$x]) != "" ) {
+ $t_t = $at_t[$x] ;
+ $l_t = $al_t[$x] ;
+ $r .= "<font color=red size=+1>".
+ "The link ".
+ $l_f.":".$t_f." &harr; ".$l_t.":".$t_t.
+ " has been added.</font><br>" ;
+ intl_add_doit ( $c ) ;
+ }
+ }
+ $yt = "" ;
+ $yl = "" ;
+ }
+
+ $sk = $wgUser->getSkin();
+ $si = "Special:Intl" ;
+
+ if ( $xl == "" ) $xl = $wgLanguageCode ;
+
+ $oxt = $xt ;
+ $oyt = $yt ;
+ $nt = Title::newFromText ( $xt ) ;
+ $xt = $nt->getPrefixedText () ;
+ $nt = Title::newFromText ( $yt ) ;
+ $yt = $nt->getPrefixedText () ;
+
+ $ll1 = $ll2 = "" ;
+ $a = $wgLang->getLanguageNames();
+ $ak = array_keys ( $a ) ;
+ foreach ( $ak AS $k ) {
+ $sel = "" ;
+ if ( $k == $xl ) $sel = " SELECTED" ;
+ $ll1 .= "<option{$sel} value='{$k}'>{$a[$k]}</option>\n" ;
+ $sel = "" ;
+ if ( $k == $yl ) $sel = " SELECTED" ;
+ $ll2 .= "<option{$sel} value='{$k}'>{$a[$k]}</option>\n" ;
+ }
+
+ $r .= "<h2>Add or update a link</h2>" ;
+
+ if ( $oxt != "" ) {
+ $zl = "See the group of articles interlinked for ".$a[$xl].":".$xt ;
+ $zl = $sk->makeKnownLink($si,$zl,"mode=zoom&xl={$xl}&xt={$oxt}")."<br>\n" ;
+ $al = getArticleLink ( $oxt , $xl ) ;
+ $r .= $zl.$al ;
+ }
+
+ $r .= "Note: You can also type the language code before the target (e.g., 'en:target'). The selection of the drop down box will then be ignored.<br>\n" ;
+
+ $r .= "<FORM method=post>\n" ;
+
+ $r .= "<li>Source \n" ;
+ $r .= "<select name=l_f>\n{$ll1}</select>\n " ;
+ $r .= "<input type=text name=t_f value=\"{$xt}\">\n" ;
+ $r .= "</li>\n" ;
+
+ for ( $x = 0 ; $x < 10 ; $x++ ) {
+ $r .= "<li>Destin. \n" ;
+ $r .= "<select name='al_t[{$x}]'>\n{$ll2}</select>\n " ;
+ $r .= "<input type=text name='at_t[{$x}]' value=\"{$yt}\">\n" ;
+ $r .= "</li>\n" ;
+ }
+
+ $r .= "<INPUT type=checkbox name=backlink checked>Add link in both directions<br>\n" ;
+
+ $r .= "<INPUT type=submit name=doit value='Do it'>\n" ;
+
+ $r .= "</FORM>\n" ;
+
+ return $r ;
+ }
+
+function eliminate_doubles ( &$list ) { # Real ugly
+ $ak = array_keys ( $list ) ;
+ foreach ( $ak AS $k1 ) {
+ if ( $list[$k1]->hidden ) continue ;
+ foreach ( $ak AS $k2 ) {
+ if ( $k1 != $k2 &&
+ $list[$k1]->title_from == $list[$k2]->title_to &&
+ $list[$k1]->title_to == $list[$k2]->title_from &&
+ $list[$k1]->lang_from == $list[$k2]->lang_to &&
+ $list[$k1]->lang_to == $list[$k2]->lang_from ) {
+ $list[$k1]->both = true ;
+ $list[$k2]->hidden = true ;
+ break ;
+ }
+ }
+ }
+ }
+
+function displayLinks ( $list , $opt = "" ) {
+ eliminate_doubles ( $list ) ;
+ global $wgLang , $wgUser , $mode ;
+ $si = "Special:Intl" ;
+ $sk = $wgUser->getSkin();
+ $ln = $wgLang->getLanguageNames();
+ $r = "" ;
+
+ if ( !isset ( $opt->showdel ) ) $opt->showdel = true ;
+
+ global $limit , $offset , $intlparam ;
+ if ( $intlparam != "" ) {
+ $r .= wfShowingResults( $offset, $limit );
+ $sl = wfViewPrevNext( $offset, $limit,
+ $wgLang->specialPage( "Intl".$intlparam ) );
+ $r .= "<br>{$sl}\n" ;
+ }
+
+ $r .= "<table border=1 cellpadding=2 cellspacing=0>\n" ;
+ $r .= "<tr>\n" ;
+ $r .= "<th colspan=2>From</th>\n" ;
+ $r .= "<th>&nbsp;</th>\n" ;
+ $r .= "<th colspan=2>To</th>\n" ;
+ if ( $mode != "zoom" ) $r .= "<th>&nbsp;</th>\n" ;
+ if ( $opt->showdel ) $r .= "<th colspan=3>Delete</th>\n" ;
+ if ( $opt->display != "" ) $r .= "<th colspan=3>".$opt->display."</th>\n" ;
+ $r .= "</tr>\n" ;
+
+ foreach ( $list AS $q ) {
+ if ( $q->hidden ) continue ;
+ $zoom = "xl={$q->lang_from}&xt=".urlencode($q->title_from) ;
+ $zoom = $sk->makeKnownLink($si,"[&Sigma;]","mode=zoom&{$zoom}") ;
+ $del1 = "xl={$q->lang_from}&xt=".urlencode($q->title_from)."&yl={$q->lang_to}" ;
+ $del2 = $sk->makeKnownLink($si,"[&harr;]","mode=delete&{$del1}&back=yes") ;
+ $del1 = $sk->makeKnownLink($si,"[&rarr;]","mode=delete&{$del1}") ;
+ $del1a = "xl={$q->lang_to}&xt=".urlencode($q->title_to)."&yl={$q->lang_from}" ;
+ $del1a = $sk->makeKnownLink($si,"[&larr;]","mode=delete&{$del1a}") ;
+ $sign = "&rarr;" ;
+ if ( $q->both ) $sign = "&harr;" ;
+ else $del1a = "&nbsp;" ;
+
+ $r .= "<tr>\n" ;
+ $r .= "<td>".$ln[$q->lang_from]."</td>\n" ;
+ $r .= "<td>".getArticleLink($q->title_from,$q->lang_from)."</td>\n" ;
+ $r .= "<td> {$sign} </td>\n" ;
+ $r .= "<td>".$ln[$q->lang_to]."</td>\n" ;
+ $r .= "<td>".getArticleLink($q->title_to,$q->lang_to)."</td>\n" ;
+ if ( $mode != "zoom" ) $r .= "<td>{$zoom}</td>\n" ;
+ if ( $opt->showdel ) {
+ $r .= "<td>{$del1}</td>\n" ;
+ $r .= "<td>{$del1a}</td>\n" ;
+ $r .= "<td>{$del2}</td>\n" ;
+ }
+ if ( $opt->display != "" ) {
+ if ( $q->display == "" ) $q->display = "&nbsp;" ;
+ $r .= "<td>{$q->display}</td>\n" ;
+ }
+ $r .= "</tr>\n" ;
+ }
+ $r .= "</table>\n" ;
+ if ( $intlparam != "" )
+ $r .= "{$sl}<br>\n" ;
+ return $r ;
+ }
+
+function intl_outgoing ( $c ) {
+ global $wgLanguageCode ;
+ global $limit , $offset , $intlparam ;
+ $intlparam = "&mode=outgoinglinks" ;
+ $list = array() ;
+ $r = "<h2>Outgoing links</h2>\n" ;
+ $sql = "SELECT * FROM ilinks WHERE lang_from='{$wgLanguageCode}' LIMIT {$offset}, {$limit}";
+ $res = mysql_query ( $sql , $c ) ;
+ while ( $q = mysql_fetch_object ( $res ) ) $list[] = $q ;
+ mysql_free_result ( $res ) ;
+ $r .= displayLinks ( $list ) ;
+ return $r ;
+ }
+
+function intl_incoming ( $c ) {
+ global $wgLanguageCode ;
+ global $limit , $offset , $intlparam ;
+ $intlparam = "&mode=incominglinks" ;
+ $list = array() ;
+ $r = "<h2>Incoming links</h2>\n" ;
+ $sql="SELECT * FROM ilinks WHERE lang_to='{$wgLanguageCode}' LIMIT {$offset}, {$limit}";
+ $res = mysql_query ( $sql , $c ) ;
+ while ( $q = mysql_fetch_object ( $res ) ) $list[] = $q ;
+ mysql_free_result ( $res ) ;
+ $r .= displayLinks ( $list ) ;
+ return $r ;
+ }
+
+function intl_all ( $c ) {
+ global $wgLanguageCode ;
+ global $limit , $offset , $intlparam ;
+ $intlparam = "&mode=alllinks" ;
+ $list = array() ;
+ $r = "<h2>All links</h2>\n" ;
+ $sql = "SELECT * FROM ilinks LIMIT {$offset}, {$limit}";
+ $res = mysql_query ( $sql , $c ) ;
+ while ( $q = mysql_fetch_object ( $res ) ) $list[] = $q ;
+ mysql_free_result ( $res ) ;
+ $r .= displayLinks ( $list ) ;
+ return $r ;
+ }
+
+
+function do_zoom ( &$found , &$list , $c ) {
+ $news = array () ;
+ foreach ( $found AS $x ) {
+ if ( $x->new ) {
+$sql = "SELECT * FROM ilinks WHERE
+( lang_from='{$x->lang}' AND title_from='{$x->title}' ) OR
+( lang_to='{$x->lang}' AND title_to='{$x->title}' )
+" ;
+ $res = mysql_query ( $sql , $c ) ;
+ while ( $q = mysql_fetch_object ( $res ) ) {
+ $i->orig = $q ;
+ $i->lang = $q->lang_from ;
+ $i->title = $q->title_from ;
+ $news[] = $i ;
+
+ $i->lang = $q->lang_to ;
+ $i->title = $q->title_to ;
+ $news[] = $i ;
+ }
+ mysql_free_result ( $res ) ;
+ }
+ }
+ $ak = array_keys ( $found ) ;
+ foreach ( $ak AS $x ) $found[$x]->new = false ;
+
+ # Adding new ones
+ $isnewone = false ;
+ foreach ( $news AS $n ) {
+ $didfind = 0 ;
+ foreach ( $found AS $f ) {
+ if($n->lang==$f->lang AND $n->title==$f->title) {
+ $didfind=1;
+ if ( $f->new ) $list[] = $n->orig ;
+ }
+ }
+ if ( $didfind == 0 ) {
+ $i->lang = $n->lang ;
+ $i->title = $n->title ;
+ $i->new = true ;
+ $found[] = $i ;
+ $list[] = $n->orig ;
+ $isnewone = true ;
+ }
+ }
+
+ if ( $isnewone ) do_zoom ( $found , $list , $c ) ;
+ }
+
+function getMissingLinks ( $found , $list ) {
+ $a = $r = array () ;
+ foreach ( $found AS $f1 ) {
+ foreach ( $found AS $f2 ) {
+ if ( $f1 != $f2 ) {
+ $i->lang_from = $f1->lang ;
+ $i->lang_to = $f2->lang ;
+ $i->title_from = $f1->title ;
+ $i->title_to = $f2->title ;
+ $a[] = $i ;
+ }
+ }
+ }
+ foreach ( $a AS $x ) {
+ $f = false ;
+ foreach ( $list AS $l ) {
+ if ( $x->lang_from == $l->lang_from &&
+ $x->lang_to == $l->lang_to &&
+ $x->title_from == $l->title_from &&
+ $x->title_to == $l->title_to ) {
+ $f = true ;
+ break ;
+ }
+ }
+ if ( !$f ) $r[] = $x ;
+ }
+ return $r ;
+ }
+
+function intl_zoom2 ( $c ) {
+ global $doit , $ZLF , $ZLT , $ZTF , $ZTT , $ZCB ;
+ global $l_f , $l_t , $t_f , $t_t , $backlink ;
+ $r = "<h2>Adding selected language links</h2>\n" ;
+ $r .= "<OL>\n" ;
+ $ak = array_keys ( $ZCB ) ;
+ foreach ( $ak AS $cnt ) {
+ if ( $ZCB[$cnt] == "on" ) {
+ $l_f = $ZLF[$cnt] ;
+ $l_t = $ZLT[$cnt] ;
+ $t_f = $ZTF[$cnt] ;
+ $t_t = $ZTT[$cnt] ;
+ $backlink = "on" ;
+ intl_add_doit ( $c ) ;
+ $r .= "<li>$l_f:$t_f &harr; $l_t:$t_t</li>\n" ;
+ }
+ }
+ $r .= "</OL>\n" ;
+ return $r ;
+ }
+
+function intl_zoom ( $c ) {
+ global $doit ;
+ global $wgLanguageCode , $wgLang ;
+ global $xl , $xt ;
+ if ( isset ( $doit ) ) return intl_zoom2 ( $c ) ;
+ $ln = $wgLang->getLanguageNames();
+ $list = array() ;
+ $found = array () ;
+ $r = "<h2>Interlinked articles group</h2>\n" ;
+ $initial->lang = $xl ;
+ $initial->title = urldecode ( $xt ) ;
+ $initial->new = true ;
+ $found[] = $initial ;
+
+ do_zoom ( $found , $list , $c ) ;
+
+ $involved = array() ;
+ foreach ( $found AS $f )
+ $involved[] = $ln[$f->lang].":".getArticleLink ( $f->title , $f->lang ) ;
+ $r .= "Involved are ".implode ( ", " , $involved )."<br>\n" ;
+
+ $r .= displayLinks ( $list ) ;
+
+ $list2 = getMissingLinks ( $found , $list ) ;
+
+ if ( count ( $list2 ) > 0 ) {
+ $r .= "<h3>Missing links</h3>\n" ;
+ $opt->showdel = false ;
+ $opt->display = "Create" ;
+ $ak = array_keys ( $list2 ) ;
+ $cnt = 1 ;
+ foreach ( $ak AS $a ) {
+ $b = $list2[$a] ;
+ $z = "<input type=checkbox name='ZCB[{$cnt}]' checked>\n" ;
+ $z.="<input type=hidden name='ZLF[{$cnt}]' value='{$b->lang_from}'>\n";
+ $z.="<input type=hidden name='ZLT[{$cnt}]' value='{$b->lang_to}'>\n";
+ $z.="<input type=hidden name='ZTF[{$cnt}]' value='{$b->title_from}'>\n";
+ $z.="<input type=hidden name='ZTT[{$cnt}]' value='{$b->title_to}'>\n";
+ $list2[$a]->display = $z ;
+ $cnt++ ;
+ }
+ $r .= "<FORM method=post>\n" ;
+ $r .= displayLinks ( $list2 , $opt ) ;
+ $r .= "<INPUT type=submit name=doit value='Create selected links'>\n" ;
+ $r .= " (Note: This is still buggy, I don't know why...)" ;
+ $r .= "</FORM>\n" ;
+ }
+
+ return $r ;
+ }
+
+function intl_delete ( $c ) {
+ global $wgLang ;
+ global $xt , $xl , $yl , $back ;
+ $title = urldecode ( $xt ) ;
+ $ln = $wgLang->getLanguageNames();
+
+ $sql = "DELETE FROM ilinks WHERE
+lang_from='{$xl}' AND
+lang_to='{$yl}' AND
+title_from='{$title}'
+" ;
+ $res = mysql_query ( $sql , $c ) ;
+
+ $r = "<h2>Deletion</h2>" ;
+ $r .= "The link from ".$ln[$xl].":".$title." to ".$ln[$yl]." has been deleted.<br>" ;
+
+ appendRecentChanges ( "- ".$ln[$xl].":".getArticleLink($title,$xl)." &rarr;" ) ;
+
+ # Backlink?
+ if ( $back != "yes" ) return $r ;
+
+ $sql = "DELETE FROM ilinks WHERE
+lang_to='{$xl}' AND
+lang_from='{$yl}' AND
+title_to='{$title}'
+" ;
+ $res = mysql_query ( $sql , $c ) ;
+
+ appendRecentChanges ( "- &rarr;".$ln[$xl].":".getArticleLink($title,$xl) ) ;
+
+ $r .= "As was the backlink.<br>" ;
+ return $r ;
+ }
+?>
diff --git a/includes/SpecialIpblocklist.php b/includes/SpecialIpblocklist.php
new file mode 100644
index 000000000000..e46748f88c38
--- /dev/null
+++ b/includes/SpecialIpblocklist.php
@@ -0,0 +1,128 @@
+<?
+
+function wfSpecialIpblocklist()
+{
+ global $wgUser, $wgOut, $action, $ip;
+
+ $fields = array( "wpUnblockAddress" );
+ wfCleanFormFields( $fields );
+ $ipu = new IPUnblockForm();
+
+ if ( "success" == $action ) {
+ $msg = str_replace( "$1", $ip, wfMsg( "ipusuccess" ) );
+ $ipu->showList( $msg );
+ } else if ( "submit" == $action ) {
+ if ( ! $wgUser->isSysop() ) {
+ $wgOut->sysopRequired();
+ return;
+ }
+ $ipu->doSubmit();
+ } else if ( "unblock" == $action ) {
+ $ipu->showForm( "" );
+ } else {
+ $ipu->showList( "" );
+ }
+}
+
+class IPUnblockForm {
+
+ function showForm( $err )
+ {
+ global $wgOut, $wgUser, $wgLang;
+ global $ip, $wpUnblockAddress;
+
+ $wgOut->setPagetitle( wfMsg( "unblockip" ) );
+ $wgOut->addWikiText( wfMsg( "unblockiptext" ) );
+
+ if ( ! $wpUnblockAddress ) { $wpUnblockAddress = $ip; }
+ $ipa = wfMsg( "ipaddress" );
+ $ipus = wfMsg( "ipusubmit" );
+ $action = wfLocalUrlE( $wgLang->specialPage( "Ipblocklist" ),
+ "action=submit" );
+
+ if ( "" != $err ) {
+ $wgOut->setSubtitle( wfMsg( "formerror" ) );
+ $wgOut->addHTML( "<p><font color='red' size='+1'>{$err}</font>\n" );
+ }
+ $wgOut->addHTML( "<p>
+<form id=\"unblockip\" method=\"post\" action=\"{$action}\">
+<table border=0><tr>
+<td align=right>{$ipa}:</td>
+<td align=left>
+<input tabindex=1 type=text size=20 name=\"wpUnblockAddress\" value=\"{$wpUnblockAddress}\">
+</td></tr><tr>
+<td>&nbsp;</td><td align=left>
+<input tabindex=2 type=submit name=\"wpBlock\" value=\"{$ipus}\">
+</td></tr></table>
+</form>\n" );
+
+ }
+
+ function doSubmit()
+ {
+ global $wgOut, $wgUser, $wgLang;
+ global $ip, $wpUnblockAddress;
+ $fname = "IPUnblockForm::doSubmit";
+
+ if ( ! preg_match( "/\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/",
+ $wpUnblockAddress ) ) {
+ $this->showForm( wfMsg( "badipaddress" ) );
+ return;
+ }
+ $sql = "DELETE FROM ipblocks WHERE ipb_address='{$wpUnblockAddress}'";
+ wfQuery( $sql, $fname );
+
+ $success = wfLocalUrl( $wgLang->specialPage( "Ipblocklist" ),
+ "action=success&ip={$wpUnblockAddress}" );
+ $wgOut->redirect( $success );
+ }
+
+ function showList( $msg )
+ {
+ global $wgOut, $wgUser, $wgLang;
+ global $ip;
+
+ $wgOut->setPagetitle( wfMsg( "ipblocklist" ) );
+ if ( "" != $msg ) {
+ $wgOut->setSubtitle( $msg );
+ }
+ $sql = "SELECT ipb_timestamp,ipb_address,ipb_user,ipb_by,ipb_reason " .
+ "FROM ipblocks ORDER BY ipb_timestamp";
+ $res = wfQuery( $sql, "IPUnblockForm::showList" );
+
+ $wgOut->addHTML( "<ul>" );
+ $sk = $wgUser->getSkin();
+ while ( $row = wfFetchObject( $res ) ) {
+ $addr = $row->ipb_address;
+ $name = User::whoIs( $row->ipb_by );
+ $ulink = $sk->makeKnownLink( $wgLang->getNsText( Namespace::getUser() ). ":{$name}", $name );
+ $d = $wgLang->timeanddate( $row->ipb_timestamp, true );
+
+ $line = str_replace( "$1", $d, wfMsg( "blocklistline" ) );
+ $line = str_replace( "$2", $ulink, $line );
+ $line = str_replace( "$3", $row->ipb_address, $line );
+
+ $wgOut->addHTML( "<li>{$line}" );
+ $clink = "<a href=\"" . wfLocalUrlE( $wgLang->specialPage(
+ "Contributions" ), "target={$addr}" ) . "\">" .
+ wfMsg( "contribslink" ) . "</a>";
+ $wgOut->addHTML( " ({$clink})" );
+
+ if ( $wgUser->isSysop() ) {
+ $ublink = "<a href=\"" . wfLocalUrlE( $wgLang->specialPage(
+ "Ipblocklist" ), "action=unblock&ip={$addr}" ) . "\">" .
+ wfMsg( "unblocklink" ) . "</a>";
+ $wgOut->addHTML( " ({$ublink})" );
+ }
+ if ( "" != $row->ipb_reason ) {
+ $wgOut->addHTML( " <em>(" . wfEscapeHTML( $row->ipb_reason ) .
+ ")</em>" );
+ }
+ $wgOut->addHTML( "</li>\n" );
+ }
+ wfFreeResult( $res );
+ $wgOut->addHTML( "</ul>\n" );
+ }
+}
+
+?>
diff --git a/includes/SpecialListusers.php b/includes/SpecialListusers.php
new file mode 100644
index 000000000000..3d9c098d8505
--- /dev/null
+++ b/includes/SpecialListusers.php
@@ -0,0 +1,42 @@
+<?
+
+function wfSpecialListusers()
+{
+ global $wgUser, $wgOut, $wgLang, $offset, $limit;
+
+ if ( ! $limit ) {
+ $limit = $wgUser->getOption( "rclimit" );
+ if ( ! $limit ) { $limit = 50; }
+ }
+ if ( ! $offset ) { $offset = 0; }
+
+ $top = wfShowingResults( $offset, $limit );
+ $wgOut->addHTML( "<p>{$top}\n" );
+
+ $sl = wfViewPrevNext( $offset, $limit,
+ $wgLang->specialPage( "Listusers" ) );
+ $wgOut->addHTML( "<br>{$sl}\n<ol start=" . ( $offset + 1 ) . ">" );
+
+ $sql = "SELECT user_name,user_rights FROM user ORDER BY " .
+ "user_name LIMIT {$offset}, {$limit}";
+ $res = wfQuery( $sql, "wfSpecialListusers" );
+
+ $sk = $wgUser->getSkin();
+ while ( $s = wfFetchObject( $res ) ) {
+ $n = $s->user_name;
+ $r = $s->user_rights;
+
+ $l = $sk->makeLink( $wgLang->getNsText(
+ Namespace::getUser() ) . ":{$n}", $n );
+
+ if ( "" != $r ) {
+ $link = $sk->makeKnownLink( wfMsg( "administrators" ), $r );
+ $l .= " ({$link})";
+ }
+ $wgOut->addHTML( "<li>{$l}</li>\n" );
+ }
+ wfFreeResult( $res );
+ $wgOut->addHTML( "</ol><p>{$sl}\n" );
+}
+
+?>
diff --git a/includes/SpecialLockdb.php b/includes/SpecialLockdb.php
new file mode 100644
index 000000000000..efc75a0d0b78
--- /dev/null
+++ b/includes/SpecialLockdb.php
@@ -0,0 +1,96 @@
+<?
+
+function wfSpecialLockdb()
+{
+ global $wgUser, $wgOut, $action;
+
+ if ( ! $wgUser->isDeveloper() ) {
+ $wgOut->developerRequired();
+ return;
+ }
+ $fields = array( "wpLockReason" );
+ wfCleanFormFields( $fields );
+
+ $f = new DBLockForm();
+
+ if ( "success" == $action ) { $f->showSuccess(); }
+ else if ( "submit" == $action ) { $f->doSubmit(); }
+ else { $f->showForm( "" ); }
+}
+
+class DBLockForm {
+
+ function showForm( $err )
+ {
+ global $wgOut, $wgUser, $wgLang;
+ global $wpLockConfirm;
+
+ $wgOut->setPagetitle( wfMsg( "lockdb" ) );
+ $wgOut->addWikiText( wfMsg( "lockdbtext" ) );
+
+ if ( "" != $err ) {
+ $wgOut->setSubtitle( wfMsg( "formerror" ) );
+ $wgOut->addHTML( "<p><font color='red' size='+1'>{$err}</font>\n" );
+ }
+ $lc = wfMsg( "lockconfirm" );
+ $lb = wfMsg( "lockbtn" );
+ $elr = wfMsg( "enterlockreason" );
+ $action = wfLocalUrlE( $wgLang->specialPage( "Lockdb" ),
+ "action=submit" );
+
+ $wgOut->addHTML( "<p>
+<form id=\"lockdb\" method=\"post\" action=\"{$action}\">
+{$elr}:
+<textarea name=\"wpLockReason\" rows=10 cols=60 wrap=virtual>
+</textarea>
+<table border=0><tr>
+<td align=right>
+<input type=checkbox name=\"wpLockConfirm\">
+</td>
+<td align=left>{$lc}<td>
+</tr><tr>
+<td>&nbsp;</td><td align=left>
+<input type=submit name=\"wpLock\" value=\"{$lb}\">
+</td></tr></table>
+</form>\n" );
+
+ }
+
+ function doSubmit()
+ {
+ global $wgOut, $wgUser, $wgLang;
+ global $wpLockConfirm, $wpLockReason, $wgReadOnlyFile;
+
+ if ( ! $wpLockConfirm ) {
+ $this->showForm( wfMsg( "locknoconfirm" ) );
+ return;
+ }
+ $fp = fopen( $wgReadOnlyFile, "w" );
+
+ if ( false === $fp ) {
+ $wgOut->fileNotFoundError( $wgReadOnlyFile );
+ return;
+ }
+ fwrite( $fp, $wpLockReason );
+ fwrite( $fp, "\n<p>(by " . $wgUser->getName() . " at " .
+ $wgLang->timeanddate( date( "YmdHis" ) ) . ")\n" );
+ fclose( $fp );
+
+ $success = wfLocalUrl( $wgLang->specialPage( "Lockdb" ),
+ "action=success" );
+ $wgOut->redirect( $success );
+ }
+
+ function showSuccess()
+ {
+ global $wgOut, $wgUser;
+ global $ip;
+
+ $wgOut->setPagetitle( wfMsg( "lockdb" ) );
+ $wgOut->setSubtitle( wfMsg( "lockdbsuccesssub" ) );
+ $text = str_replace( "$1", $ip, wfMsg( "lockdbsuccesstext" ) );
+ $wgOut->addWikiText( $text );
+ }
+}
+
+?>
diff --git a/includes/SpecialLonelypages.php b/includes/SpecialLonelypages.php
new file mode 100644
index 000000000000..b62b48225a1a
--- /dev/null
+++ b/includes/SpecialLonelypages.php
@@ -0,0 +1,46 @@
+<?
+
+function wfSpecialLonelypages()
+{
+ global $wgUser, $wgOut, $wgLang, $wgTitle;
+ global $limit, $offset; # From query string
+ $fname = "wfSpecialLonelypages";
+
+ global $wgMiserMode;
+ if ( $wgMiserMode ) {
+ $wgOut->addWikiText( wfMsg( "perfdisabled" ) );
+ return;
+ }
+
+ if ( ! $limit ) {
+ $limit = $wgUser->getOption( "rclimit" );
+ if ( ! $limit ) { $limit = 50; }
+ }
+ if ( ! $offset ) { $offset = 0; }
+
+ $sql = "SELECT cur_title FROM cur LEFT JOIN links ON " .
+ "cur_id=l_to WHERE l_to IS NULL AND cur_namespace=0 AND " .
+ "cur_is_redirect=0 ORDER BY cur_title LIMIT {$offset}, {$limit}";
+ $res = wfQuery( $sql, $fname );
+
+ $sk = $wgUser->getSkin();
+
+ $top = wfShowingResults( $offset, $limit );
+ $wgOut->addHTML( "<p>{$top}\n" );
+
+ $sl = wfViewPrevNext( $offset, $limit,
+ $wgLang->specialPage( "Lonelypages" ) );
+ $wgOut->addHTML( "<br>{$sl}\n" );
+
+ $s = "<ol start=" . ( $offset + 1 ) . ">";
+ while ( $obj = wfFetchObject( $res ) ) {
+ $link = $sk->makeKnownLink( $obj->cur_title, "" );
+ $s .= "<li>{$link}</li>\n";
+ }
+ wfFreeResult( $res );
+ $s .= "</ol>";
+ $wgOut->addHTML( $s );
+ $wgOut->addHTML( "<p>{$sl}\n" );
+}
+
+?>
diff --git a/includes/SpecialLongpages.php b/includes/SpecialLongpages.php
new file mode 100644
index 000000000000..3807abdb2d8c
--- /dev/null
+++ b/includes/SpecialLongpages.php
@@ -0,0 +1,47 @@
+<?
+
+function wfSpecialLongpages()
+{
+ global $wgUser, $wgOut, $wgLang, $wgTitle;
+ global $limit, $offset; # From query string
+ $fname = "wfSpecialLongpages";
+
+ global $wgMiserMode;
+ if ( $wgMiserMode ) {
+ $wgOut->addWikiText( wfMsg( "perfdisabled" ) );
+ return;
+ }
+
+ if ( ! $limit ) {
+ $limit = $wgUser->getOption( "rclimit" );
+ if ( ! $limit ) { $limit = 50; }
+ }
+ if ( ! $offset ) { $offset = 0; }
+
+ $sql = "SELECT cur_title, LENGTH(cur_text) AS len FROM cur " .
+ "WHERE cur_namespace=0 AND cur_is_redirect=0 ORDER BY " .
+ "LENGTH(cur_text) DESC LIMIT {$offset}, {$limit}";
+ $res = wfQuery( $sql, $fname );
+
+ $sk = $wgUser->getSkin();
+
+ $top = wfShowingResults( $offset, $limit );
+ $wgOut->addHTML( "<p>{$top}\n" );
+
+ $sl = wfViewPrevNext( $offset, $limit,
+ $wgLang->specialPage( "Longpages" ) );
+ $wgOut->addHTML( "<br>{$sl}\n" );
+
+ $s = "<ol start=" . ( $offset + 1 ) . ">";
+ while ( $obj = wfFetchObject( $res ) ) {
+ $nb = str_replace( "$1", $obj->len, wfMsg( "nbytes" ) );
+ $link = $sk->makeKnownLink( $obj->cur_title, "" );
+ $s .= "<li>{$link} ({$nb})</li>\n";
+ }
+ wfFreeResult( $res );
+ $s .= "</ol>";
+ $wgOut->addHTML( $s );
+ $wgOut->addHTML( "<p>{$sl}\n" );
+}
+
+?>
diff --git a/includes/SpecialMaintenance.php b/includes/SpecialMaintenance.php
new file mode 100644
index 000000000000..9a830e4fba59
--- /dev/null
+++ b/includes/SpecialMaintenance.php
@@ -0,0 +1,368 @@
+<?
+
+function sns()
+ {
+ global $wgLang ;
+ $ns = $wgLang->getNamespaces() ;
+ return $ns[-1] ;
+ }
+
+function wfSpecialMaintenance ()
+ {
+ global $wgUser, $wgOut, $wgLang, $wgTitle, $subfunction, $wgLanguageCode, $submitmll;
+ global $wgMiserMode;
+ if ( $wgMiserMode ) {
+ $wgOut->addWikiText( wfMsg( "perfdisabled" ) );
+ return;
+ }
+
+ if ( $subfunction == "disambiguations" ) return wfSpecialDisambiguations() ;
+ if ( $subfunction == "doubleredirects" ) return wfSpecialDoubleRedirects() ;
+ if ( $subfunction == "brokenredirects" ) return wfSpecialBrokenRedirects() ;
+ if ( $subfunction == "selflinks" ) return wfSpecialSelfLinks() ;
+ if ( $subfunction == "mispeelings" ) return wfSpecialMispeelings() ;
+ if ( $subfunction == "missinglanguagelinks" ) return wfSpecialMissingLanguageLinks() ;
+ if ( isset ( $submitmll ) ) return wfSpecialMissingLanguageLinks() ;
+
+ $sk = $wgUser->getSkin();
+ $ns = $wgLang->getNamespaces() ;
+ $r = wfMsg("maintnancepagetext") ;
+ $r .= "<UL>\n" ;
+ $r .= "<li>".getMPL("disambiguations")."</li>\n" ;
+ $r .= "<li>".getMPL("doubleredirects")."</li>\n" ;
+ $r .= "<li>".getMPL("brokenredirects")."</li>\n" ;
+ $r .= "<li>".getMPL("selflinks")."</li>\n" ;
+ $r .= "<li>".getMPL("mispeelings")."</li>\n" ;
+
+ $r .= "<li>";
+ $l = getMPL("missinglanguagelinks");
+ $l = str_replace ( "</a>" , "" , $l ) ;
+ $l = str_replace ( "<a " , "<FORM method=post " , $l ) ;
+ $l = explode ( ">" , $l ) ;
+ $l = $l[0] ;
+ $r .= $l.">\n" ;
+ $r .= "<input type=submit name='submitmll' value='" ;
+ $r .= wfMsg("missinglanguagelinksbutton");
+ $r .= "'>\n" ;
+ $r .= "<select name=thelang>\n" ;
+ $a = $wgLang->getLanguageNames();
+ $ak = array_keys ( $a ) ;
+ foreach ( $ak AS $k ) {
+ if ( $k != $wgLanguageCode )
+ $r .= "<option value='{$k}'>{$a[$k]}</option>\n" ;
+ }
+ $r .= "</select>\n" ;
+ $r .= "</FORM>\n</li>" ;
+
+ $r .= "</UL>\n" ;
+ $wgOut->addHTML ( $r ) ;
+ }
+
+function getMPL ( $x )
+ {
+ global $wgUser , $wgLang;
+ $sk = $wgUser->getSkin() ;
+ return $sk->makeKnownLink(sns().":Maintenance",wfMsg($x),"subfunction={$x}") ;
+ }
+
+function getMaintenancePageBacklink()
+ {
+ global $wgUser , $wgLang , $subfunction ;
+ $sk = $wgUser->getSkin() ;
+ $ns = $wgLang->getNamespaces() ;
+ $r = $sk->makeKnownLink (
+ $ns[-1].":Maintenance",
+ wfMsg("maintenancebacklink") ) ;
+ $t = wfMsg ( $subfunction ) ;
+
+ $s = "<table width=100% border=0><tr><td>";
+ $s .= "<h2>{$t}</h2></td><td align=right>";
+ $s .= "{$r}</td></tr></table>\n" ;
+ return $s ;
+ }
+
+
+function wfSpecialDisambiguations()
+{
+ global $wgUser, $wgOut, $wgLang, $wgTitle;
+ global $limit, $offset; # From query string
+ $fname = "wfSpecialDisambiguations";
+
+ if ( ! $limit ) {
+ $limit = $wgUser->getOption( "rclimit" );
+ if ( ! $limit ) { $limit = 50; }
+ }
+ if ( ! $offset ) { $offset = 0; }
+
+ $dp = wfStrencode( wfMsg("disambiguationspage") );
+
+ $sql = "SELECT la.l_from,la.l_to,"
+ . " lb.l_from AS source,lb.l_to AS dest,"
+ . " c.cur_id, c.cur_title AS dt"
+ . " FROM links AS la, links AS lb, cur AS c, cur AS d"
+ . " WHERE la.l_from='{$dp}'"
+ . " AND la.l_to=lb.l_to"
+ . " AND la.l_from<>lb.l_from"
+ . " AND c.cur_id=lb.l_to"
+ . " AND c.cur_namespace=0"
+ . " AND d.cur_title=lb.l_from"
+ . " AND d.cur_namespace=0"
+ . " LIMIT {$offset}, {$limit}";
+
+ $res = wfQuery( $sql, $fname );
+
+ $sk = $wgUser->getSkin();
+
+ $top = "<p>".wfMsg("disambiguationstext")."</p><br>\n";
+ $top = str_replace ( "$1" , $sk->makeKnownLink ( $dp ) , $top ) ;
+ $top = getMaintenancePageBacklink().$top ;
+ $top .= wfShowingResults( $offset, $limit );
+ $wgOut->addHTML( "<p>{$top}\n" );
+
+ $sl = wfViewPrevNext( $offset, $limit, "REPLACETHIS" ) ;
+ $sl = str_replace ( "REPLACETHIS" , sns().":Maintenance&subfunction=disambiguations" , $sl ) ;
+ $wgOut->addHTML( "<br>{$sl}\n" );
+
+ $s = "<ol start=" . ( $offset + 1 ) . ">";
+ while ( $obj = wfFetchObject( $res ) ) {
+ $l1 = $sk->makeKnownLink ( $obj->source , "" , "redirect=no" ) ;
+ $l2 = $sk->makeKnownLink ( $obj->dt ) ;
+ $l3 = $sk->makeBrokenLink ( $obj->source , "(".wfMsg("qbedit").")" , "redirect=no" ) ;
+ $s .= "<li>{$l1} {$l3} => {$l2}</li>\n" ;
+ }
+ wfFreeResult( $res );
+ $s .= "</ol>";
+ $wgOut->addHTML( $s );
+ $wgOut->addHTML( "<p>{$sl}\n" );
+}
+
+function wfSpecialDoubleRedirects()
+{
+ global $wgUser, $wgOut, $wgLang, $wgTitle;
+ global $limit, $offset; # From query string
+ $fname = "wfSpecialDoubleRedirects";
+
+ if ( ! $limit ) {
+ $limit = $wgUser->getOption( "rclimit" );
+ if ( ! $limit ) { $limit = 50; }
+ }
+ if ( ! $offset ) { $offset = 0; }
+
+ $sql = "SELECT l_from,l_to,cb.cur_text AS rt,cb.cur_title AS ti FROM links,cur AS ca, cur AS cb WHERE ca.cur_is_redirect=1 AND cb.cur_is_redirect=1 AND l_to=cb.cur_id AND l_from=ca.cur_title AND ca.cur_namespace=0 LIMIT {$offset}, {$limit}" ;
+
+ $res = wfQuery( $sql, $fname );
+
+ $top = getMaintenancePageBacklink();
+ $top .= "<p>".wfMsg("doubleredirectstext")."</p><br>\n";
+ $top .= wfShowingResults( $offset, $limit );
+ $wgOut->addHTML( "<p>{$top}\n" );
+
+ $sl = wfViewPrevNext( $offset, $limit, "REPLACETHIS" ) ;
+ $sl = str_replace ( "REPLACETHIS" , sns().":Maintenance&subfunction=doubleredirects" , $sl ) ;
+ $wgOut->addHTML( "<br>{$sl}\n" );
+
+ $sk = $wgUser->getSkin();
+ $s = "<ol start=" . ( $offset + 1 ) . ">";
+ while ( $obj = wfFetchObject( $res ) ) {
+ $n = explode ( "\n" , $obj->rt ) ;
+ $n = $n[0] ;
+ $l1 = $sk->makeKnownLink ( $obj->l_from , "" , "redirect=no" ) ;
+ $l2 = $sk->makeKnownLink ( $obj->ti , "" , "redirect=no" ) ;
+ $l3 = $sk->makeBrokenLink ( $obj->l_from , "(".wfMsg("qbedit").")" , "redirect=no" ) ;
+ $s .= "<li>{$l1} {$l3} => {$l2} (\"{$n}\")</li>\n" ;
+ }
+ wfFreeResult( $res );
+ $s .= "</ol>";
+ $wgOut->addHTML( $s );
+ $wgOut->addHTML( "<p>{$sl}\n" );
+}
+
+function wfSpecialBrokenRedirects()
+{
+ global $wgUser, $wgOut, $wgLang, $wgTitle;
+ global $limit, $offset; # From query string
+ $fname = "wfSpecialBrokenRedirects";
+
+ if ( ! $limit ) {
+ $limit = $wgUser->getOption( "rclimit" );
+ if ( ! $limit ) { $limit = 50; }
+ }
+ if ( ! $offset ) { $offset = 0; }
+
+ $sql = "SELECT bl_to,cur_title FROM brokenlinks,cur WHERE cur_is_redirect=1 AND cur_namespace=0 AND bl_from=cur_id LIMIT {$offset}, {$limit}" ;
+
+ $res = wfQuery( $sql, $fname );
+
+ $top = getMaintenancePageBacklink();
+ $top .= "<p>".wfMsg("brokenredirectstext")."</p><br>\n";
+ $top .= wfShowingResults( $offset, $limit );
+ $wgOut->addHTML( "<p>{$top}\n" );
+
+ $sl = wfViewPrevNext( $offset, $limit, "REPLACETHIS" ) ;
+ $sl = str_replace ( "REPLACETHIS" , sns().":Maintenance&subfunction=brokenredirects" , $sl ) ;
+ $wgOut->addHTML( "<br>{$sl}\n" );
+
+ $sk = $wgUser->getSkin();
+ $s = "<ol start=" . ( $offset + 1 ) . ">";
+ while ( $obj = wfFetchObject( $res ) ) {
+ $l1 = $sk->makeKnownLink ( $obj->cur_title , "" , "redirect=no" ) ;
+ $l2 = $sk->makeBrokenLink ( $obj->cur_title , "(".wfMsg("qbedit").")" , "redirect=no" ) ;
+ $l3 = $sk->makeBrokenLink ( $obj->bl_to , "" , "redirect=no" ) ;
+ $s .= "<li>{$l1} {$l2} => {$l3}</li>\n" ;
+ }
+ wfFreeResult( $res );
+ $s .= "</ol>";
+ $wgOut->addHTML( $s );
+ $wgOut->addHTML( "<p>{$sl}\n" );
+}
+
+function wfSpecialSelfLinks()
+{
+ global $wgUser, $wgOut, $wgLang, $wgTitle;
+ global $limit, $offset; # From query string
+ $fname = "wfSpecialSelfLinks";
+
+ if ( ! $limit ) {
+ $limit = $wgUser->getOption( "rclimit" );
+ if ( ! $limit ) { $limit = 50; }
+ }
+ if ( ! $offset ) { $offset = 0; }
+
+ $sql = "SELECT cur_title FROM cur,links WHERE cur_is_redirect=0 AND cur_namespace=0 AND l_from=cur_title AND l_to=cur_id LIMIT {$offset}, {$limit}";
+
+ $res = wfQuery( $sql, $fname );
+
+ $top = getMaintenancePageBacklink();
+ $top .= "<p>".wfMsg("selflinkstext")."</p><br>\n";
+ $top .= wfShowingResults( $offset, $limit );
+ $wgOut->addHTML( "<p>{$top}\n" );
+
+ $sl = wfViewPrevNext( $offset, $limit, "REPLACETHIS" ) ;
+ $sl = str_replace ( "REPLACETHIS" , sns().":Maintenance&subfunction=selflinks" , $sl ) ;
+ $wgOut->addHTML( "<br>{$sl}\n" );
+
+ $sk = $wgUser->getSkin();
+ $s = "<ol start=" . ( $offset + 1 ) . ">";
+ while ( $obj = wfFetchObject( $res ) )
+ $s .= "<li>".$sk->makeKnownLink ( $obj->cur_title )."</li>\n" ;
+ wfFreeResult( $res );
+ $s .= "</ol>";
+ $wgOut->addHTML( $s );
+ $wgOut->addHTML( "<p>{$sl}\n" );
+}
+
+function wfSpecialMispeelings ()
+{
+ global $wgUser, $wgOut, $wgLang, $wgTitle;
+ global $limit, $offset; # From query string
+ $sk = $wgUser->getSkin();
+ $fname = "wfSpecialMispeelings";
+
+ if ( ! $limit ) {
+ $limit = $wgUser->getOption( "rclimit" );
+ if ( ! $limit ) { $limit = 50; }
+ }
+ if ( ! $offset ) { $offset = 0; }
+
+ # Determine page name
+ $ms = wfMsg ( "mispeelingspage" ) ;
+ $mss = str_replace ( " " , "_" , $ms ) ;
+ $msp = $wgLang->getNsText(4).":".$ms ;
+ $msl = $sk->makeKnownLink ( $msp ) ;
+
+ # Load list from database
+ $sql = "SELECT cur_text FROM cur WHERE cur_title='{$mss}' AND cur_namespace=4" ;
+ $res = wfQuery( $sql, $fname );
+ $obj = wfFetchObject ( $res ) ;
+ $l = $obj->cur_text ;
+ $l = explode ( "\n" , $l ) ;
+ $a = array () ;
+ foreach ( $l as $x )
+ if ( substr ( trim ( $x ) , 0 , 1 ) == "*" )
+ $a[] = strtolower ( trim ( substr ( trim ( $x ) , 1 ) ) );
+ asort ( $a ) ;
+
+ $cnt = 0 ;
+ $b = array () ;
+ foreach ( $a AS $x ) {
+ if ( $cnt < $offset+$limit && $x != "" ) {
+ $y = $x ;
+ $x = preg_replace( '/^(\S+).*$/', '$1', $x );
+ #$sql = "SELECT DISTINCT cur_title FROM cur WHERE cur_namespace=0 AND cur_is_redirect=0 AND (MATCH(cur_ind_text) AGAINST ('" . wfStrencode( $wgLang->stripForSearch( $x ) ) . "'))" ;
+ $sql = "SELECT DISTINCT cur_title FROM cur,searchindex WHERE cur_id=si_page AND cur_namespace=0 AND cur_is_redirect=0 AND (MATCH(si_text) AGAINST ('" . wfStrencode( $wgLang->stripForSearch( $x ) ) . "'))" ;
+ $res = wfQuery( $sql, $fname );
+ while ( $obj = wfFetchObject ( $res ) ) {
+ if ( $cnt >= $offset AND $cnt < $offset+$limit ) {
+ if ( $y != "" ) {
+ if ( count ( $b ) > 0 ) $b[] = "</OL>\n" ;
+ $b[] = "<H3>{$y}</H3>\n<OL start=".($cnt+1).">\n" ;
+ $y = "" ;
+ }
+ $b[] = "<li>".
+ $sk->makeKnownLink ( $obj->cur_title ).
+ " (".
+ $sk->makeBrokenLink ( $obj->cur_title , wfMsg ( "qbedit" ) ).
+ ")</li>\n" ;
+ }
+ $cnt++ ;
+ }
+ }
+ }
+ $top = getMaintenancePageBacklink();
+ $top .= "<p>".str_replace("$1",$msl,wfMsg("mispeelingstext"))."</p><br>\n";
+ $top .= wfShowingResults( $offset, $limit );
+ $wgOut->addHTML( "<p>{$top}\n" );
+
+ $sl = wfViewPrevNext( $offset, $limit, "REPLACETHIS" ) ;
+ $sl = str_replace ( "REPLACETHIS" , sns().":Maintenance&subfunction=mispeelings" , $sl ) ;
+ $wgOut->addHTML( "<br>{$sl}\n" );
+
+ $s = implode ( "" , $b ) ;
+ if ( count ( $b ) > 0 ) $s .= "</ol>";
+ $wgOut->addHTML( $s );
+ $wgOut->addHTML( "<p>{$sl}\n" );
+}
+
+
+function wfSpecialMissingLanguageLinks()
+{
+ global $wgUser, $wgOut, $wgLang, $wgTitle, $thelang, $subfunction;
+ global $limit, $offset; # From query string
+ $fname = "wfSpecialMissingLanguageLinks";
+ $subfunction = "missinglanguagelinks" ;
+ if ( $thelang == "w" ) $thelang = "en" ; # Fix for international wikis
+
+ if ( ! $limit ) {
+ $limit = $wgUser->getOption( "rclimit" );
+ if ( ! $limit ) { $limit = 50; }
+ }
+ if ( ! $offset ) { $offset = 0; }
+
+ $sql = "SELECT cur_title FROM cur WHERE cur_namespace=0 AND cur_is_redirect=0 AND cur_title NOT LIKE '%/%' AND cur_text NOT LIKE '%[[{$thelang}:%' LIMIT {$offset}, {$limit}";
+
+ $res = wfQuery( $sql, $fname );
+
+
+ $mll = wfMsg("missinglanguagelinkstext");
+ $mll = str_replace ( "$1" , $wgLang->getLanguageName($thelang) , $mll ) ;
+
+ $top = getMaintenancePageBacklink();
+ $top .= "<p>$mll</p><br>";
+ $top .= wfShowingResults( $offset, $limit );
+ $wgOut->addHTML( "<p>{$top}\n" );
+
+ $sl = wfViewPrevNext( $offset, $limit, "REPLACETHIS" ) ;
+ $sl = str_replace ( "REPLACETHIS" , sns().":Maintenance&subfunction=missinglanguagelinks&thelang={$thelang}" , $sl ) ;
+ $wgOut->addHTML( "<br>{$sl}\n" );
+
+ $sk = $wgUser->getSkin();
+ $s = "<ol start=" . ( $offset + 1 ) . ">";
+ while ( $obj = wfFetchObject( $res ) )
+ $s .= "<li>".$sk->makeKnownLink ( $obj->cur_title )."</li>\n" ;
+ wfFreeResult( $res );
+ $s .= "</ol>";
+ $wgOut->addHTML( $s );
+ $wgOut->addHTML( "<p>{$sl}\n" );
+}
+
+?>
diff --git a/includes/SpecialMovepage.php b/includes/SpecialMovepage.php
new file mode 100644
index 000000000000..2a67b9599193
--- /dev/null
+++ b/includes/SpecialMovepage.php
@@ -0,0 +1,424 @@
+<?
+
+function wfSpecialMovepage()
+{
+ global $wgUser, $wgOut, $action, $target;
+
+ if ( 0 == $wgUser->getID() or $wgUser->isBlocked() ) {
+ $wgOut->errorpage( "movenologin", "movenologintext" );
+ return;
+ }
+ if ( wfReadOnly() ) {
+ $wgOut->readOnlyPage();
+ return;
+ }
+ $fields = array( "wpNewTitle", "wpOldTitle" );
+ wfCleanFormFields( $fields );
+
+ $f = new MovePageForm();
+
+ if ( "success" == $action ) { $f->showSuccess(); }
+ else if ( "submit" == $action ) { $f->doSubmit(); }
+ else { $f->showForm( "" ); }
+}
+
+class MovePageForm {
+
+ var $ot, $nt; # Old, new Title objects
+ var $ons, $nns; # Namespaces
+ var $odt, $ndt; # Pagenames (dbkey form)
+ var $oft, $nft; # Full page titles (DBkey form)
+ var $ofx, $nfx; # Full page titles (Text form)
+ var $oldid, $newid; # "cur_id" field (yes, both from "cur")
+ var $talkmoved = 0;
+
+ function showForm( $err )
+ {
+ global $wgOut, $wgUser, $wgLang;
+ global $wpNewTitle, $wpOldTitle, $wpMovetalk, $target;
+
+ $wgOut->setPagetitle( wfMsg( "movepage" ) );
+
+ if ( ! $wpOldTitle ) {
+ $target = wfCleanQueryVar( $target );
+ if ( "" == $target ) {
+ $wgOut->errorpage( "notargettitle", "notargettext" );
+ return;
+ }
+ $wpOldTitle = $target;
+ }
+ $ot = Title::newFromURL( $wpOldTitle );
+ $ott = $ot->getPrefixedText();
+
+ $wgOut->addWikiText( wfMsg( "movepagetext" ) );
+ if ( ! Namespace::isTalk( $ot->getNamespace() ) )
+ $wgOut->addWikiText( "\n\n" . wfMsg( "movepagetalktext" ) );
+
+ $ma = wfMsg( "movearticle" );
+ $newt = wfMsg( "newtitle" );
+ $mpb = wfMsg( "movepagebtn" );
+ $movetalk = wfMsg( "movetalk" );
+
+ $action = wfLocalUrlE( $wgLang->specialPage( "Movepage" ),
+ "action=submit" );
+
+ if ( "" != $err ) {
+ $wgOut->setSubtitle( wfMsg( "formerror" ) );
+ $wgOut->addHTML( "<p><font color='red' size='+1'>{$err}</font>\n" );
+ }
+ $wgOut->addHTML( "<p>
+<form id=\"movepage\" method=\"post\" action=\"{$action}\">
+<table border=0><tr>
+<td align=right>{$ma}:</td>
+<td align=left><strong>{$ott}</strong></td>
+</tr><tr>
+<td align=right>{$newt}:</td>
+<td align=left>
+<input type=text size=40 name=\"wpNewTitle\" value=\"{$wpNewTitle}\">
+<input type=hidden name=\"wpOldTitle\" value=\"{$wpOldTitle}\">
+</td>
+</tr>" );
+
+ if ( ! Namespace::isTalk( $ot->getNamespace() ) ) {
+ $wgOut->addHTML(
+"<tr>
+<td align=right>
+<input type=checkbox name=\"wpMovetalk\" checked value=\"1\">
+</td><td>{$movetalk}</td>
+</tr>" );
+ }
+ $wgOut->addHTML(
+"<tr>
+<td>&nbsp;</td><td align=left>
+<input type=submit name=\"wpMove\" value=\"{$mpb}\">
+</td></tr></table>
+</form>\n" );
+
+ }
+
+ function doSubmit()
+ {
+ global $wgOut, $wgUser, $wgLang;
+ global $wpNewTitle, $wpOldTitle, $wpMovetalk, $target;
+ global $wgDeferredUpdateList;
+ $fname = "MovePageForm::doSubmit";
+
+ $this->ot = Title::newFromText( $wpOldTitle );
+ $this->nt = Title::newFromText( $wpNewTitle );
+ $this->ons = $this->ot->getNamespace();
+ $this->nns = $this->nt->getNamespace();
+ $this->odt = wfStrencode( $this->ot->getDBkey() );
+ $this->ndt = wfStrencode( $this->nt->getDBkey() );
+ $this->oft = wfStrencode( $this->ot->getPrefixedDBkey() );
+ $this->nft = wfStrencode( $this->nt->getPrefixedDBkey() );
+ $this->ofx = $this->ot->getPrefixedText();
+ $this->nfx = $this->nt->getPrefixedText();
+
+ $this->oldid = $this->ot->getArticleID();
+ $this->newid = $this->nt->getArticleID();
+
+ if ( strlen( trim( $this->ndt ) ) < 2 ) {
+ $this->showForm( wfMsg( "articleexists" ) );
+ return;
+ }
+ if ( ( ! Namespace::isMovable( $this->ons ) ) ||
+ ( "" == $this->odt ) ||
+ ( "" != $this->ot->getInterwiki() ) ||
+ ( ! Namespace::isMovable( $nns ) ) ||
+ ( "" == $this->ndt ) ||
+ ( "" != $this->nt->getInterwiki() ) ) {
+ $this->showForm( wfMsg( "badarticleerror" ) );
+ return;
+ }
+ # The move is allowed only if (1) the target doesn't exist, or
+ # (2) the target is a redirect to the source, and has no history
+ # (so we can undo bad moves right after they're done).
+
+ if ( 0 != $this->newid ) { # Target exists; check for validity
+ if ( ! $this->isValidTarget() ) {
+ $this->showForm( wfMsg( "articleexists" ) );
+ return;
+ }
+ $this->moveOverExistingRedirect();
+ } else { # Target didn't exist, do normal move.
+ $this->moveToNewTitle();
+ }
+
+ $this->updateWatchlists();
+
+ $u = new SearchUpdate( $this->oldid, $this->nt->getPrefixedDBkey() );
+ $u->doUpdate();
+ $u = new SearchUpdate( $this->newid, $this->ot->getPrefixedDBkey(), "" );
+ $u->doUpdate();
+
+ # Move talk page if (1) the checkbox says to, (2) the source
+ # and target namespaces are identical, (3) the namespaces are not
+ # themselves talk namespaces, and of course (4) it exists.
+
+ if ( ( 1 == $wpMovetalk ) &&
+ ( ! Namespace::isTalk( $this->ons ) ) &&
+ ( $this->ons == $this->nns ) ) {
+
+ $this->ons = $this->nns = Namespace::getTalk( $this->ons );
+
+ $this->ot = Title::newFromText( Title::makeName(
+ $this->ons, $wpOldTitle ) );
+ $this->nt = Title::newFromText( Title::makeName(
+ $this->nns, $wpNewTitle ) );
+
+ # odt, ndt, ofx, nfx remain the same
+
+ $this->oft = wfStrencode( $this->ot->getPrefixedDBkey() );
+ $this->nft = wfStrencode( $this->nt->getPrefixedDBkey() );
+
+ $this->oldid = $this->ot->getArticleID();
+ $this->newid = $this->nt->getArticleID();
+
+ if ( 0 != $this->oldid ) {
+ if ( 0 != $this->newid ) {
+ if ( $this->isValidTarget() ) {
+ $this->moveOverExistingRedirect();
+ $this->talkmoved = 1;
+ } else {
+ $this->talkmoved = 'invalid';
+ }
+ } else {
+ $this->moveToNewTitle();
+ $this->talkmoved = 1;
+ }
+ $u = new SearchUpdate( $this->oldid, $this->nt->getPrefixedDBkey() );
+ $u->doUpdate();
+ $u = new SearchUpdate( $this->newid, $this->ot->getPrefixedDBkey(), "" );
+ $u->doUpdate();
+ }
+ }
+ $success = wfLocalUrl( $wgLang->specialPage( "Movepage" ),
+ "action=success&oldtitle=" . wfUrlencode( $this->ofx ) .
+ "&newtitle=" . wfUrlencode( $this->nfx ) .
+ "&talkmoved={$this->talkmoved}" );
+
+ $wgOut->redirect( $success );
+ }
+
+ function showSuccess()
+ {
+ global $wgOut, $wgUser;
+ global $newtitle, $oldtitle, $talkmoved;
+
+ $wgOut->setPagetitle( wfMsg( "movepage" ) );
+ $wgOut->setSubtitle( wfMsg( "pagemovedsub" ) );
+
+ $fields = array( "oldtitle", "newtitle" );
+ wfCleanFormFields( $fields );
+
+ $text = str_replace( "$1", $oldtitle, wfMsg( "pagemovedtext" ) );
+ $text = str_replace( "$2", $newtitle, $text );
+ $wgOut->addWikiText( $text );
+
+ if ( 1 == $talkmoved ) {
+ $wgOut->addHTML( "\n<p>" . wfMsg( "talkpagemoved" ) );
+ } elseif( 'invalid' == $talkmoved ) {
+ $wgOut->addHTML( "\n<p><strong>" . wfMsg( "talkexists" ) . "</strong>" );
+ } else {
+ $ot = Title::newFromURL( $oldtitle );
+ if ( ! Namespace::isTalk( $ot->getNamespace() ) ) {
+ $wgOut->addHTML( "\n<p>" . wfMsg( "talkpagenotmoved" ) );
+ }
+ }
+ }
+
+ # Is the the existing target title valid?
+
+ function isValidTarget()
+ {
+ $fname = "MovePageForm::isValidTarget";
+
+ $sql = "SELECT cur_is_redirect,cur_text FROM cur " .
+ "WHERE cur_id={$this->newid}";
+ $res = wfQuery( $sql, $fname );
+ $obj = wfFetchObject( $res );
+
+ if ( 0 == $obj->cur_is_redirect ) { return false; }
+
+ if ( preg_match( "/\\[\\[\\s*([^\\]]*)]]/", $obj->cur_text, $m ) ) {
+ $rt = Title::newFromText( $m[1] );
+ if ( 0 != strcmp( wfStrencode( $rt->getPrefixedDBkey() ),
+ $this->oft ) ) {
+ return false;
+ }
+ }
+ $sql = "SELECT old_id FROM old WHERE old_namespace={$this->nns} " .
+ "AND old_title='{$this->ndt}'";
+ $res = wfQuery( $sql, $fname );
+ if ( 0 != wfNumRows( $res ) ) { return false; }
+
+ return true;
+ }
+
+ # Move page to title which is presently a redirect to the source
+ # page. Handling link tables here is tricky.
+
+ function moveOverExistingRedirect()
+ {
+ global $wgUser;
+ $fname = "MovePageForm::moveOverExistingRedirect";
+ $mt = wfMsg( "movedto" );
+
+ $now = wfTimestampNow();
+ $sql = "UPDATE cur SET cur_touched='{$now}'," .
+ "cur_namespace={$this->nns},cur_title='{$this->ndt}' " .
+ "WHERE cur_id={$this->oldid}";
+ wfQuery( $sql, $fname );
+
+ $sql = "UPDATE cur SET cur_touched='{$now}'," .
+ "cur_namespace={$this->ons},cur_title='{$this->odt}'," .
+ "cur_text='#REDIRECT [[{$this->nft}]]\n',cur_comment='" .
+ "{$mt} \\\"{$this->nft}\\\"',cur_user='" . $wgUser->getID() .
+ "',cur_minor_edit=0,cur_counter=0,cur_restrictions=''," .
+ "cur_user_text='" . wfStrencode( $wgUser->getName() ) . "'," .
+ "cur_is_redirect=1,cur_is_new=0 WHERE cur_id={$this->newid}";
+ wfQuery( $sql, $fname );
+
+ $sql = "UPDATE old SET " .
+ "old_namespace={$this->nns},old_title='{$this->ndt}' WHERE " .
+ "old_namespace={$this->ons} AND old_title='{$this->odt}'";
+ wfQuery( $sql, $fname );
+
+ $sql = "UPDATE recentchanges SET ".
+ "rc_namespace={$this->nns}, rc_title='{$this->ndt}' WHERE ".
+ "rc_cur_id={$this->oldid}";
+ wfQuery( $sql, $fname );
+
+ $sql = "INSERT INTO recentchanges (rc_namespace,rc_title,
+ rc_comment,rc_user,rc_user_text,rc_timestamp,
+ rc_cur_time,rc_cur_id,rc_new)
+ VALUES ({$this->ons},'{$this->odt}'," .
+ "'{$mt} \\\"{$this->nft}\\\"','" .
+ $wgUser->getID() . "','" . wfStrencode( $wgUser->getName() ) .
+ "','{$now}','{$now}',{$this->newid},1)";
+ wfQuery( $sql, $fname );
+
+ # The only link from here should be the old redirect
+
+ $sql = "DELETE FROM links WHERE l_from='{$this->nft}'";
+ wfQuery( $sql, $fname );
+
+ $sql = "UPDATE links SET l_from='{$this->nft}' WHERE l_from='{$this->oft}'";
+ wfQuery( $sql, $fname );
+
+ # Swap links. Using MAXINT as a temp; if there's ever an article
+ # with id 4294967295, this will fail, but I think that's pretty safe
+
+ $sql = "UPDATE links SET l_to=4294967295 WHERE l_to={$this->oldid}";
+ wfQuery( $sql, $fname );
+
+ $sql = "UPDATE links SET l_to={$this->oldid} WHERE l_to={$this->newid}";
+ wfQuery( $sql, $fname );
+
+ $sql = "UPDATE links SET l_to={$this->newid} WHERE l_to=4294967295";
+ wfQuery( $sql, $fname );
+
+ # Note: the insert below must be after the updates above!
+
+ $sql = "INSERT INTO links (l_from,l_to) VALUES ('{$this->oft}',{$this->oldid})";
+ wfQuery( $sql, $fname );
+
+ $sql = "UPDATE imagelinks SET il_from='{$this->nft}' WHERE il_from='{$this->oft}'";
+ wfQuery( $sql, $fname );
+ }
+
+ # Move page to non-existing title.
+
+ function moveToNewTitle()
+ {
+ global $wgUser;
+ $fname = "MovePageForm::moveToNewTitle";
+ $mt = wfMsg( "movedto" );
+
+ $sql = "UPDATE cur SET cur_touched='{$now}'," .
+ "cur_namespace={$this->nns},cur_title='{$this->ndt}' " .
+ "WHERE cur_id={$this->oldid}";
+ wfQuery( $sql, $fname );
+
+ $now = wfTimestampNow();
+ $won = wfInvertTimestamp( $now );
+ $common = "{$this->ons},'{$this->odt}'," .
+ "'{$mt} \\\"{$this->nft}\\\"','" .
+ $wgUser->getID() . "','" . wfStrencode( $wgUser->getName() ) .
+ "','{$now}'";
+ $sql = "INSERT INTO cur (cur_namespace,cur_title," .
+ "cur_comment,cur_user,cur_user_text,cur_timestamp,inverse_timestamp," .
+ "cur_touched,cur_text,cur_is_redirect,cur_is_new) " .
+ "VALUES ({$common},'{$won}','{$now}','#REDIRECT [[{$this->nft}]]\n',1,1)";
+ wfQuery( $sql, $fname );
+ $this->newid = wfInsertId();
+
+ $sql = "UPDATE old SET " .
+ "old_namespace={$this->nns},old_title='{$this->ndt}' WHERE " .
+ "old_namespace={$this->ons} AND old_title='{$this->odt}'";
+ wfQuery( $sql, $fname );
+
+ $sql = "UPDATE recentchanges SET ".
+ "rc_namespace={$this->nns}, rc_title='{$this->ndt}' WHERE ".
+ "rc_namespace={$this->ons} AND rc_title='{$this->odt}'";
+ wfQuery( $sql, $fname );
+
+ $sql = "INSERT INTO recentchanges (rc_namespace,rc_title,
+ rc_comment,rc_user,rc_user_text,rc_timestamp,
+ rc_cur_time,rc_cur_id,rc_new)
+ VALUES ({$common},'{$now}',{$this->newid},1)";
+ wfQuery( $sql, $fname );
+
+ $sql = "UPDATE links SET l_from='{$this->nft}' WHERE l_from='{$this->oft}'";
+ wfQuery( $sql, $fname );
+
+ $sql = "UPDATE links SET l_to={$this->newid} WHERE l_to={$this->oldid}";
+ wfQuery( $sql, $fname );
+
+ $sql = "INSERT INTO links (l_from,l_to) VALUES ('{$this->oft}',{$this->oldid})";
+ wfQuery( $sql, $fname );
+
+ # Non-existent target may have had broken links to it; these must
+ # now be removed and made into good links.
+
+ $sql = "SELECT bl_from FROM brokenlinks WHERE bl_to='{$this->nft}'";
+ $res = wfQuery( $sql, $fname );
+
+ while ( $rec = wfFetchObject( $res ) ) {
+ $lid = $rec->bl_from;
+ $lt = wfStrencode( Article::nameOf( $lid ) );
+ $sql = "INSERT INTO links (l_from,l_to) VALUES ('{$lt}',$this->oldid)";
+ wfQuery( $sql, $fname );
+ }
+ $sql = "DELETE FROM brokenlinks WHERE bl_to='{$this->nft}'";
+ wfQuery( $sql, $fname );
+
+ $sql = "UPDATE imagelinks SET il_from='{$this->nft}' WHERE il_from='{$this->oft}'";
+ wfQuery( $sql, $fname );
+ }
+
+ function updateWatchlists()
+ {
+ $oldnamespace = $this->ons & ~1;
+ $newnamespace = $this->nns & ~1;
+ $oldtitle = $this->odt;
+ $newtitle = $this->ndt;
+
+ if( $oldnamespace == $newnamespace and $oldtitle == $newtitle )
+ return;
+
+ $sql = "SELECT wl_user FROM watchlist
+ WHERE wl_namespace={$oldnamespace} AND wl_title='{$oldtitle}'";
+ $res = wfQuery( $sql, $fname );
+ if( $s = wfFetchObject( $res ) ) {
+ $sql = "REPLACE INTO watchlist (wl_user,wl_namespace,wl_title)
+ VALUES ({$s->wl_user},{$newnamespace},'{$newtitle}')";
+ while( $s = wfFetchObject( $res ) ) {
+ $sql .= ",({$s->wl_user},{$newnamespace},'{$newtitle}')";
+ }
+ wfQuery( $sql, $fname );
+ }
+ }
+
+}
+?>
diff --git a/includes/SpecialNeglectedpages.php b/includes/SpecialNeglectedpages.php
new file mode 100644
index 000000000000..42d40069f8d6
--- /dev/null
+++ b/includes/SpecialNeglectedpages.php
@@ -0,0 +1,13 @@
+<?
+# Suggestion from mailing list: lists pages in order
+# least recently reviewed.
+#
+
+function wfSpecialNeglectedpages()
+{
+ global $wgUser, $wgOut;
+
+ $wgOut->addHTML( "<p>(TODO: neglected pages)" );
+}
+
+?>
diff --git a/includes/SpecialNewpages.php b/includes/SpecialNewpages.php
new file mode 100644
index 000000000000..47e09d621219
--- /dev/null
+++ b/includes/SpecialNewpages.php
@@ -0,0 +1,54 @@
+<?
+
+function wfSpecialNewpages()
+{
+ global $wgUser, $wgOut, $wgLang, $wgTitle;
+ global $limit, $offset; # From query string
+ $fname = "wfSpecialNewpages";
+
+ if ( ! $limit ) {
+ $limit = $wgUser->getOption( "rclimit" );
+ if ( ! $limit ) { $limit = 50; }
+ }
+ if ( ! $offset ) { $offset = 0; }
+
+# $sql = "SELECT cur_title,cur_user,cur_user_text,cur_comment," .
+# "cur_timestamp FROM cur " .
+# "WHERE cur_is_new=1 AND cur_namespace=0 AND cur_is_redirect=0 " .
+# " ORDER BY cur_timestamp DESC LIMIT {$offset}, {$limit}";
+ $sql = "SELECT rc_title AS cur_title,rc_user AS cur_user,rc_user_text AS cur_user_text,cur_comment," .
+ "rc_timestamp AS cur_timestamp FROM recentchanges,cur " .
+ "WHERE rc_cur_id=cur_id AND rc_new=1 AND rc_namespace=0 AND cur_text NOT LIKE '#REDIRECT%' " .
+ " ORDER BY rc_timestamp DESC LIMIT {$offset}, {$limit}";
+ $res = wfQuery( $sql, $fname );
+
+ $top = wfShowingResults( $offset, $limit );
+ $wgOut->addHTML( "<p>{$top}\n" );
+
+ $sl = wfViewPrevNext( $offset, $limit,
+ $wgLang->specialPage( "Newpages" ) );
+ $wgOut->addHTML( "<br>{$sl}\n" );
+
+ $sk = $wgUser->getSkin();
+ $s = "<ol start=" . ( $offset + 1 ) . ">";
+ while ( $obj = wfFetchObject( $res ) ) {
+ $u = $obj->cur_user;
+ $ut = $obj->cur_user_text;
+ $c = wfEscapeHTML( $obj->cur_comment );
+ if ( 0 == $u ) { $ul = $ut; }
+ else { $ul = $sk->makeLink( $wgLang->getNsText(2).":{$ut}", $ut ); }
+
+ $d = $wgLang->timeanddate( $obj->cur_timestamp, true );
+ $link = $sk->makeKnownLink( $obj->cur_title, "" );
+ $s .= "<li>{$d} {$link} . . {$ul}";
+
+ if ( "" != $c && "*" != $c ) { $s .= " <em>({$c})</em>"; }
+ $s .= "</li>\n";
+ }
+ wfFreeResult( $res );
+ $s .= "</ol>";
+ $wgOut->addHTML( $s );
+ $wgOut->addHTML( "<p>{$sl}\n" );
+}
+
+?>
diff --git a/includes/SpecialPopularpages.php b/includes/SpecialPopularpages.php
new file mode 100644
index 000000000000..387ecbe6a7ee
--- /dev/null
+++ b/includes/SpecialPopularpages.php
@@ -0,0 +1,47 @@
+<?
+
+function wfSpecialPopularpages()
+{
+ global $wgUser, $wgOut, $wgLang, $wgTitle;
+ global $limit, $offset; # From query string
+ $fname = "wfSpecialPopularpages";
+
+ global $wgMiserMode;
+ if ( $wgMiserMode ) {
+ $wgOut->addWikiText( wfMsg( "perfdisabled" ) );
+ return;
+ }
+
+ if ( ! $limit ) {
+ $limit = $wgUser->getOption( "rclimit" );
+ if ( ! $limit ) { $limit = 50; }
+ }
+ if ( ! $offset ) { $offset = 0; }
+
+ $sql = "SELECT DISTINCT cur_title, cur_counter FROM cur " .
+ "WHERE cur_namespace=0 AND cur_is_redirect=0 ORDER BY " .
+ "cur_counter DESC LIMIT {$offset}, {$limit}";
+ $res = wfQuery( $sql, $fname );
+
+ $sk = $wgUser->getSkin();
+
+ $top = wfShowingResults( $offset, $limit );
+ $wgOut->addHTML( "<p>{$top}\n" );
+
+ $sl = wfViewPrevNext( $offset, $limit,
+ $wgLang->specialPage( "Popularpages" ) );
+ $wgOut->addHTML( "<br>{$sl}\n" );
+
+ $s = "<ol start=" . ( $offset + 1 ) . ">";
+ while ( $obj = wfFetchObject( $res ) ) {
+ $nv = str_replace( "$1", $obj->cur_counter, wfMsg( "nviews" ) );
+ $link = $sk->makeKnownLink( $obj->cur_title, "" );
+ $s .= "<li>{$link} ({$nv})</li>\n";
+ }
+ wfFreeResult( $res );
+ $s .= "</ol>";
+ $wgOut->addHTML( $s );
+ $wgOut->addHTML( "<p>{$sl}\n" );
+}
+
+?>
diff --git a/includes/SpecialPreferences.php b/includes/SpecialPreferences.php
new file mode 100644
index 000000000000..fb0dfac11c30
--- /dev/null
+++ b/includes/SpecialPreferences.php
@@ -0,0 +1,260 @@
+<?
+function wfSpecialPreferences()
+{
+ global $wgUser, $wgOut, $action;
+ global $wpSaveprefs, $wpReset;
+
+ $fields = array( "wpOldpass", "wpNewpass", "wpRetype",
+ "wpEmail", "wpNick" );
+ wfCleanFormFields( $fields );
+
+ if ( 0 == $wgUser->getID() ) {
+ $wgOut->errorpage( "prefsnologin", "prefsnologintext" );
+ return;
+ }
+ if ( wfReadOnly() ) {
+ $wgOut->readOnlyPage();
+ return;
+ }
+ if ( isset( $wpReset ) ) {
+ resetPrefs();
+ mainPrefsForm( WfMsg( "prefsreset" ) );
+ } else if ( "submit" == $action || isset( $wpSaveprefs ) ) {
+ savePreferences();
+ } else {
+ resetPrefs();
+ mainPrefsForm( "" );
+ }
+}
+
+/* private */ function savePreferences()
+{
+ global $wgUser, $wgLang, $wgDeferredUpdateList;
+ global $wpQuickbar, $wpOldpass, $wpNewpass, $wpRetype;
+ global $wpSkin, $wpMath, $wpEmail, $wpEmailFlag, $wpNick, $wpSearch, $wpRecent;
+ global $wpSearchLines, $wpSearchChars, $wpStubs;
+ global $wpRows, $wpCols, $wpHourDiff, $HTTP_POST_VARS;
+
+ if ( "" != $wpNewpass ) {
+ if ( $wpNewpass != $wpRetype ) {
+ mainPrefsForm( wfMsg( "badretype" ) );
+ return;
+ }
+ $ep = User::encryptPassword( $wpOldpass );
+ if ( $ep != $wgUser->getPassword() ) {
+ if ( $ep != $wgUser->getNewpassword() ) {
+ mainPrefsForm( wfMsg( "wrongpassword" ) );
+ return;
+ }
+ }
+ $wgUser->setPassword( $wpNewpass );
+ }
+ $wgUser->setEmail( $wpEmail );
+ $wgUser->setOption( "nickname", $wpNick );
+ $wgUser->setOption( "quickbar", $wpQuickbar );
+ $wgUser->setOption( "skin", $wpSkin );
+ $wgUser->setOption( "math", $wpMath );
+ $wgUser->setOption( "searchlimit", $wpSearch );
+ $wgUser->setOption( "contextlines", $wpSearchLines );
+ $wgUser->setOption( "contextchars", $wpSearchChars );
+ $wgUser->setOption( "rclimit", $wpRecent );
+ $wgUser->setOption( "rows", $wpRows );
+ $wgUser->setOption( "cols", $wpCols );
+ $wgUser->setOption( "stubthreshold", $wpStubs );
+ $wgUser->setOption( "timecorrection", $wpHourDiff );
+
+ if ( $wpEmailFlag ) { $wgUser->setOption( "disablemail", 1 ); }
+ else { $wgUser->setOption( "disablemail", 0 ); }
+
+ $togs = $wgLang->getUserToggles();
+ foreach ( $togs as $tname => $ttext ) {
+ if ( array_key_exists( "wpOp$tname", $HTTP_POST_VARS ) ) {
+ $wgUser->setOption( $tname, 1 );
+ } else {
+ $wgUser->setOption( $tname, 0 );
+ }
+ }
+ $wgUser->setCookies();
+ $up = new UserUpdate();
+ array_push( $wgDeferredUpdateList, $up );
+ mainPrefsForm( wfMsg( "savedprefs" ) );
+}
+
+/* private */ function resetPrefs()
+{
+ global $wgUser, $wgLang;
+ global $wpQuickbar, $wpOldpass, $wpNewpass, $wpRetype, $wpStubs;
+ global $wpRows, $wpCols, $wpSkin, $wpMath, $wpEmail, $wpEmailFlag, $wpNick;
+ global $wpSearch, $wpRecent, $HTTP_POST_VARS;
+ global $wpHourDiff, $wpSearchLines, $wpSearchChars;
+
+ $wpOldpass = $wpNewpass = $wpRetype = "";
+ $wpEmail = $wgUser->getEmail();
+ if ( 1 == $wgUser->getOption( "disablemail" ) ) { $wpEmailFlag = 1; }
+ else { $wpEmailFlag = 0; }
+ $wpNick = $wgUser->getOption( "nickname" );
+
+ $wpQuickbar = $wgUser->getOption( "quickbar" );
+ $wpSkin = $wgUser->getOption( "skin" );
+ $wpMath = $wgUser->getOption( "math" );
+ $wpRows = $wgUser->getOption( "rows" );
+ $wpCols = $wgUser->getOption( "cols" );
+ $wpStubs = $wgUser->getOption( "stubthreshold" );
+ $wpHourDiff = $wgUser->getOption( "timecorrection" );
+ $wpSearch = $wgUser->getOption( "searchlimit" );
+ $wpSearchLines = $wgUser->getOption( "contextlines" );
+ $wpSearchChars = $wgUser->getOption( "contextchars" );
+ $wpRecent = $wgUser->getOption( "rclimit" );
+
+ $togs = $wgLang->getUserToggles();
+ foreach ( $togs as $tname => $ttext ) {
+ $HTTP_POST_VARS["wpOp$tname"] = $wgUser->getOption( $tname );
+ }
+}
+
+/* private */ function mainPrefsForm( $err )
+{
+ global $wgUser, $wgOut, $wgLang;
+ global $wpQuickbar, $wpOldpass, $wpNewpass, $wpRetype;
+ global $wpSkin, $wpMath, $wpEmail, $wpEmailFlag, $wpNick, $wpSearch, $wpRecent;
+ global $wpRows, $wpCols, $wpSaveprefs, $wpReset, $wpHourDiff;
+ global $wpSearchLines, $wpSearchChars, $wpStubs;
+
+ $wgOut->setPageTitle( wfMsg( "preferences" ) );
+ $wgOut->setArticleFlag( false );
+ $wgOut->setRobotpolicy( "noindex,nofollow" );
+
+ if ( "" != $err ) {
+ $wgOut->addHTML( "<font size='+1' color='red'>$err</font>\n<p>" );
+ }
+ $uname = $wgUser->getName();
+ $uid = $wgUser->getID();
+
+ $wgOut->addHTML( "<p>" . str_replace( array("$1","$2"), array($uname,$uid),
+ wfMsg( "prefslogintext" ) ) . "\n" );
+
+ $qbs = $wgLang->getQuickbarSettings();
+ $skins = $wgLang->getSkinNames();
+ $mathopts = $wgLang->getMathNames();
+ $togs = $wgLang->getUserToggles();
+
+ $action = wfLocalUrlE( $wgLang->specialPage( "Preferences" ),
+ "action=submit" );
+ $qb = wfMsg( "qbsettings" );
+ $cp = wfMsg( "changepassword" );
+ $sk = wfMsg( "skin" );
+ $math = wfMsg( "math" );
+ $opw = wfMsg( "oldpassword" );
+ $npw = wfMsg( "newpassword" );
+ $rpw = wfMsg( "retypenew" );
+ $svp = wfMsg( "saveprefs" );
+ $rsp = wfMsg( "resetprefs" );
+ $tbs = wfMsg( "textboxsize" );
+ $tbr = wfMsg( "rows" );
+ $tbc = wfMsg( "columns" );
+ $ltz = wfMsg( "localtime" );
+ $tzt = wfMsg( "timezonetext" );
+ $tzo = wfMsg( "timezoneoffset" );
+ $yem = wfMsg( "youremail" );
+ $emf = wfMsg( "emailflag" );
+ $ynn = wfMsg( "yournick" );
+ $stt = wfMsg ( "stubthreshold" ) ;
+ $srh = wfMsg( "searchresultshead" );
+ $rpp = wfMsg( "resultsperpage" );
+ $scl = wfMsg( "contextlines" );
+ $scc = wfMsg( "contextchars" );
+ $rcc = wfMsg( "recentchangescount" );
+
+ $wgOut->addHTML( "<form id=\"preferences\" action=\"$action\"
+method=\"post\"><table border=\"1\"><tr><td valign=top nowrap><b>$qb:</b><br>\n" );
+
+ # Quickbar setting
+ #
+ for ( $i = 0; $i < count( $qbs ); ++$i ) {
+ if ( $i == $wpQuickbar ) { $checked = " checked"; }
+ else { $checked = ""; }
+ $wgOut->addHTML( "<input type=radio name=\"wpQuickbar\"
+value=\"$i\"$checked> {$qbs[$i]}<br>\n" );
+ }
+
+ # Fields for changing password
+ #
+ $wpOldpass = wfEscapeHTML( $wpOldpass );
+ $wpNewpass = wfEscapeHTML( $wpNewpass );
+ $wpRetype = wfEscapeHTML( $wpRetype );
+
+ $wgOut->addHTML( "</td><td vaign=top nowrap><b>$cp:</b><br>
+$opw: <input type=password name=\"wpOldpass\" value=\"$wpOldpass\" size=20><br>
+$npw: <input type=password name=\"wpNewpass\" value=\"$wpNewpass\" size=20><br>
+$rpw: <input type=password name=\"wpRetype\" value=\"$wpRetype\" size=20><br>
+</td></tr>\n" );
+
+ # Skin setting
+ #
+ $wgOut->addHTML( "<tr><td valign=top nowrap><b>$sk:</b><br>\n" );
+ for ( $i = 0; $i < count( $skins ); ++$i ) {
+ if ( $i == $wpSkin ) { $checked = " checked"; }
+ else { $checked = ""; }
+ $wgOut->addHTML( "<input type=radio name=\"wpSkin\"
+value=\"$i\"$checked> {$skins[$i]}<br>\n" );
+ }
+
+ # Various checkbox options
+ #
+ $wgOut->addHTML( "</td><td rowspan=2 valign=top nowrap>\n" );
+ foreach ( $togs as $tname => $ttext ) {
+ if ( 1 == $wgUser->getOption( $tname ) ) {
+ $checked = " checked";
+ } else {
+ $checked = "";
+ }
+ $wgOut->addHTML( "<input type=checkbox value=\"1\" "
+ . "name=\"wpOp$tname\"$checked>$ttext<br>\n" );
+ }
+ $wgOut->addHTML( "</td>" );
+
+ # Math setting
+ #
+ $wgOut->addHTML( "<tr><td valign=top nowrap><b>$math:</b><br>\n" );
+ for ( $i = 0; $i < count( $mathopts ); ++$i ) {
+ if ( $i == $wpMath ) { $checked = " checked"; }
+ else { $checked = ""; }
+ $wgOut->addHTML( "<input type=radio name=\"wpMath\"
+value=\"$i\"$checked> {$mathopts[$i]}<br>\n" );
+ }
+
+ $wgOut->addHTML( "</td></tr><tr>" );
+
+ # Textbox rows, cols
+ #
+ $wgOut->addHTML( "<td valign=top nowrap><b>$tbs:</b><br>
+$tbr: <input type=text name=\"wpRows\" value=\"{$wpRows}\" size=6><br>
+$tbc: <input type=text name=\"wpCols\" value=\"{$wpCols}\" size=6><br><br>
+<b>$ltz</b><br>
+$tzo*: <input type=text name=\"wpHourDiff\" value=\"{$wpHourDiff}\" size=6>
+</td>" );
+
+ # Email, etc.
+ #
+ $wpEmail = wfEscapeHTML( $wpEmail );
+ $wpNick = wfEscapeHTML( $wpNick );
+ if ( $wpEmailFlag ) { $emfc = "checked"; }
+ else { $emfc = ""; }
+
+ $wgOut->addHTML( "<td valign=top nowrap>
+$yem: <input type=text name=\"wpEmail\" value=\"{$wpEmail}\" size=20><br>
+<input type=checkbox $emfc value=\"1\" name=\"wpEmailFlag\"> $emf<br>
+$ynn: <input type=text name=\"wpNick\" value=\"{$wpNick}\" size=12><br>
+$rcc: <input type=text name=\"wpRecent\" value=\"$wpRecent\" size=6><br>
+$stt: <input type=text name=\"wpStubs\" value=\"$wpStubs\" size=6><br>
+<strong>{$srh}:</strong><br>
+$rpp: <input type=text name=\"wpSearch\" value=\"$wpSearch\" size=6><br>
+$scl: <input type=text name=\"wpSearchLines\" value=\"$wpSearchLines\" size=6><br>
+$scc: <input type=text name=\"wpSearchChars\" value=\"$wpSearchChars\" size=6></td>
+</tr><tr>
+<td align=center><input type=submit name=\"wpSaveprefs\" value=\"$svp\"></td>
+<td align=center><input type=submit name=\"wpReset\" value=\"$rsp\"></td>
+</tr></table>* {$tzt} </form>\n" );
+}
+
+?>
diff --git a/includes/SpecialRandompage.php b/includes/SpecialRandompage.php
new file mode 100644
index 000000000000..b3110f653201
--- /dev/null
+++ b/includes/SpecialRandompage.php
@@ -0,0 +1,29 @@
+<?
+
+function wfSpecialRandompage()
+{
+ global $wgOut, $wgTitle, $wgArticle, $force;
+ $fname = "wfSpecialRandompage";
+
+ wfSeedRandom();
+ $sqlget = "SELECT cur_id,cur_title
+ FROM cur USE INDEX (cur_random)
+ WHERE cur_namespace=0 AND cur_is_redirect=0
+ AND cur_random>RAND()
+ ORDER BY cur_random
+ LIMIT 1";
+ $res = wfQuery( $sqlget, $fname );
+ if( $s = wfFetchObject( $res ) ) {
+ $sql = "UPDATE cur SET cur_random=RAND() WHERE cur_id={$s->cur_id}";
+ wfQuery( $sql, $fname );
+ $rt = wfUrlEncode( $s->cur_title );
+ } else {
+ # No articles?!
+ $rt = "";
+ }
+
+ $wgOut->reportTime(); # for logfile
+ $wgOut->redirect( wfLocalUrl( $rt ) );
+}
+
+?>
diff --git a/includes/SpecialRecentchanges.php b/includes/SpecialRecentchanges.php
new file mode 100644
index 000000000000..1412193bc72d
--- /dev/null
+++ b/includes/SpecialRecentchanges.php
@@ -0,0 +1,172 @@
+<?
+
+function wfSpecialRecentchanges()
+{
+ global $wgUser, $wgOut, $wgLang, $wgTitle;
+ global $days, $limit, $hideminor, $from, $hidebots; # From query string
+ $fname = "wfSpecialRecentchanges";
+
+ $sql = "SELECT MAX(rc_timestamp) AS lastmod FROM recentchanges";
+ $res = wfQuery( $sql, $fname );
+ $s = wfFetchObject( $res );
+ $wgOut->checkLastModified( $s->lastmod );
+
+ $rctext = wfMsg( "recentchangestext" );
+ $sql = "SELECT cur_text FROM cur WHERE cur_namespace=4 AND cur_title='Recentchanges'";
+ $res = wfQuery( $sql, $fname );
+ if( ( $s = wfFetchObject( $res ) ) and ( $s->cur_text != "" ) ) {
+ $rctext = $s->cur_text;
+ }
+ $wgOut->addWikiText( $rctext );
+
+ if ( ! $days ) {
+ $days = $wgUser->getOption( "rcdays" );
+ if ( ! $days ) { $days = 3; }
+ }
+ if ( ! $limit ) {
+ $limit = $wgUser->getOption( "rclimit" );
+ if ( ! $limit ) { $limit = 100; }
+ }
+ $cutoff = date( "YmdHis", time() - ( $days * 86400 ) );
+ if(preg_match('/^[0-9]{14}$/', $from) and $from > $cutoff) {
+ $cutoff = $from;
+ } else {
+ unset($from);
+ }
+
+ $sk = $wgUser->getSkin();
+
+ if ( ! isset( $hideminor ) ) {
+ $hideminor = $wgUser->getOption( "hideminor" );
+ }
+ if ( $hideminor ) {
+ $hidem = "AND rc_minor=0";
+ $mlink = $sk->makeKnownLink( $wgLang->specialPage( "Recentchanges" ),
+ WfMsg( "show" ), "days={$days}&limit={$limit}&hideminor=0" );
+ } else {
+ $hidem = "";
+ $mlink = $sk->makeKnownLink( $wgLang->specialPage( "Recentchanges" ),
+ WfMsg( "hide" ), "days={$days}&limit={$limit}&hideminor=1" );
+ }
+
+ if ( !isset( $hidebots ) ) {
+ $hidebots = 1;
+ }
+ if( $hidebots ) {
+ $hidem .= " AND rc_bot=0";
+ }
+
+ $uid = $wgUser->getID();
+ $sql2 = "SELECT rc_cur_id,rc_namespace,rc_title,rc_user,rc_new," .
+ "rc_comment,rc_user_text,rc_timestamp,rc_minor,rc_this_oldid,rc_last_oldid,rc_bot" . ($uid ? ",wl_user" : "") . " FROM recentchanges " .
+ ($uid ? "LEFT OUTER JOIN watchlist ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace & 65534 " : "") .
+ "WHERE rc_timestamp > '{$cutoff}' {$hidem} " .
+ "ORDER BY rc_timestamp DESC LIMIT {$limit}";
+ $res = wfQuery( $sql2, $fname );
+
+ if(isset($from)) {
+ $note = str_replace( "$1", $limit, wfMsg( "rcnotefrom" ) );
+ $note = str_replace( "$2", $wgLang->timeanddate( $from, true ), $note );
+ } else {
+ $note = str_replace( "$1", $limit, wfMsg( "rcnote" ) );
+ $note = str_replace( "$2", $days, $note );
+ }
+ $wgOut->addHTML( "\n<hr>\n{$note}\n<br>" );
+
+ $note = rcDayLimitLinks( $days, $limit );
+
+ $now = date( "YmdHis" );
+ $note .= "<br>\n" . str_replace( "$1",
+ $sk->makeKnownLink( $wgLang->specialPage( "Recentchanges" ),
+ $wgLang->timeanddate( $now, true ), "from=$now" ),
+ wfMsg( "rclistfrom" ) );
+
+ $wgOut->addHTML( "{$note}\n" );
+
+ $count1 = wfNumRows( $res );
+ $obj1 = wfFetchObject( $res );
+
+ $s = $sk->beginRecentChangesList();
+ while ( $limit ) {
+ if ( ( 0 == $count1 ) ) { break; }
+
+ $ts = $obj1->rc_timestamp;
+ $u = $obj1->rc_user;
+ $ut = $obj1->rc_user_text;
+ $ns = $obj1->rc_namespace;
+ $ttl = $obj1->rc_title;
+ $com = $obj1->rc_comment;
+ $me = ( $obj1->rc_minor > 0 );
+ $new = ( $obj1->rc_new > 0 );
+ $watched = ($obj1->wl_user > 0);
+ $oldid = $obj1->rc_this_oldid ;
+ $diffid = $obj1->rc_last_oldid ;
+
+ $obj1 = wfFetchObject( $res );
+ --$count1;
+ if ( ! ( $hideminor && $me ) ) {
+ $s .= $sk->recentChangesLine( $ts, $u, $ut, $ns, $ttl,
+ $com, $me, $new, $watched, $oldid , $diffid );
+ --$limit;
+ }
+ }
+ $s .= $sk->endRecentChangesList();
+
+ wfFreeResult( $res );
+ $wgOut->addHTML( $s );
+}
+
+function rcCountLink( $lim, $d, $page="Recentchanges", $more="" )
+{
+ global $wgUser, $wgLang;
+ $sk = $wgUser->getSkin();
+ $s = $sk->makeKnownLink( $wgLang->specialPage( $page ),
+ ($lim ? "{$lim}" : wfMsg( "all" ) ), "{$more}" .
+ ($d ? "days={$d}&" : "") . "limit={$lim}" );
+ return $s;
+}
+
+function rcDaysLink( $lim, $d, $page="Recentchanges", $more="" )
+{
+ global $wgUser, $wgLang;
+ $sk = $wgUser->getSkin();
+ $s = $sk->makeKnownLink( $wgLang->specialPage( $page ),
+ ($d ? "{$d}" : wfMsg( "all" ) ), "{$more}days={$d}" .
+ ($lim ? "&limit={$lim}" : "") );
+ return $s;
+}
+
+function rcDayLimitLinks( $days, $limit, $page="Recentchanges", $more="", $doall = false )
+{
+ if ($more != "") $more .= "&";
+ $cl = rcCountLink( 50, $days, $page, $more ) . " | " .
+ rcCountLink( 100, $days, $page, $more ) . " | " .
+ rcCountLink( 250, $days, $page, $more ) . " | " .
+ rcCountLink( 500, $days, $page, $more ) .
+ ( $doall ? ( " | " . rcCountLink( 0, $days, $page, $more ) ) : "" );
+ $dl = rcDaysLink( $limit, 1, $page, $more ) . " | " .
+ rcDaysLink( $limit, 3, $page, $more ) . " | " .
+ rcDaysLink( $limit, 7, $page, $more ) . " | " .
+ rcDaysLink( $limit, 14, $page, $more ) . " | " .
+ rcDaysLink( $limit, 30, $page, $more ) .
+ ( $doall ? ( " | " . rcDaysLink( $limit, 0, $page, $more ) ) : "" );
+ $note = str_replace( "$1", $cl, wfMsg( "rclinks" ) );
+ $note = str_replace( "$2", $dl, $note );
+ $note = str_replace( "$3", $mlink, $note );
+ return $note;
+}
+
+function rcLimitLinks( $page="Recentchanges", $more="", $doall = false )
+{
+ if ($more != "") $more .= "&";
+ $cl = rcCountLink( 50, 0, $page, $more ) . " | " .
+ rcCountLink( 100, 0, $page, $more ) . " | " .
+ rcCountLink( 250, 0, $page, $more ) . " | " .
+ rcCountLink( 500, 0, $page, $more ) .
+ ( $doall ? ( " | " . rcCountLink( 0, $days, $page, $more ) ) : "" );
+ $note = str_replace( "$1", $cl, wfMsg( "rclinks" ) );
+ $note = str_replace( "$3", $mlink, $note );
+ return $note;
+}
+
+?>
diff --git a/includes/SpecialRecentchangeslinked.php b/includes/SpecialRecentchangeslinked.php
new file mode 100644
index 000000000000..6625173a842b
--- /dev/null
+++ b/includes/SpecialRecentchangeslinked.php
@@ -0,0 +1,89 @@
+<?
+global $IP;
+include_once( "$IP/SpecialRecentchanges.php" );
+
+function wfSpecialRecentchangeslinked()
+{
+ global $wgUser, $wgOut, $wgLang, $wgTitle;
+ global $days, $limit, $target, $hideminor; # From query string
+ $fname = "wfSpecialRecentchangeslinked";
+
+ $wgOut->setPagetitle( wfMsg( "recentchanges" ) );
+ $sk = $wgUser->getSkin();
+
+ if ( "" == $target ) {
+ $wgOut->errorpage( "notargettitle", "notargettext" );
+ return;
+ }
+ $nt = Title::newFromURL( $target );
+ $sub = str_replace( "$1", $nt->getPrefixedText(), wfMsg( "rclsub" ) );
+ $wgOut->setSubtitle( $sub );
+
+ if ( ! $days ) {
+ $days = $wgUser->getOption( "rcdays" );
+ if ( ! $days ) { $days = 7; }
+ }
+ if ( ! $limit ) {
+ $limit = $wgUser->getOption( "rclimit" );
+ if ( ! $limit ) { $limit = 100; }
+ }
+ $cutoff = date( "YmdHis", time() - ( $days * 86400 ) );
+
+ if ( ! isset( $hideminor ) ) {
+ $hideminor = $wgUser->getOption( "hideminor" );
+ }
+ if ( $hideminor ) {
+ $mlink = $sk->makeKnownLink( $wgLang->specialPage( "Recentchangeslinked" ),
+ WfMsg( "show" ), "target=" . wfEscapeHTML( $nt->getPrefixedURL() ) .
+ "&days={$days}&limit={$limit}&hideminor=0" );
+ } else {
+ $mlink = $sk->makeKnownLink( $wgLang->specialPage( "Recentchangeslinked" ),
+ WfMsg( "hide" ), "target=" . wfEscapeHTML( $nt->getPrefixedURL() ) .
+ "&days={$days}&limit={$limit}&hideminor=1" );
+ }
+ if ( $hideminor ) {
+ $cmq = "AND cur_minor_edit=0";
+ } else { $cmq = ""; }
+
+ $sql = "SELECT cur_id,cur_namespace,cur_title,cur_user,cur_comment," .
+ "cur_user_text,cur_timestamp,cur_minor_edit,cur_is_new FROM links, cur " .
+ "WHERE cur_timestamp > '{$cutoff}' {$cmq} AND l_to=cur_id AND l_from='" .
+ wfStrencode( $nt->getPrefixedDBkey() ) . "' GROUP BY cur_id " .
+ "ORDER BY inverse_timestamp LIMIT {$limit}";
+ $res = wfQuery( $sql, $fname );
+
+ $note = str_replace( "$1", $limit, wfMsg( "rcnote" ) );
+ $note = str_replace( "$2", $days, $note );
+ $wgOut->addHTML( "<hr>\n{$note}\n<br>" );
+
+ $tu = "target=" . $nt->getPrefixedURL();
+ $note = rcDayLimitlinks( $days, $limit, "Recentchangeslinked", $tu );
+ $wgOut->addHTML( "{$note}\n" );
+
+ $s = $sk->beginRecentChangesList();
+ $count = wfNumRows( $res );
+
+ while ( $limit ) {
+ if ( 0 == $count ) { break; }
+ $obj = wfFetchObject( $res );
+ --$count;
+
+ $ts = $obj->cur_timestamp;
+ $u = $obj->cur_user;
+ $ut = $obj->cur_user_text;
+ $ns = $obj->cur_namespace;
+ $ttl = $obj->cur_title;
+ $com = $obj->cur_comment;
+ $me = ( $obj->cur_minor_edit > 0 );
+ $new = ( $obj->cur_is_new > 0 );
+
+ $s .= $sk->recentChangesLine( $ts, $u, $ut, $ns, $ttl, $com, $me, $new );
+ --$limit;
+ }
+ $s .= $sk->endRecentChangesList();
+
+ wfFreeResult( $res );
+ $wgOut->addHTML( $s );
+}
+
+?>
diff --git a/includes/SpecialShortpages.php b/includes/SpecialShortpages.php
new file mode 100644
index 000000000000..57efef409bdf
--- /dev/null
+++ b/includes/SpecialShortpages.php
@@ -0,0 +1,46 @@
+<?
+
+function wfSpecialShortpages()
+{
+ global $wgUser, $wgOut, $wgLang, $wgTitle;
+ global $limit, $offset; # From query string
+ $fname = "wfSpecialShortpages";
+
+ global $wgMiserMode;
+ if ( $wgMiserMode ) {
+ $wgOut->addWikiText( wfMsg( "perfdisabled" ) );
+ return;
+ }
+
+ if ( ! $limit ) {
+ $limit = $wgUser->getOption( "rclimit" );
+ if ( ! $limit ) { $limit = 50; }
+ }
+ if ( ! $offset ) { $offset = 0; }
+
+ $sql = "SELECT cur_title, LENGTH(cur_text) AS len FROM cur " .
+ "WHERE cur_namespace=0 AND cur_is_redirect=0 ORDER BY " .
+ "LENGTH(cur_text) LIMIT {$offset}, {$limit}";
+ $res = wfQuery( $sql, $fname );
+
+ $top = wfShowingResults( $offset, $limit );
+ $wgOut->addHTML( "<p>{$top}\n" );
+
+ $sl = wfViewPrevNext( $offset, $limit,
+ $wgLang->specialPage( "Shortpages" ) );
+ $wgOut->addHTML( "<br>{$sl}\n" );
+
+ $sk = $wgUser->getSkin();
+ $s = "<ol start=" . ( $offset + 1 ) . ">";
+ while ( $obj = wfFetchObject( $res ) ) {
+ $nb = str_replace( "$1", $obj->len, wfMsg( "nbytes" ) );
+ $link = $sk->makeKnownLink( $obj->cur_title, "" );
+ $s .= "<li>{$link} ({$nb})</li>\n";
+ }
+ wfFreeResult( $res );
+ $s .= "</ol>";
+ $wgOut->addHTML( $s );
+ $wgOut->addHTML( "<p>{$sl}\n" );
+}
+
+?>
diff --git a/includes/SpecialSpecialpages.php b/includes/SpecialSpecialpages.php
new file mode 100644
index 000000000000..0488a27bf550
--- /dev/null
+++ b/includes/SpecialSpecialpages.php
@@ -0,0 +1,46 @@
+<?
+
+function wfSpecialSpecialpages()
+{
+ global $wgUser, $wgOut, $wgLang;
+
+ $wgOut->setRobotpolicy( "index,nofollow" );
+
+ $sk = $wgUser->getSkin();
+ $validSP = $wgLang->getValidSpecialPages();
+ $wgOut->addHTML( "<h2>" . wfMsg( "spheading" ) . "</h2>\n<ul>" );
+
+ foreach ( $validSP as $name => $desc ) {
+ if ( "" == $desc ) { continue; }
+ $link = $sk->makeKnownLink( $wgLang->specialPage( $name ), $desc );
+ $wgOut->addHTML( "<li>{$link}</li>\n" );
+ }
+ $wgOut->addHTML( "</ul>\n" );
+
+ if ( $wgUser->isSysop() ) {
+ $sysopSP = $wgLang->getSysopSpecialPages();
+ $wgOut->addHTML( "<h2>" . wfMsg( "sysopspheading" ) . "</h2>\n<ul>" );
+
+ foreach ( $sysopSP as $name => $desc ) {
+ if ( "" == $desc ) { continue; }
+ $link = $sk->makeKnownLink( $wgLang->specialPage( $name ), $desc );
+ $wgOut->addHTML( "<li>{$link}</li>\n" );
+ }
+ $wgOut->addHTML( "</ul>\n" );
+ }
+
+ if ( $wgUser->isDeveloper() ) {
+ $devSP = $wgLang->getDeveloperSpecialPages();
+ $wgOut->addHTML( "<h2>" . wfMsg( "developerspheading" ) .
+ "</h2>\n<ul>" );
+
+ foreach ( $devSP as $name => $desc ) {
+ if ( "" == $desc ) { continue; }
+ $link = $sk->makeKnownLink( $wgLang->specialPage( $name ), $desc );
+ $wgOut->addHTML( "<li>{$link}</li>\n" );
+ }
+ $wgOut->addHTML( "</ul>\n" );
+ }
+}
+
+?>
diff --git a/includes/SpecialStatistics.php b/includes/SpecialStatistics.php
new file mode 100644
index 000000000000..cbbbb64c1d9f
--- /dev/null
+++ b/includes/SpecialStatistics.php
@@ -0,0 +1,53 @@
+<?
+
+function wfSpecialStatistics()
+{
+ global $wgUser, $wgOut;
+ $fname = "wfSpecialStatistics";
+
+ $wgOut->addHTML( "<h2>" . wfMsg( "sitestats" ) . "</h2>\n" );
+
+ $sql = "SELECT COUNT(cur_id) AS total FROM cur";
+ $res = wfQuery( $sql, $fname );
+ $row = wfFetchObject( $res );
+ $total = $row->total;
+
+ $sql = "SELECT ss_total_views, ss_total_edits, ss_good_articles " .
+ "FROM site_stats WHERE ss_row_id=1";
+ $res = wfQuery( $sql, $fname );
+ $row = wfFetchObject( $res );
+ $views = $row->ss_total_views;
+ $edits = $row->ss_total_edits;
+ $good = $row->ss_good_articles;
+
+ $text = str_replace( "$1", $total, wfMsg( "sitestatstext" ) );
+ $text = str_replace( "$2", $good, $text );
+ $text = str_replace( "$3", $views, $text );
+ $text = str_replace( "$4", $edits, $text );
+ $text = str_replace( "$5", sprintf( "%.2f", $edits / $total ), $text );
+ $text = str_replace( "$6", sprintf( "%.2f", $views / $edits ), $text );
+
+ $wgOut->addHTML( $text );
+ $wgOut->addHTML( "<h2>" . wfMsg( "userstats" ) . "</h2>\n" );
+
+ $sql = "SELECT COUNT(user_id) AS total FROM user";
+ $res = wfQuery( $sql, $fname );
+ $row = wfFetchObject( $res );
+ $total = $row->total;
+
+ $sql = "SELECT COUNT(user_id) AS total FROM user " .
+ "WHERE user_rights <> ''";
+ $res = wfQuery( $sql, $fname );
+ $row = wfFetchObject( $res );
+ $admins = $row->total;
+
+ $sk = $wgUser->getSkin();
+ $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" );
+
+ $text = str_replace( "$1", $total, wfMsg( "userstatstext" ) );
+ $text = str_replace( "$2", $admins, $text );
+ $text = str_replace( "$3", $ap, $text );
+ $wgOut->addHTML( $text );
+}
+
+?>
diff --git a/includes/SpecialUndelete.php b/includes/SpecialUndelete.php
new file mode 100644
index 000000000000..8f786c75e374
--- /dev/null
+++ b/includes/SpecialUndelete.php
@@ -0,0 +1,161 @@
+<?
+
+function wfSpecialUndelete( )
+{
+ global $wgLang, $wgUser, $wgOut, $action, $target, $timestamp, $restore;
+
+ if( isset($target ) ) {
+ $t = Title::newFromURL( $target );
+ $title = $t->mDbkeyform;
+ $namespace = $t->mNamespace;
+ if( isset( $timestamp ) ) {
+ return doUndeleteShowRevision( $namespace, $title, $timestamp );
+ }
+ if( isset( $action ) and isset( $restore) and $action == "submit" ) {
+ return doUndeleteArticle( $namespace, $title );
+ }
+ return doUndeleteShowHistory( $namespace, $title );
+ }
+
+ # List undeletable articles
+ $sql = "SELECT ar_namespace,ar_title, COUNT(*) AS count FROM archive GROUP BY ar_namespace,ar_title ORDER BY ar_title";
+ $res = wfQuery( $sql );
+
+ $wgOut->setPagetitle( wfMsg( "undeletepage" ) );
+ $wgOut->addWikiText( wfMsg( "undeletepagetext" ) );
+
+ $special = $wgLang->getNsText( Namespace::getSpecial() );
+ $sk = $wgUser->getSkin();
+ $wgOut->addHTML( "<ul>\n" );
+ while ($row = wfFetchObject( $res )) {
+ $n = ($row->ar_namespace ?
+ ($wgLang->getNsText( $row->ar_namespace ) . ":") : "").
+ $row->ar_title;
+
+ $wgOut->addHTML( "<li>" .
+ $sk->makeKnownLink( $wgLang->specialPage( "Undelete" ),
+ $n, "target=" . urlencode($n) ) . " " .
+ str_replace( '$1', $row->count, wfMsg( "undeleterevisions" )) );
+ }
+ $wgOut->addHTML( "</ul>\n" );
+
+ return $ret;
+}
+
+/* private */ function doUndeleteShowRevision( $namespace, $title, $timestamp ) {
+ global $wgLang, $wgUser, $wgOut, $action, $target, $timestamp, $restore;
+
+ if(!preg_match("/[0-9]{14}/",$timestamp)) return 0;
+
+ $sql = "SELECT ar_text FROM archive WHERE ar_namespace={$namespace} AND ar_title=\"{$title}\" AND ar_timestamp={$timestamp}";
+ $ret = wfQuery( $sql );
+ $row = wfFetchObject( $ret );
+
+ $wgOut->setPagetitle( wfMsg( "undeletepage" ) );
+ $wgOut->addWikiText( "(" . str_replace("$1",
+ $wgLang->date($timestamp, true), wfMsg( "undeleterevision" ))
+ . ")\n<hr>\n" . $row->ar_text );
+
+ return 0;
+}
+
+/* private */ function doUndeleteShowHistory( $namespace, $title ) {
+ global $wgLang, $wgUser, $wgOut, $action, $target, $timestamp, $restore;
+
+ $sk = $wgUser->getSkin();
+ $wgOut->setPagetitle( wfMsg( "undeletepage" ) );
+ $wgOut->addWikiText( wfMsg( "undeletehistory" ) . "\n<hr>\n" . $row->ar_text );
+
+ $action = wfLocalUrlE( $wgLang->specialPage( "Undelete" ), "action=submit" );
+ $wgOut->addHTML("<p>
+<form id=\"undelete\" method=\"post\" action=\"{$action}\">
+<input type=hidden name=\"target\" value=\"{$target}\">
+<input type=submit name=\"restore\" value=\"".wfMsg("undeletebtn")."\">
+</form>");
+
+ $log = wfGetSQL("cur", "cur_text", "cur_namespace=4 AND cur_title=\"".wfMsg("dellogpage")."\"" );
+ if(preg_match("/^(.*".
+ preg_quote( ($namespace ? ($wgLang->getNsText($namespace) . ":") : "")
+ . str_replace("_", " ", $title), "/" ).".*)$/m", $log, $m)) {
+ $wgOut->addWikiText( $m[1] );
+ }
+
+ $sql = "SELECT ar_minor_edit,ar_timestamp,ar_user,ar_user_text,ar_comment
+ FROM archive WHERE ar_namespace={$namespace} AND ar_title=\"{$title}\"
+ ORDER BY ar_timestamp DESC";
+ $ret = wfQuery( $sql );
+
+ $special = $wgLang->getNsText( Namespace::getSpecial() );
+ $wgOut->addHTML("<ul>");
+ while( $row = wfFetchObject( $ret ) ) {
+ $wgOut->addHTML( "<li>" .
+ $sk->makeKnownLink( $wgLang->specialPage( "Undelete" ),
+ $wgLang->timeanddate( $row->ar_timestamp, true ),
+ "target=" . urlencode($target) . "&timestamp={$row->ar_timestamp}" ) . " " .
+ ". . {$row->ar_user_text}" .
+ " <i>(" . htmlspecialchars($row->ar_comment) . "</i>)\n");
+
+ }
+ $wgOut->addHTML("</ul>");
+
+ return 0;
+}
+
+/* private */ function doUndeleteArticle( $namespace, $title )
+ {
+ global $wgUser, $wgOut, $wgLang, $target;
+
+ $fname = "doUndeleteArticle";
+
+ if ( "" == $title ) {
+ $wgOut->fatalError( wfMsg( "cannotundelete" ) );
+ return;
+ }
+ $t = addslashes($title);
+
+ # Move article and history from the "archive" table
+ $sql = "SELECT COUNT(*) AS count FROM cur WHERE cur_namespace={$namespace} AND cur_title='{$t}'";
+ $res = wfQuery( $sql );
+ $row = wfFetchObject( $res );
+ if( $row->count == 0) {
+ # Have to create new article...
+ $max = wfGetSQL( "archive", "MAX(ar_timestamp)", "ar_namespace={$namespace} AND ar_title='{$t}'" );
+ $sql = "INSERT INTO cur (cur_namespace,cur_title,cur_text," .
+ "cur_comment,cur_user,cur_user_text,cur_timestamp,inverse_timestamp,cur_minor_edit,cur_random)" .
+ "SELECT ar_namespace,ar_title,ar_text,ar_comment," .
+ "ar_user,ar_user_text,ar_timestamp,99999999999999-ar_timestamp,ar_minor_edit,RAND() FROM archive " .
+ "WHERE ar_namespace={$namespace} AND ar_title='{$t}' AND ar_timestamp={$max}";
+ wfQuery( $sql, $fname );
+ $newid = wfInsertId();
+ $oldones = "AND ar_timestamp<{$max}";
+ } else {
+ # If already exists, put history entirely into old table
+ $oldones = "";
+ $newid = 0;
+ }
+
+ $sql = "INSERT INTO old (old_namespace,old_title,old_text," .
+ "old_comment,old_user,old_user_text,old_timestamp,inverse_timestamp,old_minor_edit," .
+ "old_flags) SELECT ar_namespace,ar_title,ar_text,ar_comment," .
+ "ar_user,ar_user_text,ar_timestamp,99999999999999-ar_timestamp,ar_minor_edit,ar_flags " .
+ "FROM archive WHERE ar_namespace={$namespace} AND ar_title='{$t}' {$oldones}";
+ wfQuery( $sql, $fname );
+
+ # Finally, clean up the link tables
+ if( $newid ) {
+ $to = Title::newFromDBKey( $target );
+ $to->resetArticleID( $newid );
+ }
+
+ # Now that it's safely stored, take it out of the archive
+ $sql = "DELETE FROM archive WHERE ar_namespace={$namespace} AND " .
+ "ar_title='{$t}'";
+ wfQuery( $sql, $fname );
+
+
+ # Touch the log?
+
+ $wgOut->addWikiText(str_replace("$1", $target, wfMsg("undeletedtext")));
+ return 0;
+ }
+?>
diff --git a/includes/SpecialUnlockdb.php b/includes/SpecialUnlockdb.php
new file mode 100644
index 000000000000..7baee945f301
--- /dev/null
+++ b/includes/SpecialUnlockdb.php
@@ -0,0 +1,82 @@
+<?
+
+function wfSpecialUnlockdb()
+{
+ global $wgUser, $wgOut, $action;
+
+ if ( ! $wgUser->isDeveloper() ) {
+ $wgOut->developerRequired();
+ return;
+ }
+ $f = new DBUnlockForm();
+
+ if ( "success" == $action ) { $f->showSuccess(); }
+ else if ( "submit" == $action ) { $f->doSubmit(); }
+ else { $f->showForm( "" ); }
+}
+
+class DBUnlockForm {
+
+ function showForm( $err )
+ {
+ global $wgOut, $wgUser, $wgLang;
+ global $wpLockConfirm;
+
+ $wgOut->setPagetitle( wfMsg( "unlockdb" ) );
+ $wgOut->addWikiText( wfMsg( "unlockdbtext" ) );
+
+ if ( "" != $err ) {
+ $wgOut->setSubtitle( wfMsg( "formerror" ) );
+ $wgOut->addHTML( "<p><font color='red' size='+1'>{$err}</font>\n" );
+ }
+ $lc = wfMsg( "unlockconfirm" );
+ $lb = wfMsg( "unlockbtn" );
+ $action = wfLocalUrlE( $wgLang->specialPage( "Unlockdb" ),
+ "action=submit" );
+
+ $wgOut->addHTML( "<p>
+<form id=\"unlockdb\" method=\"post\" action=\"{$action}\">
+<table border=0><tr>
+<td align=right>
+<input type=checkbox name=\"wpLockConfirm\">
+</td>
+<td align=\"left\">{$lc}<td>
+</tr><tr>
+<td>&nbsp;</td><td align=left>
+<input type=submit name=\"wpLock\" value=\"{$lb}\">
+</td></tr></table>
+</form>\n" );
+
+ }
+
+ function doSubmit()
+ {
+ global $wgOut, $wgUser, $wgLang;
+ global $wpLockConfirm, $wgReadOnlyFile;
+
+ if ( ! $wpLockConfirm ) {
+ $this->showForm( wfMsg( "locknoconfirm" ) );
+ return;
+ }
+ if ( ! unlink( $wgReadOnlyFile ) ) {
+ $wgOut->fileDeleteError( $wgReadOnlyFile );
+ return;
+ }
+ $success = wfLocalUrl( $wgLang->specialPage( "Unlockdb" ),
+ "action=success" );
+ $wgOut->redirect( $success );
+ }
+
+ function showSuccess()
+ {
+ global $wgOut, $wgUser;
+ global $ip;
+
+ $wgOut->setPagetitle( wfMsg( "unlockdb" ) );
+ $wgOut->setSubtitle( wfMsg( "unlockdbsuccesssub" ) );
+ $text = str_replace( "$1", $ip, wfMsg( "unlockdbsuccesstext" ) );
+ $wgOut->addWikiText( $text );
+ }
+}
+
+?>
diff --git a/includes/SpecialUnusedimages.php b/includes/SpecialUnusedimages.php
new file mode 100644
index 000000000000..9157073acd4c
--- /dev/null
+++ b/includes/SpecialUnusedimages.php
@@ -0,0 +1,56 @@
+<?
+
+function wfSpecialUnusedimages()
+{
+ global $wgUser, $wgOut, $wgLang, $wgTitle;
+ global $limit, $offset; # From query string
+ $fname = "wfSpecialUnusedimages";
+
+ if ( ! $limit ) {
+ $limit = $wgUser->getOption( "rclimit" );
+ if ( ! $limit ) { $limit = 50; }
+ }
+ if ( ! $offset ) { $offset = 0; }
+
+ $sql = "SELECT img_name,img_user,img_user_text,img_timestamp,img_description " .
+ "FROM image LEFT JOIN imagelinks ON img_name=il_to WHERE il_to IS NULL " .
+ "ORDER BY img_timestamp LIMIT {$offset}, {$limit}";
+ $res = wfQuery( $sql, $fname );
+
+ $sk = $wgUser->getSkin();
+
+ $wgOut->addHTML( wfMsg( "unusedimagestext" ) );
+ $top = wfShowingResults( $offset, $limit );
+ $wgOut->addHTML( "<p>{$top}\n" );
+
+ $sl = wfViewPrevNext( $offset, $limit,
+ $wgLang->specialPage( "Unusedimages" ) );
+ $wgOut->addHTML( "<br>{$sl}\n" );
+
+ $ins = $wgLang->getNsText ( 6 ) ;
+ $s = "<ol start=" . ( $offset + 1 ) . ">";
+ while ( $obj = wfFetchObject( $res ) ) {
+ $name = $obj->img_name;
+ $dlink = $sk->makeKnownLink( "{$ins}:{$name}", wfMsg( "imgdesc" ) );
+ $ilink = "<a href=\"" . wfImageUrl( $name ) . "\">{$name}</a>";
+
+ $d = $wgLang->timeanddate( $obj->img_timestamp, true );
+ $u = $obj->img_user;
+ $ut = $obj->img_user_text;
+ $c = $obj->img_description;
+
+ if ( 0 == $u ) { $ul = $ut; }
+ else { $ul = $sk->makeLink( $wgLang->getNsText(2).":{$ut}", $ut ); }
+
+ $s .= "<li>({$dlink}) {$ilink} . . {$d} . . {$ul}";
+
+ if ( "" != $c && "*" != $c ) { $s .= " <em>({$c})</em>"; }
+ $s .= "</li>\n";
+ }
+ wfFreeResult( $res );
+ $s .= "</ol>";
+ $wgOut->addHTML( $s );
+ $wgOut->addHTML( "<p>{$sl}\n" );
+}
+
+?>
diff --git a/includes/SpecialUpload.php b/includes/SpecialUpload.php
new file mode 100644
index 000000000000..fc7093fd4c10
--- /dev/null
+++ b/includes/SpecialUpload.php
@@ -0,0 +1,245 @@
+<?
+
+function wfSpecialUpload()
+{
+ global $wgUser, $wgOut, $wpUpload, $wpReUpload, $action;
+
+ $fields = array( "wpUploadFile", "wpUploadDescription" );
+ wfCleanFormFields( $fields );
+
+ if ( 0 == $wgUser->getID() ) {
+ $wgOut->errorpage( "uploadnologin", "uploadnologintext" );
+ return;
+ }
+ if ( wfReadOnly() ) {
+ $wgOut->readOnlyPage();
+ return;
+ }
+ if ( isset( $wpReUpload) ) {
+ unsaveUploadedFile();
+ mainUploadForm( "" );
+ } else if ( "submit" == $action || isset( $wpUpload ) ) {
+ processUpload();
+ } else {
+ mainUploadForm( "" );
+ }
+}
+
+function processUpload()
+{
+ global $wgUser, $wgOut, $wgLang, $wpUploadAffirm, $wpUploadFile;
+ global $wpUploadDescription, $wpIgnoreWarning;
+ global $HTTP_POST_FILES, $wgUploadDirectory;
+ global $wpUploadSaveName, $wpUploadTempName, $wpUploadSize;
+ global $wgSavedFile, $wgUploadOldVersion, $wpUploadOldVersion;
+
+ if ( 1 != $wpUploadAffirm ) {
+ mainUploadForm( WfMsg( "noaffirmation" ) );
+ return;
+ }
+ if ( ! $wpUploadTempName ) {
+ $wpUploadTempName = $HTTP_POST_FILES['wpUploadFile']['tmp_name'];
+ }
+ if ( ! $wpUploadSize ) {
+ $wpUploadSize = $HTTP_POST_FILES['wpUploadFile']['size'];
+ }
+ $prev = error_reporting( E_ALL & ~( E_NOTICE | E_WARNING ) );
+ $oname = wfCleanQueryVar( $HTTP_POST_FILES['wpUploadFile']['name'] );
+ if ( $wpUploadSaveName != "" ) $wpUploadSaveName = wfCleanQueryVar( $wpUploadSaveName );
+ error_reporting( $prev );
+
+ if ( "" != $oname ) {
+ $basename = strrchr( $oname, "/" );
+ if ( false === $basename ) { $basename = $oname; }
+ else ( $basename = substr( $basename, 1 ) );
+
+ $ext = strrchr( $basename, "." );
+ if ( false === $ext ) { $ext = ""; }
+ else { $ext = substr( $ext, 1 ); }
+
+ if ( "" == $ext ) { $xl = 0; } else { $xl = strlen( $ext ) + 1; }
+ $partname = substr( $basename, 0, strlen( $basename ) - $xl );
+
+ if ( strlen( $partname ) < 3 ) {
+ mainUploadForm( WfMsg( "minlength" ) );
+ return;
+ }
+ $nt = Title::newFromText( $basename );
+ $wpUploadSaveName = $nt->getDBkey();
+
+ saveUploadedFile();
+ if ( ( ! $wpIgnoreWarning ) &&
+ ( 0 != strcmp( ucfirst( $basename ), $wpUploadSaveName ) ) ) {
+ $warn = str_replace( "$1", $wpUploadSaveName,
+ wfMsg( "badfilename" ) );
+ return uploadWarning( $warn );
+ }
+ $extensions = array( "png", "jpg", "jpeg", "ogg" );
+ if ( ( ! $wpIgnoreWarning ) &&
+ ( ! in_array( strtolower( $ext ), $extensions ) ) ) {
+ $warn = str_replace( "$1", $ext, wfMsg( "badfiletype" ) );
+ return uploadWarning( $warn );
+ }
+ if ( ( ! $wpIgnoreWarning ) && ( $wpUploadSize > 150000 ) ) {
+ return uploadWarning( WfMsg( "largefile" ) );
+ }
+ }
+ if ( isset( $wpUploadOldVersion ) ) {
+ $wgUploadOldVersion = $wpUploadOldVersion;
+ }
+ wfRecordUpload( $wpUploadSaveName, $wgUploadOldVersion,
+ $wpUploadSize, $wpUploadDescription );
+
+ $sk = $wgUser->getSkin();
+ $ilink = $sk->makeMediaLink( $wpUploadSaveName, wfImageUrl(
+ $wpUploadSaveName ) );
+ $dname = $wgLang->getNsText( Namespace::getImage() ) . ":{$wpUploadSaveName}";
+ $dlink = $sk->makeKnownLink( $dname, $dname );
+
+ $wgOut->addHTML( "<h2>" . wfMsg( "successfulupload" ) . "</h2>\n" );
+ $text = str_replace( "$1", $ilink, wfMsg( "fileuploaded" ) );
+ $text = str_replace( "$2", $dlink, $text );
+ $wgOut->addHTML( "<p>{$text}\n" );
+ $wgOut->returnToMain( false );
+}
+
+function saveUploadedFile()
+{
+ global $wpUploadSaveName, $wpUploadTempName;
+ global $wgSavedFile, $wgUploadOldVersion;
+ global $wgUploadDirectory, $wgOut;
+
+ $dest = wfImageDir( $wpUploadSaveName );
+ $archive = wfImageArchiveDir( $wpUploadSaveName );
+ $wgSavedFile = "{$dest}/{$wpUploadSaveName}";
+
+ if ( is_file( $wgSavedFile ) ) {
+ $wgUploadOldVersion = date( "YmdHis" ) . "!{$wpUploadSaveName}";
+
+ if ( ! rename( $wgSavedFile, "${archive}/{$wgUploadOldVersion}" ) ) {
+ $wgOut->fileRenameError( $wgSavedFile,
+ "${archive}/{$wgUploadOldVersion}" );
+ return;
+ }
+ } else {
+ $wgUploadOldVersion = "";
+ }
+ if ( ! move_uploaded_file( $wpUploadTempName, $wgSavedFile ) ) {
+ $wgOut->fileCopyError( $wpUploadTempName, $wgSavedFile );
+ }
+ chmod( $wgSavedFile, 0644 );
+}
+
+function unsaveUploadedFile()
+{
+ global $wgSavedFile, $wgUploadOldVersion;
+ global $wpSavedFile, $wpUploadOldVersion;
+ global $wgUploadDirectory, $wgOut;
+
+ $wgSavedFile = $wpSavedFile;
+ $wgUploadOldVersion = $wpUploadOldVersion;
+
+ if ( ! unlink( $wgSavedFile ) ) {
+ $wgOut->fileDeleteError( $wgSavedFile );
+ return;
+ }
+ if ( "" != $wgUploadOldVersion ) {
+ $hash = md5( substr( $wgUploadOldVersion, 15 ) );
+ $archive = "{$wgUploadDirectory}/archive/" . $hash{0} .
+ "/" . substr( $hash, 0, 2 );
+
+ if ( ! rename( "{$archive}/{$wgUploadOldVersion}", $wgSavedFile ) ) {
+ $wgOut->fileRenameError( "{$archive}/{$wgUploadOldVersion}",
+ $wgSavedFile );
+ }
+ }
+}
+
+function uploadWarning( $warning )
+{
+ global $wgOut, $wgUser, $wgLang, $wgUploadDirectory;
+ global $wpUpload, $wpReUpload, $wpUploadAffirm, $wpUploadFile;
+ global $wpUploadDescription, $wpIgnoreWarning;
+ global $wpUploadSaveName, $wpUploadTempName, $wpUploadSize;
+ global $wgSavedFile, $wgUploadOldVersion;
+ global $wpSavedFile, $wpUploadOldVersion;
+
+ $sub = wfMsg( "uploadwarning" );
+ $wgOut->addHTML( "<h2>{$sub}</h2>\n" );
+ $wgOut->addHTML( "<h4><font color=red>{$warning}</font></h4>\n" );
+
+ $save = wfMsg( "savefile" );
+ $reupload = wfMsg( "reupload" );
+ $iw = wfMsg( "ignorewarning" );
+ $reup = wfMsg( "reuploaddesc" );
+ $action = wfLocalUrlE( $wgLang->specialPage( "Upload" ),
+ "action=submit" );
+
+ $wgOut->addHTML( "
+<form id=\"uploadwarning\" method=\"post\" enctype=\"multipart/form-data\"
+action=\"{$action}\">
+<input type=hidden name=\"wpUploadAffirm\" value=\"1\">
+<input type=hidden name=\"wpIgnoreWarning\" value=\"1\">
+<input type=hidden name=\"wpUploadDescription\" value=\"" . htmlspecialchars( $wpUploadDescription ) . "\">
+<input type=hidden name=\"wpUploadSaveName\" value=\"" . htmlspecialchars( $wpUploadSaveName ) . "\">
+<input type=hidden name=\"wpUploadTempName\" value=\"" . htmlspecialchars( $wpUploadTempName ) . "\">
+<input type=hidden name=\"wpUploadSize\" value=\"" . htmlspecialchars( $wpUploadSize ) . "\">
+<input type=hidden name=\"wpSavedFile\" value=\"" . htmlspecialchars( $wgSavedFile ) . "\">
+<input type=hidden name=\"wpUploadOldVersion\" value=\"" . htmlspecialchars( $wgUploadOldVersion) . "\">
+<table border=0><tr>
+<tr><td align=right>
+<input tabindex=2 type=submit name=\"wpUpload\" value=\"{$save}\">
+</td><td align=left>{$iw}</td></tr>
+<tr><td align=right>
+<input tabindex=2 type=submit name=\"wpReUpload\" value=\"{$reupload}\">
+</td><td align=left>{$reup}</td></tr></table></form>\n" );
+}
+
+function mainUploadForm( $msg )
+{
+ global $wgOut, $wgUser, $wgLang, $wgUploadDirectory;
+ global $wpUpload, $wpUploadAffirm, $wpUploadFile;
+ global $wpUploadDescription, $wpIgnoreWarning;
+
+ if ( "" != $msg ) {
+ $sub = wfMsg( "uploaderror" );
+ $wgOut->addHTML( "<h2>{$sub}</h2>\n" .
+ "<h4><font color=red>{$msg}</font></h4>\n" );
+ } else {
+ $sub = wfMsg( "uploadfile" );
+ $wgOut->addHTML( "<h2>{$sub}</h2>\n" );
+ }
+ $wgOut->addHTML( "<p>" . wfMsg( "uploadtext" ) );
+ $sk = $wgUser->getSkin();
+
+ $fn = wfMsg( "filename" );
+ $fd = wfMsg( "filedesc" );
+ $ulb = wfMsg( "uploadbtn" );
+
+ $clink = $sk->makeKnownLink( wfMsg( "copyrightpage" ),
+ wfMsg( "copyrightpagename" ) );
+ $ca = str_replace( "$1", $clink, wfMsg( "affirmation" ) );
+ $iw = wfMsg( "ignorewarning" );
+
+ $action = wfLocalUrl( $wgLang->specialPage( "Upload" ) );
+ $wgOut->addHTML( "
+<form id=\"upload\" method=\"post\" enctype=\"multipart/form-data\"
+action=\"{$action}\">
+<table border=0><tr>
+<td align=right>{$fn}:</td><td align=left>
+<input tabindex=1 type=file name=\"wpUploadFile\" value=\""
+ . htmlspecialchars( $wpUploadFile ) . "\" size=40>
+</td></tr><tr>
+<td align=right>{$fd}:</td><td align=left>
+<input tabindex=2 type=text name=\"wpUploadDescription\" value=\""
+ . htmlspecialchars( $wpUploadDescription ) . "\" size=40>
+</td></tr><tr>
+<td align=right>
+<input tabindex=3 type=checkbox name=\"wpUploadAffirm\" value=\"1\">
+</td><td align=left>{$ca}</td></tr>
+<tr><td>&nbsp;</td><td align=left>
+<input tabindex=5 type=submit name=\"wpUpload\" value=\"{$ulb}\">
+</td></tr></table></form>\n" );
+}
+
+?>
diff --git a/includes/SpecialUserlogin.php b/includes/SpecialUserlogin.php
new file mode 100644
index 000000000000..ed16443dd712
--- /dev/null
+++ b/includes/SpecialUserlogin.php
@@ -0,0 +1,248 @@
+<?
+
+function wfSpecialUserlogin()
+{
+ global $wpCreateaccount, $wpLoginattempt, $wpMailmypassword;
+ global $action;
+
+ $fields = array( "wpName", "wpPassword", "wpName",
+ "wpPassword", "wpRetype", "wpEmail" );
+ wfCleanFormFields( $fields );
+
+ if ( isset( $wpCreateaccount ) ) {
+ addNewAccount();
+ } else if ( isset( $wpMailmypassword ) ) {
+ mailPassword();
+ } else if ( "submit" == $action || isset( $wpLoginattempt ) ) {
+ processLogin();
+ } else {
+ mainLoginForm( "" );
+ }
+}
+
+/* private */ function addNewAccount()
+{
+ global $wgUser, $wgOut, $wpPassword, $wpRetype, $wpName, $wpRemember;
+ global $wpEmail, $wgDeferredUpdateList;
+
+ if ( 0 != strcmp( $wpPassword, $wpRetype ) ) {
+ mainLoginForm( wfMsg( "badretype" ) );
+ return;
+ }
+ $wpName = trim( $wpName );
+ if ( ( "" == $wpName ) ||
+ preg_match( "/^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/", $wpName ) ) {
+ mainLoginForm( wfMsg( "noname" ) );
+ return;
+ }
+ if ( wfReadOnly() ) {
+ $wgOut->readOnlyPage();
+ return;
+ }
+ $u = User::newFromName( $wpName );
+
+ if ( 0 != $u->idForName() ) {
+ mainLoginForm( wfMsg( "userexists" ) );
+ return;
+ }
+ $u->addToDatabase();
+ $u->setPassword( $wpPassword );
+ $u->setEmail( $wpEmail );
+ if ( 1 == $wpRemember ) { $r = 1; }
+ else { $r = 0; }
+ $u->setOption( "rememberpassword", $r );
+
+ $wgUser = $u;
+ $m = str_replace( "$1", $wgUser->getName(), wfMsg( "welcomecreation" ) );
+ successfulLogin( $m );
+}
+
+/* private */ function processLogin()
+{
+ global $wgUser, $wpName, $wpPassword, $wpRemember;
+ global $returnto;
+
+ if ( "" == $wpName ) {
+ mainLoginForm( wfMsg( "noname" ) );
+ return;
+ }
+ $u = User::newFromName( $wpName );
+ $id = $u->idForName();
+ if ( 0 == $id ) {
+ $m = str_replace( "$1", $u->getName(), wfMsg( "nosuchuser" ) );
+ mainLoginForm( $m );
+ return;
+ }
+ $u->setId( $id );
+ $u->loadFromDatabase();
+ $ep = User::encryptPassword( $wpPassword );
+ if ( 0 != strcmp( $ep, $u->getPassword() ) ) {
+ if ( 0 != strcmp( $ep, $u->getNewpassword() ) ) {
+ mainLoginForm( wfMsg( "wrongpassword" ) );
+ return;
+ }
+ }
+ # We've verified now, update the real record
+ #
+ if ( 1 == $wpRemember ) { $r = 1; }
+ else { $r = 0; }
+ $u->setOption( "rememberpassword", $r );
+
+ $wgUser = $u;
+ $m = str_replace( "$1", $wgUser->getName(), wfMsg( "loginsuccess" ) );
+ successfulLogin( $m );
+}
+
+/* private */ function mailPassword()
+{
+ global $wgUser, $wpName, $wgDeferredUpdateList, $wgOutputEncoding;
+
+ if ( "" == $wpName ) {
+ mainLoginForm( wfMsg( "noname" ) );
+ return;
+ }
+ $u = User::newFromName( $wpName );
+ $id = $u->idForName();
+ if ( 0 == $id ) {
+ $m = str_replace( "$1", $u->getName(), wfMsg( "nosuchuser" ) );
+ mainLoginForm( $m );
+ return;
+ }
+ $u->setId( $id );
+ $u->loadFromDatabase();
+
+ if ( "" == $u->getEmail() ) {
+ $m = str_replace( "$1", $u->getName(), wfMsg( "noemail" ) );
+ mainLoginForm( $m );
+ return;
+ }
+ $np = User::randomPassword();
+ $u->setNewpassword( $np );
+ setcookie( "wcUserPassword", "", time() - 3600 );
+ $u->saveSettings();
+
+ $ip = getenv( "REMOTE_ADDR" );
+ if ( "" == $ip ) { $ip = "(Unknown)"; }
+
+ $m = str_replace( "$1", $ip, wfMsg( "passwordremindertext" ) );
+ $m = str_replace( "$2", $u->getName(), $m );
+ $m = str_replace( "$3", $np, $m );
+
+ #FIXME: Generilize the email addresses for 3rd party sites...
+ mail( $u->getEmail(), wfMsg( "passwordremindertitle" ), $m,
+ "MIME-Version: 1.0\r\n" .
+ "Content-type: text/plain; charset={$wgOutputEncoding}\r\n" .
+ "Content-transfer-encoding: 8bit\r\n" .
+ "From: Wikipedia Mail <apache@www.wikipedia.org>\r\n" .
+ "Reply-To: webmaster@www.wikipedia.org" );
+ $m = str_replace( "$1", $u->getName(), wfMsg( "passwordsent" ) );
+ mainLoginForm( $m );
+}
+
+/* private */ function successfulLogin( $msg )
+{
+ global $wgUser, $wgOut, $returnto;
+ global $wgDeferredUpdateList;
+
+ $wgUser->setCookies();
+ $up = new UserUpdate();
+ array_push( $wgDeferredUpdateList, $up );
+
+ $wgOut->setPageTitle( wfMsg( "loginsuccesstitle" ) );
+ $wgOut->setRobotpolicy( "noindex,nofollow" );
+ $wgOut->setArticleFlag( false );
+ $wgOut->addHTML( $msg . "\n<p>" );
+ $wgOut->returnToMain();
+}
+
+/* private */ function mainLoginForm( $err )
+{
+ global $wgUser, $wgOut, $wgLang, $returnto;
+ global $wpName, $wpPassword, $wpRetype, $wpRemember;
+ global $wpEmail, $HTTP_COOKIE_VARS;
+
+ $le = wfMsg( "loginerror" );
+ $yn = wfMsg( "yourname" );
+ $yp = wfMsg( "yourpassword" );
+ $ypa = wfMsg( "yourpasswordagain" );
+ $rmp = wfMsg( "remembermypassword" );
+ $ayn = wfMsg( "areyounew" );
+ $nuo = wfMsg( "newusersonly" );
+ $li = wfMsg( "login" );
+ $ca = wfMsg( "createaccount" );
+ $ye = wfMsg( "youremail" );
+ $efl = wfMsg( "emailforlost" );
+ $mmp = wfMsg( "mailmypassword" );
+
+ $name = $wpName;
+ if ( "" == $name ) {
+ if ( 0 != $wgUser->getID() ) {
+ $name = $wgUser->getName();
+ } else {
+ $name = $HTTP_COOKIE_VARS["wcUserName"];
+ }
+ }
+ $pwd = $wpPassword;
+
+ $wgOut->setPageTitle( wfMsg( "userlogin" ) );
+ $wgOut->setRobotpolicy( "noindex,nofollow" );
+ $wgOut->setArticleFlag( false );
+
+ if ( "" == $err ) {
+ $wgOut->addHTML( "<h2>$li:</h2>\n" );
+ } else {
+ $wgOut->addHTML( "<h2>$le:</h2>\n<font size='+1' color='red'>$err</font>\n" );
+ }
+ if ( 1 == $wgUser->getOption( "rememberpassword" ) ) {
+ $checked = " checked";
+ } else {
+ $checked = "";
+ }
+ $q = "action=submit";
+ if ( "" != $returnto ) { $q .= "&returnto=" . wfUrlencode($returnto); }
+ $action = wfLocalUrlE( $wgLang->specialPage( "Userlogin" ), $q );
+
+ $wpName = wfEscapeHTML( $wpName );
+ $wpPassword = wfEscapeHTML( $wpPassword );
+ $wpRetype = wfEscapeHTML( $wpRetype );
+ $wpEmail = wfEscapeHTML( $wpEmail );
+
+ $wgOut->addHTML( "
+<form id=\"userlogin\" method=\"post\" action=\"{$action}\">
+<table border=0><tr>
+<td align=right>$yn:</td>
+<td colspan=2 align=left>
+<input tabindex=1 type=text name=\"wpName\" value=\"{$name}\" size=20>
+</td></tr><tr>
+<td align=right>$yp:</td>
+<td align=left>
+<input tabindex=2 type=password name=\"wpPassword\" value=\"{$pwd}\" size=20>
+</td>
+<td align=left>
+<input tabindex=3 type=submit name=\"wpLoginattempt\" value=\"{$li}\">
+</td></tr>
+<tr><td colspan=3>&nbsp;</td></tr><tr>
+<td align=right>$ypa:</td>
+<td align=left>
+<input tabindex=4 type=password name=\"wpRetype\" value=\"{$wpRetype}\" size=20>
+</td><td>$nuo</td></tr>
+<tr>
+<td align=right>$ye:</td>
+<td align=left>
+<input tabindex=5 type=text name=\"wpEmail\" value=\"{$wpEmail}\" size=20>
+</td><td align=left>
+<input tabindex=6 type=submit name=\"wpCreateaccount\" value=\"{$ca}\">
+</td></tr>
+<tr>
+<td colspan=3 align=left>
+<input tabindex=7 type=checkbox name=\"wpRemember\" value=\"1\"$checked>$rmp
+</td></tr>
+<tr><td colspan=3>&nbsp;</td></tr><tr>
+<td colspan=3 align=left>
+<p>$efl<br>
+<input tabindex=8 type=submit name=\"wpMailmypassword\" value=\"{$mmp}\">
+</td></tr></table>
+</form>\n" );
+}
+
+?>
diff --git a/includes/SpecialUserlogout.php b/includes/SpecialUserlogout.php
new file mode 100644
index 000000000000..094335177b23
--- /dev/null
+++ b/includes/SpecialUserlogout.php
@@ -0,0 +1,13 @@
+<?
+
+function wfSpecialUserlogout()
+{
+ global $wgUser, $wgOut, $returnto;
+
+ $wgUser->logout();
+ $wgOut->setRobotpolicy( "noindex,nofollow" );
+ $wgOut->addHTML( wfMsg( "logouttext" ) . "\n<p>" );
+ $wgOut->returnToMain();
+}
+
+?>
diff --git a/includes/SpecialVote.php b/includes/SpecialVote.php
new file mode 100644
index 000000000000..de0f8ffe9720
--- /dev/null
+++ b/includes/SpecialVote.php
@@ -0,0 +1,10 @@
+<?
+
+function wfSpecialVote()
+{
+ global $wgUser, $wgOut;
+
+ $wgOut->addHTML( "<p>(TODO: Vote)" );
+}
+
+?>
diff --git a/includes/SpecialWantedpages.php b/includes/SpecialWantedpages.php
new file mode 100644
index 000000000000..3f84ce333af5
--- /dev/null
+++ b/includes/SpecialWantedpages.php
@@ -0,0 +1,74 @@
+<?
+global $IP;
+include_once ( "$IP/LogPage.php" ) ;
+
+function wfSpecialWantedpages()
+{
+ global $wgUser, $wgOut, $wgLang, $wgTitle;
+ global $limit, $offset; # From query string
+ $fname = "wfSpecialWantedpages";
+
+ # Cache
+ $vsp = $wgLang->getValidSpecialPages() ;
+ $mw = $vsp["Wantedpages"] ;
+ $mw = str_replace ( " " , "_" , $mw ) ; # DBKEY
+ $log = new LogPage ( $mw ) ;
+ $log->mUpdateRecentChanges = false ;
+
+ $wgOut->setRobotpolicy( "noindex,nofollow" );
+ global $wgMiserMode;
+ if ( $wgMiserMode ) {
+ $s = "=== " . wfMsg( "perfdisabled" ) . " ===\n" ;
+ $s .= $log->getContent() ;
+ $wgOut->addWikiText ( $s ) ;
+ return;
+ }
+
+ if ( ! $limit ) {
+ $limit = $wgUser->getOption( "rclimit" );
+ if ( ! $limit ) { $limit = 50; }
+ }
+ if ( ! $offset ) { $offset = 0; }
+
+ $cache = "" ; # To be saved, eventually
+
+ $sql = "SELECT bl_to, COUNT( DISTINCT bl_from ) as nlinks " .
+ "FROM brokenlinks GROUP BY bl_to HAVING nlinks > 1 " .
+ "ORDER BY nlinks DESC LIMIT {$offset}, {$limit}";
+ $res = wfQuery( $sql, $fname );
+
+ $sk = $wgUser->getSkin();
+
+ $top = wfShowingResults( $offset, $limit );
+ $wgOut->addHTML( "<p>{$top}\n" );
+
+ $sl = wfViewPrevNext( $offset, $limit,
+ $wgLang->specialpage( "Wantedpages" ) );
+ $wgOut->addHTML( "<br>{$sl}\n" );
+
+ $s = "<ol start=" . ( $offset + 1 ) . ">";
+ while ( $obj = wfFetchObject( $res ) ) {
+ $nt = Title::newFromDBkey( $obj->bl_to );
+
+ $plink = $sk->makeBrokenLink( $nt->getPrefixedText(), "" );
+ $nl = str_replace( "$1", $obj->nlinks, wfMsg( "nlinks" ) );
+ $nlink = $sk->makeKnownLink( $wgLang->specialPage(
+ "Whatlinkshere" ), $nl, "target=" . $nt->getPrefixedURL() );
+
+ $cache .= "* [[".$nt->getPrefixedText()."]] ({$nl})\n" ;
+
+ $s .= "<li>{$plink} ({$nlink})</li>\n";
+ }
+ wfFreeResult( $res );
+ $s .= "</ol>";
+ $wgOut->addHTML( $s );
+ $wgOut->addHTML( "<p>{$sl}\n" );
+
+ # Saving cache
+ if ( $offset > 0 OR $limit < 50 ) return ; #Not suitable
+ $log->mContent = $cache ;
+ $log->mContentLoaded = true ;
+ $log->saveContent() ;
+}
+
+?>
diff --git a/includes/SpecialWatchlist.php b/includes/SpecialWatchlist.php
new file mode 100644
index 000000000000..e9df9ef34446
--- /dev/null
+++ b/includes/SpecialWatchlist.php
@@ -0,0 +1,90 @@
+<?
+global $IP;
+include_once( "$IP/SpecialRecentchanges.php" );
+
+function wfSpecialWatchlist()
+{
+ global $wgUser, $wgOut, $wgLang, $wgTitle;
+ global $days, $limit, $target; # From query string
+ $fname = "wfSpecialWatchlist";
+
+ $wgOut->setPagetitle( wfMsg( "watchlist" ) );
+ $sub = str_replace( "$1", $wgUser->getName(), wfMsg( "watchlistsub" ) );
+ $wgOut->setSubtitle( $sub );
+ $wgOut->setRobotpolicy( "index,follow" );
+
+ if ( ! isset( $days ) ) {
+ $days = $wgUser->getOption( "rcdays" );
+ if ( ! $days ) { $days = 3; }
+ }
+ if ( ! isset( $limit ) ) {
+ $limit = $wgUser->getOption( "rclimit" );
+ if ( ! $limit ) { $limit = 100; }
+ }
+ if ( $days == 0 ) {
+ $docutoff = '';
+ } else {
+ $docutoff = "cur_timestamp > '" .
+ date( "YmdHis", time() - ( $days * 86400 ) )
+ . "' AND";
+ }
+ if ( $limit == 0 ) {
+ $dolimit = "";
+ } else {
+ $dolimit = "LIMIT $limit";
+ }
+
+ $uid = $wgUser->getID();
+ if( $uid == 0 ) {
+ $wgOut->addHTML( wfMsg( "nowatchlist" ) );
+ return;
+ }
+
+# $sql = "SELECT DISTINCT
+# cur_id,cur_namespace,cur_title,cur_comment,
+# cur_user,cur_user_text,cur_timestamp,cur_minor_edit,cur_is_new
+# FROM cur,watchlist
+# WHERE wl_user={$uid} AND wl_namespace=cur_namespace & (~1) AND wl_title=cur_title
+# ORDER BY cur_timestamp DESC {$dolimit}";
+ $sql = "SELECT DISTINCT
+ cur_id,cur_namespace,cur_title,cur_comment,
+ cur_user,cur_user_text,cur_timestamp,cur_minor_edit,cur_is_new
+ FROM cur,watchlist
+ WHERE wl_user={$uid} AND wl_title=cur_title
+ AND (cur_namespace=wl_namespace OR cur_namespace=wl_namespace+1)
+ ORDER BY inverse_timestamp {$dolimit}";
+ $res = wfQuery( $sql, $fname );
+ if ( wfNumRows( $res ) == 0 ) {
+ $wgOut->addHTML( wfMsg( "nowatchlist" ) );
+ return;
+ }
+
+ $note = str_replace( "$1", $limit, wfMsg( "rcnote" ) );
+ $note = str_replace( "$2", $days, $note );
+ $wgOut->addHTML( "\n<hr>\n{$note}\n<br>" );
+ $note = rcDayLimitlinks( $days, $limit, "Watchlist", "", true );
+ $wgOut->addHTML( "{$note}\n" );
+
+ $sk = $wgUser->getSkin();
+ $s = $sk->beginRecentChangesList();
+
+ while ( $obj = wfFetchObject( $res ) ) {
+ $ts = $obj->cur_timestamp;
+ $u = $obj->cur_user;
+ $ut = $obj->cur_user_text;
+ $ns = $obj->cur_namespace;
+ $ttl = $obj->cur_title;
+ $com = $obj->cur_comment;
+ $me = ( $obj->cur_minor_edit > 0 );
+ $new = ( $obj->cur_is_new > 0 );
+ $watched = true;
+
+ $s .= $sk->recentChangesLine( $ts, $u, $ut, $ns, $ttl, $com, $me, $new, $watched );
+ }
+ $s .= $sk->endRecentChangesList();
+
+ wfFreeResult( $res );
+ $wgOut->addHTML( $s );
+}
+
+?>
diff --git a/includes/SpecialWhatlinkshere.php b/includes/SpecialWhatlinkshere.php
new file mode 100644
index 000000000000..f9d2a26cd6e0
--- /dev/null
+++ b/includes/SpecialWhatlinkshere.php
@@ -0,0 +1,101 @@
+<?
+
+function wfSpecialWhatlinkshere()
+{
+ global $wgUser, $wgOut, $target;
+ $fname = "wfSpecialWhatlinkshere";
+
+ if ( "" == $target ) {
+ $wgOut->errorpage( "notargettitle", "notargettext" );
+ return;
+ }
+ $nt = Title::newFromURL( wfCleanQueryVar( $target ) );
+ $wgOut->setPagetitle( $nt->getPrefixedText() );
+ $wgOut->setSubtitle( wfMsg( "linklistsub" ) );
+
+ $id = $nt->getArticleID();
+ $sk = $wgUser->getSkin();
+ $isredir = " (" . wfMsg( "isredirect" ) . ")\n";
+
+ if ( 0 == $id ) {
+ $sql = "SELECT DISTINCT bl_from FROM brokenlinks WHERE bl_to='" .
+ wfStrencode( $nt->getPrefixedDBkey() ) . "' LIMIT 500";
+ $res = wfQuery( $sql, $fname );
+
+ if ( 0 == wfNumRows( $res ) ) {
+ $wgOut->addHTML( wfMsg( "nolinkshere" ) );
+ } else {
+ $wgOut->addHTML( wfMsg( "linkshere" ) );
+ $wgOut->addHTML( "\n<ul>" );
+
+ while ( $row = wfFetchObject( $res ) ) {
+ $lid = $row->bl_from;
+ $sql = "SELECT cur_namespace,cur_title,cur_is_redirect " .
+ "FROM cur WHERE cur_id={$lid}";
+ $res2 = wfQuery( $sql, $fname );
+ $s = wfFetchObject( $res2 );
+
+ $n = Title::makeName( $s->cur_namespace, $s->cur_title );
+ $link = $sk->makeKnownLink( $n, "", "redirect=no" );
+ $wgOut->addHTML( "<li>{$link}" );
+
+ if ( 1 == $s->cur_is_redirect ) {
+ $wgOut->addHTML( $isredir );
+ wfShowIndirectLinks( 1, $lid );
+ }
+ $wgOut->addHTML( "</li>\n" );
+ }
+ $wgOut->addHTML( "</ul>\n" );
+ wfFreeResult( $res );
+ }
+ } else {
+ wfShowIndirectLinks( 0, $id );
+ }
+}
+
+function wfShowIndirectLinks( $level, $lid )
+{
+ global $wgOut, $wgUser;
+ $fname = "wfShowIndirectLinks";
+
+ $sql = "SELECT DISTINCT l_from FROM links WHERE l_to={$lid} LIMIT 500";
+ $res = wfQuery( $sql, $fname );
+
+ if ( 0 == wfNumRows( $res ) ) {
+ if ( 0 == $level ) {
+ $wgOut->addHTML( wfMsg( "nolinkshere" ) );
+ }
+ return;
+ }
+ if ( 0 == $level ) {
+ $wgOut->addHTML( wfMsg( "linkshere" ) );
+ }
+ $sk = $wgUser->getSkin();
+ $isredir = " (" . wfMsg( "isredirect" ) . ")\n";
+
+ $wgOut->addHTML( "<ul>" );
+ while ( $row = wfFetchObject( $res ) ) {
+ $nt = Title::newFromDBkey( $row->l_from );
+ $ns = $nt->getNamespace();
+ $t = wfStrencode( $nt->getDBkey() );
+
+ $link = $sk->makeKnownLink( $row->l_from, "", "redirect=no" );
+ $wgOut->addHTML( "<li>{$link}" );
+
+ $sql = "SELECT cur_id,cur_is_redirect FROM cur " .
+ "WHERE cur_namespace={$ns} AND cur_title='{$t}'";
+ $res2 = wfQuery( $sql, $fname );
+ $s = wfFetchObject( $res2 );
+
+ if ( 1 == $s->cur_is_redirect ) {
+ $wgOut->addHTML( $isredir );
+ if ( $level < 2 ) {
+ wfShowIndirectLinks( $level + 1, $s->cur_id );
+ }
+ }
+ $wgOut->addHTML( "</il>\n" );
+ }
+ $wgOut->addHTML( "</ul>\n" );
+}
+
+?>
diff --git a/includes/Title.php b/includes/Title.php
new file mode 100644
index 000000000000..9c0f6d27db19
--- /dev/null
+++ b/includes/Title.php
@@ -0,0 +1,370 @@
+<?
+# See title.doc
+global $IP;
+include_once( "$IP/Interwiki.php" );
+
+class Title {
+ /* private */ var $mTextform, $mUrlform, $mDbkeyform;
+ /* private */ var $mNamespace, $mInterwiki, $mFragment;
+ /* private */ var $mArticleID, $mRestrictions, $mRestrictionsLoaded;
+
+ /* private */ function Title()
+ {
+ $this->mInterwiki = $this->mUrlform =
+ $this->mTextform = $this->mDbkeyform = "";
+ $this->mArticleID = -1;
+ $this->mNamespace = 0;
+ $this->mRestrictionsLoaded = false;
+ $this->mRestrictions = array();
+ }
+
+ # Static factory methods
+ #
+ function newFromDBkey( $key )
+ {
+ $t = new Title();
+ $t->mDbkeyform = $key;
+ $t->secureAndSplit();
+ return $t;
+ }
+
+ function newFromText( $text )
+ {
+ # Note - mixing latin1 named entities and unicode numbered
+ # ones will result in a bad link.
+ $trans = get_html_translation_table( HTML_ENTITIES );
+ $trans = array_flip( $trans );
+ $text = strtr( $text, $trans );
+
+ $text = wfMungeToUtf8( $text );
+
+ $text = urldecode( $text );
+
+ $t = new Title();
+ $t->mDbkeyform = str_replace( " ", "_", $text );
+ $t->secureAndSplit();
+ return $t;
+ }
+
+ function newFromURL( $url )
+ {
+ global $wgLang, $wgServer, $HTTP_SERVER_VARS;
+
+ $t = new Title();
+ $s = urldecode( $url ); # This is technically wrong, as anything
+ # we've gotten is already decoded by PHP.
+ # Kept for backwards compatibility with
+ # buggy URLs we had for a while...
+
+ # For links that came from outside, check for alternate/legacy
+ # character encoding.
+ if( strncmp($wgServer, $HTTP_SERVER_VARS["HTTP_REFERER"], strlen( $wgServer ) ) )
+ $s = $wgLang->checkTitleEncoding( $s );
+
+ $t->mDbkeyform = str_replace( " ", "_", $s );
+ $t->secureAndSplit();
+ return $t;
+ }
+
+ function legalChars()
+ {
+ return "-,.()' &;%!?_0-9A-Za-z\\/:\\x80-\\xFF";
+ }
+
+ function getInterwikiLink( $key )
+ {
+ global $wgValidInterwikis;
+
+ if ( array_key_exists( $key, $wgValidInterwikis ) ) {
+ return $wgValidInterwikis[$key];
+ } else return "";
+ }
+
+ function getText() { return $this->mTextform; }
+ function getURL() { return $this->mUrlform; }
+ function getDBkey() { return $this->mDbkeyform; }
+ function getNamespace() { return $this->mNamespace; }
+ function setNamespace( $n ) { $this->mNamespace = $n; }
+ function getInterwiki() { return $this->mInterwiki; }
+ function getFragment() { return $this->mFragment; }
+
+ /* static */ function indexTitle( $ns, $title )
+ {
+ global $wgDBminWordLen, $wgLang;
+
+ $lc = SearchEngine::legalSearchChars() . "&#;";
+ $t = $wgLang->stripForSearch( $title );
+ $t = preg_replace( "/[^{$lc}]+/", " ", $t );
+ $t = strtolower( $t );
+
+ # Handle 's, s'
+ $t = preg_replace( "/([{$lc}]+)'s( |$)/", "\\1 \\1's ", $t );
+ $t = preg_replace( "/([{$lc}]+)s'( |$)/", "\\1s ", $t );
+
+ $t = preg_replace( "/\\s+/", " ", $t );
+
+ if ( $ns == Namespace::getImage() ) {
+ $t = preg_replace( "/ (png|gif|jpg|jpeg|ogg)$/", "", $t );
+ }
+ return trim( $t );
+ }
+
+ function getIndexTitle()
+ {
+ return Title::indexTitle( $this->mNamespace, $this->mTextform );
+ }
+
+ /* static */ function makeName( $ns, $title )
+ {
+ global $wgLang;
+
+ $n = $wgLang->getNsText( $ns );
+ if ( "" == $n ) { return $title; }
+ else { return "{$n}:{$title}"; }
+ }
+
+ function getPrefixedDBkey()
+ {
+ $s = $this->prefix( $this->mDbkeyform );
+ $s = str_replace( " ", "_", $s );
+ return $s;
+ }
+
+ function getPrefixedText()
+ {
+ $s = $this->prefix( $this->mTextform );
+ $s = str_replace( "_", " ", $s );
+ return $s;
+ }
+
+ function getPrefixedURL()
+ {
+ $s = $this->prefix( $this->mDbkeyform );
+ $s = str_replace( " ", "_", $s );
+
+ $s = urlencode ( $s ) ;
+ # Cleaning up URL to make it look nice -- is this safe?
+ $s = preg_replace( "/%3[Aa]/", ":", $s );
+ $s = preg_replace( "/%2[Ff]/", "/", $s );
+ $s = str_replace( "%28", "(", $s );
+ $s = str_replace( "%29", ")", $s );
+ return $s;
+ }
+
+ function getFullURL()
+ {
+ global $wgLang, $wgArticlePath, $wgValidInterwikis;
+
+ if ( "" == $this->mInterwiki ) {
+ $p = $wgArticlePath;
+ } else {
+ $p = $wgValidInterwikis[$this->mInterwiki];
+ }
+ $n = $wgLang->getNsText( $this->mNamespace );
+ if ( "" != $n ) { $n .= ":"; }
+ $u = str_replace( "$1", $n . $this->mUrlform, $p );
+ if ( "" != $this->mFragment ) {
+ $u .= "#" . $this->mFragment;
+ }
+ return $u;
+ }
+
+ function getEditURL()
+ {
+ global $wgServer, $wgScript;
+
+ if ( "" != $this->mInterwiki ) { return ""; }
+ $s = wfLocalUrl( $this->getPrefixedURL(), "action=edit" );
+
+ return $s;
+ }
+
+ function isExternal() { return ( "" != $this->mInterwiki ); }
+
+ function isProtected()
+ {
+ if ( -1 == $this->mNamespace ) { return true; }
+ $a = $this->getRestrictions();
+ if ( in_array( "sysop", $a ) ) { return true; }
+ return false;
+ }
+
+ function isLog()
+ {
+ if ( $this->mNamespace != Namespace::getWikipedia() ) {
+ return false;
+ }
+ if ( ( 0 == strcmp( wfMsg( "uploadlogpage" ), $this->mDbkeyform ) ) ||
+ ( 0 == strcmp( wfMsg( "dellogpage" ), $this->mDbkeyform ) ) ) {
+ return true;
+ }
+ return false;
+ }
+
+ function userIsWatching()
+ {
+ global $wgUser;
+
+ if ( -1 == $this->mNamespace ) { return false; }
+ if ( 0 == $wgUser->getID() ) { return false; }
+
+ return $wgUser->isWatched( $this );
+ }
+
+ function userCanEdit()
+ {
+ global $wgUser;
+
+ if ( -1 == $this->mNamespace ) { return false; }
+ # if ( 0 == $this->getArticleID() ) { return false; }
+ if ( $this->mDbkeyform == "_" ) { return false; }
+
+ $ur = $wgUser->getRights();
+ foreach ( $this->getRestrictions() as $r ) {
+ if ( "" != $r && ( ! in_array( $r, $ur ) ) ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ function getRestrictions()
+ {
+ $id = $this->getArticleID();
+ if ( 0 == $id ) { return array(); }
+
+ if ( ! $this->mRestrictionsLoaded ) {
+ $res = wfGetSQL( "cur", "cur_restrictions", "cur_id=$id" );
+ $this->mRestrictions = explode( ",", trim( $res ) );
+ $this->mRestrictionsLoaded = true;
+ }
+ return $this->mRestrictions;
+ }
+
+ function getArticleID()
+ {
+ global $wgLinkCache;
+
+ if ( -1 != $this->mArticleID ) { return $this->mArticleID; }
+ $this->mArticleID = $wgLinkCache->addLink(
+ $this->getPrefixedDBkey() );
+ return $this->mArticleID;
+ }
+
+ function resetArticleID( $newid )
+ {
+ global $wgLinkCache;
+ $wgLinkCache->clearBadLink( $this->getPrefixedDBkey() );
+
+ if ( 0 == $newid ) { $this->mArticleID = -1; }
+ else { $this->mArticleID = $newid; }
+ $this->mRestrictionsLoaded = false;
+ $this->mRestrictions = array();
+ }
+
+ /* private */ function prefix( $name )
+ {
+ global $wgLang;
+
+ $p = "";
+ if ( "" != $this->mInterwiki ) {
+ $p = $this->mInterwiki . ":";
+ }
+ if ( 0 != $this->mNamespace ) {
+ $p .= $wgLang->getNsText( $this->mNamespace ) . ":";
+ }
+ return $p . $name;
+ }
+
+ # Assumes that mDbkeyform has been set, and is urldecoded
+ # and uses undersocres, but not otherwise munged. This function
+ # removes illegal characters, splits off the winterwiki and
+ # namespace prefixes, sets the other forms, and canonicalizes
+ # everything. This one function is really at the core of
+ # Wiki--don't mess with it unless you're really sure you know
+ # what you're doing.
+ #
+ /* private */ function secureAndSplit()
+ {
+ global $wgLang, $wgValidInterwikis, $wgLocalInterwiki;
+
+ $validNamespaces = $wgLang->getNamespaces();
+ unset( $validNamespaces[0] );
+
+ $this->mInterwiki = $this->mFragment = "";
+ $this->mNamespace = 0;
+
+ $t = preg_replace( "/[\\s_]+/", "_", $this->mDbkeyform );
+ if ( "_" == $t{0} ) { $t = substr( $t, 1 ); }
+ $l = strlen( $t );
+ if ( $l && ( "_" == $t{$l-1} ) ) { $t = substr( $t, 0, $l-1 ); }
+ if ( "" == $t ) { $t = "_"; }
+
+ $this->mDbkeyform = $t;
+ $done = false;
+
+ $imgpre = ":" . $wgLang->getNsText( Namespace::getImage() ) . ":";
+ if ( 0 == strncasecmp( $imgpre, $t, strlen( $imgpre ) ) ) {
+ $t = substr( $t, 1 );
+ }
+ if ( ":" == $t{0} ) {
+ $r = substr( $t, 1 );
+ } else {
+ if ( preg_match( "/^([A-Za-z0-9_\\x80-\\xff]+):(.*)$/", $t, $m ) ) {
+ #$p = strtolower( $m[1] );
+ $p = $m[1];
+ if ( array_key_exists( $p, $wgValidInterwikis ) ) {
+ $t = $m[2];
+ $this->mInterwiki = $p;
+
+ if ( preg_match( "/^([A-Za-z0-9_\\x80-\\xff]+):(.*)$/",
+ $t, $m ) ) {
+ $p = strtolower( $m[1] );
+ } else {
+ $done = true;
+ }
+ if($this->mInterwiki != $wgLocalInterwiki)
+ $done = true;
+ }
+ if ( ! $done ) {
+ if ( $ns = $wgLang->getNsIndex( str_replace( " ", "_", $p ))) {
+ $t = $m[2];
+ $this->mNamespace = $ns;
+ }
+ # foreach ( $validNamespaces as $ns ) {
+ # if ( 0 == strcasecmp( $p, $ns ) ) {
+ # $t = $m[2];
+ # $this->mNamespace = $wgLang->getNsIndex(
+ # str_replace( " ", "_", $p ) );
+ # break;
+ # }
+ # }
+ }
+ }
+ $r = $t;
+ }
+ if ( 0 == strcmp( $this->mInterwiki, $wgLocalInterwiki ) ) {
+ $this->mInterwiki = "";
+ }
+ # We already know that some pages won't be in the database!
+ #
+ if ( "" != $this->mInterwiki || -1 == $this->mNamespace ) {
+ $this->mArticleID = 0;
+ }
+ $f = strstr( $r, "#" );
+ if ( false !== $f ) {
+ $this->mFragment = substr( $f, 1 );
+ $r = substr( $r, 0, strlen( $r ) - strlen( $f ) );
+ }
+ # Strip illegal characters.
+ #
+ $tc = Title::legalChars();
+ $t = preg_replace( "/[^{$tc}]/", "", $r );
+
+ if( $this->mInterwiki == "") $t = $wgLang->ucfirst( $t );
+ $this->mDbkeyform = $t;
+ $this->mUrlform = wfUrlencode( $t );
+ $this->mTextform = str_replace( "_", " ", $t );
+ }
+}
+?>
diff --git a/includes/UpdateClasses.php b/includes/UpdateClasses.php
new file mode 100644
index 000000000000..ed80289e601d
--- /dev/null
+++ b/includes/UpdateClasses.php
@@ -0,0 +1,11 @@
+<?
+# See deferred.doc
+global $IP;
+include_once( "$IP/UserUpdate.php" );
+include_once( "$IP/ViewCountUpdate.php" );
+include_once( "$IP/SiteStatsUpdate.php" );
+include_once( "$IP/LinksUpdate.php" );
+include_once( "$IP/SearchUpdate.php" );
+include_once( "$IP/UserTalkUpdate.php" );
+
+?>
diff --git a/includes/User.php b/includes/User.php
new file mode 100644
index 000000000000..127fe34eac74
--- /dev/null
+++ b/includes/User.php
@@ -0,0 +1,530 @@
+<?
+# See user.doc
+
+class User {
+ /* private */ var $mId, $mName, $mPassword, $mEmail, $mNewtalk;
+ /* private */ var $mRights, $mOptions;
+ /* private */ var $mDataLoaded, $mNewpassword;
+ /* private */ var $mSkin;
+ /* private */ var $mBlockedby, $mBlockreason;
+ /* private */ var $mTouched;
+
+ function User()
+ {
+ $this->loadDefaults();
+ }
+
+ # Static factory method
+ #
+ function newFromName( $name )
+ {
+ $u = new User();
+
+ # Clean up name according to title rules
+
+ $t = Title::newFromText( $name );
+ $u->setName( $t->getText() );
+ return $u;
+ }
+
+ /* static */ function whoIs( $id )
+ {
+ return wfGetSQL( "user", "user_name", "user_id=$id" );
+ }
+
+ /* static */ function idFromName( $name )
+ {
+ $nt = Title::newFromText( $name );
+ $sql = "SELECT user_id FROM user WHERE user_name='" .
+ wfStrencode( $nt->getText() ) . "'";
+ $res = wfQuery( $sql, "User::idFromName" );
+
+ if ( 0 == wfNumRows( $res ) ) { return 0; }
+ else {
+ $s = wfFetchObject( $res );
+ return $s->user_id;
+ }
+ }
+
+ # does the string match an anonymous user IP address?
+ /* static */ function isIP( $name ) {
+ return preg_match("/^\d{1,3}\.\d{1,3}.\d{1,3}\.\d{1,3}$/",$name);
+
+ }
+
+
+
+ /* static */ function randomPassword()
+ {
+ $pwchars = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz";
+ $l = strlen( $pwchars ) - 1;
+
+ wfSeedRandom();
+ $np = $pwchars{mt_rand( 0, $l )} . $pwchars{mt_rand( 0, $l )} .
+ $pwchars{mt_rand( 0, $l )} . chr( mt_rand(48, 57) ) .
+ $pwchars{mt_rand( 0, $l )} . $pwchars{mt_rand( 0, $l )} .
+ $pwchars{mt_rand( 0, $l )};
+ return $np;
+ }
+
+ function loadDefaults()
+ {
+ global $wgLang ;
+
+ $this->mId = $this->mNewtalk = 0;
+ $this->mName = getenv( "REMOTE_ADDR" );
+ $this->mEmail = "";
+ $this->mPassword = $this->mNewpassword = "";
+ $this->mRights = array();
+ $defOpt = $wgLang->getDefaultUserOptions() ;
+ foreach ( $defOpt as $oname => $val ) {
+ $this->mOptions[$oname] = $val;
+ }
+ unset( $this->mSkin );
+ $this->mDataLoaded = false;
+ $this->mBlockedby = -1; # Unset
+ $this->mTouched = '0'; # Allow any pages to be cached
+ }
+
+ /* private */ function getBlockedStatus()
+ {
+ if ( -1 != $this->mBlockedby ) { return; }
+
+ $remaddr = getenv( "REMOTE_ADDR" );
+ if ( 0 == $this->mId ) {
+ $sql = "SELECT ipb_by,ipb_reason FROM ipblocks WHERE " .
+ "ipb_address='$remaddr'";
+ } else {
+ $sql = "SELECT ipb_by,ipb_reason FROM ipblocks WHERE " .
+ "(ipb_address='$remaddr' OR ipb_user={$this->mId})";
+ }
+ $res = wfQuery( $sql, "User::getBlockedStatus" );
+ if ( 0 == wfNumRows( $res ) ) {
+ $this->mBlockedby = 0;
+ return;
+ }
+ $s = wfFetchObject( $res );
+ $this->mBlockedby = $s->ipb_by;
+ $this->mBlockreason = $s->ipb_reason;
+ }
+
+ function isBlocked()
+ {
+ $this->getBlockedStatus();
+ if ( 0 == $this->mBlockedby ) { return false; }
+ return true;
+ }
+
+ function blockedBy() {
+ $this->getBlockedStatus();
+ return $this->mBlockedby;
+ }
+
+ function blockedFor() {
+ $this->getBlockedStatus();
+ return $this->mBlockreason;
+ }
+
+ function loadFromSession()
+ {
+ global $HTTP_COOKIE_VARS, $wsUserID, $wsUserName, $wsUserPassword;
+
+ if ( isset( $wsUserID ) ) {
+ if ( 0 != $wsUserID ) {
+ $sId = $wsUserID;
+ } else {
+ $this->mId = 0;
+ return;
+ }
+ } else if ( isset( $HTTP_COOKIE_VARS["wcUserID"] ) ) {
+ $sId = $HTTP_COOKIE_VARS["wcUserID"];
+ $wsUserID = $sId;
+ } else {
+ $this->mId = 0;
+ return;
+ }
+ if ( isset( $wsUserName ) ) {
+ $sName = $wsUserName;
+ } else if ( isset( $HTTP_COOKIE_VARS["wcUserName"] ) ) {
+ $sName = $HTTP_COOKIE_VARS["wcUserName"];
+ $wsUserName = $sName;
+ } else {
+ $this->mId = 0;
+ return;
+ }
+ if ( isset( $wsUserPassword ) ) {
+ $sPass = $wsUserPassword;
+ } else if ( isset( $HTTP_COOKIE_VARS["wcUserPassword"] ) ) {
+ $sPass = $HTTP_COOKIE_VARS["wcUserPassword"];
+ $wsUserPassword = $sPass;
+ } else {
+ $this->mId = 0;
+ return;
+ }
+ $this->mId = $sId;
+ $this->loadFromDatabase();
+
+ if ( ( $sName == $this->mName ) &&
+ ( ( $sPass == $this->mPassword ) ||
+ ( $sPass == $this->mNewpassword ) ) ) {
+ return;
+ }
+ $this->loadDefaults(); # Can't log in from session
+ }
+
+ function loadFromDatabase()
+ {
+ if ( $this->mDataLoaded ) { return; }
+ # check in separate table if there are changes to the talk page
+ $this->mNewtalk=0; # reset talk page status
+ if($this->mId) {
+ $sql = "SELECT 1 FROM user_newtalk WHERE user_id={$this->mId}";
+ $res = wfQuery ($sql, "User::loadFromDatabase" );
+
+ if (wfNumRows($res)>0) {
+ $this->mNewtalk= 1;
+ }
+ wfFreeResult( $res );
+ } else {
+ $sql = "SELECT 1 FROM user_newtalk WHERE user_ip='{$this->mName}'";
+ $res = wfQuery ($sql, "User::loadFromDatabase" );
+
+ if (wfNumRows($res)>0) {
+ $this->mNewtalk= 1;
+ }
+ wfFreeResult( $res );
+ }
+ if(!$this->mId) {
+ $this->mDataLoaded = true;
+ return;
+ } # the following stuff is for non-anonymous users only
+
+ $sql = "SELECT user_name,user_password,user_newpassword,user_email," .
+ "user_options,user_rights,user_touched FROM user WHERE user_id=" .
+ "{$this->mId}";
+ $res = wfQuery( $sql, "User::loadFromDatabase" );
+
+ if ( wfNumRows( $res ) > 0 ) {
+ $s = wfFetchObject( $res );
+ $this->mName = $s->user_name;
+ $this->mEmail = $s->user_email;
+ $this->mPassword = $s->user_password;
+ $this->mNewpassword = $s->user_newpassword;
+ $this->decodeOptions( $s->user_options );
+ $this->mRights = explode( ",", strtolower( $s->user_rights ) );
+ $this->mTouched = $s->user_touched;
+ }
+
+ wfFreeResult( $res );
+ $this->mDataLoaded = true;
+ }
+
+ function getID() { return $this->mId; }
+ function setID( $v ) {
+ $this->mId = $v;
+ $this->mDataLoaded = false;
+ }
+
+ function getName() {
+ $this->loadFromDatabase();
+ return $this->mName;
+ }
+
+ function setName( $str )
+ {
+ $this->loadFromDatabase();
+ $this->mName = $str;
+ }
+
+ function getNewtalk()
+ {
+ $this->loadFromDatabase();
+ return ( 0 != $this->mNewtalk );
+ }
+
+ function setNewtalk( $val )
+ {
+ $this->loadFromDatabase();
+ $this->mNewtalk = $val;
+ $this->invalidateCache();
+ }
+
+ function invalidateCache() {
+ $this->loadFromDatabase();
+ $this->mTouched = wfTimestampNow();
+ # Don't forget to save the options after this or
+ # it won't take effect!
+ }
+
+ function validateCache( $timestamp ) {
+ $this->loadFromDatabase();
+ return ($timestamp >= $this->mTouched);
+ }
+
+ function getPassword()
+ {
+ $this->loadFromDatabase();
+ return $this->mPassword;
+ }
+
+ function getNewpassword()
+ {
+ $this->loadFromDatabase();
+ return $this->mNewpassword;
+ }
+
+ /* static */ function encryptPassword( $p )
+ {
+ $np = md5( $p );
+ return $np;
+ }
+
+ function setPassword( $str )
+ {
+ $this->loadFromDatabase();
+ $this->mPassword = User::encryptPassword( $str );
+ $this->mNewpassword = "";
+ }
+
+ function setNewpassword( $str )
+ {
+ $this->loadFromDatabase();
+ $this->mNewpassword = User::encryptPassword( $str );
+ }
+
+ function getEmail()
+ {
+ $this->loadFromDatabase();
+ return $this->mEmail;
+ }
+
+ function setEmail( $str )
+ {
+ $this->loadFromDatabase();
+ $this->mEmail = $str;
+ }
+
+ function getOption( $oname )
+ {
+ $this->loadFromDatabase();
+ if ( array_key_exists( $oname, $this->mOptions ) ) {
+ return $this->mOptions[$oname];
+ } else {
+ return "";
+ }
+ }
+
+ function setOption( $oname, $val )
+ {
+ $this->loadFromDatabase();
+ $this->mOptions[$oname] = $val;
+ $this->invalidateCache();
+ }
+
+ function getRights()
+ {
+ $this->loadFromDatabase();
+ return $this->mRights;
+ }
+
+ function isSysop()
+ {
+ $this->loadFromDatabase();
+ if ( 0 == $this->mId ) { return false; }
+
+ return in_array( "sysop", $this->mRights );
+ }
+
+ function isDeveloper()
+ {
+ $this->loadFromDatabase();
+ if ( 0 == $this->mId ) { return false; }
+
+ return in_array( "developer", $this->mRights );
+ }
+
+ function isBot()
+ {
+ $this->loadFromDatabase();
+ if ( 0 == $this->mId ) { return false; }
+
+ return in_array( "bot", $this->mRights );
+ }
+
+ function &getSkin()
+ {
+ if ( ! isset( $this->mSkin ) ) {
+ $skinNames = Skin::getSkinNames();
+ $s = $this->getOption( "skin" );
+ if ( "" == $s ) { $s = 0; }
+
+ if ( $s >= count( $skinNames ) ) { $sn = "SkinStandard"; }
+ else $sn = "Skin" . $skinNames[$s];
+ $this->mSkin = new $sn;
+ }
+ return $this->mSkin;
+ }
+
+ function isWatched( $title )
+ {
+ # Note - $title should be a Title _object_
+ # Pages and their talk pages are considered equivalent for watching;
+ # remember that talk namespaces are numbered as page namespace+1.
+ if( $this->mId ) {
+ $sql = "SELECT 1 FROM watchlist
+ WHERE wl_user={$this->mId} AND
+ wl_namespace = " . ($title->getNamespace() & ~1) . " AND
+ wl_title='" . wfStrencode( $title->getDBkey() ) . "'";
+ $res = wfQuery( $sql );
+ return (wfNumRows( $res ) > 0);
+ } else {
+ return false;
+ }
+ }
+
+ function addWatch( $title )
+ {
+ if( $this->mId ) {
+ # REPLACE instead of INSERT because occasionally someone
+ # accidentally reloads a watch-add operation.
+ $sql = "REPLACE INTO watchlist (wl_user, wl_namespace,wl_title)
+ VALUES ({$this->mId}," . (($title->getNamespace() | 1) - 1) .
+ ",'" . wfStrencode( $title->getDBkey() ) . "')";
+ wfQuery( $sql );
+ $this->invalidateCache();
+ }
+ }
+
+ function removeWatch( $title )
+ {
+ if( $this->mId ) {
+ $sql = "DELETE FROM watchlist WHERE wl_user={$this->mId} AND
+ wl_namespace=" . (($title->getNamespace() | 1) - 1) .
+ " AND wl_title='" . wfStrencode( $title->getDBkey() ) . "'";
+ wfQuery( $sql );
+ $this->invalidateCache();
+ }
+ }
+
+
+ /* private */ function encodeOptions()
+ {
+ $a = array();
+ foreach ( $this->mOptions as $oname => $oval ) {
+ array_push( $a, "{$oname}={$oval}" );
+ }
+ $s = implode( "\n", $a );
+ return wfStrencode( $s );
+ }
+
+ /* private */ function decodeOptions( $str )
+ {
+ $a = explode( "\n", $str );
+ foreach ( $a as $s ) {
+ if ( preg_match( "/^(.[^=]*)=(.*)$/", $s, $m ) ) {
+ $this->mOptions[$m[1]] = $m[2];
+ }
+ }
+ }
+
+ function setCookies()
+ {
+ global $wsUserID, $wsUserName, $wsUserPassword;
+ global $wgCookieExpiration;
+ if ( 0 == $this->mId ) return;
+ $this->loadFromDatabase();
+ $exp = time() + $wgCookieExpiration;
+
+ $wsUserID = $this->mId;
+ setcookie( "wcUserID", $this->mId, $exp, "/" );
+
+ $wsUserName = $this->mName;
+ setcookie( "wcUserName", $this->mName, $exp, "/" );
+
+ $wsUserPassword = $this->mPassword;
+ if ( 1 == $this->getOption( "rememberpassword" ) ) {
+ setcookie( "wcUserPassword", $this->mPassword, $exp, "/" );
+ } else {
+ setcookie( "wcUserPassword", "", time() - 3600 );
+ }
+ }
+
+ function logout()
+ {
+ global $wsUserID;
+ $this->mId = 0;
+
+ $wsUserID = 0;
+
+ setcookie( "wcUserID", "", time() - 3600 );
+ setcookie( "wcUserPassword", "", time() - 3600 );
+ }
+
+ function saveSettings()
+ {
+ global $wgUser;
+
+ if(!$this->mNewtalk) {
+
+ if($this->mId) {
+ $sql="DELETE FROM user_newtalk WHERE user_id={$this->mId}";
+ wfQuery ($sql,"User::saveSettings");
+ } else {
+
+
+ $sql="DELETE FROM user_newtalk WHERE user_ip='{$this->mName}'";
+ wfQuery ($sql,"User::saveSettings");
+
+ }
+ }
+
+ if ( 0 == $this->mId ) { return; }
+
+ $sql = "UPDATE user SET " .
+ "user_name= '" . wfStrencode( $this->mName ) . "', " .
+ "user_password= '" . wfStrencode( $this->mPassword ) . "', " .
+ "user_newpassword= '" . wfStrencode( $this->mNewpassword ) . "', " .
+ "user_email= '" . wfStrencode( $this->mEmail ) . "', " .
+ "user_options= '" . $this->encodeOptions() . "', " .
+ "user_rights= '" . wfStrencode( implode( ",", $this->mRights ) ) . "', " .
+ "user_touched= '" . wfStrencode( $this->mTouched ) .
+ "' WHERE user_id={$this->mId}";
+ wfQuery( $sql, "User::saveSettings" );
+ }
+
+ # Checks if a user with the given name exists
+ #
+ function idForName()
+ {
+ $gotid = 0;
+ $s = trim( $this->mName );
+ if ( 0 == strcmp( "", $s ) ) return 0;
+
+ $sql = "SELECT user_id FROM user WHERE user_name='" .
+ wfStrencode( $s ) . "'";
+ $res = wfQuery( $sql, "User::idForName" );
+ if ( 0 == wfNumRows( $res ) ) { return 0; }
+
+ $s = wfFetchObject( $res );
+ if ( "" == $s ) return 0;
+
+ $gotid = $s->user_id;
+ wfFreeResult( $res );
+ return $gotid;
+ }
+
+ function addToDatabase()
+ {
+ $sql = "INSERT INTO user (user_name,user_password,user_newpassword," .
+ "user_email, user_rights, user_options) " .
+ " VALUES ('" . wfStrencode( $this->mName ) . "', '" .
+ wfStrencode( $this->mPassword ) . "', '" .
+ wfStrencode( $this->mNewpassword ) . "', '" .
+ wfStrencode( $this->mEmail ) . "', '" .
+ wfStrencode( implode( ",", $this->mRights ) ) . "', '" .
+ $this->encodeOptions() . "')";
+ wfQuery( $sql, "User::addToDatabase" );
+ $this->mId = $this->idForName();
+ }
+}
+?>
diff --git a/includes/UserTalkUpdate.php b/includes/UserTalkUpdate.php
new file mode 100644
index 000000000000..011a7ba71118
--- /dev/null
+++ b/includes/UserTalkUpdate.php
@@ -0,0 +1,63 @@
+<?
+# See deferred.doc
+
+class UserTalkUpdate {
+
+ /* private */ var $mAction, $mNamespace, $mTitle;
+
+ function UserTalkUpdate( $action, $ns, $title )
+ {
+ $this->mAction = $action;
+ $this->mNamespace = $ns;
+ $this->mTitle = str_replace( "_", " ", $title );
+ }
+
+ function doUpdate()
+ {
+
+ global $wgUser, $wgLang;
+ $fname = "UserTalkUpdate::doUpdate";
+
+ # If namespace isn't User_talk:, do nothing.
+
+ if ( $this->mNamespace != Namespace::getTalk(
+ Namespace::getUser() ) ) {
+ return;
+ }
+ # If the user talk page is our own, clear the flag
+ # whether we are reading it or writing it.
+ if ( 0 == strcmp( $this->mTitle, $wgUser->getName() ) ) {
+
+ $wgUser->setNewtalk( 0 );
+ $wgUser->saveSettings();
+
+ } else {
+ # Not ours. If writing, mark it as modified.
+
+ if ( 1 == $this->mAction ) {
+ $user = new User();
+ $user->setID(User::idFromName($this->mTitle));
+ if ($id=$user->getID()) {
+ $sql = "INSERT INTO user_newtalk (user_id) values ({$id})";
+
+ } else { #anon
+
+ if(preg_match("/^\d{1,3}\.\d{1,3}.\d{1,3}\.\d{1,3}$/",$this->mTitle)) { #real anon (user:xxx.xxx.xxx.xxx)
+
+ $sql = "INSERT INTO user_newtalk (user_id,user_ip) values (0,\"{$this->mTitle}\")";
+
+ }
+
+ }
+
+ if($sql && !$user->getNewtalk()) { # only insert if real user and it's not already there
+ wfQuery( $sql, $fname );
+ }
+ }
+ }
+
+
+ }
+}
+
+?>
diff --git a/includes/UserUpdate.php b/includes/UserUpdate.php
new file mode 100644
index 000000000000..1d28cf38f9ad
--- /dev/null
+++ b/includes/UserUpdate.php
@@ -0,0 +1,15 @@
+<?
+# See deferred.doc
+
+class UserUpdate {
+
+ function UserUpdate() { }
+
+ function doUpdate()
+ {
+ global $wgUser;
+ $wgUser->saveSettings();
+ }
+}
+
+?>
diff --git a/includes/Utf8Case.php b/includes/Utf8Case.php
new file mode 100644
index 000000000000..1f0208e55e17
--- /dev/null
+++ b/includes/Utf8Case.php
@@ -0,0 +1,1520 @@
+<?
+$wgInputEncoding = "utf-8";
+$wgOutputEncoding = "utf-8";
+
+# Simple 1:1 upper/lowercase switching arrays for utf-8 text
+# Won't get context-sensitive things yet
+
+# Hack for bugs in ucfirst() and company
+
+$wikiUpperChars = array(
+ "a" => "A",
+ "b" => "B",
+ "c" => "C",
+ "d" => "D",
+ "e" => "E",
+ "f" => "F",
+ "g" => "G",
+ "h" => "H",
+ "i" => "I",
+ "j" => "J",
+ "k" => "K",
+ "l" => "L",
+ "m" => "M",
+ "n" => "N",
+ "o" => "O",
+ "p" => "P",
+ "q" => "Q",
+ "r" => "R",
+ "s" => "S",
+ "t" => "T",
+ "u" => "U",
+ "v" => "V",
+ "w" => "W",
+ "x" => "X",
+ "y" => "Y",
+ "z" => "Z",
+ "\xc2\xb5" => "\xce\x9c",
+ "\xc3\xa0" => "\xc3\x80",
+ "\xc3\xa1" => "\xc3\x81",
+ "\xc3\xa2" => "\xc3\x82",
+ "\xc3\xa3" => "\xc3\x83",
+ "\xc3\xa4" => "\xc3\x84",
+ "\xc3\xa5" => "\xc3\x85",
+ "\xc3\xa6" => "\xc3\x86",
+ "\xc3\xa7" => "\xc3\x87",
+ "\xc3\xa8" => "\xc3\x88",
+ "\xc3\xa9" => "\xc3\x89",
+ "\xc3\xaa" => "\xc3\x8a",
+ "\xc3\xab" => "\xc3\x8b",
+ "\xc3\xac" => "\xc3\x8c",
+ "\xc3\xad" => "\xc3\x8d",
+ "\xc3\xae" => "\xc3\x8e",
+ "\xc3\xaf" => "\xc3\x8f",
+ "\xc3\xb0" => "\xc3\x90",
+ "\xc3\xb1" => "\xc3\x91",
+ "\xc3\xb2" => "\xc3\x92",
+ "\xc3\xb3" => "\xc3\x93",
+ "\xc3\xb4" => "\xc3\x94",
+ "\xc3\xb5" => "\xc3\x95",
+ "\xc3\xb6" => "\xc3\x96",
+ "\xc3\xb8" => "\xc3\x98",
+ "\xc3\xb9" => "\xc3\x99",
+ "\xc3\xba" => "\xc3\x9a",
+ "\xc3\xbb" => "\xc3\x9b",
+ "\xc3\xbc" => "\xc3\x9c",
+ "\xc3\xbd" => "\xc3\x9d",
+ "\xc3\xbe" => "\xc3\x9e",
+ "\xc3\xbf" => "\xc5\xb8",
+ "\xc4\x81" => "\xc4\x80",
+ "\xc4\x83" => "\xc4\x82",
+ "\xc4\x85" => "\xc4\x84",
+ "\xc4\x87" => "\xc4\x86",
+ "\xc4\x89" => "\xc4\x88",
+ "\xc4\x8b" => "\xc4\x8a",
+ "\xc4\x8d" => "\xc4\x8c",
+ "\xc4\x8f" => "\xc4\x8e",
+ "\xc4\x91" => "\xc4\x90",
+ "\xc4\x93" => "\xc4\x92",
+ "\xc4\x95" => "\xc4\x94",
+ "\xc4\x97" => "\xc4\x96",
+ "\xc4\x99" => "\xc4\x98",
+ "\xc4\x9b" => "\xc4\x9a",
+ "\xc4\x9d" => "\xc4\x9c",
+ "\xc4\x9f" => "\xc4\x9e",
+ "\xc4\xa1" => "\xc4\xa0",
+ "\xc4\xa3" => "\xc4\xa2",
+ "\xc4\xa5" => "\xc4\xa4",
+ "\xc4\xa7" => "\xc4\xa6",
+ "\xc4\xa9" => "\xc4\xa8",
+ "\xc4\xab" => "\xc4\xaa",
+ "\xc4\xad" => "\xc4\xac",
+ "\xc4\xaf" => "\xc4\xae",
+ "\xc4\xb1" => "I",
+ "\xc4\xb3" => "\xc4\xb2",
+ "\xc4\xb5" => "\xc4\xb4",
+ "\xc4\xb7" => "\xc4\xb6",
+ "\xc4\xba" => "\xc4\xb9",
+ "\xc4\xbc" => "\xc4\xbb",
+ "\xc4\xbe" => "\xc4\xbd",
+ "\xc5\x80" => "\xc4\xbf",
+ "\xc5\x82" => "\xc5\x81",
+ "\xc5\x84" => "\xc5\x83",
+ "\xc5\x86" => "\xc5\x85",
+ "\xc5\x88" => "\xc5\x87",
+ "\xc5\x8b" => "\xc5\x8a",
+ "\xc5\x8d" => "\xc5\x8c",
+ "\xc5\x8f" => "\xc5\x8e",
+ "\xc5\x91" => "\xc5\x90",
+ "\xc5\x93" => "\xc5\x92",
+ "\xc5\x95" => "\xc5\x94",
+ "\xc5\x97" => "\xc5\x96",
+ "\xc5\x99" => "\xc5\x98",
+ "\xc5\x9b" => "\xc5\x9a",
+ "\xc5\x9d" => "\xc5\x9c",
+ "\xc5\x9f" => "\xc5\x9e",
+ "\xc5\xa1" => "\xc5\xa0",
+ "\xc5\xa3" => "\xc5\xa2",
+ "\xc5\xa5" => "\xc5\xa4",
+ "\xc5\xa7" => "\xc5\xa6",
+ "\xc5\xa9" => "\xc5\xa8",
+ "\xc5\xab" => "\xc5\xaa",
+ "\xc5\xad" => "\xc5\xac",
+ "\xc5\xaf" => "\xc5\xae",
+ "\xc5\xb1" => "\xc5\xb0",
+ "\xc5\xb3" => "\xc5\xb2",
+ "\xc5\xb5" => "\xc5\xb4",
+ "\xc5\xb7" => "\xc5\xb6",
+ "\xc5\xba" => "\xc5\xb9",
+ "\xc5\xbc" => "\xc5\xbb",
+ "\xc5\xbe" => "\xc5\xbd",
+ "\xc5\xbf" => "S",
+ "\xc6\x83" => "\xc6\x82",
+ "\xc6\x85" => "\xc6\x84",
+ "\xc6\x88" => "\xc6\x87",
+ "\xc6\x8c" => "\xc6\x8b",
+ "\xc6\x92" => "\xc6\x91",
+ "\xc6\x95" => "\xc7\xb6",
+ "\xc6\x99" => "\xc6\x98",
+ "\xc6\xa1" => "\xc6\xa0",
+ "\xc6\xa3" => "\xc6\xa2",
+ "\xc6\xa5" => "\xc6\xa4",
+ "\xc6\xa8" => "\xc6\xa7",
+ "\xc6\xad" => "\xc6\xac",
+ "\xc6\xb0" => "\xc6\xaf",
+ "\xc6\xb4" => "\xc6\xb3",
+ "\xc6\xb6" => "\xc6\xb5",
+ "\xc6\xb9" => "\xc6\xb8",
+ "\xc6\xbd" => "\xc6\xbc",
+ "\xc6\xbf" => "\xc7\xb7",
+ "\xc7\x85" => "\xc7\x84",
+ "\xc7\x86" => "\xc7\x84",
+ "\xc7\x88" => "\xc7\x87",
+ "\xc7\x89" => "\xc7\x87",
+ "\xc7\x8b" => "\xc7\x8a",
+ "\xc7\x8c" => "\xc7\x8a",
+ "\xc7\x8e" => "\xc7\x8d",
+ "\xc7\x90" => "\xc7\x8f",
+ "\xc7\x92" => "\xc7\x91",
+ "\xc7\x94" => "\xc7\x93",
+ "\xc7\x96" => "\xc7\x95",
+ "\xc7\x98" => "\xc7\x97",
+ "\xc7\x9a" => "\xc7\x99",
+ "\xc7\x9c" => "\xc7\x9b",
+ "\xc7\x9d" => "\xc6\x8e",
+ "\xc7\x9f" => "\xc7\x9e",
+ "\xc7\xa1" => "\xc7\xa0",
+ "\xc7\xa3" => "\xc7\xa2",
+ "\xc7\xa5" => "\xc7\xa4",
+ "\xc7\xa7" => "\xc7\xa6",
+ "\xc7\xa9" => "\xc7\xa8",
+ "\xc7\xab" => "\xc7\xaa",
+ "\xc7\xad" => "\xc7\xac",
+ "\xc7\xaf" => "\xc7\xae",
+ "\xc7\xb2" => "\xc7\xb1",
+ "\xc7\xb3" => "\xc7\xb1",
+ "\xc7\xb5" => "\xc7\xb4",
+ "\xc7\xb9" => "\xc7\xb8",
+ "\xc7\xbb" => "\xc7\xba",
+ "\xc7\xbd" => "\xc7\xbc",
+ "\xc7\xbf" => "\xc7\xbe",
+ "\xc8\x81" => "\xc8\x80",
+ "\xc8\x83" => "\xc8\x82",
+ "\xc8\x85" => "\xc8\x84",
+ "\xc8\x87" => "\xc8\x86",
+ "\xc8\x89" => "\xc8\x88",
+ "\xc8\x8b" => "\xc8\x8a",
+ "\xc8\x8d" => "\xc8\x8c",
+ "\xc8\x8f" => "\xc8\x8e",
+ "\xc8\x91" => "\xc8\x90",
+ "\xc8\x93" => "\xc8\x92",
+ "\xc8\x95" => "\xc8\x94",
+ "\xc8\x97" => "\xc8\x96",
+ "\xc8\x99" => "\xc8\x98",
+ "\xc8\x9b" => "\xc8\x9a",
+ "\xc8\x9d" => "\xc8\x9c",
+ "\xc8\x9f" => "\xc8\x9e",
+ "\xc8\xa3" => "\xc8\xa2",
+ "\xc8\xa5" => "\xc8\xa4",
+ "\xc8\xa7" => "\xc8\xa6",
+ "\xc8\xa9" => "\xc8\xa8",
+ "\xc8\xab" => "\xc8\xaa",
+ "\xc8\xad" => "\xc8\xac",
+ "\xc8\xaf" => "\xc8\xae",
+ "\xc8\xb1" => "\xc8\xb0",
+ "\xc8\xb3" => "\xc8\xb2",
+ "\xc9\x93" => "\xc6\x81",
+ "\xc9\x94" => "\xc6\x86",
+ "\xc9\x96" => "\xc6\x89",
+ "\xc9\x97" => "\xc6\x8a",
+ "\xc9\x99" => "\xc6\x8f",
+ "\xc9\x9b" => "\xc6\x90",
+ "\xc9\xa0" => "\xc6\x93",
+ "\xc9\xa3" => "\xc6\x94",
+ "\xc9\xa8" => "\xc6\x97",
+ "\xc9\xa9" => "\xc6\x96",
+ "\xc9\xaf" => "\xc6\x9c",
+ "\xc9\xb2" => "\xc6\x9d",
+ "\xc9\xb5" => "\xc6\x9f",
+ "\xca\x80" => "\xc6\xa6",
+ "\xca\x83" => "\xc6\xa9",
+ "\xca\x88" => "\xc6\xae",
+ "\xca\x8a" => "\xc6\xb1",
+ "\xca\x8b" => "\xc6\xb2",
+ "\xca\x92" => "\xc6\xb7",
+ "\xcd\x85" => "\xce\x99",
+ "\xce\xac" => "\xce\x86",
+ "\xce\xad" => "\xce\x88",
+ "\xce\xae" => "\xce\x89",
+ "\xce\xaf" => "\xce\x8a",
+ "\xce\xb1" => "\xce\x91",
+ "\xce\xb2" => "\xce\x92",
+ "\xce\xb3" => "\xce\x93",
+ "\xce\xb4" => "\xce\x94",
+ "\xce\xb5" => "\xce\x95",
+ "\xce\xb6" => "\xce\x96",
+ "\xce\xb7" => "\xce\x97",
+ "\xce\xb8" => "\xce\x98",
+ "\xce\xb9" => "\xce\x99",
+ "\xce\xba" => "\xce\x9a",
+ "\xce\xbb" => "\xce\x9b",
+ "\xce\xbc" => "\xce\x9c",
+ "\xce\xbd" => "\xce\x9d",
+ "\xce\xbe" => "\xce\x9e",
+ "\xce\xbf" => "\xce\x9f",
+ "\xcf\x80" => "\xce\xa0",
+ "\xcf\x81" => "\xce\xa1",
+ "\xcf\x82" => "\xce\xa3",
+ "\xcf\x83" => "\xce\xa3",
+ "\xcf\x84" => "\xce\xa4",
+ "\xcf\x85" => "\xce\xa5",
+ "\xcf\x86" => "\xce\xa6",
+ "\xcf\x87" => "\xce\xa7",
+ "\xcf\x88" => "\xce\xa8",
+ "\xcf\x89" => "\xce\xa9",
+ "\xcf\x8a" => "\xce\xaa",
+ "\xcf\x8b" => "\xce\xab",
+ "\xcf\x8c" => "\xce\x8c",
+ "\xcf\x8d" => "\xce\x8e",
+ "\xcf\x8e" => "\xce\x8f",
+ "\xcf\x90" => "\xce\x92",
+ "\xcf\x91" => "\xce\x98",
+ "\xcf\x95" => "\xce\xa6",
+ "\xcf\x96" => "\xce\xa0",
+ "\xcf\x9b" => "\xcf\x9a",
+ "\xcf\x9d" => "\xcf\x9c",
+ "\xcf\x9f" => "\xcf\x9e",
+ "\xcf\xa1" => "\xcf\xa0",
+ "\xcf\xa3" => "\xcf\xa2",
+ "\xcf\xa5" => "\xcf\xa4",
+ "\xcf\xa7" => "\xcf\xa6",
+ "\xcf\xa9" => "\xcf\xa8",
+ "\xcf\xab" => "\xcf\xaa",
+ "\xcf\xad" => "\xcf\xac",
+ "\xcf\xaf" => "\xcf\xae",
+ "\xcf\xb0" => "\xce\x9a",
+ "\xcf\xb1" => "\xce\xa1",
+ "\xcf\xb2" => "\xce\xa3",
+ "\xcf\xb5" => "\xce\x95",
+ "\xd0\xb0" => "\xd0\x90",
+ "\xd0\xb1" => "\xd0\x91",
+ "\xd0\xb2" => "\xd0\x92",
+ "\xd0\xb3" => "\xd0\x93",
+ "\xd0\xb4" => "\xd0\x94",
+ "\xd0\xb5" => "\xd0\x95",
+ "\xd0\xb6" => "\xd0\x96",
+ "\xd0\xb7" => "\xd0\x97",
+ "\xd0\xb8" => "\xd0\x98",
+ "\xd0\xb9" => "\xd0\x99",
+ "\xd0\xba" => "\xd0\x9a",
+ "\xd0\xbb" => "\xd0\x9b",
+ "\xd0\xbc" => "\xd0\x9c",
+ "\xd0\xbd" => "\xd0\x9d",
+ "\xd0\xbe" => "\xd0\x9e",
+ "\xd0\xbf" => "\xd0\x9f",
+ "\xd1\x80" => "\xd0\xa0",
+ "\xd1\x81" => "\xd0\xa1",
+ "\xd1\x82" => "\xd0\xa2",
+ "\xd1\x83" => "\xd0\xa3",
+ "\xd1\x84" => "\xd0\xa4",
+ "\xd1\x85" => "\xd0\xa5",
+ "\xd1\x86" => "\xd0\xa6",
+ "\xd1\x87" => "\xd0\xa7",
+ "\xd1\x88" => "\xd0\xa8",
+ "\xd1\x89" => "\xd0\xa9",
+ "\xd1\x8a" => "\xd0\xaa",
+ "\xd1\x8b" => "\xd0\xab",
+ "\xd1\x8c" => "\xd0\xac",
+ "\xd1\x8d" => "\xd0\xad",
+ "\xd1\x8e" => "\xd0\xae",
+ "\xd1\x8f" => "\xd0\xaf",
+ "\xd1\x90" => "\xd0\x80",
+ "\xd1\x91" => "\xd0\x81",
+ "\xd1\x92" => "\xd0\x82",
+ "\xd1\x93" => "\xd0\x83",
+ "\xd1\x94" => "\xd0\x84",
+ "\xd1\x95" => "\xd0\x85",
+ "\xd1\x96" => "\xd0\x86",
+ "\xd1\x97" => "\xd0\x87",
+ "\xd1\x98" => "\xd0\x88",
+ "\xd1\x99" => "\xd0\x89",
+ "\xd1\x9a" => "\xd0\x8a",
+ "\xd1\x9b" => "\xd0\x8b",
+ "\xd1\x9c" => "\xd0\x8c",
+ "\xd1\x9d" => "\xd0\x8d",
+ "\xd1\x9e" => "\xd0\x8e",
+ "\xd1\x9f" => "\xd0\x8f",
+ "\xd1\xa1" => "\xd1\xa0",
+ "\xd1\xa3" => "\xd1\xa2",
+ "\xd1\xa5" => "\xd1\xa4",
+ "\xd1\xa7" => "\xd1\xa6",
+ "\xd1\xa9" => "\xd1\xa8",
+ "\xd1\xab" => "\xd1\xaa",
+ "\xd1\xad" => "\xd1\xac",
+ "\xd1\xaf" => "\xd1\xae",
+ "\xd1\xb1" => "\xd1\xb0",
+ "\xd1\xb3" => "\xd1\xb2",
+ "\xd1\xb5" => "\xd1\xb4",
+ "\xd1\xb7" => "\xd1\xb6",
+ "\xd1\xb9" => "\xd1\xb8",
+ "\xd1\xbb" => "\xd1\xba",
+ "\xd1\xbd" => "\xd1\xbc",
+ "\xd1\xbf" => "\xd1\xbe",
+ "\xd2\x81" => "\xd2\x80",
+ "\xd2\x8d" => "\xd2\x8c",
+ "\xd2\x8f" => "\xd2\x8e",
+ "\xd2\x91" => "\xd2\x90",
+ "\xd2\x93" => "\xd2\x92",
+ "\xd2\x95" => "\xd2\x94",
+ "\xd2\x97" => "\xd2\x96",
+ "\xd2\x99" => "\xd2\x98",
+ "\xd2\x9b" => "\xd2\x9a",
+ "\xd2\x9d" => "\xd2\x9c",
+ "\xd2\x9f" => "\xd2\x9e",
+ "\xd2\xa1" => "\xd2\xa0",
+ "\xd2\xa3" => "\xd2\xa2",
+ "\xd2\xa5" => "\xd2\xa4",
+ "\xd2\xa7" => "\xd2\xa6",
+ "\xd2\xa9" => "\xd2\xa8",
+ "\xd2\xab" => "\xd2\xaa",
+ "\xd2\xad" => "\xd2\xac",
+ "\xd2\xaf" => "\xd2\xae",
+ "\xd2\xb1" => "\xd2\xb0",
+ "\xd2\xb3" => "\xd2\xb2",
+ "\xd2\xb5" => "\xd2\xb4",
+ "\xd2\xb7" => "\xd2\xb6",
+ "\xd2\xb9" => "\xd2\xb8",
+ "\xd2\xbb" => "\xd2\xba",
+ "\xd2\xbd" => "\xd2\xbc",
+ "\xd2\xbf" => "\xd2\xbe",
+ "\xd3\x82" => "\xd3\x81",
+ "\xd3\x84" => "\xd3\x83",
+ "\xd3\x88" => "\xd3\x87",
+ "\xd3\x8c" => "\xd3\x8b",
+ "\xd3\x91" => "\xd3\x90",
+ "\xd3\x93" => "\xd3\x92",
+ "\xd3\x95" => "\xd3\x94",
+ "\xd3\x97" => "\xd3\x96",
+ "\xd3\x99" => "\xd3\x98",
+ "\xd3\x9b" => "\xd3\x9a",
+ "\xd3\x9d" => "\xd3\x9c",
+ "\xd3\x9f" => "\xd3\x9e",
+ "\xd3\xa1" => "\xd3\xa0",
+ "\xd3\xa3" => "\xd3\xa2",
+ "\xd3\xa5" => "\xd3\xa4",
+ "\xd3\xa7" => "\xd3\xa6",
+ "\xd3\xa9" => "\xd3\xa8",
+ "\xd3\xab" => "\xd3\xaa",
+ "\xd3\xad" => "\xd3\xac",
+ "\xd3\xaf" => "\xd3\xae",
+ "\xd3\xb1" => "\xd3\xb0",
+ "\xd3\xb3" => "\xd3\xb2",
+ "\xd3\xb5" => "\xd3\xb4",
+ "\xd3\xb9" => "\xd3\xb8",
+ "\xd5\xa1" => "\xd4\xb1",
+ "\xd5\xa2" => "\xd4\xb2",
+ "\xd5\xa3" => "\xd4\xb3",
+ "\xd5\xa4" => "\xd4\xb4",
+ "\xd5\xa5" => "\xd4\xb5",
+ "\xd5\xa6" => "\xd4\xb6",
+ "\xd5\xa7" => "\xd4\xb7",
+ "\xd5\xa8" => "\xd4\xb8",
+ "\xd5\xa9" => "\xd4\xb9",
+ "\xd5\xaa" => "\xd4\xba",
+ "\xd5\xab" => "\xd4\xbb",
+ "\xd5\xac" => "\xd4\xbc",
+ "\xd5\xad" => "\xd4\xbd",
+ "\xd5\xae" => "\xd4\xbe",
+ "\xd5\xaf" => "\xd4\xbf",
+ "\xd5\xb0" => "\xd5\x80",
+ "\xd5\xb1" => "\xd5\x81",
+ "\xd5\xb2" => "\xd5\x82",
+ "\xd5\xb3" => "\xd5\x83",
+ "\xd5\xb4" => "\xd5\x84",
+ "\xd5\xb5" => "\xd5\x85",
+ "\xd5\xb6" => "\xd5\x86",
+ "\xd5\xb7" => "\xd5\x87",
+ "\xd5\xb8" => "\xd5\x88",
+ "\xd5\xb9" => "\xd5\x89",
+ "\xd5\xba" => "\xd5\x8a",
+ "\xd5\xbb" => "\xd5\x8b",
+ "\xd5\xbc" => "\xd5\x8c",
+ "\xd5\xbd" => "\xd5\x8d",
+ "\xd5\xbe" => "\xd5\x8e",
+ "\xd5\xbf" => "\xd5\x8f",
+ "\xd6\x80" => "\xd5\x90",
+ "\xd6\x81" => "\xd5\x91",
+ "\xd6\x82" => "\xd5\x92",
+ "\xd6\x83" => "\xd5\x93",
+ "\xd6\x84" => "\xd5\x94",
+ "\xd6\x85" => "\xd5\x95",
+ "\xd6\x86" => "\xd5\x96",
+ "\xe1\xb8\x81" => "\xe1\xb8\x80",
+ "\xe1\xb8\x83" => "\xe1\xb8\x82",
+ "\xe1\xb8\x85" => "\xe1\xb8\x84",
+ "\xe1\xb8\x87" => "\xe1\xb8\x86",
+ "\xe1\xb8\x89" => "\xe1\xb8\x88",
+ "\xe1\xb8\x8b" => "\xe1\xb8\x8a",
+ "\xe1\xb8\x8d" => "\xe1\xb8\x8c",
+ "\xe1\xb8\x8f" => "\xe1\xb8\x8e",
+ "\xe1\xb8\x91" => "\xe1\xb8\x90",
+ "\xe1\xb8\x93" => "\xe1\xb8\x92",
+ "\xe1\xb8\x95" => "\xe1\xb8\x94",
+ "\xe1\xb8\x97" => "\xe1\xb8\x96",
+ "\xe1\xb8\x99" => "\xe1\xb8\x98",
+ "\xe1\xb8\x9b" => "\xe1\xb8\x9a",
+ "\xe1\xb8\x9d" => "\xe1\xb8\x9c",
+ "\xe1\xb8\x9f" => "\xe1\xb8\x9e",
+ "\xe1\xb8\xa1" => "\xe1\xb8\xa0",
+ "\xe1\xb8\xa3" => "\xe1\xb8\xa2",
+ "\xe1\xb8\xa5" => "\xe1\xb8\xa4",
+ "\xe1\xb8\xa7" => "\xe1\xb8\xa6",
+ "\xe1\xb8\xa9" => "\xe1\xb8\xa8",
+ "\xe1\xb8\xab" => "\xe1\xb8\xaa",
+ "\xe1\xb8\xad" => "\xe1\xb8\xac",
+ "\xe1\xb8\xaf" => "\xe1\xb8\xae",
+ "\xe1\xb8\xb1" => "\xe1\xb8\xb0",
+ "\xe1\xb8\xb3" => "\xe1\xb8\xb2",
+ "\xe1\xb8\xb5" => "\xe1\xb8\xb4",
+ "\xe1\xb8\xb7" => "\xe1\xb8\xb6",
+ "\xe1\xb8\xb9" => "\xe1\xb8\xb8",
+ "\xe1\xb8\xbb" => "\xe1\xb8\xba",
+ "\xe1\xb8\xbd" => "\xe1\xb8\xbc",
+ "\xe1\xb8\xbf" => "\xe1\xb8\xbe",
+ "\xe1\xb9\x81" => "\xe1\xb9\x80",
+ "\xe1\xb9\x83" => "\xe1\xb9\x82",
+ "\xe1\xb9\x85" => "\xe1\xb9\x84",
+ "\xe1\xb9\x87" => "\xe1\xb9\x86",
+ "\xe1\xb9\x89" => "\xe1\xb9\x88",
+ "\xe1\xb9\x8b" => "\xe1\xb9\x8a",
+ "\xe1\xb9\x8d" => "\xe1\xb9\x8c",
+ "\xe1\xb9\x8f" => "\xe1\xb9\x8e",
+ "\xe1\xb9\x91" => "\xe1\xb9\x90",
+ "\xe1\xb9\x93" => "\xe1\xb9\x92",
+ "\xe1\xb9\x95" => "\xe1\xb9\x94",
+ "\xe1\xb9\x97" => "\xe1\xb9\x96",
+ "\xe1\xb9\x99" => "\xe1\xb9\x98",
+ "\xe1\xb9\x9b" => "\xe1\xb9\x9a",
+ "\xe1\xb9\x9d" => "\xe1\xb9\x9c",
+ "\xe1\xb9\x9f" => "\xe1\xb9\x9e",
+ "\xe1\xb9\xa1" => "\xe1\xb9\xa0",
+ "\xe1\xb9\xa3" => "\xe1\xb9\xa2",
+ "\xe1\xb9\xa5" => "\xe1\xb9\xa4",
+ "\xe1\xb9\xa7" => "\xe1\xb9\xa6",
+ "\xe1\xb9\xa9" => "\xe1\xb9\xa8",
+ "\xe1\xb9\xab" => "\xe1\xb9\xaa",
+ "\xe1\xb9\xad" => "\xe1\xb9\xac",
+ "\xe1\xb9\xaf" => "\xe1\xb9\xae",
+ "\xe1\xb9\xb1" => "\xe1\xb9\xb0",
+ "\xe1\xb9\xb3" => "\xe1\xb9\xb2",
+ "\xe1\xb9\xb5" => "\xe1\xb9\xb4",
+ "\xe1\xb9\xb7" => "\xe1\xb9\xb6",
+ "\xe1\xb9\xb9" => "\xe1\xb9\xb8",
+ "\xe1\xb9\xbb" => "\xe1\xb9\xba",
+ "\xe1\xb9\xbd" => "\xe1\xb9\xbc",
+ "\xe1\xb9\xbf" => "\xe1\xb9\xbe",
+ "\xe1\xba\x81" => "\xe1\xba\x80",
+ "\xe1\xba\x83" => "\xe1\xba\x82",
+ "\xe1\xba\x85" => "\xe1\xba\x84",
+ "\xe1\xba\x87" => "\xe1\xba\x86",
+ "\xe1\xba\x89" => "\xe1\xba\x88",
+ "\xe1\xba\x8b" => "\xe1\xba\x8a",
+ "\xe1\xba\x8d" => "\xe1\xba\x8c",
+ "\xe1\xba\x8f" => "\xe1\xba\x8e",
+ "\xe1\xba\x91" => "\xe1\xba\x90",
+ "\xe1\xba\x93" => "\xe1\xba\x92",
+ "\xe1\xba\x95" => "\xe1\xba\x94",
+ "\xe1\xba\x9b" => "\xe1\xb9\xa0",
+ "\xe1\xba\xa1" => "\xe1\xba\xa0",
+ "\xe1\xba\xa3" => "\xe1\xba\xa2",
+ "\xe1\xba\xa5" => "\xe1\xba\xa4",
+ "\xe1\xba\xa7" => "\xe1\xba\xa6",
+ "\xe1\xba\xa9" => "\xe1\xba\xa8",
+ "\xe1\xba\xab" => "\xe1\xba\xaa",
+ "\xe1\xba\xad" => "\xe1\xba\xac",
+ "\xe1\xba\xaf" => "\xe1\xba\xae",
+ "\xe1\xba\xb1" => "\xe1\xba\xb0",
+ "\xe1\xba\xb3" => "\xe1\xba\xb2",
+ "\xe1\xba\xb5" => "\xe1\xba\xb4",
+ "\xe1\xba\xb7" => "\xe1\xba\xb6",
+ "\xe1\xba\xb9" => "\xe1\xba\xb8",
+ "\xe1\xba\xbb" => "\xe1\xba\xba",
+ "\xe1\xba\xbd" => "\xe1\xba\xbc",
+ "\xe1\xba\xbf" => "\xe1\xba\xbe",
+ "\xe1\xbb\x81" => "\xe1\xbb\x80",
+ "\xe1\xbb\x83" => "\xe1\xbb\x82",
+ "\xe1\xbb\x85" => "\xe1\xbb\x84",
+ "\xe1\xbb\x87" => "\xe1\xbb\x86",
+ "\xe1\xbb\x89" => "\xe1\xbb\x88",
+ "\xe1\xbb\x8b" => "\xe1\xbb\x8a",
+ "\xe1\xbb\x8d" => "\xe1\xbb\x8c",
+ "\xe1\xbb\x8f" => "\xe1\xbb\x8e",
+ "\xe1\xbb\x91" => "\xe1\xbb\x90",
+ "\xe1\xbb\x93" => "\xe1\xbb\x92",
+ "\xe1\xbb\x95" => "\xe1\xbb\x94",
+ "\xe1\xbb\x97" => "\xe1\xbb\x96",
+ "\xe1\xbb\x99" => "\xe1\xbb\x98",
+ "\xe1\xbb\x9b" => "\xe1\xbb\x9a",
+ "\xe1\xbb\x9d" => "\xe1\xbb\x9c",
+ "\xe1\xbb\x9f" => "\xe1\xbb\x9e",
+ "\xe1\xbb\xa1" => "\xe1\xbb\xa0",
+ "\xe1\xbb\xa3" => "\xe1\xbb\xa2",
+ "\xe1\xbb\xa5" => "\xe1\xbb\xa4",
+ "\xe1\xbb\xa7" => "\xe1\xbb\xa6",
+ "\xe1\xbb\xa9" => "\xe1\xbb\xa8",
+ "\xe1\xbb\xab" => "\xe1\xbb\xaa",
+ "\xe1\xbb\xad" => "\xe1\xbb\xac",
+ "\xe1\xbb\xaf" => "\xe1\xbb\xae",
+ "\xe1\xbb\xb1" => "\xe1\xbb\xb0",
+ "\xe1\xbb\xb3" => "\xe1\xbb\xb2",
+ "\xe1\xbb\xb5" => "\xe1\xbb\xb4",
+ "\xe1\xbb\xb7" => "\xe1\xbb\xb6",
+ "\xe1\xbb\xb9" => "\xe1\xbb\xb8",
+ "\xe1\xbc\x80" => "\xe1\xbc\x88",
+ "\xe1\xbc\x81" => "\xe1\xbc\x89",
+ "\xe1\xbc\x82" => "\xe1\xbc\x8a",
+ "\xe1\xbc\x83" => "\xe1\xbc\x8b",
+ "\xe1\xbc\x84" => "\xe1\xbc\x8c",
+ "\xe1\xbc\x85" => "\xe1\xbc\x8d",
+ "\xe1\xbc\x86" => "\xe1\xbc\x8e",
+ "\xe1\xbc\x87" => "\xe1\xbc\x8f",
+ "\xe1\xbc\x90" => "\xe1\xbc\x98",
+ "\xe1\xbc\x91" => "\xe1\xbc\x99",
+ "\xe1\xbc\x92" => "\xe1\xbc\x9a",
+ "\xe1\xbc\x93" => "\xe1\xbc\x9b",
+ "\xe1\xbc\x94" => "\xe1\xbc\x9c",
+ "\xe1\xbc\x95" => "\xe1\xbc\x9d",
+ "\xe1\xbc\xa0" => "\xe1\xbc\xa8",
+ "\xe1\xbc\xa1" => "\xe1\xbc\xa9",
+ "\xe1\xbc\xa2" => "\xe1\xbc\xaa",
+ "\xe1\xbc\xa3" => "\xe1\xbc\xab",
+ "\xe1\xbc\xa4" => "\xe1\xbc\xac",
+ "\xe1\xbc\xa5" => "\xe1\xbc\xad",
+ "\xe1\xbc\xa6" => "\xe1\xbc\xae",
+ "\xe1\xbc\xa7" => "\xe1\xbc\xaf",
+ "\xe1\xbc\xb0" => "\xe1\xbc\xb8",
+ "\xe1\xbc\xb1" => "\xe1\xbc\xb9",
+ "\xe1\xbc\xb2" => "\xe1\xbc\xba",
+ "\xe1\xbc\xb3" => "\xe1\xbc\xbb",
+ "\xe1\xbc\xb4" => "\xe1\xbc\xbc",
+ "\xe1\xbc\xb5" => "\xe1\xbc\xbd",
+ "\xe1\xbc\xb6" => "\xe1\xbc\xbe",
+ "\xe1\xbc\xb7" => "\xe1\xbc\xbf",
+ "\xe1\xbd\x80" => "\xe1\xbd\x88",
+ "\xe1\xbd\x81" => "\xe1\xbd\x89",
+ "\xe1\xbd\x82" => "\xe1\xbd\x8a",
+ "\xe1\xbd\x83" => "\xe1\xbd\x8b",
+ "\xe1\xbd\x84" => "\xe1\xbd\x8c",
+ "\xe1\xbd\x85" => "\xe1\xbd\x8d",
+ "\xe1\xbd\x91" => "\xe1\xbd\x99",
+ "\xe1\xbd\x93" => "\xe1\xbd\x9b",
+ "\xe1\xbd\x95" => "\xe1\xbd\x9d",
+ "\xe1\xbd\x97" => "\xe1\xbd\x9f",
+ "\xe1\xbd\xa0" => "\xe1\xbd\xa8",
+ "\xe1\xbd\xa1" => "\xe1\xbd\xa9",
+ "\xe1\xbd\xa2" => "\xe1\xbd\xaa",
+ "\xe1\xbd\xa3" => "\xe1\xbd\xab",
+ "\xe1\xbd\xa4" => "\xe1\xbd\xac",
+ "\xe1\xbd\xa5" => "\xe1\xbd\xad",
+ "\xe1\xbd\xa6" => "\xe1\xbd\xae",
+ "\xe1\xbd\xa7" => "\xe1\xbd\xaf",
+ "\xe1\xbd\xb0" => "\xe1\xbe\xba",
+ "\xe1\xbd\xb1" => "\xe1\xbe\xbb",
+ "\xe1\xbd\xb2" => "\xe1\xbf\x88",
+ "\xe1\xbd\xb3" => "\xe1\xbf\x89",
+ "\xe1\xbd\xb4" => "\xe1\xbf\x8a",
+ "\xe1\xbd\xb5" => "\xe1\xbf\x8b",
+ "\xe1\xbd\xb6" => "\xe1\xbf\x9a",
+ "\xe1\xbd\xb7" => "\xe1\xbf\x9b",
+ "\xe1\xbd\xb8" => "\xe1\xbf\xb8",
+ "\xe1\xbd\xb9" => "\xe1\xbf\xb9",
+ "\xe1\xbd\xba" => "\xe1\xbf\xaa",
+ "\xe1\xbd\xbb" => "\xe1\xbf\xab",
+ "\xe1\xbd\xbc" => "\xe1\xbf\xba",
+ "\xe1\xbd\xbd" => "\xe1\xbf\xbb",
+ "\xe1\xbe\x80" => "\xe1\xbe\x88",
+ "\xe1\xbe\x81" => "\xe1\xbe\x89",
+ "\xe1\xbe\x82" => "\xe1\xbe\x8a",
+ "\xe1\xbe\x83" => "\xe1\xbe\x8b",
+ "\xe1\xbe\x84" => "\xe1\xbe\x8c",
+ "\xe1\xbe\x85" => "\xe1\xbe\x8d",
+ "\xe1\xbe\x86" => "\xe1\xbe\x8e",
+ "\xe1\xbe\x87" => "\xe1\xbe\x8f",
+ "\xe1\xbe\x90" => "\xe1\xbe\x98",
+ "\xe1\xbe\x91" => "\xe1\xbe\x99",
+ "\xe1\xbe\x92" => "\xe1\xbe\x9a",
+ "\xe1\xbe\x93" => "\xe1\xbe\x9b",
+ "\xe1\xbe\x94" => "\xe1\xbe\x9c",
+ "\xe1\xbe\x95" => "\xe1\xbe\x9d",
+ "\xe1\xbe\x96" => "\xe1\xbe\x9e",
+ "\xe1\xbe\x97" => "\xe1\xbe\x9f",
+ "\xe1\xbe\xa0" => "\xe1\xbe\xa8",
+ "\xe1\xbe\xa1" => "\xe1\xbe\xa9",
+ "\xe1\xbe\xa2" => "\xe1\xbe\xaa",
+ "\xe1\xbe\xa3" => "\xe1\xbe\xab",
+ "\xe1\xbe\xa4" => "\xe1\xbe\xac",
+ "\xe1\xbe\xa5" => "\xe1\xbe\xad",
+ "\xe1\xbe\xa6" => "\xe1\xbe\xae",
+ "\xe1\xbe\xa7" => "\xe1\xbe\xaf",
+ "\xe1\xbe\xb0" => "\xe1\xbe\xb8",
+ "\xe1\xbe\xb1" => "\xe1\xbe\xb9",
+ "\xe1\xbe\xb3" => "\xe1\xbe\xbc",
+ "\xe1\xbe\xbe" => "\xce\x99",
+ "\xe1\xbf\x83" => "\xe1\xbf\x8c",
+ "\xe1\xbf\x90" => "\xe1\xbf\x98",
+ "\xe1\xbf\x91" => "\xe1\xbf\x99",
+ "\xe1\xbf\xa0" => "\xe1\xbf\xa8",
+ "\xe1\xbf\xa1" => "\xe1\xbf\xa9",
+ "\xe1\xbf\xa5" => "\xe1\xbf\xac",
+ "\xe1\xbf\xb3" => "\xe1\xbf\xbc",
+ "\xe2\x85\xb0" => "\xe2\x85\xa0",
+ "\xe2\x85\xb1" => "\xe2\x85\xa1",
+ "\xe2\x85\xb2" => "\xe2\x85\xa2",
+ "\xe2\x85\xb3" => "\xe2\x85\xa3",
+ "\xe2\x85\xb4" => "\xe2\x85\xa4",
+ "\xe2\x85\xb5" => "\xe2\x85\xa5",
+ "\xe2\x85\xb6" => "\xe2\x85\xa6",
+ "\xe2\x85\xb7" => "\xe2\x85\xa7",
+ "\xe2\x85\xb8" => "\xe2\x85\xa8",
+ "\xe2\x85\xb9" => "\xe2\x85\xa9",
+ "\xe2\x85\xba" => "\xe2\x85\xaa",
+ "\xe2\x85\xbb" => "\xe2\x85\xab",
+ "\xe2\x85\xbc" => "\xe2\x85\xac",
+ "\xe2\x85\xbd" => "\xe2\x85\xad",
+ "\xe2\x85\xbe" => "\xe2\x85\xae",
+ "\xe2\x85\xbf" => "\xe2\x85\xaf",
+ "\xe2\x93\x90" => "\xe2\x92\xb6",
+ "\xe2\x93\x91" => "\xe2\x92\xb7",
+ "\xe2\x93\x92" => "\xe2\x92\xb8",
+ "\xe2\x93\x93" => "\xe2\x92\xb9",
+ "\xe2\x93\x94" => "\xe2\x92\xba",
+ "\xe2\x93\x95" => "\xe2\x92\xbb",
+ "\xe2\x93\x96" => "\xe2\x92\xbc",
+ "\xe2\x93\x97" => "\xe2\x92\xbd",
+ "\xe2\x93\x98" => "\xe2\x92\xbe",
+ "\xe2\x93\x99" => "\xe2\x92\xbf",
+ "\xe2\x93\x9a" => "\xe2\x93\x80",
+ "\xe2\x93\x9b" => "\xe2\x93\x81",
+ "\xe2\x93\x9c" => "\xe2\x93\x82",
+ "\xe2\x93\x9d" => "\xe2\x93\x83",
+ "\xe2\x93\x9e" => "\xe2\x93\x84",
+ "\xe2\x93\x9f" => "\xe2\x93\x85",
+ "\xe2\x93\xa0" => "\xe2\x93\x86",
+ "\xe2\x93\xa1" => "\xe2\x93\x87",
+ "\xe2\x93\xa2" => "\xe2\x93\x88",
+ "\xe2\x93\xa3" => "\xe2\x93\x89",
+ "\xe2\x93\xa4" => "\xe2\x93\x8a",
+ "\xe2\x93\xa5" => "\xe2\x93\x8b",
+ "\xe2\x93\xa6" => "\xe2\x93\x8c",
+ "\xe2\x93\xa7" => "\xe2\x93\x8d",
+ "\xe2\x93\xa8" => "\xe2\x93\x8e",
+ "\xe2\x93\xa9" => "\xe2\x93\x8f",
+ "\xef\xbd\x81" => "\xef\xbc\xa1",
+ "\xef\xbd\x82" => "\xef\xbc\xa2",
+ "\xef\xbd\x83" => "\xef\xbc\xa3",
+ "\xef\xbd\x84" => "\xef\xbc\xa4",
+ "\xef\xbd\x85" => "\xef\xbc\xa5",
+ "\xef\xbd\x86" => "\xef\xbc\xa6",
+ "\xef\xbd\x87" => "\xef\xbc\xa7",
+ "\xef\xbd\x88" => "\xef\xbc\xa8",
+ "\xef\xbd\x89" => "\xef\xbc\xa9",
+ "\xef\xbd\x8a" => "\xef\xbc\xaa",
+ "\xef\xbd\x8b" => "\xef\xbc\xab",
+ "\xef\xbd\x8c" => "\xef\xbc\xac",
+ "\xef\xbd\x8d" => "\xef\xbc\xad",
+ "\xef\xbd\x8e" => "\xef\xbc\xae",
+ "\xef\xbd\x8f" => "\xef\xbc\xaf",
+ "\xef\xbd\x90" => "\xef\xbc\xb0",
+ "\xef\xbd\x91" => "\xef\xbc\xb1",
+ "\xef\xbd\x92" => "\xef\xbc\xb2",
+ "\xef\xbd\x93" => "\xef\xbc\xb3",
+ "\xef\xbd\x94" => "\xef\xbc\xb4",
+ "\xef\xbd\x95" => "\xef\xbc\xb5",
+ "\xef\xbd\x96" => "\xef\xbc\xb6",
+ "\xef\xbd\x97" => "\xef\xbc\xb7",
+ "\xef\xbd\x98" => "\xef\xbc\xb8",
+ "\xef\xbd\x99" => "\xef\xbc\xb9",
+ "\xef\xbd\x9a" => "\xef\xbc\xba",
+ "\xf0\x90\x90\xa8" => "\xf0\x90\x90\x80",
+ "\xf0\x90\x90\xa9" => "\xf0\x90\x90\x81",
+ "\xf0\x90\x90\xaa" => "\xf0\x90\x90\x82",
+ "\xf0\x90\x90\xab" => "\xf0\x90\x90\x83",
+ "\xf0\x90\x90\xac" => "\xf0\x90\x90\x84",
+ "\xf0\x90\x90\xad" => "\xf0\x90\x90\x85",
+ "\xf0\x90\x90\xae" => "\xf0\x90\x90\x86",
+ "\xf0\x90\x90\xaf" => "\xf0\x90\x90\x87",
+ "\xf0\x90\x90\xb0" => "\xf0\x90\x90\x88",
+ "\xf0\x90\x90\xb1" => "\xf0\x90\x90\x89",
+ "\xf0\x90\x90\xb2" => "\xf0\x90\x90\x8a",
+ "\xf0\x90\x90\xb3" => "\xf0\x90\x90\x8b",
+ "\xf0\x90\x90\xb4" => "\xf0\x90\x90\x8c",
+ "\xf0\x90\x90\xb5" => "\xf0\x90\x90\x8d",
+ "\xf0\x90\x90\xb6" => "\xf0\x90\x90\x8e",
+ "\xf0\x90\x90\xb7" => "\xf0\x90\x90\x8f",
+ "\xf0\x90\x90\xb8" => "\xf0\x90\x90\x90",
+ "\xf0\x90\x90\xb9" => "\xf0\x90\x90\x91",
+ "\xf0\x90\x90\xba" => "\xf0\x90\x90\x92",
+ "\xf0\x90\x90\xbb" => "\xf0\x90\x90\x93",
+ "\xf0\x90\x90\xbc" => "\xf0\x90\x90\x94",
+ "\xf0\x90\x90\xbd" => "\xf0\x90\x90\x95",
+ "\xf0\x90\x90\xbe" => "\xf0\x90\x90\x96",
+ "\xf0\x90\x90\xbf" => "\xf0\x90\x90\x97",
+ "\xf0\x90\x91\x80" => "\xf0\x90\x90\x98",
+ "\xf0\x90\x91\x81" => "\xf0\x90\x90\x99",
+ "\xf0\x90\x91\x82" => "\xf0\x90\x90\x9a",
+ "\xf0\x90\x91\x83" => "\xf0\x90\x90\x9b",
+ "\xf0\x90\x91\x84" => "\xf0\x90\x90\x9c",
+ "\xf0\x90\x91\x85" => "\xf0\x90\x90\x9d",
+ "\xf0\x90\x91\x86" => "\xf0\x90\x90\x9e",
+ "\xf0\x90\x91\x87" => "\xf0\x90\x90\x9f",
+ "\xf0\x90\x91\x88" => "\xf0\x90\x90\xa0",
+ "\xf0\x90\x91\x89" => "\xf0\x90\x90\xa1",
+ "\xf0\x90\x91\x8a" => "\xf0\x90\x90\xa2",
+ "\xf0\x90\x91\x8b" => "\xf0\x90\x90\xa3",
+ "\xf0\x90\x91\x8c" => "\xf0\x90\x90\xa4",
+ "\xf0\x90\x91\x8d" => "\xf0\x90\x90\xa5"
+);
+
+$wikiLowerChars = array (
+ "A" => "a",
+ "B" => "b",
+ "C" => "c",
+ "D" => "d",
+ "E" => "e",
+ "F" => "f",
+ "G" => "g",
+ "H" => "h",
+ "I" => "i",
+ "J" => "j",
+ "K" => "k",
+ "L" => "l",
+ "M" => "m",
+ "N" => "n",
+ "O" => "o",
+ "P" => "p",
+ "Q" => "q",
+ "R" => "r",
+ "S" => "s",
+ "T" => "t",
+ "U" => "u",
+ "V" => "v",
+ "W" => "w",
+ "X" => "x",
+ "Y" => "y",
+ "Z" => "z",
+ "\xc3\x80" => "\xc3\xa0",
+ "\xc3\x81" => "\xc3\xa1",
+ "\xc3\x82" => "\xc3\xa2",
+ "\xc3\x83" => "\xc3\xa3",
+ "\xc3\x84" => "\xc3\xa4",
+ "\xc3\x85" => "\xc3\xa5",
+ "\xc3\x86" => "\xc3\xa6",
+ "\xc3\x87" => "\xc3\xa7",
+ "\xc3\x88" => "\xc3\xa8",
+ "\xc3\x89" => "\xc3\xa9",
+ "\xc3\x8a" => "\xc3\xaa",
+ "\xc3\x8b" => "\xc3\xab",
+ "\xc3\x8c" => "\xc3\xac",
+ "\xc3\x8d" => "\xc3\xad",
+ "\xc3\x8e" => "\xc3\xae",
+ "\xc3\x8f" => "\xc3\xaf",
+ "\xc3\x90" => "\xc3\xb0",
+ "\xc3\x91" => "\xc3\xb1",
+ "\xc3\x92" => "\xc3\xb2",
+ "\xc3\x93" => "\xc3\xb3",
+ "\xc3\x94" => "\xc3\xb4",
+ "\xc3\x95" => "\xc3\xb5",
+ "\xc3\x96" => "\xc3\xb6",
+ "\xc3\x98" => "\xc3\xb8",
+ "\xc3\x99" => "\xc3\xb9",
+ "\xc3\x9a" => "\xc3\xba",
+ "\xc3\x9b" => "\xc3\xbb",
+ "\xc3\x9c" => "\xc3\xbc",
+ "\xc3\x9d" => "\xc3\xbd",
+ "\xc3\x9e" => "\xc3\xbe",
+ "\xc4\x80" => "\xc4\x81",
+ "\xc4\x82" => "\xc4\x83",
+ "\xc4\x84" => "\xc4\x85",
+ "\xc4\x86" => "\xc4\x87",
+ "\xc4\x88" => "\xc4\x89",
+ "\xc4\x8a" => "\xc4\x8b",
+ "\xc4\x8c" => "\xc4\x8d",
+ "\xc4\x8e" => "\xc4\x8f",
+ "\xc4\x90" => "\xc4\x91",
+ "\xc4\x92" => "\xc4\x93",
+ "\xc4\x94" => "\xc4\x95",
+ "\xc4\x96" => "\xc4\x97",
+ "\xc4\x98" => "\xc4\x99",
+ "\xc4\x9a" => "\xc4\x9b",
+ "\xc4\x9c" => "\xc4\x9d",
+ "\xc4\x9e" => "\xc4\x9f",
+ "\xc4\xa0" => "\xc4\xa1",
+ "\xc4\xa2" => "\xc4\xa3",
+ "\xc4\xa4" => "\xc4\xa5",
+ "\xc4\xa6" => "\xc4\xa7",
+ "\xc4\xa8" => "\xc4\xa9",
+ "\xc4\xaa" => "\xc4\xab",
+ "\xc4\xac" => "\xc4\xad",
+ "\xc4\xae" => "\xc4\xaf",
+ "\xc4\xb0" => "i",
+ "\xc4\xb2" => "\xc4\xb3",
+ "\xc4\xb4" => "\xc4\xb5",
+ "\xc4\xb6" => "\xc4\xb7",
+ "\xc4\xb9" => "\xc4\xba",
+ "\xc4\xbb" => "\xc4\xbc",
+ "\xc4\xbd" => "\xc4\xbe",
+ "\xc4\xbf" => "\xc5\x80",
+ "\xc5\x81" => "\xc5\x82",
+ "\xc5\x83" => "\xc5\x84",
+ "\xc5\x85" => "\xc5\x86",
+ "\xc5\x87" => "\xc5\x88",
+ "\xc5\x8a" => "\xc5\x8b",
+ "\xc5\x8c" => "\xc5\x8d",
+ "\xc5\x8e" => "\xc5\x8f",
+ "\xc5\x90" => "\xc5\x91",
+ "\xc5\x92" => "\xc5\x93",
+ "\xc5\x94" => "\xc5\x95",
+ "\xc5\x96" => "\xc5\x97",
+ "\xc5\x98" => "\xc5\x99",
+ "\xc5\x9a" => "\xc5\x9b",
+ "\xc5\x9c" => "\xc5\x9d",
+ "\xc5\x9e" => "\xc5\x9f",
+ "\xc5\xa0" => "\xc5\xa1",
+ "\xc5\xa2" => "\xc5\xa3",
+ "\xc5\xa4" => "\xc5\xa5",
+ "\xc5\xa6" => "\xc5\xa7",
+ "\xc5\xa8" => "\xc5\xa9",
+ "\xc5\xaa" => "\xc5\xab",
+ "\xc5\xac" => "\xc5\xad",
+ "\xc5\xae" => "\xc5\xaf",
+ "\xc5\xb0" => "\xc5\xb1",
+ "\xc5\xb2" => "\xc5\xb3",
+ "\xc5\xb4" => "\xc5\xb5",
+ "\xc5\xb6" => "\xc5\xb7",
+ "\xc5\xb8" => "\xc3\xbf",
+ "\xc5\xb9" => "\xc5\xba",
+ "\xc5\xbb" => "\xc5\xbc",
+ "\xc5\xbd" => "\xc5\xbe",
+ "\xc6\x81" => "\xc9\x93",
+ "\xc6\x82" => "\xc6\x83",
+ "\xc6\x84" => "\xc6\x85",
+ "\xc6\x86" => "\xc9\x94",
+ "\xc6\x87" => "\xc6\x88",
+ "\xc6\x89" => "\xc9\x96",
+ "\xc6\x8a" => "\xc9\x97",
+ "\xc6\x8b" => "\xc6\x8c",
+ "\xc6\x8e" => "\xc7\x9d",
+ "\xc6\x8f" => "\xc9\x99",
+ "\xc6\x90" => "\xc9\x9b",
+ "\xc6\x91" => "\xc6\x92",
+ "\xc6\x93" => "\xc9\xa0",
+ "\xc6\x94" => "\xc9\xa3",
+ "\xc6\x96" => "\xc9\xa9",
+ "\xc6\x97" => "\xc9\xa8",
+ "\xc6\x98" => "\xc6\x99",
+ "\xc6\x9c" => "\xc9\xaf",
+ "\xc6\x9d" => "\xc9\xb2",
+ "\xc6\x9f" => "\xc9\xb5",
+ "\xc6\xa0" => "\xc6\xa1",
+ "\xc6\xa2" => "\xc6\xa3",
+ "\xc6\xa4" => "\xc6\xa5",
+ "\xc6\xa6" => "\xca\x80",
+ "\xc6\xa7" => "\xc6\xa8",
+ "\xc6\xa9" => "\xca\x83",
+ "\xc6\xac" => "\xc6\xad",
+ "\xc6\xae" => "\xca\x88",
+ "\xc6\xaf" => "\xc6\xb0",
+ "\xc6\xb1" => "\xca\x8a",
+ "\xc6\xb2" => "\xca\x8b",
+ "\xc6\xb3" => "\xc6\xb4",
+ "\xc6\xb5" => "\xc6\xb6",
+ "\xc6\xb7" => "\xca\x92",
+ "\xc6\xb8" => "\xc6\xb9",
+ "\xc6\xbc" => "\xc6\xbd",
+ "\xc7\x84" => "\xc7\x86",
+ "\xc7\x85" => "\xc7\x86",
+ "\xc7\x87" => "\xc7\x89",
+ "\xc7\x88" => "\xc7\x89",
+ "\xc7\x8a" => "\xc7\x8c",
+ "\xc7\x8b" => "\xc7\x8c",
+ "\xc7\x8d" => "\xc7\x8e",
+ "\xc7\x8f" => "\xc7\x90",
+ "\xc7\x91" => "\xc7\x92",
+ "\xc7\x93" => "\xc7\x94",
+ "\xc7\x95" => "\xc7\x96",
+ "\xc7\x97" => "\xc7\x98",
+ "\xc7\x99" => "\xc7\x9a",
+ "\xc7\x9b" => "\xc7\x9c",
+ "\xc7\x9e" => "\xc7\x9f",
+ "\xc7\xa0" => "\xc7\xa1",
+ "\xc7\xa2" => "\xc7\xa3",
+ "\xc7\xa4" => "\xc7\xa5",
+ "\xc7\xa6" => "\xc7\xa7",
+ "\xc7\xa8" => "\xc7\xa9",
+ "\xc7\xaa" => "\xc7\xab",
+ "\xc7\xac" => "\xc7\xad",
+ "\xc7\xae" => "\xc7\xaf",
+ "\xc7\xb1" => "\xc7\xb3",
+ "\xc7\xb2" => "\xc7\xb3",
+ "\xc7\xb4" => "\xc7\xb5",
+ "\xc7\xb6" => "\xc6\x95",
+ "\xc7\xb7" => "\xc6\xbf",
+ "\xc7\xb8" => "\xc7\xb9",
+ "\xc7\xba" => "\xc7\xbb",
+ "\xc7\xbc" => "\xc7\xbd",
+ "\xc7\xbe" => "\xc7\xbf",
+ "\xc8\x80" => "\xc8\x81",
+ "\xc8\x82" => "\xc8\x83",
+ "\xc8\x84" => "\xc8\x85",
+ "\xc8\x86" => "\xc8\x87",
+ "\xc8\x88" => "\xc8\x89",
+ "\xc8\x8a" => "\xc8\x8b",
+ "\xc8\x8c" => "\xc8\x8d",
+ "\xc8\x8e" => "\xc8\x8f",
+ "\xc8\x90" => "\xc8\x91",
+ "\xc8\x92" => "\xc8\x93",
+ "\xc8\x94" => "\xc8\x95",
+ "\xc8\x96" => "\xc8\x97",
+ "\xc8\x98" => "\xc8\x99",
+ "\xc8\x9a" => "\xc8\x9b",
+ "\xc8\x9c" => "\xc8\x9d",
+ "\xc8\x9e" => "\xc8\x9f",
+ "\xc8\xa2" => "\xc8\xa3",
+ "\xc8\xa4" => "\xc8\xa5",
+ "\xc8\xa6" => "\xc8\xa7",
+ "\xc8\xa8" => "\xc8\xa9",
+ "\xc8\xaa" => "\xc8\xab",
+ "\xc8\xac" => "\xc8\xad",
+ "\xc8\xae" => "\xc8\xaf",
+ "\xc8\xb0" => "\xc8\xb1",
+ "\xc8\xb2" => "\xc8\xb3",
+ "\xce\x86" => "\xce\xac",
+ "\xce\x88" => "\xce\xad",
+ "\xce\x89" => "\xce\xae",
+ "\xce\x8a" => "\xce\xaf",
+ "\xce\x8c" => "\xcf\x8c",
+ "\xce\x8e" => "\xcf\x8d",
+ "\xce\x8f" => "\xcf\x8e",
+ "\xce\x91" => "\xce\xb1",
+ "\xce\x92" => "\xce\xb2",
+ "\xce\x93" => "\xce\xb3",
+ "\xce\x94" => "\xce\xb4",
+ "\xce\x95" => "\xce\xb5",
+ "\xce\x96" => "\xce\xb6",
+ "\xce\x97" => "\xce\xb7",
+ "\xce\x98" => "\xce\xb8",
+ "\xce\x99" => "\xce\xb9",
+ "\xce\x9a" => "\xce\xba",
+ "\xce\x9b" => "\xce\xbb",
+ "\xce\x9c" => "\xce\xbc",
+ "\xce\x9d" => "\xce\xbd",
+ "\xce\x9e" => "\xce\xbe",
+ "\xce\x9f" => "\xce\xbf",
+ "\xce\xa0" => "\xcf\x80",
+ "\xce\xa1" => "\xcf\x81",
+ "\xce\xa3" => "\xcf\x83",
+ "\xce\xa4" => "\xcf\x84",
+ "\xce\xa5" => "\xcf\x85",
+ "\xce\xa6" => "\xcf\x86",
+ "\xce\xa7" => "\xcf\x87",
+ "\xce\xa8" => "\xcf\x88",
+ "\xce\xa9" => "\xcf\x89",
+ "\xce\xaa" => "\xcf\x8a",
+ "\xce\xab" => "\xcf\x8b",
+ "\xcf\x9a" => "\xcf\x9b",
+ "\xcf\x9c" => "\xcf\x9d",
+ "\xcf\x9e" => "\xcf\x9f",
+ "\xcf\xa0" => "\xcf\xa1",
+ "\xcf\xa2" => "\xcf\xa3",
+ "\xcf\xa4" => "\xcf\xa5",
+ "\xcf\xa6" => "\xcf\xa7",
+ "\xcf\xa8" => "\xcf\xa9",
+ "\xcf\xaa" => "\xcf\xab",
+ "\xcf\xac" => "\xcf\xad",
+ "\xcf\xae" => "\xcf\xaf",
+ "\xcf\xb4" => "\xce\xb8",
+ "\xd0\x80" => "\xd1\x90",
+ "\xd0\x81" => "\xd1\x91",
+ "\xd0\x82" => "\xd1\x92",
+ "\xd0\x83" => "\xd1\x93",
+ "\xd0\x84" => "\xd1\x94",
+ "\xd0\x85" => "\xd1\x95",
+ "\xd0\x86" => "\xd1\x96",
+ "\xd0\x87" => "\xd1\x97",
+ "\xd0\x88" => "\xd1\x98",
+ "\xd0\x89" => "\xd1\x99",
+ "\xd0\x8a" => "\xd1\x9a",
+ "\xd0\x8b" => "\xd1\x9b",
+ "\xd0\x8c" => "\xd1\x9c",
+ "\xd0\x8d" => "\xd1\x9d",
+ "\xd0\x8e" => "\xd1\x9e",
+ "\xd0\x8f" => "\xd1\x9f",
+ "\xd0\x90" => "\xd0\xb0",
+ "\xd0\x91" => "\xd0\xb1",
+ "\xd0\x92" => "\xd0\xb2",
+ "\xd0\x93" => "\xd0\xb3",
+ "\xd0\x94" => "\xd0\xb4",
+ "\xd0\x95" => "\xd0\xb5",
+ "\xd0\x96" => "\xd0\xb6",
+ "\xd0\x97" => "\xd0\xb7",
+ "\xd0\x98" => "\xd0\xb8",
+ "\xd0\x99" => "\xd0\xb9",
+ "\xd0\x9a" => "\xd0\xba",
+ "\xd0\x9b" => "\xd0\xbb",
+ "\xd0\x9c" => "\xd0\xbc",
+ "\xd0\x9d" => "\xd0\xbd",
+ "\xd0\x9e" => "\xd0\xbe",
+ "\xd0\x9f" => "\xd0\xbf",
+ "\xd0\xa0" => "\xd1\x80",
+ "\xd0\xa1" => "\xd1\x81",
+ "\xd0\xa2" => "\xd1\x82",
+ "\xd0\xa3" => "\xd1\x83",
+ "\xd0\xa4" => "\xd1\x84",
+ "\xd0\xa5" => "\xd1\x85",
+ "\xd0\xa6" => "\xd1\x86",
+ "\xd0\xa7" => "\xd1\x87",
+ "\xd0\xa8" => "\xd1\x88",
+ "\xd0\xa9" => "\xd1\x89",
+ "\xd0\xaa" => "\xd1\x8a",
+ "\xd0\xab" => "\xd1\x8b",
+ "\xd0\xac" => "\xd1\x8c",
+ "\xd0\xad" => "\xd1\x8d",
+ "\xd0\xae" => "\xd1\x8e",
+ "\xd0\xaf" => "\xd1\x8f",
+ "\xd1\xa0" => "\xd1\xa1",
+ "\xd1\xa2" => "\xd1\xa3",
+ "\xd1\xa4" => "\xd1\xa5",
+ "\xd1\xa6" => "\xd1\xa7",
+ "\xd1\xa8" => "\xd1\xa9",
+ "\xd1\xaa" => "\xd1\xab",
+ "\xd1\xac" => "\xd1\xad",
+ "\xd1\xae" => "\xd1\xaf",
+ "\xd1\xb0" => "\xd1\xb1",
+ "\xd1\xb2" => "\xd1\xb3",
+ "\xd1\xb4" => "\xd1\xb5",
+ "\xd1\xb6" => "\xd1\xb7",
+ "\xd1\xb8" => "\xd1\xb9",
+ "\xd1\xba" => "\xd1\xbb",
+ "\xd1\xbc" => "\xd1\xbd",
+ "\xd1\xbe" => "\xd1\xbf",
+ "\xd2\x80" => "\xd2\x81",
+ "\xd2\x8c" => "\xd2\x8d",
+ "\xd2\x8e" => "\xd2\x8f",
+ "\xd2\x90" => "\xd2\x91",
+ "\xd2\x92" => "\xd2\x93",
+ "\xd2\x94" => "\xd2\x95",
+ "\xd2\x96" => "\xd2\x97",
+ "\xd2\x98" => "\xd2\x99",
+ "\xd2\x9a" => "\xd2\x9b",
+ "\xd2\x9c" => "\xd2\x9d",
+ "\xd2\x9e" => "\xd2\x9f",
+ "\xd2\xa0" => "\xd2\xa1",
+ "\xd2\xa2" => "\xd2\xa3",
+ "\xd2\xa4" => "\xd2\xa5",
+ "\xd2\xa6" => "\xd2\xa7",
+ "\xd2\xa8" => "\xd2\xa9",
+ "\xd2\xaa" => "\xd2\xab",
+ "\xd2\xac" => "\xd2\xad",
+ "\xd2\xae" => "\xd2\xaf",
+ "\xd2\xb0" => "\xd2\xb1",
+ "\xd2\xb2" => "\xd2\xb3",
+ "\xd2\xb4" => "\xd2\xb5",
+ "\xd2\xb6" => "\xd2\xb7",
+ "\xd2\xb8" => "\xd2\xb9",
+ "\xd2\xba" => "\xd2\xbb",
+ "\xd2\xbc" => "\xd2\xbd",
+ "\xd2\xbe" => "\xd2\xbf",
+ "\xd3\x81" => "\xd3\x82",
+ "\xd3\x83" => "\xd3\x84",
+ "\xd3\x87" => "\xd3\x88",
+ "\xd3\x8b" => "\xd3\x8c",
+ "\xd3\x90" => "\xd3\x91",
+ "\xd3\x92" => "\xd3\x93",
+ "\xd3\x94" => "\xd3\x95",
+ "\xd3\x96" => "\xd3\x97",
+ "\xd3\x98" => "\xd3\x99",
+ "\xd3\x9a" => "\xd3\x9b",
+ "\xd3\x9c" => "\xd3\x9d",
+ "\xd3\x9e" => "\xd3\x9f",
+ "\xd3\xa0" => "\xd3\xa1",
+ "\xd3\xa2" => "\xd3\xa3",
+ "\xd3\xa4" => "\xd3\xa5",
+ "\xd3\xa6" => "\xd3\xa7",
+ "\xd3\xa8" => "\xd3\xa9",
+ "\xd3\xaa" => "\xd3\xab",
+ "\xd3\xac" => "\xd3\xad",
+ "\xd3\xae" => "\xd3\xaf",
+ "\xd3\xb0" => "\xd3\xb1",
+ "\xd3\xb2" => "\xd3\xb3",
+ "\xd3\xb4" => "\xd3\xb5",
+ "\xd3\xb8" => "\xd3\xb9",
+ "\xd4\xb1" => "\xd5\xa1",
+ "\xd4\xb2" => "\xd5\xa2",
+ "\xd4\xb3" => "\xd5\xa3",
+ "\xd4\xb4" => "\xd5\xa4",
+ "\xd4\xb5" => "\xd5\xa5",
+ "\xd4\xb6" => "\xd5\xa6",
+ "\xd4\xb7" => "\xd5\xa7",
+ "\xd4\xb8" => "\xd5\xa8",
+ "\xd4\xb9" => "\xd5\xa9",
+ "\xd4\xba" => "\xd5\xaa",
+ "\xd4\xbb" => "\xd5\xab",
+ "\xd4\xbc" => "\xd5\xac",
+ "\xd4\xbd" => "\xd5\xad",
+ "\xd4\xbe" => "\xd5\xae",
+ "\xd4\xbf" => "\xd5\xaf",
+ "\xd5\x80" => "\xd5\xb0",
+ "\xd5\x81" => "\xd5\xb1",
+ "\xd5\x82" => "\xd5\xb2",
+ "\xd5\x83" => "\xd5\xb3",
+ "\xd5\x84" => "\xd5\xb4",
+ "\xd5\x85" => "\xd5\xb5",
+ "\xd5\x86" => "\xd5\xb6",
+ "\xd5\x87" => "\xd5\xb7",
+ "\xd5\x88" => "\xd5\xb8",
+ "\xd5\x89" => "\xd5\xb9",
+ "\xd5\x8a" => "\xd5\xba",
+ "\xd5\x8b" => "\xd5\xbb",
+ "\xd5\x8c" => "\xd5\xbc",
+ "\xd5\x8d" => "\xd5\xbd",
+ "\xd5\x8e" => "\xd5\xbe",
+ "\xd5\x8f" => "\xd5\xbf",
+ "\xd5\x90" => "\xd6\x80",
+ "\xd5\x91" => "\xd6\x81",
+ "\xd5\x92" => "\xd6\x82",
+ "\xd5\x93" => "\xd6\x83",
+ "\xd5\x94" => "\xd6\x84",
+ "\xd5\x95" => "\xd6\x85",
+ "\xd5\x96" => "\xd6\x86",
+ "\xe1\xb8\x80" => "\xe1\xb8\x81",
+ "\xe1\xb8\x82" => "\xe1\xb8\x83",
+ "\xe1\xb8\x84" => "\xe1\xb8\x85",
+ "\xe1\xb8\x86" => "\xe1\xb8\x87",
+ "\xe1\xb8\x88" => "\xe1\xb8\x89",
+ "\xe1\xb8\x8a" => "\xe1\xb8\x8b",
+ "\xe1\xb8\x8c" => "\xe1\xb8\x8d",
+ "\xe1\xb8\x8e" => "\xe1\xb8\x8f",
+ "\xe1\xb8\x90" => "\xe1\xb8\x91",
+ "\xe1\xb8\x92" => "\xe1\xb8\x93",
+ "\xe1\xb8\x94" => "\xe1\xb8\x95",
+ "\xe1\xb8\x96" => "\xe1\xb8\x97",
+ "\xe1\xb8\x98" => "\xe1\xb8\x99",
+ "\xe1\xb8\x9a" => "\xe1\xb8\x9b",
+ "\xe1\xb8\x9c" => "\xe1\xb8\x9d",
+ "\xe1\xb8\x9e" => "\xe1\xb8\x9f",
+ "\xe1\xb8\xa0" => "\xe1\xb8\xa1",
+ "\xe1\xb8\xa2" => "\xe1\xb8\xa3",
+ "\xe1\xb8\xa4" => "\xe1\xb8\xa5",
+ "\xe1\xb8\xa6" => "\xe1\xb8\xa7",
+ "\xe1\xb8\xa8" => "\xe1\xb8\xa9",
+ "\xe1\xb8\xaa" => "\xe1\xb8\xab",
+ "\xe1\xb8\xac" => "\xe1\xb8\xad",
+ "\xe1\xb8\xae" => "\xe1\xb8\xaf",
+ "\xe1\xb8\xb0" => "\xe1\xb8\xb1",
+ "\xe1\xb8\xb2" => "\xe1\xb8\xb3",
+ "\xe1\xb8\xb4" => "\xe1\xb8\xb5",
+ "\xe1\xb8\xb6" => "\xe1\xb8\xb7",
+ "\xe1\xb8\xb8" => "\xe1\xb8\xb9",
+ "\xe1\xb8\xba" => "\xe1\xb8\xbb",
+ "\xe1\xb8\xbc" => "\xe1\xb8\xbd",
+ "\xe1\xb8\xbe" => "\xe1\xb8\xbf",
+ "\xe1\xb9\x80" => "\xe1\xb9\x81",
+ "\xe1\xb9\x82" => "\xe1\xb9\x83",
+ "\xe1\xb9\x84" => "\xe1\xb9\x85",
+ "\xe1\xb9\x86" => "\xe1\xb9\x87",
+ "\xe1\xb9\x88" => "\xe1\xb9\x89",
+ "\xe1\xb9\x8a" => "\xe1\xb9\x8b",
+ "\xe1\xb9\x8c" => "\xe1\xb9\x8d",
+ "\xe1\xb9\x8e" => "\xe1\xb9\x8f",
+ "\xe1\xb9\x90" => "\xe1\xb9\x91",
+ "\xe1\xb9\x92" => "\xe1\xb9\x93",
+ "\xe1\xb9\x94" => "\xe1\xb9\x95",
+ "\xe1\xb9\x96" => "\xe1\xb9\x97",
+ "\xe1\xb9\x98" => "\xe1\xb9\x99",
+ "\xe1\xb9\x9a" => "\xe1\xb9\x9b",
+ "\xe1\xb9\x9c" => "\xe1\xb9\x9d",
+ "\xe1\xb9\x9e" => "\xe1\xb9\x9f",
+ "\xe1\xb9\xa0" => "\xe1\xb9\xa1",
+ "\xe1\xb9\xa2" => "\xe1\xb9\xa3",
+ "\xe1\xb9\xa4" => "\xe1\xb9\xa5",
+ "\xe1\xb9\xa6" => "\xe1\xb9\xa7",
+ "\xe1\xb9\xa8" => "\xe1\xb9\xa9",
+ "\xe1\xb9\xaa" => "\xe1\xb9\xab",
+ "\xe1\xb9\xac" => "\xe1\xb9\xad",
+ "\xe1\xb9\xae" => "\xe1\xb9\xaf",
+ "\xe1\xb9\xb0" => "\xe1\xb9\xb1",
+ "\xe1\xb9\xb2" => "\xe1\xb9\xb3",
+ "\xe1\xb9\xb4" => "\xe1\xb9\xb5",
+ "\xe1\xb9\xb6" => "\xe1\xb9\xb7",
+ "\xe1\xb9\xb8" => "\xe1\xb9\xb9",
+ "\xe1\xb9\xba" => "\xe1\xb9\xbb",
+ "\xe1\xb9\xbc" => "\xe1\xb9\xbd",
+ "\xe1\xb9\xbe" => "\xe1\xb9\xbf",
+ "\xe1\xba\x80" => "\xe1\xba\x81",
+ "\xe1\xba\x82" => "\xe1\xba\x83",
+ "\xe1\xba\x84" => "\xe1\xba\x85",
+ "\xe1\xba\x86" => "\xe1\xba\x87",
+ "\xe1\xba\x88" => "\xe1\xba\x89",
+ "\xe1\xba\x8a" => "\xe1\xba\x8b",
+ "\xe1\xba\x8c" => "\xe1\xba\x8d",
+ "\xe1\xba\x8e" => "\xe1\xba\x8f",
+ "\xe1\xba\x90" => "\xe1\xba\x91",
+ "\xe1\xba\x92" => "\xe1\xba\x93",
+ "\xe1\xba\x94" => "\xe1\xba\x95",
+ "\xe1\xba\xa0" => "\xe1\xba\xa1",
+ "\xe1\xba\xa2" => "\xe1\xba\xa3",
+ "\xe1\xba\xa4" => "\xe1\xba\xa5",
+ "\xe1\xba\xa6" => "\xe1\xba\xa7",
+ "\xe1\xba\xa8" => "\xe1\xba\xa9",
+ "\xe1\xba\xaa" => "\xe1\xba\xab",
+ "\xe1\xba\xac" => "\xe1\xba\xad",
+ "\xe1\xba\xae" => "\xe1\xba\xaf",
+ "\xe1\xba\xb0" => "\xe1\xba\xb1",
+ "\xe1\xba\xb2" => "\xe1\xba\xb3",
+ "\xe1\xba\xb4" => "\xe1\xba\xb5",
+ "\xe1\xba\xb6" => "\xe1\xba\xb7",
+ "\xe1\xba\xb8" => "\xe1\xba\xb9",
+ "\xe1\xba\xba" => "\xe1\xba\xbb",
+ "\xe1\xba\xbc" => "\xe1\xba\xbd",
+ "\xe1\xba\xbe" => "\xe1\xba\xbf",
+ "\xe1\xbb\x80" => "\xe1\xbb\x81",
+ "\xe1\xbb\x82" => "\xe1\xbb\x83",
+ "\xe1\xbb\x84" => "\xe1\xbb\x85",
+ "\xe1\xbb\x86" => "\xe1\xbb\x87",
+ "\xe1\xbb\x88" => "\xe1\xbb\x89",
+ "\xe1\xbb\x8a" => "\xe1\xbb\x8b",
+ "\xe1\xbb\x8c" => "\xe1\xbb\x8d",
+ "\xe1\xbb\x8e" => "\xe1\xbb\x8f",
+ "\xe1\xbb\x90" => "\xe1\xbb\x91",
+ "\xe1\xbb\x92" => "\xe1\xbb\x93",
+ "\xe1\xbb\x94" => "\xe1\xbb\x95",
+ "\xe1\xbb\x96" => "\xe1\xbb\x97",
+ "\xe1\xbb\x98" => "\xe1\xbb\x99",
+ "\xe1\xbb\x9a" => "\xe1\xbb\x9b",
+ "\xe1\xbb\x9c" => "\xe1\xbb\x9d",
+ "\xe1\xbb\x9e" => "\xe1\xbb\x9f",
+ "\xe1\xbb\xa0" => "\xe1\xbb\xa1",
+ "\xe1\xbb\xa2" => "\xe1\xbb\xa3",
+ "\xe1\xbb\xa4" => "\xe1\xbb\xa5",
+ "\xe1\xbb\xa6" => "\xe1\xbb\xa7",
+ "\xe1\xbb\xa8" => "\xe1\xbb\xa9",
+ "\xe1\xbb\xaa" => "\xe1\xbb\xab",
+ "\xe1\xbb\xac" => "\xe1\xbb\xad",
+ "\xe1\xbb\xae" => "\xe1\xbb\xaf",
+ "\xe1\xbb\xb0" => "\xe1\xbb\xb1",
+ "\xe1\xbb\xb2" => "\xe1\xbb\xb3",
+ "\xe1\xbb\xb4" => "\xe1\xbb\xb5",
+ "\xe1\xbb\xb6" => "\xe1\xbb\xb7",
+ "\xe1\xbb\xb8" => "\xe1\xbb\xb9",
+ "\xe1\xbc\x88" => "\xe1\xbc\x80",
+ "\xe1\xbc\x89" => "\xe1\xbc\x81",
+ "\xe1\xbc\x8a" => "\xe1\xbc\x82",
+ "\xe1\xbc\x8b" => "\xe1\xbc\x83",
+ "\xe1\xbc\x8c" => "\xe1\xbc\x84",
+ "\xe1\xbc\x8d" => "\xe1\xbc\x85",
+ "\xe1\xbc\x8e" => "\xe1\xbc\x86",
+ "\xe1\xbc\x8f" => "\xe1\xbc\x87",
+ "\xe1\xbc\x98" => "\xe1\xbc\x90",
+ "\xe1\xbc\x99" => "\xe1\xbc\x91",
+ "\xe1\xbc\x9a" => "\xe1\xbc\x92",
+ "\xe1\xbc\x9b" => "\xe1\xbc\x93",
+ "\xe1\xbc\x9c" => "\xe1\xbc\x94",
+ "\xe1\xbc\x9d" => "\xe1\xbc\x95",
+ "\xe1\xbc\xa8" => "\xe1\xbc\xa0",
+ "\xe1\xbc\xa9" => "\xe1\xbc\xa1",
+ "\xe1\xbc\xaa" => "\xe1\xbc\xa2",
+ "\xe1\xbc\xab" => "\xe1\xbc\xa3",
+ "\xe1\xbc\xac" => "\xe1\xbc\xa4",
+ "\xe1\xbc\xad" => "\xe1\xbc\xa5",
+ "\xe1\xbc\xae" => "\xe1\xbc\xa6",
+ "\xe1\xbc\xaf" => "\xe1\xbc\xa7",
+ "\xe1\xbc\xb8" => "\xe1\xbc\xb0",
+ "\xe1\xbc\xb9" => "\xe1\xbc\xb1",
+ "\xe1\xbc\xba" => "\xe1\xbc\xb2",
+ "\xe1\xbc\xbb" => "\xe1\xbc\xb3",
+ "\xe1\xbc\xbc" => "\xe1\xbc\xb4",
+ "\xe1\xbc\xbd" => "\xe1\xbc\xb5",
+ "\xe1\xbc\xbe" => "\xe1\xbc\xb6",
+ "\xe1\xbc\xbf" => "\xe1\xbc\xb7",
+ "\xe1\xbd\x88" => "\xe1\xbd\x80",
+ "\xe1\xbd\x89" => "\xe1\xbd\x81",
+ "\xe1\xbd\x8a" => "\xe1\xbd\x82",
+ "\xe1\xbd\x8b" => "\xe1\xbd\x83",
+ "\xe1\xbd\x8c" => "\xe1\xbd\x84",
+ "\xe1\xbd\x8d" => "\xe1\xbd\x85",
+ "\xe1\xbd\x99" => "\xe1\xbd\x91",
+ "\xe1\xbd\x9b" => "\xe1\xbd\x93",
+ "\xe1\xbd\x9d" => "\xe1\xbd\x95",
+ "\xe1\xbd\x9f" => "\xe1\xbd\x97",
+ "\xe1\xbd\xa8" => "\xe1\xbd\xa0",
+ "\xe1\xbd\xa9" => "\xe1\xbd\xa1",
+ "\xe1\xbd\xaa" => "\xe1\xbd\xa2",
+ "\xe1\xbd\xab" => "\xe1\xbd\xa3",
+ "\xe1\xbd\xac" => "\xe1\xbd\xa4",
+ "\xe1\xbd\xad" => "\xe1\xbd\xa5",
+ "\xe1\xbd\xae" => "\xe1\xbd\xa6",
+ "\xe1\xbd\xaf" => "\xe1\xbd\xa7",
+ "\xe1\xbe\x88" => "\xe1\xbe\x80",
+ "\xe1\xbe\x89" => "\xe1\xbe\x81",
+ "\xe1\xbe\x8a" => "\xe1\xbe\x82",
+ "\xe1\xbe\x8b" => "\xe1\xbe\x83",
+ "\xe1\xbe\x8c" => "\xe1\xbe\x84",
+ "\xe1\xbe\x8d" => "\xe1\xbe\x85",
+ "\xe1\xbe\x8e" => "\xe1\xbe\x86",
+ "\xe1\xbe\x8f" => "\xe1\xbe\x87",
+ "\xe1\xbe\x98" => "\xe1\xbe\x90",
+ "\xe1\xbe\x99" => "\xe1\xbe\x91",
+ "\xe1\xbe\x9a" => "\xe1\xbe\x92",
+ "\xe1\xbe\x9b" => "\xe1\xbe\x93",
+ "\xe1\xbe\x9c" => "\xe1\xbe\x94",
+ "\xe1\xbe\x9d" => "\xe1\xbe\x95",
+ "\xe1\xbe\x9e" => "\xe1\xbe\x96",
+ "\xe1\xbe\x9f" => "\xe1\xbe\x97",
+ "\xe1\xbe\xa8" => "\xe1\xbe\xa0",
+ "\xe1\xbe\xa9" => "\xe1\xbe\xa1",
+ "\xe1\xbe\xaa" => "\xe1\xbe\xa2",
+ "\xe1\xbe\xab" => "\xe1\xbe\xa3",
+ "\xe1\xbe\xac" => "\xe1\xbe\xa4",
+ "\xe1\xbe\xad" => "\xe1\xbe\xa5",
+ "\xe1\xbe\xae" => "\xe1\xbe\xa6",
+ "\xe1\xbe\xaf" => "\xe1\xbe\xa7",
+ "\xe1\xbe\xb8" => "\xe1\xbe\xb0",
+ "\xe1\xbe\xb9" => "\xe1\xbe\xb1",
+ "\xe1\xbe\xba" => "\xe1\xbd\xb0",
+ "\xe1\xbe\xbb" => "\xe1\xbd\xb1",
+ "\xe1\xbe\xbc" => "\xe1\xbe\xb3",
+ "\xe1\xbf\x88" => "\xe1\xbd\xb2",
+ "\xe1\xbf\x89" => "\xe1\xbd\xb3",
+ "\xe1\xbf\x8a" => "\xe1\xbd\xb4",
+ "\xe1\xbf\x8b" => "\xe1\xbd\xb5",
+ "\xe1\xbf\x8c" => "\xe1\xbf\x83",
+ "\xe1\xbf\x98" => "\xe1\xbf\x90",
+ "\xe1\xbf\x99" => "\xe1\xbf\x91",
+ "\xe1\xbf\x9a" => "\xe1\xbd\xb6",
+ "\xe1\xbf\x9b" => "\xe1\xbd\xb7",
+ "\xe1\xbf\xa8" => "\xe1\xbf\xa0",
+ "\xe1\xbf\xa9" => "\xe1\xbf\xa1",
+ "\xe1\xbf\xaa" => "\xe1\xbd\xba",
+ "\xe1\xbf\xab" => "\xe1\xbd\xbb",
+ "\xe1\xbf\xac" => "\xe1\xbf\xa5",
+ "\xe1\xbf\xb8" => "\xe1\xbd\xb8",
+ "\xe1\xbf\xb9" => "\xe1\xbd\xb9",
+ "\xe1\xbf\xba" => "\xe1\xbd\xbc",
+ "\xe1\xbf\xbb" => "\xe1\xbd\xbd",
+ "\xe1\xbf\xbc" => "\xe1\xbf\xb3",
+ "\xe2\x84\xa6" => "\xcf\x89",
+ "\xe2\x84\xaa" => "k",
+ "\xe2\x84\xab" => "\xc3\xa5",
+ "\xe2\x85\xa0" => "\xe2\x85\xb0",
+ "\xe2\x85\xa1" => "\xe2\x85\xb1",
+ "\xe2\x85\xa2" => "\xe2\x85\xb2",
+ "\xe2\x85\xa3" => "\xe2\x85\xb3",
+ "\xe2\x85\xa4" => "\xe2\x85\xb4",
+ "\xe2\x85\xa5" => "\xe2\x85\xb5",
+ "\xe2\x85\xa6" => "\xe2\x85\xb6",
+ "\xe2\x85\xa7" => "\xe2\x85\xb7",
+ "\xe2\x85\xa8" => "\xe2\x85\xb8",
+ "\xe2\x85\xa9" => "\xe2\x85\xb9",
+ "\xe2\x85\xaa" => "\xe2\x85\xba",
+ "\xe2\x85\xab" => "\xe2\x85\xbb",
+ "\xe2\x85\xac" => "\xe2\x85\xbc",
+ "\xe2\x85\xad" => "\xe2\x85\xbd",
+ "\xe2\x85\xae" => "\xe2\x85\xbe",
+ "\xe2\x85\xaf" => "\xe2\x85\xbf",
+ "\xe2\x92\xb6" => "\xe2\x93\x90",
+ "\xe2\x92\xb7" => "\xe2\x93\x91",
+ "\xe2\x92\xb8" => "\xe2\x93\x92",
+ "\xe2\x92\xb9" => "\xe2\x93\x93",
+ "\xe2\x92\xba" => "\xe2\x93\x94",
+ "\xe2\x92\xbb" => "\xe2\x93\x95",
+ "\xe2\x92\xbc" => "\xe2\x93\x96",
+ "\xe2\x92\xbd" => "\xe2\x93\x97",
+ "\xe2\x92\xbe" => "\xe2\x93\x98",
+ "\xe2\x92\xbf" => "\xe2\x93\x99",
+ "\xe2\x93\x80" => "\xe2\x93\x9a",
+ "\xe2\x93\x81" => "\xe2\x93\x9b",
+ "\xe2\x93\x82" => "\xe2\x93\x9c",
+ "\xe2\x93\x83" => "\xe2\x93\x9d",
+ "\xe2\x93\x84" => "\xe2\x93\x9e",
+ "\xe2\x93\x85" => "\xe2\x93\x9f",
+ "\xe2\x93\x86" => "\xe2\x93\xa0",
+ "\xe2\x93\x87" => "\xe2\x93\xa1",
+ "\xe2\x93\x88" => "\xe2\x93\xa2",
+ "\xe2\x93\x89" => "\xe2\x93\xa3",
+ "\xe2\x93\x8a" => "\xe2\x93\xa4",
+ "\xe2\x93\x8b" => "\xe2\x93\xa5",
+ "\xe2\x93\x8c" => "\xe2\x93\xa6",
+ "\xe2\x93\x8d" => "\xe2\x93\xa7",
+ "\xe2\x93\x8e" => "\xe2\x93\xa8",
+ "\xe2\x93\x8f" => "\xe2\x93\xa9",
+ "\xef\xbc\xa1" => "\xef\xbd\x81",
+ "\xef\xbc\xa2" => "\xef\xbd\x82",
+ "\xef\xbc\xa3" => "\xef\xbd\x83",
+ "\xef\xbc\xa4" => "\xef\xbd\x84",
+ "\xef\xbc\xa5" => "\xef\xbd\x85",
+ "\xef\xbc\xa6" => "\xef\xbd\x86",
+ "\xef\xbc\xa7" => "\xef\xbd\x87",
+ "\xef\xbc\xa8" => "\xef\xbd\x88",
+ "\xef\xbc\xa9" => "\xef\xbd\x89",
+ "\xef\xbc\xaa" => "\xef\xbd\x8a",
+ "\xef\xbc\xab" => "\xef\xbd\x8b",
+ "\xef\xbc\xac" => "\xef\xbd\x8c",
+ "\xef\xbc\xad" => "\xef\xbd\x8d",
+ "\xef\xbc\xae" => "\xef\xbd\x8e",
+ "\xef\xbc\xaf" => "\xef\xbd\x8f",
+ "\xef\xbc\xb0" => "\xef\xbd\x90",
+ "\xef\xbc\xb1" => "\xef\xbd\x91",
+ "\xef\xbc\xb2" => "\xef\xbd\x92",
+ "\xef\xbc\xb3" => "\xef\xbd\x93",
+ "\xef\xbc\xb4" => "\xef\xbd\x94",
+ "\xef\xbc\xb5" => "\xef\xbd\x95",
+ "\xef\xbc\xb6" => "\xef\xbd\x96",
+ "\xef\xbc\xb7" => "\xef\xbd\x97",
+ "\xef\xbc\xb8" => "\xef\xbd\x98",
+ "\xef\xbc\xb9" => "\xef\xbd\x99",
+ "\xef\xbc\xba" => "\xef\xbd\x9a",
+ "\xf0\x90\x90\x80" => "\xf0\x90\x90\xa8",
+ "\xf0\x90\x90\x81" => "\xf0\x90\x90\xa9",
+ "\xf0\x90\x90\x82" => "\xf0\x90\x90\xaa",
+ "\xf0\x90\x90\x83" => "\xf0\x90\x90\xab",
+ "\xf0\x90\x90\x84" => "\xf0\x90\x90\xac",
+ "\xf0\x90\x90\x85" => "\xf0\x90\x90\xad",
+ "\xf0\x90\x90\x86" => "\xf0\x90\x90\xae",
+ "\xf0\x90\x90\x87" => "\xf0\x90\x90\xaf",
+ "\xf0\x90\x90\x88" => "\xf0\x90\x90\xb0",
+ "\xf0\x90\x90\x89" => "\xf0\x90\x90\xb1",
+ "\xf0\x90\x90\x8a" => "\xf0\x90\x90\xb2",
+ "\xf0\x90\x90\x8b" => "\xf0\x90\x90\xb3",
+ "\xf0\x90\x90\x8c" => "\xf0\x90\x90\xb4",
+ "\xf0\x90\x90\x8d" => "\xf0\x90\x90\xb5",
+ "\xf0\x90\x90\x8e" => "\xf0\x90\x90\xb6",
+ "\xf0\x90\x90\x8f" => "\xf0\x90\x90\xb7",
+ "\xf0\x90\x90\x90" => "\xf0\x90\x90\xb8",
+ "\xf0\x90\x90\x91" => "\xf0\x90\x90\xb9",
+ "\xf0\x90\x90\x92" => "\xf0\x90\x90\xba",
+ "\xf0\x90\x90\x93" => "\xf0\x90\x90\xbb",
+ "\xf0\x90\x90\x94" => "\xf0\x90\x90\xbc",
+ "\xf0\x90\x90\x95" => "\xf0\x90\x90\xbd",
+ "\xf0\x90\x90\x96" => "\xf0\x90\x90\xbe",
+ "\xf0\x90\x90\x97" => "\xf0\x90\x90\xbf",
+ "\xf0\x90\x90\x98" => "\xf0\x90\x91\x80",
+ "\xf0\x90\x90\x99" => "\xf0\x90\x91\x81",
+ "\xf0\x90\x90\x9a" => "\xf0\x90\x91\x82",
+ "\xf0\x90\x90\x9b" => "\xf0\x90\x91\x83",
+ "\xf0\x90\x90\x9c" => "\xf0\x90\x91\x84",
+ "\xf0\x90\x90\x9d" => "\xf0\x90\x91\x85",
+ "\xf0\x90\x90\x9e" => "\xf0\x90\x91\x86",
+ "\xf0\x90\x90\x9f" => "\xf0\x90\x91\x87",
+ "\xf0\x90\x90\xa0" => "\xf0\x90\x91\x88",
+ "\xf0\x90\x90\xa1" => "\xf0\x90\x91\x89",
+ "\xf0\x90\x90\xa2" => "\xf0\x90\x91\x8a",
+ "\xf0\x90\x90\xa3" => "\xf0\x90\x91\x8b",
+ "\xf0\x90\x90\xa4" => "\xf0\x90\x91\x8c",
+ "\xf0\x90\x90\xa5" => "\xf0\x90\x91\x8d"
+);
+
+# Base stuff useful to all UTF-8 based language files
+class LanguageUtf8 extends Language {
+
+ function ucfirst( $string ) {
+ # For most languages, this is a wrapper for ucfirst()
+ # But that doesn't work right in a UTF-8 locale
+ global $wikiUpperChars, $wikiLowerChars;
+ return preg_replace (
+ "/^([\\x00-\\x7f]|[\\xc0-\\xff][\\x80-\\xbf]*)/e",
+ "strtr ( \"\$1\" , \$wikiUpperChars )",
+ $string );
+ }
+
+ function stripForSearch( $string ) {
+ # MySQL fulltext index doesn't grok utf-8, so we
+ # need to fold cases and convert to hex
+ global $wikiLowerChars;
+ return preg_replace(
+ "/([\\xc0-\\xff][\\x80-\\xbf]*)/e",
+ "'U8' . bin2hex( strtr( \"\$1\", \$wikiLowerChars ) )",
+ $string );
+ }
+}
+
+?> \ No newline at end of file
diff --git a/includes/ViewCountUpdate.php b/includes/ViewCountUpdate.php
new file mode 100644
index 000000000000..676ce808f8f5
--- /dev/null
+++ b/includes/ViewCountUpdate.php
@@ -0,0 +1,21 @@
+<?
+# See deferred.doc
+
+class ViewCountUpdate {
+
+ var $mPageID;
+
+ function ViewCountUpdate( $pageid )
+ {
+ $this->mPageID = $pageid;
+ }
+
+ function doUpdate()
+ {
+ $sql = "UPDATE LOW_PRIORITY cur SET cur_counter=(1+cur_counter)," .
+ "cur_timestamp=cur_timestamp WHERE cur_id={$this->mPageID}";
+ $res = wfQuery( $sql, "ViewCountUpdate::doUpdate" );
+ }
+}
+
+?>