r18598 - Code Review

From MediaWiki.org

Jump to: navigation, search
Repository:MediaWiki
Revision:r18597 | r18598 (on ViewVC) | r18599 >
Date:23:53, 26 December 2006
Author:simetrical
Status:new
Tags:
Comment:(bug 7169) Use Ajax to watch/unwatch articles. Patch by Dan Li with some modification by me.
Modified paths:

Diff [purge]

Index: trunk/phase3/skins/common/ajaxwatch.js
===================================================================
--- trunk/phase3/skins/common/ajaxwatch.js	(revision 0)
+++ trunk/phase3/skins/common/ajaxwatch.js	(revision 18598)
@@ -0,0 +1,127 @@
+// dependencies:
+// * ajax.js:
+  /*extern sajax_init_object, sajax_do_call */
+// * wikibits.js:
+  /*extern changeText, akeytt, hookEvent */
+
+// These should have been initialized in the generated js
+/*extern wgAjaxWatch, wgArticleId */
+
+if(typeof wgAjaxWatch === "undefined" || !wgAjaxWatch) {
+	var wgAjaxWatch = {
+		watchMsg: "Watch",
+		unwatchMsg: "Unwatch",
+		watchingMsg: "Watching...",
+		unwatchingMsg: "Unwatching..."
+	};
+}
+
+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.setLinkText = function(newText) {
+	changeText(wgAjaxWatch.watchLink1, newText);
+	if (wgAjaxWatch.watchLink2) {
+		changeText(wgAjaxWatch.watchLink2, newText);
+	}
+};
+
+wgAjaxWatch.setLinkID = function(newId) {
+	wgAjaxWatch.watchLink1.id = newId;
+	akeytt(newId); // update tooltips for Monobook
+};
+
+wgAjaxWatch.ajaxCall = function() {
+	if(!wgAjaxWatch.supported || wgAjaxWatch.inprogress) {
+		return;
+	}
+	wgAjaxWatch.inprogress = true;
+	wgAjaxWatch.setLinkText(wgAjaxWatch.watching ? wgAjaxWatch.unwatchingMsg : wgAjaxWatch.watchingMsg);
+	sajax_do_call("wfAjaxWatch", [wgArticleId, (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.processResult = function(request) {
+	if(!wgAjaxWatch.supported) {
+		return;
+	}
+	var response = request.responseText;
+	if(response == "<err#>") {
+		window.location.href = wgAjaxWatch.oldHref;
+		return;
+	} else if(response == "<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.watching = false;
+		wgAjaxWatch.setLinkText(wgAjaxWatch.watchMsg);
+		wgAjaxWatch.setLinkID("ca-watch");
+		wgAjaxWatch.oldHref = wgAjaxWatch.oldHref.replace(/action=unwatch/, "action=watch");
+	}
+	wgAjaxWatch.inprogress = false;
+	if(wgAjaxWatch.timeoutID) {
+		window.clearTimeout(wgAjaxWatch.timeoutID);
+	}
+	return;
+};
+
+wgAjaxWatch.onLoad = function() {
+	var el1 = document.getElementById("ca-unwatch");
+	var el2 = null;
+	if (!el1) {
+		el1 = document.getElementById("mw-unwatch-link1");
+		el2 = document.getElementById("mw-unwatch-link2");
+	}
+	if(el1) {
+		wgAjaxWatch.watching = true;
+	} else {
+		wgAjaxWatch.watching = false;
+		el1 = document.getElementById("ca-watch");
+		if (!el1) {
+			el1 = document.getElementById("mw-watch-link1");
+			el2 = document.getElementById("mw-watch-link2");
+		}
+		if(!el1) {
+			wgAjaxWatch.supported = false;
+			return;
+		}
+	}
+
+	if(!wfSupportsAjax()) {
+		wgAjaxWatch.supported = false;
+		return;
+	}
+
+	// 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.oldHref = wgAjaxWatch.watchLink1.getAttribute("href");
+	wgAjaxWatch.watchLink1.setAttribute("href", "javascript:wgAjaxWatch.ajaxCall()");
+	if (wgAjaxWatch.watchLink2) {
+		wgAjaxWatch.watchLink2.setAttribute("href", "javascript:wgAjaxWatch.ajaxCall()");
+	}
+	return;
+};
+
+hookEvent("load", wgAjaxWatch.onLoad);
+
+/**
+ * @return boolean whether the browser supports XMLHttpRequest
+ */
+function wfSupportsAjax() {
+	var request = sajax_init_object();
+	var supportsAjax = request ? true : false;
+	delete request;
+	return supportsAjax;
+}
\ No newline at end of file

Property changes on: trunk/phase3/skins/common/ajaxwatch.js
___________________________________________________________________
Name: svn:eol-style
   + native

Index: trunk/phase3/skins/common/wikibits.js
===================================================================
--- trunk/phase3/skins/common/wikibits.js	(revision 18597)
+++ trunk/phase3/skins/common/wikibits.js	(revision 18598)
@@ -473,11 +473,16 @@
 	}
 }
 
-function akeytt() {
+/**
+ * Set up accesskeys/tooltips.  If doId is specified, only set up for that id.
+ *
+ * @param mixed doId string or null
+ */
+function akeytt( doId ) {
 	if (typeof ta == "undefined" || !ta) {
 		return;
 	}
-	
+
 	var pref;
 	if (is_safari || navigator.userAgent.toLowerCase().indexOf('mac') + 1
 		|| navigator.userAgent.toLowerCase().indexOf('konqueror') + 1 ) {
@@ -492,6 +497,10 @@
 		pref = 'alt-';
 	}
 
+	if ( doId ) {
+		ta = [ta[doId]];
+	}
+
 	for (var id in ta) {
 		var n = document.getElementById(id);
 		if (n) {
@@ -881,7 +890,7 @@
 	histrowinit();
 	unhidetzbutton();
 	tabbedprefs();
-	akeytt();
+	akeytt( null );
 	scrollEditBox();
 	setupCheckboxShiftClick();
 	sortableTables();
Index: trunk/phase3/includes/AjaxDispatcher.php
===================================================================
--- trunk/phase3/includes/AjaxDispatcher.php	(revision 18597)
+++ trunk/phase3/includes/AjaxDispatcher.php	(revision 18598)
@@ -15,7 +15,7 @@
 	var $args;
 
 	function AjaxDispatcher() {
-		wfProfileIn( 'AjaxDispatcher::AjaxDispatcher' );
+		wfProfileIn( __METHOD__ );
 
 		$this->mode = "";
 
@@ -42,7 +42,7 @@
 				$this->args = array();
 			}
 		}
-		wfProfileOut( 'AjaxDispatcher::AjaxDispatcher' );
+		wfProfileOut( __METHOD__ );
 	}
 
 	function performAction() {
@@ -51,7 +51,7 @@
 		if ( empty( $this->mode ) ) {
 			return;
 		}
-		wfProfileIn( 'AjaxDispatcher::performAction' );
+		wfProfileIn( __METHOD__ );
 
 		if (! in_array( $this->func_name, $wgAjaxExportList ) ) {
 			header( 'Status: 400 Bad Request', true, 400 );
@@ -72,7 +72,7 @@
 					$result->sendHeaders();
 					$result->printText();
 				}
-				
+
 			} catch (Exception $e) {
 				if (!headers_sent()) {
 					header( 'Status: 500 Internal Error', true, 500 );
@@ -83,7 +83,7 @@
 			}
 		}
 		
-		wfProfileOut( 'AjaxDispatcher::performAction' );
+		wfProfileOut( __METHOD__ );
 		$wgOut = null;
 	}
 }
Index: trunk/phase3/includes/Setup.php
===================================================================
--- trunk/phase3/includes/Setup.php	(revision 18597)
+++ trunk/phase3/includes/Setup.php	(revision 18598)
@@ -171,6 +171,7 @@
 $wgPostCommitUpdateList = array();
 
 if ( $wgAjaxSearch ) $wgAjaxExportList[] = 'wfSajaxSearch';
+if ( $wgAjaxWatch ) $wgAjaxExportList[] = 'wfAjaxWatch';
 
 wfSeedRandom();
 
Index: trunk/phase3/includes/OutputPage.php
===================================================================
--- trunk/phase3/includes/OutputPage.php	(revision 18597)
+++ trunk/phase3/includes/OutputPage.php	(revision 18598)
@@ -524,8 +524,8 @@
 	public function output() {
 		global $wgUser, $wgOutputEncoding, $wgRequest;
 		global $wgContLanguageCode, $wgDebugRedirects, $wgMimeType;
-		global $wgJsMimeType, $wgStylePath, $wgUseAjax, $wgAjaxSearch, $wgServer;
-		global $wgStyleVersion;
+		global $wgJsMimeType, $wgStylePath, $wgUseAjax, $wgAjaxSearch, $wgAjaxWatch;
+		global $wgServer, $wgStyleVersion;
 
 		if( $this->mDoNothing ){
 			return;
@@ -536,11 +536,14 @@
 
 		if ( $wgUseAjax ) {
 			$this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajax.js?$wgStyleVersion\"></script>\n" );
-		}
+			if( $wgAjaxSearch ) {
+				$this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajaxsearch.js\"></script>\n" );
+				$this->addScript( "<script type=\"{$wgJsMimeType}\">hookEvent(\"load\", sajax_onload);</script>\n" );
+			}
 
-		if ( $wgUseAjax && $wgAjaxSearch ) {
-			$this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajaxsearch.js?$wgStyleVersion\"></script>\n" );
-			$this->addScript( "<script type=\"{$wgJsMimeType}\">hookEvent(\"load\", sajax_onload);</script>\n" );
+			if( $wgAjaxWatch && $wgUser->isLoggedIn() ) {
+				$this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajaxwatch.js\"></script>\n" );
+			}
 		}
 
 		if ( '' != $this->mRedirect ) {
Index: trunk/phase3/includes/AjaxFunctions.php
===================================================================
--- trunk/phase3/includes/AjaxFunctions.php	(revision 18597)
+++ trunk/phase3/includes/AjaxFunctions.php	(revision 18598)
@@ -129,4 +129,43 @@
 	return $response;
 }
 
