BUG#1402 Make link color of tab subject page link on talk page indicate whether artic...
[lhc/web/wiklou.git] / includes / EditPage.php
index e6e0ba7..1726381 100644 (file)
@@ -2,7 +2,6 @@
 /**
  * Contain the EditPage class
  * @package MediaWiki
- * @version $Id$
  */
 
 /**
@@ -17,6 +16,7 @@
 class EditPage {
        var $mArticle;
        var $mTitle;
+       var $mMetaData = '';
        
        # Form values
        var $save = false, $preview = false;
@@ -35,6 +35,102 @@ class EditPage {
                $this->mTitle =& $wgTitle;
        }
 
+       /**
+        * This is the function that extracts metadata from the article body on the first view.
+        * To turn the feature on, set $wgUseMetadataEdit = true ; in LocalSettings
+        *  and set $wgMetadataWhitelist to the *full* title of the template whitelist
+        */
+       function extractMetaDataFromArticle ()
+       {
+               global $wgUseMetadataEdit , $wgMetadataWhitelist , $wgLang ;
+               $this->mMetaData = "" ;
+               if ( !$wgUseMetadataEdit ) return ;
+               if ( $wgMetadataWhitelist == "" ) return ;
+               $s = "" ;
+               $t = $this->mArticle->getContent ( true ) ;
+
+               # MISSING : <nowiki> filtering
+
+               # Categories and language links
+               $t = explode ( "\n" , $t ) ;
+               $catlow = strtolower ( $wgLang->getNsText ( NS_CATEGORY ) ) ;
+               $cat = $ll = array() ;
+               foreach ( $t AS $key => $x )
+               {
+                       $y = trim ( strtolower ( $x ) ) ;
+                       while ( substr ( $y , 0 , 2 ) == "[[" )
+                       {
+                               $y = explode ( "]]" , trim ( $x ) ) ;
+                               $first = array_shift ( $y ) ;
+                               $first = explode ( ":" , $first ) ;
+                               $ns = array_shift ( $first ) ;
+                               $ns = trim ( str_replace ( "[" , "" , $ns ) ) ;
+                               if ( strlen ( $ns ) == 2 OR strtolower ( $ns ) == $catlow )
+                               {
+                                       $add = "[[" . $ns . ":" . implode ( ":" , $first ) . "]]" ;
+                                       if ( strtolower ( $ns ) == $catlow ) $cat[] = $add ;
+                                       else $ll[] = $add ;
+                                       $x = implode ( "]]" , $y ) ;
+                                       $t[$key] = $x ;
+                                       $y = trim ( strtolower ( $x ) ) ;
+                               }
+                       }
+               }
+               if ( count ( $cat ) ) $s .= implode ( " " , $cat ) . "\n" ;
+               if ( count ( $ll ) ) $s .= implode ( " " , $ll ) . "\n" ;
+               $t = implode ( "\n" , $t ) ;
+
+               # Load whitelist
+               $sat = array () ; # stand-alone-templates; must be lowercase
+               $wl_title = Title::newFromText ( $wgMetadataWhitelist ) ;
+               $wl_article = new Article ( $wl_title ) ;
+               $wl = explode ( "\n" , $wl_article->getContent(true) ) ;
+               foreach ( $wl AS $x )
+               {
+                       $isentry = false ;
+                       $x = trim ( $x ) ;
+                       while ( substr ( $x , 0 , 1 ) == "*" )
+                       {
+                               $isentry = true ;
+                               $x = trim ( substr ( $x , 1 ) ) ;
+                       }
+                       if ( $isentry )
+                       {
+                               $sat[] = strtolower ( $x ) ;
+                       }
+                          
+               }
+
+               # Templates, but only some
+               $t = explode ( "{{" , $t ) ;
+               $tl = array () ;
+               foreach ( $t AS $key => $x )
+               {
+                       $y = explode ( "}}" , $x , 2 ) ;
+                       if ( count ( $y ) == 2 )
+                       {
+                               $z = $y[0] ;
+                               $z = explode ( "|" , $z ) ;
+                               $tn = array_shift ( $z ) ;
+                               if ( in_array ( strtolower ( $tn ) , $sat ) )
+                               {
+                                       $tl[] = "{{" . $y[0] . "}}" ;
+                                       $t[$key] = $y[1] ;
+                                       $y = explode ( "}}" , $y[1] , 2 ) ;
+                               }
+                               else $t[$key] = "{{" . $x ;
+                       }
+                       else if ( $key != 0 ) $t[$key] = "{{" . $x ;
+                       else $t[$key] = $x ;
+               }
+               if ( count ( $tl ) ) $s .= implode ( " " , $tl ) ;
+               $t = implode ( "" , $t ) ;
+
+               $t = str_replace ( "\n\n\n" , "\n" , $t ) ;
+               $this->mArticle->mContent = $t ;
+               $this->mMetaData = $s ;
+       }
+
        /**
         * This is the function that gets called for "action=edit".
         */
