From ed8e8e58335db38a0ee0eedfc4d4417611dd645f Mon Sep 17 00:00:00 2001 From: Antoine Musso Date: Sat, 20 Jan 2007 21:05:36 +0000 Subject: [PATCH] Optional feature : 'Ajax show editors' based on an idea by Tim Starling Merge from hashar's branch. --- RELEASE-NOTES | 3 + includes/AjaxFunctions.php | 98 +++++++++++++++++++++++++ includes/AjaxHooks.php | 29 ++++++++ includes/DefaultSettings.php | 11 ++- includes/EditPage.php | 13 ++++ includes/Setup.php | 1 + index.php | 18 +++-- languages/messages/MessagesEn.php | 5 ++ maintenance/archives/patch-editings.sql | 14 ++++ maintenance/updaters.inc | 1 + skins/common/ajaxshoweditors.js | 62 ++++++++++++++++ skins/common/ajaxwatch.js | 2 +- skins/monobook/main.css | 23 +++++- 13 files changed, 270 insertions(+), 10 deletions(-) create mode 100644 includes/AjaxHooks.php create mode 100644 maintenance/archives/patch-editings.sql create mode 100644 skins/common/ajaxshoweditors.js diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 40c28e756c..f3ed5cf173 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -28,6 +28,9 @@ lighter making things easier to read. == Major new features == +$wgAjaxShowEditors will let your users see who else is editing the page +they are currently editing. + == Changes since 1.9 == * (bug 7292) Fix site statistics when moving pages in/out of content namespaces diff --git a/includes/AjaxFunctions.php b/includes/AjaxFunctions.php index 600cd573ed..8631b4e573 100644 --- a/includes/AjaxFunctions.php +++ b/includes/AjaxFunctions.php @@ -168,4 +168,102 @@ function wfAjaxWatch($pageID = "", $watch = "") { return $watch ? '' : ''; } + +/** + * Return a list of Editors currently editing the article. + * Based on an idea by Tim Starling. + * + * @author Ashar Voultoiz + * @author Tim Starling + */ +function wfAjaxShowEditors( $articleId, $username ) { + global $wgOut; + $articleId = intval($articleId); + + // Validate request + $title = Title::newFromID( $articleId ); + if( !($title) ) { return 'ERR: page id invalid'; } + + $user = User::newFromSession() ; + if( !$user ) { return 'ERR: user invalid'; } + + $username = $user->getName(); + if( !( $user->isLoggedIn() or User::isIP( $username ) ) ) { return 'ERR: user not found'; } + + + // When did the user started editing ? + $dbr =& wfGetDB(DB_SLAVE); + $userStarted = $dbr->selectField( 'editings', + 'editings_started', + array( + 'editings_user' => $username, + 'editings_page' => $title->getArticleID(), + ), + __METHOD__ + ); + + // He just started editing, assume NOW + if(!$userStarted) { $userStarted = $dbr->timestamp(); } + + # Either create a new entry or update the touched timestamp. + # This is done using a unique index on the database : + # `editings_page_started` (`editings_page`,`editings_user`,`editings_started`) + + $dbw =& wfGetDB(DB_MASTER); + $dbw->replace( 'editings', + array( 'editings_page', 'editings_user', 'editings_started' ), + array( + 'editings_page' => $title->getArticleID() , + 'editings_user' => $username, + 'editings_started' => $userStarted , + 'editings_touched' => $dbw->timestamp(), + ), __METHOD__ + ); + + // Now we get the list of all watching users + $dbr = & wfGetDB(DB_SLAVE); + $res = $dbr->select( 'editings', + array( 'editings_user','editings_started','editings_touched' ), + array( 'editings_page' => $title->getArticleID() ), + __METHOD__ + ); + + $l = new Linker(); + + $wikitext = ''; + $unix_now = wfTimestamp(TS_UNIX); + $first = 1; + while( $editor = $dbr->fetchObject( $res ) ) { + + // Check idling time + $idle = $unix_now - wfTimestamp( TS_UNIX, $editor->editings_touched ); + + global $wgAjaxShowEditorsTimeout ; + if( $idle >= $wgAjaxShowEditorsTimeout ) { + $dbw->delete('editings', + array( + 'editings_page' => $title->getArticleID(), + 'editings_user' => $editor->editings_user, + ), + __METHOD__ + ); + continue; // we will not show the user + } + + if( $first ) { $first = 0; } + else { $wikitext .= ' ~ '; } + + $since = wfTimestamp( TS_DB, $editor->editings_started ); + $wikitext .= $since; + + $wikitext .= ' ' . $l->makeLinkObj( + Title::makeTitle( NS_USER, $editor->editings_user ), + $editor->editings_user + ); + + + $wikitext .= ' ' . wfMsg( 'ajax-se-idling', ''.$idle.'' ); + } + return $wikitext ; +} ?> diff --git a/includes/AjaxHooks.php b/includes/AjaxHooks.php new file mode 100644 index 0000000000..244d89a141 --- /dev/null +++ b/includes/AjaxHooks.php @@ -0,0 +1,29 @@ +getID(); + $userId = $user->getName(); + + $dbw =& wfGetDB(DB_MASTER); + $dbw->delete('editings', + array( + 'editings_page' => $articleId, + 'editings_user' => $userId, + ), + __METHOD__ + ); +} + +$wgHooks['ArticleSaveComplete'][] = 'wfAjaxShowEditorsCleanup'; +?> diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index ec85130ac5..742aaf1df1 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -1109,7 +1109,7 @@ $wgCacheEpoch = '20030516000000'; * to ensure that client-side caches don't keep obsolete copies of global * styles. */ -$wgStyleVersion = '51'; +$wgStyleVersion = '52'; # Server-side caching: @@ -2350,6 +2350,15 @@ $wgAjaxExportList = array( ); */ $wgAjaxWatch = false; +/** + * Let you show other peoples editing an article. + */ +$wgAjaxShowEditors = false; +/** + * Number of seconds before an user is considered as no more editing + */ +$wgAjaxShowEditorsTimeout = 60; + /** * Allow DISPLAYTITLE to change title display */ diff --git a/includes/EditPage.php b/includes/EditPage.php index e9b9713280..cfde451f6c 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -1113,6 +1113,19 @@ class EditPage { $templates = ($this->preview || $this->section) ? $this->mPreviewTemplates : $this->mArticle->getUsedTemplates(); $formattedtemplates = $sk->formatTemplates( $templates, $this->preview, $this->section != ''); + + global $wgAjaxShowEditors ; + if ( $wgAjaxShowEditors && isset( $this->mArticle ) + && isset( $this->mArticle->mRevision ) ) { + global $wgJsMimeType, $wgStylePath, $wgStyleVersion; + $wgOut->addScript( "\n" ); + $wgOut->addWikiText( + '