+/**
+ * Called for AJAX watch/unwatch requests.
+ * @param $pageID Integer ID of the page to be watched/unwatched
+ * @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)
+ */
+function wfAjaxWatch($pageID, $watch) {
+	if(wfReadOnly())
+		return '<err#>'; // redirect to action=(un)watch, which will display the database lock message
+
+	if(('w' !== $watch && 'u' !== $watch) || !is_numeric($pageID))
+		return '<err#>';
+	$watch = 'w' === $watch;
+	$pageID = intval($pageID);
+
+	$title = Title::newFromID($pageID);
+	if(!$title)
+		return '<err#>';
+	$article = new Article($title);
+	$watching = $title->userIsWatching();
+
+	if($watch) {
+		if(!$watching) {
+			$dbw =& wfGetDB(DB_MASTER);
+			$dbw->begin();
+			$article->doWatch();
+			$dbw->commit();
+		}
+	} else {
+		if($watching) {
+			$dbw =& wfGetDB(DB_MASTER);
+			$dbw->begin();
+			$article->doUnwatch();
+			$dbw->commit();
+		}
+	}
+
+	return $watch ? '<w#>' : '<u#>';
+}
 ?>
Index: trunk/phase3/includes/DefaultSettings.php
===================================================================
--- trunk/phase3/includes/DefaultSettings.php	(revision 18597)
+++ trunk/phase3/includes/DefaultSettings.php	(revision 18598)
@@ -1042,7 +1042,7 @@
  * to ensure that client-side caches don't keep obsolete copies of global
  * styles.
  */
