Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 6e63bf5
Showing
4 changed files
with
148 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
<?php | ||
|
||
Object::add_extension('SiteTree', 'ConcurrentEditingSiteTree'); | ||
Object::add_extension('LeftAndMain', 'ConcurrentEditingLeftAndMain'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<?php | ||
|
||
class ConcurrentEditingLeftAndMain extends LeftAndMainDecorator { | ||
static $allowed_actions = array( | ||
'concurrentEditingPing', | ||
); | ||
|
||
static $edit_timeout = 35; | ||
|
||
|
||
function init() { | ||
parent::init(); | ||
Requirements::javascript('concurrentediting/javascript/ConcurrentEditing.js'); | ||
} | ||
|
||
function concurrentEditingPing() { | ||
if (!isset($_REQUEST['ID'])) die('no id passed'); | ||
|
||
$page = $this->owner->getRecord($_REQUEST['ID']); | ||
if (!$page) { | ||
// Page has not been found | ||
$return = array('status' => 'not_found'); | ||
} elseif ($page->getIsDeletedFromStage()) { | ||
// Page has been deleted from stage | ||
$return = array('status' => 'deleted'); | ||
} else { | ||
// Mark me as editing if I'm not already | ||
$page->UsersCurrentlyEditing()->add(Member::currentUser()); | ||
DB::query("UPDATE SiteTree_UsersCurrentlyEditing SET LastPing = '".date('Y-m-d H:i:s')."' | ||
WHERE MemberID = ".Member::currentUserID()." AND SiteTreeID = {$page->ID}"); | ||
|
||
// Page exists, who else is editing it? | ||
$names = array(); | ||
foreach($page->UsersCurrentlyEditing() as $user) { | ||
if ($user->ID == Member::currentUserId()) continue; | ||
$names[] = trim($user->FirstName . ' ' . $user->Surname); | ||
} | ||
$return = array('status' => 'editing', 'names' => $names); | ||
|
||
// Has it been published since the CMS first loaded it? | ||
$usersSaveCount = isset($_REQUEST['SaveCount']) ? $_REQUEST['SaveCount'] : $page->SaveCount; | ||
if ($usersSaveCount < $page->SaveCount) { | ||
$return = array('status' => 'not_current_version'); | ||
} | ||
} | ||
|
||
// Delete pings older than *timeout* from the cache... | ||
DB::query("DELETE FROM SiteTree_UsersCurrentlyEditing WHERE LastPing < '".date('Y-m-d H:i:s', time()-self::$edit_timeout)."'"); | ||
|
||
return Convert::array2json($return); | ||
} | ||
|
||
function onAfterSave(&$record) { | ||
$record->SaveCount++; | ||
$record->writeWithoutVersion(); | ||
FormResponse::add('CurrentPage.setSaveCount('.$record->SaveCount.');'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?php | ||
|
||
class ConcurrentEditingSiteTree extends DataObjectDecorator { | ||
function extraStatics() { | ||
return array( | ||
'db' => array( | ||
'SaveCount' => 'Int' | ||
), | ||
'defaults' => array( | ||
'SaveCount' => '0' | ||
), | ||
'many_many' => array( | ||
'UsersCurrentlyEditing' => 'Member' | ||
), | ||
'many_many_extraFields' => array( | ||
'UsersCurrentlyEditing' => array( | ||
'LastPing' => 'SSDatetime' | ||
) | ||
) | ||
); | ||
} | ||
|
||
function updateCMSFields(&$fields) { | ||
$alert = new LiteralField("SiteTree_Alert", '<div deletedfromstage="'.((int) $this->owner->getIsDeletedFromStage()).'" savecount="'.$this->owner->SaveCount.'" id="SiteTree_Alert"></div>'); | ||
$fields->insertBefore($alert, 'Root'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
var pagePingInterval = 15; | ||
|
||
var CurrentPage = { | ||
id: function() { return $('Form_EditForm_ID') ? $('Form_EditForm_ID').value : null; }, | ||
saveCount: function() { return $('SiteTree_Alert') ? $('SiteTree_Alert').getAttribute('savecount') : null; }, | ||
setSaveCount: function(count) { if ($('SiteTree_Alert')) { $('SiteTree_Alert').setAttribute('savecount', count); } }, | ||
isDeleted: function() { return $('SiteTree_Alert') ? $('SiteTree_Alert').getAttribute('deletedfromstage') : null; } | ||
} | ||
|
||
setInterval(function() { | ||
if ($('Form_EditForm_ID')) { | ||
new Ajax.Request("admin/concurrentEditingPing?ID="+CurrentPage.id()+'&SaveCount='+CurrentPage.saveCount(), { | ||
onSuccess: function(t) { | ||
var data = eval('('+t.responseText+')'); | ||
var hasAlert = false; | ||
|
||
switch(data.status) { | ||
case 'editing': | ||
$('SiteTree_Alert').style.border = '2px solid #B5D4FE'; | ||
$('SiteTree_Alert').style.backgroundColor = '#F8FAFC'; | ||
if (data.names.length) { | ||
hasAlert = true; | ||
$('SiteTree_Alert').innerHTML = "This page is also being edited by: "+data.names.join(', '); | ||
} | ||
break; | ||
case 'deleted': | ||
// handle deletion by another user (but not us, or if we're already looking at a deleted version) | ||
if (CurrentPage.isDeleted() == 0) { | ||
$('SiteTree_Alert').style.border = '2px solid #ffd324'; | ||
$('SiteTree_Alert').style.backgroundColor = '#fff6bf'; | ||
$('SiteTree_Alert').innerHTML = "This page has been deleted since you opened it."; | ||
hasAlert = true; | ||
} | ||
break; | ||
case 'not_current_version': | ||
// handle another user publishing | ||
$('SiteTree_Alert').style.border = '2px solid #FFD324'; | ||
$('SiteTree_Alert').style.backgroundColor = '#fff6bf'; | ||
$('SiteTree_Alert').innerHTML = "This page has been saved since you opened it. You may want to reload it, or risk overwriting changes."; | ||
hasAlert = true; | ||
break; | ||
case 'not_found': | ||
break; | ||
} | ||
|
||
if (hasAlert) { | ||
$('SiteTree_Alert').style.padding = '5px'; | ||
$('SiteTree_Alert').style.marginBottom = '5px'; | ||
$('SiteTree_Alert').style.display = 'block'; | ||
} else { | ||
$('SiteTree_Alert').innerHTML = ''; | ||
$('SiteTree_Alert').style.padding = '0px'; | ||
$('SiteTree_Alert').style.marginBottom = '0px'; | ||
if ($('SiteTree_Alert').style.display != 'none') $('SiteTree_Alert').style.display = 'none'; | ||
} | ||
} | ||
}); | ||
} | ||
}, pagePingInterval*1000); |