@@ -44,6 +140,11 @@ class EditPage {
                $wgOut->setArticleFlag(false);
 
                $this->importFormData( $wgRequest );
+               
+               if( $this->live ) {
+                       $this->livePreview();
+                       return;
+               }
 
                if ( ! $this->mTitle->userCanEdit() ) {
                        $wgOut->readOnlyPage( $this->mArticle->getContent( true ), true );
@@ -67,10 +168,15 @@ class EditPage {
                }
                if ( $this->save ) {
                        $this->editForm( 'save' );
-               } else if ( $this->preview or $wgUser->getOption('previewonfirst')) {
+               } else if ( $this->preview ) {
                        $this->editForm( 'preview' );
                } else { # First time through
-                       $this->editForm( 'initial' );
+                       if( $wgUser->getOption('previewonfirst') ) {
+                               $this->editForm( 'preview', true );
+                       } else {
+                               $this->extractMetaDataFromArticle () ;
+                               $this->editForm( 'initial', true );
+                       }
                }
        }
 
@@ -83,6 +189,7 @@ class EditPage {
                # whitespace from the text boxes. This may be significant formatting.
                $this->textbox1 = rtrim( $request->getText( 'wpTextbox1' ) );
                $this->textbox2 = rtrim( $request->getText( 'wpTextbox2' ) );
+               $this->mMetaData = rtrim( $request->getText( 'metadata' ) );
                $this->summary = trim( $request->getText( 'wpSummary' ) );
 
                $this->edittime = $request->getVal( 'wpEdittime' );
@@ -97,6 +204,8 @@ class EditPage {
 
                # Section edit can come from either the form or a link
                $this->section = $request->getVal( 'wpSection', $request->getVal( 'section' ) );
+               
+               $this->live = $request->getCheck( 'live' );
        }
 
        /**
@@ -120,8 +229,9 @@ class EditPage {
         * the newly-edited page.
         *
         * @param string $formtype Type of form either : save, initial or preview
+        * @param bool $firsttime True to load form data from db
         */
-       function editForm( $formtype ) {
+       function editForm( $formtype, $firsttime = false ) {
                global $wgOut, $wgUser;
                global $wgLang, $wgContLang, $wgParser, $wgTitle;
                global $wgAllowAnonymousMinor;
@@ -148,9 +258,13 @@ class EditPage {
                # in the back door with a hand-edited submission URL.
 
                if ( 'save' == $formtype ) {
+                       # Reintegrate metadata
+                       if ( $this->mMetaData != "" ) $this->textbox1 .= "\n" . $this->mMetaData ;
+                       $this->mMetaData = "" ;
+
                        # Check for spam
                        if ( $wgSpamRegex && preg_match( $wgSpamRegex, $this->textbox1, $matches ) ) {
-                               $this->spamPage ( $matches );
+                               $this->spamPage ( $matches[0] );
                                return;
                        }
                        if ( $wgFilterCallback && $wgFilterCallback( $this->mTitle, $this->textbox1, $this->section ) ) {
@@ -179,7 +293,14 @@ class EditPage {
                                        $wgOut->redirect( $this->mTitle->getFullURL() );
                                        return;
                                }
-                               $this->mArticle->insertNewArticle( $this->textbox1, $this->summary, $this->minoredit, $this->watchthis );
+                               if (wfRunHooks('ArticleSave', $this->mArticle, $wgUser, $this->textbox1, 
+                                                          $this->summary, $this->minoredit, $this->watchthis, NULL))
+                               {
+                                       $this->mArticle->insertNewArticle( $this->textbox1, $this->summary,
+                                                                                                          $this->minoredit, $this->watchthis );
+                                       wfRunHooks('ArticleSaveComplete', $this->mArticle, $wgUser, $this->textbox1, 
+                                                          $this->summary, $this->minoredit, $this->watchthis, NULL);
+                               }
                                return;
                        }
 
@@ -238,18 +359,27 @@ class EditPage {
                                                $sectionanchor = $this->sectionAnchor( $matches[2] );
                                        }
                                }
-       
-                               # update the article here
-                               if($this->mArticle->updateArticle( $text, $this->summary, $this->minoredit, $this->watchthis, '', $sectionanchor ))
-                                       return;
-                               else
-                                       $isConflict = true;
+                               
+                               if (wfRunHooks('ArticleSave', $this->mArticle, $wgUser, $text, $this->summary,
+                                                          $this->minoredit, $this->watchthis, $sectionanchor))
+                               {
+                                       # update the article here
+                                       if($this->mArticle->updateArticle( $text, $this->summary, $this->minoredit,
+                                                                                                          $this->watchthis, '', $sectionanchor ))
+                                       {
+                                               wfRunHooks('ArticleSaveComplete', $this->mArticle, $wgUser, $text, $this->summary,
+                                                                  $this->minoredit, $this->watchthis, $sectionanchor);
+                                               return;
+                                       }
+                                       else
+                                         $isConflict = true;
+                               }
                        }
                }
                # First time through: get contents, set time for conflict
                # checking, etc.
 
-               if ( 'initial' == $formtype ) {
+               if ( 'initial' == $formtype || $firsttime ) {
                        $this->edittime = $this->mArticle->getTimestamp();
                        $this->textbox1 = $this->mArticle->getContent( true );
                        $this->summary = '';
@@ -277,11 +407,11 @@ class EditPage {
                                        $s = wfMsg('editingsection', $this->mTitle->getPrefixedText() );
                                }
                                if(!$this->preview) {
-                                       $sectitle=preg_match("/^=+(.*?)=+/mi",
-                                       $this->textbox1,
-                                       $matches);
-                                       if( !empty( $matches[1] ) ) {
-                                               $this->summary = "/* ". trim($matches[1])." */ ";
+                                       preg_match( "/^(=+)(.+)\\1/mi",
+                                               $this->textbox1,
+                                               $matches );
+                                       if( !empty( $matches[2] ) ) {
+                                               $this->summary = "/* ". trim($matches[2])." */ ";
                                        }
                                }
                        } else {
@@ -350,7 +480,7 @@ class EditPage {
 
                if( $wgUser->getOption('showtoolbar') and !$isCssJsSubpage ) {
                        # prepare toolbar for edit buttons
-                       $toolbar = $sk->getEditToolbar();
+                       $toolbar = $this->getEditToolbar();
                } else {
                        $toolbar = '';
                }
@@ -385,49 +515,15 @@ class EditPage {
 
                $checkboxhtml = $minoredithtml . $watchhtml . '<br />';
 
+               $wgOut->addHTML( '<div id="wikiPreview">' );
                if ( 'preview' == $formtype) {
-                       $previewhead='<h2>' . wfMsg( 'preview' ) . "</h2>\n<p><center><font color=\"#cc0000\">" .
-                               wfMsg( 'note' ) . wfMsg( 'previewnote' ) . "</font></center></p>\n";
-                       if ( $isConflict ) {
-                               $previewhead.='<h2>' . wfMsg( 'previewconflict' ) .
-                                       "</h2>\n";
-                       }
-
-                       $parserOptions = ParserOptions::newFromUser( $wgUser );
-                       $parserOptions->setEditSection( false );
-                       $parserOptions->setEditSectionOnRightClick( false );
-
-                       # don't parse user css/js, show message about preview
-                       # XXX: stupid php bug won't let us use $wgTitle->isCssJsSubpage() here
-
-                       if ( $isCssJsSubpage ) {
-                               if(preg_match("/\\.css$/", $wgTitle->getText() ) ) {
-                                       $previewtext = wfMsg('usercsspreview');
-                               } else if(preg_match("/\\.js$/", $wgTitle->getText() ) ) {
-                                       $previewtext = wfMsg('userjspreview');
-                               }
-                               $parserOutput = $wgParser->parse( $previewtext , $wgTitle, $parserOptions );
-                               $wgOut->addHTML( $parserOutput->mText );
-                       } else {
-                               # if user want to see preview when he edit an article
-                               if( $wgUser->getOption('previewonfirst') and ($this->textbox1 == '')) {
-                                       $this->textbox1 = $this->mArticle->getContent(true);
-                               }
-
-                               $parserOutput = $wgParser->parse( $this->mArticle->preSaveTransform( $this->textbox1 ) ."\n\n",
-                                               $wgTitle, $parserOptions );             
-                               
-                               $previewHTML = $parserOutput->mText;
-
-                               if($wgUser->getOption('previewontop')) {
-                                       $wgOut->addHTML($previewhead);
-                                       $wgOut->addHTML($previewHTML);
-                               }
-                               $wgOut->addCategoryLinks($parserOutput->getCategoryLinks());
-                               $wgOut->addLanguageLinks($parserOutput->getLanguageLinks());
+                       $previewOutput = $this->getPreviewText( $isConflict, $isCssJsSubpage );
+                       if( $wgUser->getOption('previewontop' ) ) {
+                               $wgOut->addHTML( $previewOutput );
                                $wgOut->addHTML( "<br style=\"clear:both;\" />\n" );
                        }
                }
+               $wgOut->addHTML( '</div>' );
 
                # if this is a comment, show a subject line at the top, which is also the edit summary.
                # Otherwise, show a summary field at the bottom
@@ -446,17 +542,17 @@ class EditPage {
                }
                # Prepare a list of templates used by this page
                $db =& wfGetDB( DB_SLAVE );
-               $cur = $db->tableName( 'cur' );
+               $page = $db->tableName( 'page' );
                $links = $db->tableName( 'links' );
                $id = $this->mTitle->getArticleID();
-               $sql = "SELECT cur_namespace,cur_title,cur_id ".
-                       "FROM $cur,$links WHERE l_to=cur_id AND l_from={$id} and cur_namespace=".NS_TEMPLATE;
+               $sql = "SELECT page_namespace,page_title,page_id ".
+                       "FROM $page,$links WHERE l_to=page_id AND l_from={$id} and page_namespace=".NS_TEMPLATE;
                $res = $db->query( $sql, "EditPage::editform" );
 
                if ( $db->numRows( $res ) ) {
                        $templates = '<br />'. wfMsg( 'templatesused' ) . '<ul>';
                        while ( $row = $db->fetchObject( $res ) ) {
-                               if ( $titleObj = Title::makeTitle( $row->cur_namespace, $row->cur_title ) ) {
+                               if ( $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title ) ) {
                                        $templates .= '<li>' . $sk->makeLinkObj( $titleObj ) . '</li>';
                                }
                        }
@@ -464,21 +560,56 @@ class EditPage {
                } else {        
                        $templates = '';
                }
-               $wgOut->addHTML( "
+               
+               global $wgLivePreview, $wgStylePath;
+               /**
+                * Live Preview lets us fetch rendered preview page content and
+                * add it to the page without refreshing the whole page.
+                * Set up the button for it; if not supported by the browser
+                * it will fall through to the normal form submission method.
+                */
+               if( $wgLivePreview ) {
+                       $wgOut->addHTML( '<script type="text/javascript" src="' .
+                               htmlspecialchars( $wgStylePath . '/common/preview.js' ) .
+                               '"></script>' . "\n" );
+                       $liveAction = $wgTitle->getLocalUrl( 'action=submit&wpPreview=true&live=true' );
+                       $liveOnclick = 'onclick="return !livePreview('.
+                               'getElementById(\'wikiPreview\'),' .
+                               'editform.wpTextbox1.value,' .
+                               htmlspecialchars( '"' . $liveAction . '"' ) . ')"';
+               } else {
+                       $liveOnclick = '';
+               }
+               
+               global $wgUseMetadataEdit ;
+               if ( $wgUseMetadataEdit )
+               {
+                       $metadata = $this->mMetaData ;
+                       $metadata = htmlspecialchars( $wgContLang->recodeForEdit( $metadata ) ) ;
+                       $helppage = Title::newFromText ( wfmsg("metadata_page") ) ;
+                       $top = str_replace ( "$1" , $helppage->getInternalURL() , wfmsg("metadata") ) ;
+                       $metadata = $top . "<textarea name='metadata' rows='3' cols='{$cols}'{$ew}>{$metadata}</textarea>" ;
+               }
+               else $metadata = "" ;
+
+
+               $wgOut->addHTML( <<<END
 {$toolbar}
-<form id=\"editform\" name=\"editform\" method=\"post\" action=\"$action\"
-enctype=\"multipart/form-data\">
+<form id="editform" name="editform" method="post" action="$action"
+enctype="multipart/form-data">
 {$commentsubject}
-<textarea tabindex='1' accesskey=\",\" name=\"wpTextbox1\" rows='{$rows}'
-cols='{$cols}'{$ew}>" .
-htmlspecialchars( $wgContLang->recodeForEdit( $this->textbox1 ) ) .
+<textarea tabindex='1' accesskey="," name="wpTextbox1" rows='{$rows}'
+cols='{$cols}'{$ew}>
+END
+. htmlspecialchars( $wgContLang->recodeForEdit( $this->textbox1 ) ) .
 "
 </textarea>
+{$metadata}
 <br />{$editsummary}
 {$checkboxhtml}
 <input tabindex='5' id='wpSave' type='submit' value=\"{$save}\" name=\"wpSave\" accesskey=\"".wfMsg('accesskey-save')."\"".
 " title=\"".wfMsg('tooltip-save')."\"/>
-<input tabindex='6' id='wpPreview' type='submit' value=\"{$prev}\" name=\"wpPreview\" accesskey=\"".wfMsg('accesskey-preview')."\"".
+<input tabindex='6' id='wpPreview' type='submit' $liveOnclick value=\"{$prev}\" name=\"wpPreview\" accesskey=\"".wfMsg('accesskey-preview')."\"".
 " title=\"".wfMsg('tooltip-preview')."\"/>
 <em>{$cancel}</em> | <em>{$edithelp}</em>{$templates}" );
                $wgOut->addWikiText( $copywarn );
@@ -500,11 +631,53 @@ htmlspecialchars( $wgContLang->recodeForEdit( $this->textbox1 ) ) .
                }
                $wgOut->addHTML( "</form>\n" );
                if($formtype =="preview" && !$wgUser->getOption("previewontop")) {
-                       $wgOut->addHTML($previewhead);
-                       $wgOut->addHTML($previewHTML);
+                       $wgOut->addHTML('<div id="wikiPreview">' . $previewOutput . '</div>');
                }
        }
 
+       function getPreviewText( $isConflict, $isCssJsSubpage ) {
+               global $wgOut, $wgUser, $wgTitle, $wgParser;
+               $previewhead='<h2>' . wfMsg( 'preview' ) . "</h2>\n<p><center><font color=\"#cc0000\">" .
+                       wfMsg( 'note' ) . wfMsg( 'previewnote' ) . "</font></center></p>\n";
+               if ( $isConflict ) {
+                       $previewhead.='<h2>' . wfMsg( 'previewconflict' ) .
+                               "</h2>\n";
+               }
+
+               $parserOptions = ParserOptions::newFromUser( $wgUser );
+               $parserOptions->setEditSection( false );
+               $parserOptions->setEditSectionOnRightClick( false );
+
+               # don't parse user css/js, show message about preview
+               # XXX: stupid php bug won't let us use $wgTitle->isCssJsSubpage() here
+
+               if ( $isCssJsSubpage ) {
+                       if(preg_match("/\\.css$/", $wgTitle->getText() ) ) {
+                               $previewtext = wfMsg('usercsspreview');
+                       } else if(preg_match("/\\.js$/", $wgTitle->getText() ) ) {
+                               $previewtext = wfMsg('userjspreview');
+                       }
+                       $parserOutput = $wgParser->parse( $previewtext , $wgTitle, $parserOptions );
+                       $wgOut->addHTML( $parserOutput->mText );
+               } else {
+                       # if user want to see preview when he edit an article
+                       if( $wgUser->getOption('previewonfirst') and ($this->textbox1 == '')) {
+                               $this->textbox1 = $this->mArticle->getContent(true);
+                       }
+
+                       $toparse = $this->textbox1 ;
+                       if ( $this->mMetaData != "" ) $toparse .= "\n" . $this->mMetaData ;
+                       
+                       $parserOutput = $wgParser->parse( $this->mArticle->preSaveTransform( $toparse ) ."\n\n",
+                                       $wgTitle, $parserOptions );             
+                       
+                       $previewHTML = $parserOutput->mText;
+                       $wgOut->addCategoryLinks($parserOutput->getCategoryLinks());
+                       $wgOut->addLanguageLinks($parserOutput->getLanguageLinks());
+               }
+               return $previewhead . $previewHTML;
+       }
+       
        /**
         * @todo document
         */
@@ -548,7 +721,7 @@ htmlspecialchars( $wgContLang->recodeForEdit( $this->textbox1 ) ) .
        /**
         * @todo document
         */
-       function spamPage ( $matches = array() )
+       function spamPage ( $match = false )
        {
                global $wgOut;
                $wgOut->setPageTitle( wfMsg( 'spamprotectiontitle' ) );
@@ -556,8 +729,8 @@ htmlspecialchars( $wgContLang->recodeForEdit( $this->textbox1 ) ) .
                $wgOut->setArticleRelated( false );
 
                $wgOut->addWikiText( wfMsg( 'spamprotectiontext' ) );
-               if ( isset ( $matches[0] ) ) {
-                       $wgOut->addWikiText( wfMsg( 'spamprotectionmatch', "<nowiki>{$matches[0]}</nowiki>" ) );
+               if ( $match ) {
+                       $wgOut->addWikiText( wfMsg( 'spamprotectionmatch', "<nowiki>{$match}</nowiki>" ) );
                }
                $wgOut->returnToMain( false );
        }
@@ -611,21 +784,12 @@ htmlspecialchars( $wgContLang->recodeForEdit( $this->textbox1 ) ) .
         * @todo document
         */
        function mergeChangesInto( &$text ){
-               $fname = 'EditPage::mergeChangesInto';
-               $oldDate = $this->edittime;
-               $dbw =& wfGetDB( DB_MASTER );
-               $obj = $dbw->selectRow( 'cur', array( 'cur_text' ), array( 'cur_id' => $this->mTitle->getArticleID() ), 
-                       $fname, 'FOR UPDATE' );
-
-               $yourtext = $obj->cur_text;
-               $ns = $this->mTitle->getNamespace();
-               $title = $this->mTitle->getDBkey();
-               $obj = $dbw->selectRow( 'old', 
-                       array( 'old_text','old_flags'), 
-                       array( 'old_namespace' => $ns, 'old_title' => $title, 
-                               'old_timestamp' => $dbw->timestamp($oldDate)),
-                       $fname );
-               $oldText = Article::getRevisionText( $obj );
+               $yourtext = $this->mArticle->fetchRevisionText();
+               
+               $db =& wfGetDB( DB_SLAVE );
+               $oldText = $this->mArticle->fetchRevisionText(
+                       $db->timestamp( $this->edittime ),
+                       'rev_timestamp' );
                
                if(wfMerge($oldText, $text, $yourtext, $result)){
                        $text = $result;
@@ -669,6 +833,154 @@ htmlspecialchars( $wgContLang->recodeForEdit( $this->textbox1 ) ) .
                        array_values( $replacearray ),
                        $sectionanchor );
        }
+
+       /**
+        * Shows a bulletin board style toolbar for common editing functions.
+        * It can be disabled in the user preferences.
+        * The necessary JavaScript code can be found in style/wikibits.js.
+        */
+       function getEditToolbar() {
+               global $wgStylePath, $wgLang, $wgMimeType;
+
+               /**
+                * toolarray an array of arrays which each include the filename of
+                * the button image (without path), the opening tag, the closing tag,
+                * and optionally a sample text that is inserted between the two when no
+                * selection is highlighted.
+                * The tip text is shown when the user moves the mouse over the button.
+                *
+                * Already here are accesskeys (key), which are not used yet until someone
+                * can figure out a way to make them work in IE. However, we should make
+                * sure these keys are not defined on the edit page.
+                */
+               $toolarray=array(
+                       array(  'image'=>'button_bold.png',
+                                       'open'  =>      "\'\'\'",
+                                       'close' =>      "\'\'\'",
+                                       'sample'=>      wfMsg('bold_sample'),
+                                       'tip'   =>      wfMsg('bold_tip'),
+                                       'key'   =>      'B'
+                               ),
+                       array(  'image'=>'button_italic.png',
+                                       'open'  =>      "\'\'",
+                                       'close' =>      "\'\'",
+                                       'sample'=>      wfMsg('italic_sample'),
+                                       'tip'   =>      wfMsg('italic_tip'),
+                                       'key'   =>      'I'
+                               ),
+                       array(  'image'=>'button_link.png',
+                                       'open'  =>      '[[',
+                                       'close' =>      ']]',
+                                       'sample'=>      wfMsg('link_sample'),
+                                       'tip'   =>      wfMsg('link_tip'),
+                                       'key'   =>      'L'
+                               ),
+                       array(  'image'=>'button_extlink.png',
+                                       'open'  =>      '[',
+                                       'close' =>      ']',
+                                       'sample'=>      wfMsg('extlink_sample'),
+                                       'tip'   =>      wfMsg('extlink_tip'),
+                                       'key'   =>      'X'
+                               ),
+                       array(  'image'=>'button_headline.png',
+                                       'open'  =>      "\\n== ",
+                                       'close' =>      " ==\\n",
+                                       'sample'=>      wfMsg('headline_sample'),
+                                       'tip'   =>      wfMsg('headline_tip'),
+                                       'key'   =>      'H'
+                               ),
+                       array(  'image'=>'button_image.png',
+                                       'open'  =>      '[['.$wgLang->getNsText(NS_IMAGE).":",
+                                       'close' =>      ']]',
+                                       'sample'=>      wfMsg('image_sample'),
+                                       'tip'   =>      wfMsg('image_tip'),
+                                       'key'   =>      'D'
+                               ),
+                       array(  'image' =>      'button_media.png',
+                                       'open'  =>      '[['.$wgLang->getNsText(NS_MEDIA).':',
+                                       'close' =>      ']]',
+                                       'sample'=>      wfMsg('media_sample'),
+                                       'tip'   =>      wfMsg('media_tip'),
+                                       'key'   =>      'M'
+                               ),
+                       array(  'image' =>      'button_math.png',
+                                       'open'  =>      "\\<math\\>",
+                                       'close' =>      "\\</math\\>",
+                                       'sample'=>      wfMsg('math_sample'),
+                                       'tip'   =>      wfMsg('math_tip'),
+                                       'key'   =>      'C'
+                               ),
+                       array(  'image' =>      'button_nowiki.png',
+                                       'open'  =>      "\\<nowiki\\>",
+                                       'close' =>      "\\</nowiki\\>",
+                                       'sample'=>      wfMsg('nowiki_sample'),
+                                       'tip'   =>      wfMsg('nowiki_tip'),
+                                       'key'   =>      'N'
+                               ),
+                       array(  'image' =>      'button_sig.png',
+                                       'open'  =>      '--~~~~',
+                                       'close' =>      '',
+                                       'sample'=>      '',
+                                       'tip'   =>      wfMsg('sig_tip'),
+                                       'key'   =>      'Y'
+                               ),
+                       array(  'image' =>      'button_hr.png',
+                                       'open'  =>      "\\n----\\n",
+                                       'close' =>      '',
+                                       'sample'=>      '',
+                                       'tip'   =>      wfMsg('hr_tip'),
+                                       'key'   =>      'R'
+                               )
+               );
+               $toolbar ="<script type='text/javascript'>\n/*<![CDATA[*/\n";
+
+               $toolbar.="document.writeln(\"<div id='toolbar'>\");\n";
+               foreach($toolarray as $tool) {
+
+                       $image=$wgStylePath.'/common/images/'.$tool['image'];
+                       $open=$tool['open'];
+                       $close=$tool['close'];
+                       $sample = addslashes( $tool['sample'] );
+
+                       // Note that we use the tip both for the ALT tag and the TITLE tag of the image.
+                       // Older browsers show a "speedtip" type message only for ALT.
+                       // Ideally these should be different, realistically they
+                       // probably don't need to be.
+                       $tip = addslashes( $tool['tip'] );
+
+                       #$key = $tool["key"];
+
+                       $toolbar.="addButton('$image','$tip','$open','$close','$sample');\n";
+               }
+
+               $toolbar.="addInfobox('" . addslashes( wfMsg( "infobox" ) ) . "','" . addslashes(wfMsg("infobox_alert")) . "');\n";
+               $toolbar.="document.writeln(\"</div>\");\n";
+
+               $toolbar.="/*]]>*/\n</script>";
+               return $toolbar;
+       }
+       
+       /**
+        * Output preview text only. This can be sucked into the edit page
+        * via JavaScript, and saves the server time rendering the skin as
+        * well as theoretically being more robust on the client (doesn't
+        * disturb the edit box's undo history, won't eat your text on
+        * failure, etc).
+        *
+        * @todo This doesn't include category or interlanguage links.
+        *       Would need to enhance it a bit, maybe wrap them in XML
+        *       or something... that might also require more skin
+        *       initialization, so check whether that's a problem.
+        */
+       function livePreview() {
+               global $wgOut;
+               $wgOut->disable();
+               header( 'Content-type: text/xml' );
+               header( 'Cache-control: no-cache' );
+               # FIXME
+               echo $this->getPreviewText( false, false );
+       }
+
 }
 
 ?>