-$wgStyleVersion = '37';
+$wgStyleVersion = '38';
 
 
 # Server-side caching:
@@ -2254,6 +2254,13 @@
 $wgAjaxExportList = array( );
 
 /**
+ * Enable watching/unwatching pages using AJAX.
+ * Requires $wgUseAjax to be true too.
+ * Causes wfAjaxWatch to be added to $wgAjaxExportList
+ */
+$wgAjaxWatch = false;
+
+/**
  * Allow DISPLAYTITLE to change title display
  */
 $wgAllowDisplayTitle = false ;
Index: trunk/phase3/includes/Skin.php
===================================================================
--- trunk/phase3/includes/Skin.php	(revision 18597)
+++ trunk/phase3/includes/Skin.php	(revision 18598)
@@ -23,6 +23,7 @@
 	var $rc_cache ; # Cache for Enhanced Recent Changes
 	var $rcCacheIndex ; # Recent Changes Cache Counter for visibility toggle
 	var $rcMoveIndex;
+	var $mWatchLinkNum = 0; // Appended to end of watch link id's
 	/**#@-*/
 
 	/** Constructor, call parent constructor */
@@ -392,7 +393,7 @@
 	 */
 	function getUserJs() {
 		$fname = 'Skin::getUserJs';
-		wfProfileIn( $fname );
+		wfProfileIn( __METHOD__ );
 
 		global $wgStylePath;
 		$s = "/* generated javascript */\n";
@@ -403,7 +404,20 @@
 			$s .= $commonJs;
 		}
 