'.wfMsg('ajax-se-title').'

' + . '

'. wfMsg('ajax-se-pending') . '

' + . '
' + ); + } + global $wgUseMetadataEdit ; if ( $wgUseMetadataEdit ) { $metadata = $this->mMetaData ; diff --git a/includes/Setup.php b/includes/Setup.php index 6ed7c8de43..7a88fb16e9 100644 --- a/includes/Setup.php +++ b/includes/Setup.php @@ -198,6 +198,7 @@ $wgPostCommitUpdateList = array(); if ( $wgAjaxSearch ) $wgAjaxExportList[] = 'wfSajaxSearch'; if ( $wgAjaxWatch ) $wgAjaxExportList[] = 'wfAjaxWatch'; +if ( $wgAjaxShowEditors ) $wgAjaxExportList[] = 'wfAjaxShowEditors'; wfSeedRandom(); diff --git a/index.php b/index.php index e3b753faf6..9955aa1159 100644 --- a/index.php +++ b/index.php @@ -22,13 +22,17 @@ if ($wgTitle == NULL) { # # Send Ajax requests to the Ajax dispatcher. # -if ( $wgUseAjax && $action == 'ajax' ) { - require_once( $IP . '/includes/AjaxDispatcher.php' ); - - $dispatcher = new AjaxDispatcher(); - $dispatcher->performAction(); - $mediaWiki->restInPeace( $wgLoadBalancer ); - exit; +if ( $wgUseAjax ) { + if( $action == 'ajax' ) { + require_once( $IP . '/includes/AjaxDispatcher.php' ); + + $dispatcher = new AjaxDispatcher(); + $dispatcher->performAction(); + $mediaWiki->restInPeace( $wgLoadBalancer ); + exit; + } else { + require_once( $IP . '/includes/AjaxHooks.php' ); + } } diff --git a/languages/messages/MessagesEn.php b/languages/messages/MessagesEn.php index 9e18d0e99e..3630da7368 100644 --- a/languages/messages/MessagesEn.php +++ b/languages/messages/MessagesEn.php @@ -2759,6 +2759,11 @@ Please confirm that really want to recreate this page.', 'size-megabytes' => '$1 MB', 'size-gigabytes' => '$1 GB', +# Ajax show editors +'ajax-se-title' => 'Currently editing:', +'ajax-se-pending' => 'pending refresh ... (click this box or start editing)', +'ajax-se-idling' => '($1s ago)', + ); ?> diff --git a/maintenance/archives/patch-editings.sql b/maintenance/archives/patch-editings.sql new file mode 100644 index 0000000000..33bbaa1097 --- /dev/null +++ b/maintenance/archives/patch-editings.sql @@ -0,0 +1,14 @@ +-- +-- Tracks people currently editing an article +-- Enabled with $wgAjaxShowEditors = true; +-- + +CREATE TABLE /*$wgDBprefix*/editings ( + `editings_page` int(8) NOT NULL, + `editings_user` varchar(255) NOT NULL, + `editings_started` char(14) NOT NULL, + `editings_touched` char(14) NOT NULL, + PRIMARY KEY (`editings_page`,`editings_user`), + KEY `editings_page` (`editings_page`) + KEY `editings_page_started` (`editings_page`,`editings_user`,`editings_started`) +) TYPE=InnoDB; diff --git a/maintenance/updaters.inc b/maintenance/updaters.inc index c042d5f6ac..7fb2e1d60b 100644 --- a/maintenance/updaters.inc +++ b/maintenance/updaters.inc @@ -30,6 +30,7 @@ $wgNewTables = array( array( 'transcache', 'patch-transcache.sql' ), array( 'trackbacks', 'patch-trackbacks.sql' ), array( 'externallinks', 'patch-externallinks.sql' ), + array( 'editings', 'patch-editings.sql' ), array( 'job', 'patch-job.sql' ), array( 'langlinks', 'patch-langlinks.sql' ), array( 'querycache_info', 'patch-querycacheinfo.sql' ), diff --git a/skins/common/ajaxshoweditors.js b/skins/common/ajaxshoweditors.js new file mode 100644 index 0000000000..066ce8133e --- /dev/null +++ b/skins/common/ajaxshoweditors.js @@ -0,0 +1,62 @@ +var sajax_debug_mode = false; +var canRefresh = null; +var ShowEditorsCounting = false; +var wgAjaxShowEditors = {} ; + +// The loader. Look at bottom for the sajax hook registration +wgAjaxShowEditors.onLoad = function() { + var elEditors = document.getElementById( 'ajax-se' ); + // wgAjaxShowEditors.refresh(); + elEditors.onclick = function() { wgAjaxShowEditors.refresh(); } ; + + var elTextArea = document.getElementById( 'wpTextbox1' ); + elTextArea.onkeypress = function() { wgAjaxShowEditors.refresh(); } ; + + wgAjaxShowEditors.allowRefresh(); +} + + +// Ask for new data & update UI +wgAjaxShowEditors.refresh = function() { + if( !canRefresh ) { return; } + + // Disable new requests for 5 seconds + canRefresh = false; + setTimeout( 'wgAjaxShowEditors.allowRefresh()', 5000 ); + + // Load the editors list element, it will get rewrote + var elEditorsList = document.getElementById( 'ajax-se-editors' ); + + if( wgUserName == null ) { + wgUserName = ''; + } + + // Do the ajax call to the server + sajax_do_call( "wfAjaxShowEditors", [ wgArticleId, wgUserName ], elEditorsList ); + if(!ShowEditorsCounting) { + wgAjaxShowEditors.countup(); + } +} + +wgAjaxShowEditors.countup = function() { + ShowEditorsCounting = true; + + var elEditorsList = document.getElementById( 'ajax-se-editors' ); + for(var i=0;i