Fix up AJAX watch and enable it by default.
authorAryeh Gregor <simetrical@users.mediawiki.org>
Fri, 22 Jun 2007 17:36:59 +0000 (17:36 +0000)
committerAryeh Gregor <simetrical@users.mediawiki.org>
Fri, 22 Jun 2007 17:36:59 +0000 (17:36 +0000)
* Allow it to work for nonexistent articles (pass title instead of ID)
* Use event handlers, not javascript: URLs
* Fix bug preventing akeytt from setting tooltips for a single element
* Add jsMsg() function to wikibits to allow messages to be displayed dynamically at the top of the screen (could use dismiss button?)

Some test-cases I've thrown at it worked fine (nonexistent pages, namespaced pages, pages with funny characters), but I haven't spent seven weeks in a cave meditating on what could possibly go wrong, so there may be some kind of omission somewhere.

RELEASE-NOTES
includes/AjaxFunctions.php
includes/DefaultSettings.php
skins/common/ajaxwatch.js
skins/common/shared.css
skins/common/wikibits.js

index a706f50..aa78201 100644 (file)
@@ -97,6 +97,8 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
   to cause hard-to-track-down interactions between extensions.
 * Use $wgJobClasses to determine the correct Job to instantiate for a particular
   queued task; allows extensions to introduce custom jobs
+* (bug 10326) AJAX-based page watching and unwatching has been cleaned up and
+  enabled by default.
 
 == Bugfixes since 1.10 ==
 