-		wfProfileOut( $fname );
+		global $wgUseAjax, $wgAjaxWatch;
+		if($wgUseAjax && $wgAjaxWatch) {
+			$s .= "
+
+/* AJAX (un)watch (see /skins/common/ajaxwatch.js) */
+var wgAjaxWatch = {
+	watchMsg: '".       str_replace( array("'", "\n"), array("\\'", ' '), wfMsgExt( 'watch', array() ) )."',
+	unwatchMsg: '".     str_replace( array("'", "\n"), array("\\'", ' '), wfMsgExt( 'unwatch', array() ) )."',
+	watchingMsg: '".    str_replace( array("'", "\n"), array("\\'", ' '), wfMsgExt( 'watching', array() ) )."',
+	unwatchingMsg: '".  str_replace( array("'", "\n"), array("\\'", ' '), wfMsgExt( 'unwatching', array() ) )."'
+};";
+		}
+
+		wfProfileOut( __METHOD__ );
 		return $s;
     }
 
@@ -1272,16 +1286,19 @@
 
 	function watchThisPage() {
 		global $wgOut, $wgTitle;
+		++$this->mWatchLinkNum;
 
 		if ( $wgOut->isArticleRelated() ) {
 			if ( $wgTitle->userIsWatching() ) {
 				$t = wfMsg( 'unwatchthispage' );
 				$q = 'action=unwatch';
+				$id = "mw-unwatch-link".$this->mWatchLinkNum;
 			} else {
 				$t = wfMsg( 'watchthispage' );
 				$q = 'action=watch';
+				$id = 'mw-watch-link'.$this->mWatchLinkNum;
 			}
-			$s = $this->makeKnownLinkObj( $wgTitle, $t, $q );
+			$s = $this->makeKnownLinkObj( $wgTitle, $t, $q, '', '', " id=\"$id\"" );
 		} else {
 			$s = wfMsg( 'notanarticle' );
 		}
Index: trunk/phase3/languages/messages/MessagesEn.php
===================================================================
--- trunk/phase3/languages/messages/MessagesEn.php	(revision 18597)
+++ trunk/phase3/languages/messages/MessagesEn.php	(revision 18598)
@@ -1633,6 +1633,9 @@
 'wlhideshowown'   	=> '$1 my edits',
 'wlhideshowbots'   	=> '$1 bot edits',
 'wldone'			=> 'Done.',
+# Displayed when you click the "watch" button and it's in the process of watching
+'watching' => 'Watching...',
+'unwatching' => 'Unwatching...',
 
 'enotif_mailer' 		=> '{{SITENAME}} Notification Mailer',
 'enotif_reset'			=> 'Mark all pages visited',
Views
Toolbox