Page MenuHomePhabricator

ajaxwatch.diff

Authored By
bzimport
Nov 21 2014, 9:25 PM
Size
13 KB
Referenced Files
None
Subscribers
None

ajaxwatch.diff

Index: includes/AjaxFunctions.php
===================================================================
--- includes/AjaxFunctions.php (revision 16398)
+++ includes/AjaxFunctions.php (working copy)
@@ -131,4 +131,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: includes/DefaultSettings.php
===================================================================
--- includes/DefaultSettings.php (revision 16398)
+++ includes/DefaultSettings.php (working copy)
@@ -2155,6 +2155,13 @@
$wgAjaxSearch = false;
/**
+ * Enable watching/unwatching pages using AJAX.
+ * Requires $wgUseAjax to be true too.
+ * Causes wfAjaxWatch to be added to $wgAjaxExportList
+ */
+$wgAjaxWatch = false;
+
+/**
* List of Ajax-callable functions.
* Extensions acting as Ajax callbacks must register here
*/
Index: includes/OutputPage.php
===================================================================
--- includes/OutputPage.php (revision 16398)
+++ includes/OutputPage.php (working copy)
@@ -480,7 +480,7 @@
function output() {
global $wgUser, $wgOutputEncoding, $wgRequest;
global $wgContLanguageCode, $wgDebugRedirects, $wgMimeType;
- global $wgJsMimeType, $wgStylePath, $wgUseAjax, $wgAjaxSearch, $wgScriptPath, $wgServer;
+ global $wgJsMimeType, $wgStylePath, $wgUseAjax, $wgAjaxSearch, $wgAjaxWatch, $wgScriptPath, $wgServer;
if( $this->mDoNothing ){
return;
@@ -491,11 +491,15 @@
if ( $wgUseAjax ) {
$this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajax.js\"></script>\n" );
- }
- if ( $wgUseAjax && $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( $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( $wgAjaxWatch ) {
+ $this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajaxwatch.js\"></script>\n" );
+ }
}
if ( '' != $this->mRedirect ) {
Index: includes/Setup.php
===================================================================
--- includes/Setup.php (revision 16398)
+++ includes/Setup.php (working copy)
@@ -169,6 +169,7 @@
$wgPostCommitUpdateList = array();
if ( $wgAjaxSearch ) $wgAjaxExportList[] = 'wfSajaxSearch';
+if ( $wgAjaxWatch ) $wgAjaxExportList[] = 'wfAjaxWatch';
wfSeedRandom();
Index: includes/SkinTemplate.php
===================================================================
--- includes/SkinTemplate.php (revision 16398)
+++ includes/SkinTemplate.php (working copy)
@@ -571,7 +571,7 @@
}
$text = wfMsg( $message );
- if ( $text == "&lt;$message&gt;" ) {
+ if ( wfEmptyMsg( $message, $text ) ) {
global $wgContLang;
$text = $wgContLang->getFormattedNsText( Namespace::getSubject( $title->getNamespace() ) );
}
@@ -1014,10 +1014,35 @@
// by checking for default message content
$msgKey = ucfirst($this->skinname).'.js';
$userJS = wfMsg($msgKey);
- if ('&lt;'.$msgKey.'&gt;' != $userJS) {
+ if(!wfEmptyMsg($msgKey, $userJS)) {
$s .= $userJS;
}
+ global $wgUseAjax, $wgAjaxWatch;
+ if($wgUseAjax && $wgAjaxWatch) {
+ $s .= "\n\n/* AJAX (un)watch (see /skins/common/ajaxwatch.js) */\n";
+ $s .= "var wgAjaxWatch = {\n";
+ $first = true;
+ foreach( // array of [name of property in javascript] => [key for wfMsg], [option(s) for wfMsgExt]
+ array(
+ 'watchMsg' => array('watch'),
+ 'unwatchMsg' => array('unwatch'),
+ 'watchingMsg' => array('watching'),
+ 'unwatchingMsg' => array('unwatching'),
+ 'addedWatchMsg' => array('addedwatchajaxtext', 'parse'),
+ 'removedWatchMsg' => array('removedwatchajaxtext', 'parse')
+ ) as $jsName => $msg) {
+ $msgKey = $msg[0];
+ array_shift($msg);
+ if(!$first)
+ $s .= ",\n";
+ else
+ $first = false;
+ $s .= "$jsName: '" . str_replace("'", "\\'", str_replace("\n", ' ', wfMsgExt($msgKey, $msg))) . "'";
+ }
+ $s .= "\n};";
+ }
+
wfProfileOut( $fname );
return $s;
}
Index: languages/MessagesEn.php
===================================================================
--- languages/MessagesEn.php (revision 16398)
+++ languages/MessagesEn.php (working copy)
@@ -1388,11 +1388,15 @@
make it easier to pick out.
If you want to remove the page from your watchlist later, click \"Unwatch\" in the sidebar.",
+'addedwatchajaxtext' => 'This page has been added to your [[{{ns:special}}:Watchlist|watchlist]].',
'removedwatch' => 'Removed from watchlist',
'removedwatchtext' => "The page \"[[:$1]]\" has been removed from your watchlist.",
+'removedwatchajaxtext' => 'This page has been removed from your watchlist.',
'watch' => 'Watch',
+'watching' => 'Watching...',
'watchthispage' => 'Watch this page',
'unwatch' => 'Unwatch',
+'unwatching' => 'Unwatching...',
'unwatchthispage' => 'Stop watching',
'notanarticle' => 'Not a content page',
'watchnochange' => 'None of your watched items was edited in the time period displayed.',
Index: skins/common/ajaxwatch.js
===================================================================
--- skins/common/ajaxwatch.js (revision 0)
+++ skins/common/ajaxwatch.js (revision 0)
@@ -0,0 +1,135 @@
+// dependencies:
+// * ajax.js: sajax_init_object(), sajax_do_call()
+// * wikibits.js: changeText(), akeyttfor(), hookEvent()
+
+// wgAjaxWatch should have been initialized in the generated js
+if(typeof wgAjaxWatch == "undefined" || !wgAjaxWatch) {
+ var wgAjaxWatch = {
+ watchMsg: "Watch",
+ unwatchMsg: "Unwatch",
+ watchingMsg: "Watching...",
+ unwatchingMsg: "Unwatching...",
+ addedWatchMsg: "This page has been added to your watchlist.",
+ removedWatchMsg: "This page has been removed from your watchlist."
+ };
+}
+
+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.watchLink = null; // "watch"/"unwatch" link
+wgAjaxWatch.oldHref = null; // url for action=watch/action=unwatch
+
+wgAjaxWatch.setLinkText = function(newText) {
+ changeText(wgAjaxWatch.watchLink, newText);
+}
+
+wgAjaxWatch.setLinkID = function(newId) {
+ wgAjaxWatch.watchLink.id = newId;
+ akeyttfor(newId); // update tooltip
+}
+
+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");
+ wfDisplayUserMsg("ajaxwatch", wgAjaxWatch.addedWatchMsg);
+ } else if(response == "<u#>") {
+ wgAjaxWatch.watching = false;
+ wgAjaxWatch.setLinkText(wgAjaxWatch.watchMsg);
+ wgAjaxWatch.setLinkID("ca-watch");
+ wgAjaxWatch.oldHref = wgAjaxWatch.oldHref.replace(/action=unwatch/, "action=watch");
+ wfDisplayUserMsg("ajaxwatch", wgAjaxWatch.removedWatchMsg);
+ }
+ wgAjaxWatch.inprogress = false;
+ if(wgAjaxWatch.timeoutID)
+ window.clearTimeout(wgAjaxWatch.timeoutID);
+ return;
+};
+
+wgAjaxWatch.onLoad = function() {
+ var x = document.getElementById("ca-unwatch");
+ if(x) {
+ wgAjaxWatch.watching = true;
+ } else {
+ wgAjaxWatch.watching = false;
+ x = document.getElementById("ca-watch");
+ if(!x) {
+ wgAjaxWatch.supported = false;
+ return;
+ }
+ }
+
+ if(!wfSupportsAjax()) {
+ wgAjaxWatch.supported = false;
+ return;
+ }
+
+ wgAjaxWatch.watchLink = x.firstChild;
+
+ wgAjaxWatch.oldHref = wgAjaxWatch.watchLink.getAttribute("href");
+ wgAjaxWatch.watchLink.setAttribute("href", "javascript:wgAjaxWatch.ajaxCall()");
+ return;
+};
+
+hookEvent("load", wgAjaxWatch.onLoad);
+
+/**
+ * Displays a message to the user. In old browsers, this falls back
+ * to display an alert() box.
+ * @param String msgName the name of the message; messages with the
+ * same name will overwrite each other
+ * @param String text the content of the message
+ */
+function wfDisplayUserMsg(msgName, msgText) {
+ msgName = "msg-" + msgName.replace(/ /g, "-"); // avoid collisions
+ var msg = document.getElementById(msgName);
+ if(msg) {
+ msg.innerHTML = msgText;
+ return;
+ }
+
+ var contentsub = document.getElementById("contentSub2");
+ if(!contentsub)
+ contentsub = document.getElementById("contentSub");
+ if(!contentsub || !document.createElement)
+ alert(msgText);
+
+ msg = document.createElement("div");
+ msg.id = msgName;
+ msg.className = "usermessage";
+ msg.innerHTML = msgText;
+
+ contentsub.parentNode.insertBefore(msg, contentsub.nextSibling);
+ return;
+}
+
+/**
+ * @return boolean whether the browser supports XMLHttpRequest
+ */
+function wfSupportsAjax() {
+ var request = sajax_init_object();
+ var supportsAjax = request ? true : false;
+ delete request;
+ return supportsAjax;
+}
Index: skins/common/wikibits.js
===================================================================
--- skins/common/wikibits.js (revision 16398)
+++ skins/common/wikibits.js (working copy)
@@ -459,48 +459,78 @@
txtarea.caretPos = document.selection.createRange().duplicate();
}
-function akeytt() {
- if (typeof ta == "undefined" || !ta)
- return;
- var pref = 'alt-';
+/**
+ * @return {String} browser-specific access key prefix
+ */
+function akeyprefix() {
if (is_safari || navigator.userAgent.toLowerCase().indexOf('mac') + 1
|| navigator.userAgent.toLowerCase().indexOf('konqueror') + 1 )
- pref = 'control-';
+ return 'control-';
if (is_opera)
- pref = 'shift-esc-';
+ return 'shift-esc-';
+ return 'alt-';
+}
- for (var id in ta) {
- var n = document.getElementById(id);
- if (n) {
- var a = null;
- var ak = '';
- // Are we putting accesskey in it
- if (ta[id][0].length > 0) {
- // Is this object a object? If not assume it's the next child.
+/**
+ * Sets access key and tooltip for element with specified id.
+ * @param {String} id id of the element
+ * @see #akeytt
+ * @see #akeyttfor2
+ */
+function akeyttfor(id) {
+ akeyttfor2(id, akeyprefix());
+}
- if (n.nodeName.toLowerCase() == "a") {
- a = n;
- } else {
- a = n.childNodes[0];
- }
+/**
+ * Same as akeyttfor, but can be more efficient if access key prefix
+ * is used repeatedly.
+ * @param {String} id id of the element
+ * @param {String} akeypref access key prefix
+ * @see #akeytt
+ * @see #akeyttfor
+ */
+function akeyttfor2(id, akeypref) {
+ if (typeof ta == "undefined" || !ta)
+ return false;
- if (a) {
- a.accessKey = ta[id][0];
- ak = ' ['+pref+ta[id][0]+']';
- }
- } else {
- // We don't care what type the object is when assigning tooltip
- a = n;
- ak = '';
- }
+ var n = document.getElementById(id);
+ if (!n)
+ return false;
- if (a) {
- a.title = ta[id][1]+ak;
- }
+ var a = null;
+ var ak = '';
+ // Are we putting access key on it?
+ if (ta[id][0].length > 0) {
+ // Is this object a link? If not assume it's the next child.
+ a = n.nodeName.toLowerCase() == "a" ? n : n.firstChild;
+ if (a) {
+ a.accessKey = ta[id][0];
+ ak = ' [' + akeypref + ta[id][0] + ']';
}
+ } else {
+ // We don't care what type the object is when assigning tooltip
+ a = n;
+ ak = '';
}
+
+ if (a) {
+ a.title = ta[id][1] + ak;
+ }
}
+/**
+ * Sets access keys and tooltips for all elements in the "ta" object.
+ * @see #akeyttfor
+ */
+function akeytt() {
+ if (typeof ta == "undefined" || !ta)
+ return false;
+ var pref = akeyprefix();
+ for (var id in ta)
+ akeyttfor2(id, pref);
+ return true;
+}
+
function setupRightClickEdit() {
if (document.getElementsByTagName) {
var divs = document.getElementsByTagName('div');

File Metadata

Mime Type
text/x-diff
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2846
Default Alt Text
ajaxwatch.diff (13 KB)

Event Timeline