index 86f853d..7baac89 100644 (file)
@@ -136,22 +136,29 @@ function wfSajaxSearch( $term ) {
 
 /**
  * Called for AJAX watch/unwatch requests.
- * @param $pageID Integer ID of the page to be watched/unwatched
+ * @param $pagename Prefixed title string for page to watch/unwatch
  * @param $watch String 'w' to watch, 'u' to unwatch
- * @return String '<w#>' or '<u#>' on successful watch or unwatch, respectively, or '<err#>' on error (invalid XML in case we want to add HTML sometime)
+ * @return String '<w#>' or '<u#>' on successful watch or unwatch, 
+ *   respectively, followed by an HTML message to display in the alert box; or
+ *   '<err#>' on error
  */
-function wfAjaxWatch($pageID = "", $watch = "") {
-       if(wfReadOnly())
-               return '<err#>'; // redirect to action=(un)watch, which will display the database lock message
+function wfAjaxWatch($pagename = "", $watch = "") {
+       if(wfReadOnly()) {
+               // redirect to action=(un)watch, which will display the database lock
+               // message
+               return '<err#>'; 
+       }
 
-       if(('w' !== $watch && 'u' !== $watch) || !is_numeric($pageID))
+       if('w' !== $watch && 'u' !== $watch) {
                return '<err#>';
+       }
        $watch = 'w' === $watch;
-       $pageID = intval($pageID);
 
-       $title = Title::newFromID($pageID);
-       if(!$title)
+       $title = Title::newFromText($pagename);
+       if(!$title) {
+               // Invalid title
                return '<err#>';
+       }
        $article = new Article($title);
        $watching = $title->userIsWatching();
 
@@ -170,7 +177,10 @@ function wfAjaxWatch($pageID = "", $watch = "") {
                        $dbw->commit();
                }
        }
-
-       return $watch ? '<w#>' : '<u#>';
+       if( $watch ) {
+               return '<w#>'.wfMsgExt( 'addedwatchtext', array( 'parse' ), $title->getPrefixedText() );
+       } else {
+               return '<u#>'.wfMsgExt( 'removedwatchtext', array( 'parse' ), $title->getPrefixedText() );
+       }
 }
 ?>
index a1cdd2a..0c2513e 100644 (file)
@@ -2514,7 +2514,7 @@ $wgUpdateRowsPerQuery = 10;
 /**
  * Enable AJAX framework
  */
-$wgUseAjax = false;
+$wgUseAjax = true;
 
 /**
  * Enable auto suggestion for the search bar 
@@ -2534,7 +2534,7 @@ $wgAjaxExportList = array( );
  * Requires $wgUseAjax to be true too.
  * Causes wfAjaxWatch to be added to $wgAjaxExportList
  */
-$wgAjaxWatch = false;
+$wgAjaxWatch = true;
 
 /**
  * Allow DISPLAYTITLE to change title display
index 16e4fdc..2187004 100644 (file)
@@ -2,17 +2,17 @@
 // * ajax.js:
   /*extern sajax_init_object, sajax_do_call */
 // * wikibits.js:
-  /*extern changeText, akeytt, hookEvent */
+  /*extern changeText, akeytt, hookEvent, alertMsg */
 
 // These should have been initialized in the generated js
-/*extern wgAjaxWatch, wgArticleId */
+/*extern wgAjaxWatch, wgPageName */
 
 if(typeof wgAjaxWatch === "undefined" || !wgAjaxWatch) {
        var wgAjaxWatch = {
                watchMsg: "Watch",
                unwatchMsg: "Unwatch",
                watchingMsg: "Watching...",
-               unwatchingMsg: "Unwatching..."
+               unwatchingMsg: "Unwatching...",
        };
 }
 
@@ -20,32 +20,52 @@ wgAjaxWatch.supported = true; // supported on current page and by browser
 wgAjaxWatch.watching = false; // currently watching page
 wgAjaxWatch.inprogress = false; // ajax request in progress
 wgAjaxWatch.timeoutID = null; // see wgAjaxWatch.ajaxCall
-wgAjaxWatch.watchLink1 = null; // "watch"/"unwatch" link
-wgAjaxWatch.watchLink2 = null; // second one, for (some?) non-Monobook-based
-wgAjaxWatch.oldHref = null; // url for action=watch/action=unwatch
+wgAjaxWatch.watchLinks = []; // "watch"/"unwatch" links
 
 wgAjaxWatch.setLinkText = function(newText) {
-       changeText(wgAjaxWatch.watchLink1, newText);
-       if (wgAjaxWatch.watchLink2) {
-               changeText(wgAjaxWatch.watchLink2, newText);
+       for (i = 0; i < wgAjaxWatch.watchLinks.length; i++) {
+               changeText(wgAjaxWatch.watchLinks[i], newText);
        }
 };
 
 wgAjaxWatch.setLinkID = function(newId) {
-       wgAjaxWatch.watchLink1.id = newId;
+       // We can only set the first one
+       wgAjaxWatch.watchLinks[0].setAttribute( 'id', newId );
        akeytt(newId); // update tooltips for Monobook
 };
 
+wgAjaxWatch.setHref = function( string ) {
+       for( i = 0; i < wgAjaxWatch.watchLinks.length; i++ ) {
+               if( string == 'watch' ) {
+                       wgAjaxWatch.watchLinks[i].href = wgAjaxWatch.watchLinks[i].href
+                               .replace( /&action=unwatch/, '&action=watch' );
+               } else if( string == 'unwatch' ) {
+                       wgAjaxWatch.watchLinks[i].href = wgAjaxWatch.watchLinks[i].href
+                               .replace( /&action=watch/, '&action=unwatch' );
+               }
+       }
+}
+
 wgAjaxWatch.ajaxCall = function() {
-       if(!wgAjaxWatch.supported || wgAjaxWatch.inprogress) {
-               return;
+       if(!wgAjaxWatch.supported) {
+               return true;
+       } else if (wgAjaxWatch.inprogress) {
+               return false;
        }
        wgAjaxWatch.inprogress = true;
-       wgAjaxWatch.setLinkText(wgAjaxWatch.watching ? wgAjaxWatch.unwatchingMsg : wgAjaxWatch.watchingMsg);
-       sajax_do_call("wfAjaxWatch", [wgArticleId, (wgAjaxWatch.watching ? "u" : "w")], wgAjaxWatch.processResult);
+       wgAjaxWatch.setLinkText( wgAjaxWatch.watching
+               ? wgAjaxWatch.unwatchingMsg : wgAjaxWatch.watchingMsg);
+       sajax_do_call(
+               "wfAjaxWatch",
+               [wgPageName, (wgAjaxWatch.watching ? "u" : "w")], 
+               wgAjaxWatch.processResult
+       );
        // if the request isn't done in 10 seconds, allow user to try again
-       wgAjaxWatch.timeoutID = window.setTimeout(function() { wgAjaxWatch.inprogress = false; }, 10000);
-       return;
+       wgAjaxWatch.timeoutID = window.setTimeout(
+               function() { wgAjaxWatch.inprogress = false; },
+               10000
+       );
+       return false;
 };
 
 wgAjaxWatch.processResult = function(request) {
@@ -53,20 +73,21 @@ wgAjaxWatch.processResult = function(request) {
                return;
        }
        var response = request.responseText;
-       if(response == "<err#>") {
-               window.location.href = wgAjaxWatch.oldHref;
+       if( response.match(/^<err#>/) ) {
+               window.location.href = wgAjaxWatch.watchLink1.href;
                return;
-       } else if(response == "<w#>") {
+       } else if( response.match(/^<w#>/) ) {
                wgAjaxWatch.watching = true;
                wgAjaxWatch.setLinkText(wgAjaxWatch.unwatchMsg);
                wgAjaxWatch.setLinkID("ca-unwatch");
-               wgAjaxWatch.oldHref = wgAjaxWatch.oldHref.replace(/action=watch/, "action=unwatch");
-       } else if(response == "<u#>") {
+               wgAjaxWatch.setHref( 'unwatch' );
+       } else if( response.match(/^<u#>/) ) {
                wgAjaxWatch.watching = false;
                wgAjaxWatch.setLinkText(wgAjaxWatch.watchMsg);
                wgAjaxWatch.setLinkID("ca-watch");
-               wgAjaxWatch.oldHref = wgAjaxWatch.oldHref.replace(/action=unwatch/, "action=watch");
+               wgAjaxWatch.setHref( 'watch' );
        }
+       jsMsg( response.substr(4), 'watch' );
        wgAjaxWatch.inprogress = false;
        if(wgAjaxWatch.timeoutID) {
                window.clearTimeout(wgAjaxWatch.timeoutID);
@@ -75,6 +96,8 @@ wgAjaxWatch.processResult = function(request) {
 };
 
 wgAjaxWatch.onLoad = function() {
+       // This document structure hardcoding sucks.  We should make a class and
+       // toss all this out the window.
        var el1 = document.getElementById("ca-unwatch");
        var el2 = null;
        if (!el1) {
@@ -103,13 +126,17 @@ wgAjaxWatch.onLoad = function() {
 
        // The id can be either for the parent (Monobook-based) or the element
        // itself (non-Monobook)
-       wgAjaxWatch.watchLink1 = el1.tagName.toLowerCase() == "a" ? el1 : el1.firstChild;
-       wgAjaxWatch.watchLink2 = el2 ? el2 : null;
+       wgAjaxWatch.watchLinks.push( el1.tagName.toLowerCase() == "a"
+               ? el1 : el1.firstChild );
 
-       wgAjaxWatch.oldHref = wgAjaxWatch.watchLink1.getAttribute("href");
-       wgAjaxWatch.watchLink1.setAttribute("href", "javascript:wgAjaxWatch.ajaxCall()");
-       if (wgAjaxWatch.watchLink2) {
-               wgAjaxWatch.watchLink2.setAttribute("href", "javascript:wgAjaxWatch.ajaxCall()");
+       if( el2 ) {
+               wgAjaxWatch.watchLinks.push( el2 );
+       }
+
+       // I couldn't get for (watchLink in wgAjaxWatch.watchLinks) to work, if
+       // you can be my guest.
+       for( i = 0; i < wgAjaxWatch.watchLinks.length; i++ ) {
+               wgAjaxWatch.watchLinks[i].onclick = wgAjaxWatch.ajaxCall;
        }
        return;
 };
@@ -124,4 +151,4 @@ function wfSupportsAjax() {
        var supportsAjax = request ? true : false;
        delete request;
        return supportsAjax;
-}
\ No newline at end of file
+}
index f11893e..8c10c54 100644 (file)
@@ -6,4 +6,5 @@
 .mw-plusminus-null { color: #aaa; }
 .texvc { direction: ltr; unicode-bidi: embed; }
 /* Stop floats from intruding into edit area in previews */
-#toolbar, #wpTextbox1 { clear: both; }
\ No newline at end of file
+#toolbar, #wpTextbox1 { clear: both; }
+div#mw-js-message { margin: 2em 5%; }
index a139e7e..a060364 100644 (file)
@@ -604,8 +604,7 @@ function akeytt( doId ) {
        // the original.
        var ta;
        if ( doId ) {
-               ta = new Array;
-               ta[doId] = window.ta[doId];
+               ta = [doId];
        } else {
                ta = window.ta;
        }
@@ -1214,6 +1213,53 @@ function ts_alternate(table) {
  * End of table sorting code
  */
  
+/**
+ * Add a cute little box at the top of the screen to inform the user of
+ * something, replacing any preexisting message.
+ *
+ * @param String message HTML to be put inside the right div
+ * @param String class   Used in adding a class; should be different for each
+ *   call to allow CSS/JS to hide different boxes.  null = no class used.
+ * @return Boolean       True on success, false on failure
+ */
+function jsMsg( message, class ) {
+       if ( !document.getElementById ) {
+               return false;
+       }
+       // We special-case skin structures provided by the software.  Skins that
+       // choose to abandon or significantly modify our formatting can just define
+       // an mw-js-message div to start with.
+       var messageDiv = document.getElementById( 'mw-js-message' );
+       if ( !messageDiv ) {
+               messageDiv = document.createElement( 'div' );
+               if ( document.getElementById( 'column-content' )
+               && document.getElementById( 'content' ) ) {
+                       // MonoBook, presumably
+                       document.getElementById( 'content' ).insertBefore(
+                               messageDiv,
+                               document.getElementById( 'content' ).firstChild
+                       );
+               } else if ( document.getElementById('content')
+               && document.getElementById( 'article' ) ) {
+                       // Non-Monobook but still recognizable (old-style)
+                       document.getElementById( 'article').insertBefore(
+                               messageDiv,
+                               document.getElementById( 'article' ).firstChild
+                       );
+               } else {
+                       return false;
+               }
+       }
+
+       messageDiv.setAttribute( 'id', 'mw-js-message' );
+       if( class ) {
+               messageDiv.setAttribute( 'class', 'mw-js-message-'+class );
+       }
+       messageDiv.innerHTML = message;
+       return true;
+}
+
 function runOnloadHook() {
        // don't run anything below this for non-dom browsers
        if (doneOnloadHook || !(document.getElementById && document.getElementsByTagName)) {
@@ -1243,4 +1289,4 @@ function runOnloadHook() {
 //      so the below should be redundant. It's there just in case.
 hookEvent("load", runOnloadHook);
 
-hookEvent("load", mwSetupToolbar);
\ No newline at end of file
+hookEvent("load", mwSetupToolbar);