From c05eeb66755f74272b4a5f82acc6caaeafc0fb54 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Sun, 19 Dec 2004 02:36:04 +0000 Subject: [PATCH] Add experimental 'Live Preview' feature. On supported browsers, preview can be loaded via JavaScript so it doesn't disturb the surrounding page as badly. It's also lighter on the server, as it doesn't require skin rendering. (On short pages with Turck cache, this saves 50% of apache time!) Disabled by default, as it's incomplete: interlanguage and category links aren't displayed, and there are likely other problems. Nonsupporting browsers or JS off fall back gracefully to form submission. Tested: Safari 1.2.4, Firefox 1.0, MSIE/Win 6.0 (XPSP2) --- RELEASE-NOTES | 16 ++++ includes/DefaultSettings.php | 8 ++ includes/EditPage.php | 138 ++++++++++++++++++++++++----------- skins/common/preview.js | 44 +++++++++++ 4 files changed, 164 insertions(+), 42 deletions(-) create mode 100644 skins/common/preview.js diff --git a/RELEASE-NOTES b/RELEASE-NOTES index d41040b27f..5ce29b5895 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -3,6 +3,22 @@ Security reminder: MediaWiki does not require PHP's register_globals setting since version 1.2.0. If you have it on, turn it *off* if you can. +** NOTE TO COMMITTERS: Before 1.5.0 release, rearrange these nicely +** and move 1.4 stuff to HISTORY. + +== MediaWiki 1.5 == + +New exciting things! Need further work and testing... +* user groups/permissions scheme +* e-mail change notifications +* 'live preview' reduces preview reload burden on supported browsers +* ...and more! + +Need to merge: +* SCHEMA_WORK +* stuff + + == Version Enotif+Eauthent EN+EA v2.00/CVS, 14.12.2004 == written by Thomas Gries, Berlin and Markus Arndt, Munich diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 8766062108..e5805e8af7 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -972,6 +972,14 @@ $wgAuth = null; */ $wgHooks = array(); + +/** + * Experimental preview feature to fetch rendered text + * over an XMLHttpRequest from JavaScript instead of + * forcing a submit and reload of the whole page. + * Leave disabled unless you're testing it. + */ +$wgLivePreview = false; } else { die(); diff --git a/includes/EditPage.php b/includes/EditPage.php index fc9433c308..0f4e779f1b 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -43,6 +43,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 ); @@ -96,6 +101,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' ); } /** @@ -400,49 +407,15 @@ class EditPage { $checkboxhtml = $minoredithtml . $watchhtml . '
'; + $wgOut->addHTML( '
' ); if ( 'preview' == $formtype) { - $previewhead='

' . wfMsg( 'preview' ) . "

\n

" . - wfMsg( 'note' ) . wfMsg( 'previewnote' ) . "

\n"; - if ( $isConflict ) { - $previewhead.='

' . wfMsg( 'previewconflict' ) . - "

\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( "
\n" ); } } + $wgOut->addHTML( '
' ); # 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 @@ -479,6 +452,27 @@ class EditPage { } else { $templates = ''; } + + 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( '' . "\n" ); + $liveAction = $wgTitle->getLocalUrl( 'action=submit&wpPreview=true&live=true' ); + $liveOnclick = 'onclick="return !livePreview('. + 'getElementById(\'wikiPreview\'),' . + 'editform.wpTextbox1.value,' . + htmlspecialchars( '"' . $liveAction . '"' ) . ')"'; + } else { + $liveOnclick = ''; + } + $wgOut->addHTML( " {$toolbar}
recodeForEdit( $this->textbox1 ) ) . {$checkboxhtml} - {$cancel} | {$edithelp}{$templates}" ); $wgOut->addWikiText( $copywarn ); @@ -515,11 +509,50 @@ htmlspecialchars( $wgContLang->recodeForEdit( $this->textbox1 ) ) . } $wgOut->addHTML( "
\n" ); if($formtype =="preview" && !$wgUser->getOption("previewontop")) { - $wgOut->addHTML($previewhead); - $wgOut->addHTML($previewHTML); + $wgOut->addHTML('
' . $previewOutput . '
'); } } + function getPreviewText( $isConflict, $isCssJsSubpage ) { + global $wgOut, $wgUser, $wgTitle, $wgParser; + $previewhead='

' . wfMsg( 'preview' ) . "

\n

" . + wfMsg( 'note' ) . wfMsg( 'previewnote' ) . "

\n"; + if ( $isConflict ) { + $previewhead.='

' . wfMsg( 'previewconflict' ) . + "

\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; + $wgOut->addCategoryLinks($parserOutput->getCategoryLinks()); + $wgOut->addLanguageLinks($parserOutput->getLanguageLinks()); + } + return $previewhead . $previewHTML; + } + /** * @todo document */ @@ -810,6 +843,27 @@ htmlspecialchars( $wgContLang->recodeForEdit( $this->textbox1 ) ) . $toolbar.="/*]]>*/\n"; 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 ); + } } diff --git a/skins/common/preview.js b/skins/common/preview.js new file mode 100644 index 0000000000..1a9ee4faa1 --- /dev/null +++ b/skins/common/preview.js @@ -0,0 +1,44 @@ +// Live preview + +function openXMLHttpRequest() { + if( window.XMLHttpRequest ) { + return new XMLHttpRequest(); + } else if( window.ActiveXObject ) { + return new ActiveXObject("Microsoft.XMLHTTP"); + } else { + return null; + } +} + +/** + * Returns true if could open the request, + * false otherwise (eg no browser support). + */ +function livePreview(target, text, postUrl) { + prevTarget = target; + if( !target ) { + window.alert('crash and burn'); + } + prevReq = openXMLHttpRequest(); + if( !prevReq ) return false; + + prevReq.onreadystatechange = updatePreviewText; + prevReq.open("POST", postUrl, true); + + var postData = 'wpTextbox1=' + encodeURIComponent(text); + prevReq.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + prevReq.send(postData); + return true; +} + +function updatePreviewText() { + if( prevReq.readyState != 4 ) { + return; + } + if( prevReq.status != 200 ) { + window.alert('Failed to connect: ' + prevReq.status + + ' "' + prevReq.statusText + '"'); + return; + } + prevTarget.innerHTML = prevReq.responseText; +} -- 2.20.1