diff options
Diffstat (limited to 'includes')
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> </td></tr> +<tr><td align=right> +<input type=checkbox name=\"wpConfirm\" value='1'> +</td><td>{$check}</td> +</tr><tr><td> </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> </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( "&", """, ">", "<" ), + $in ); +} + +function wfEscapeHTMLTagsOnly( $in ) { + return str_replace( + array( "\"", ">", "<" ), + array( """, ">", "<" ), + $in ); +} + +function wfUnescapeHTML( $in ) +{ + $in = str_replace( "<", "<", $in ); + $in = str_replace( ">", ">", $in ); + $in = str_replace( """, "\"", $in ); + $in = str_replace( "&", "&", $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( ">", ">", $rest ); + $text .= "<$slash$t$newparams$brace$rest"; + continue; + } + } + $text .= "<" . str_replace( ">", ">", $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}'> </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 .="< "; + } + $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" ) + . "\"> <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 .= " " ; + if ( $y->isminor ) $r .= $M ; + else $r .= " " ; + $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]}×)" ; + 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 .= " " ; + $r .= " " ; # 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> " ; + if ( $x->isnew ) $r .= $N ; + else $r .= " " ; + if ( $x->isminor ) $r .= $M ; + else $r .= " " ; + $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'> </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}&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> </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> </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> </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)." → ". + $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." ↔ ".$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> </th>\n" ; + $r .= "<th colspan=2>To</th>\n" ; + if ( $mode != "zoom" ) $r .= "<th> </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,"[Σ]","mode=zoom&{$zoom}") ; + $del1 = "xl={$q->lang_from}&xt=".urlencode($q->title_from)."&yl={$q->lang_to}" ; + $del2 = $sk->makeKnownLink($si,"[↔]","mode=delete&{$del1}&back=yes") ; + $del1 = $sk->makeKnownLink($si,"[→]","mode=delete&{$del1}") ; + $del1a = "xl={$q->lang_to}&xt=".urlencode($q->title_to)."&yl={$q->lang_from}" ; + $del1a = $sk->makeKnownLink($si,"[←]","mode=delete&{$del1a}") ; + $sign = "→" ; + if ( $q->both ) $sign = "↔" ; + else $del1a = " " ; + + $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 = " " ; + $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 ↔ $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)." →" ) ; + + # 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 ( "- →".$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> </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> </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> </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) . "×tamp={$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> </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> </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> </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> </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" ); + } +} + +?> |