From 4db1861a8dc06b5ae608144b3f3a567974dd7148 Mon Sep 17 00:00:00 2001 From: sam_marshall Date: Wed, 27 Sep 2006 11:22:57 +0000 Subject: [PATCH] Wiki: Improve page locking to make it more robust and support non-javascript users (not very friendlily, though...) --- lang/en_utf8/wiki.php | 2 + mod/wiki/lib.php | 84 ++++++++++++++++++++++++++++++++- mod/wiki/view.php | 107 ++++++++++++++++++------------------------ 3 files changed, 129 insertions(+), 64 deletions(-) diff --git a/lang/en_utf8/wiki.php b/lang/en_utf8/wiki.php index 9e8b6e620f..91d334d4f7 100644 --- a/lang/en_utf8/wiki.php +++ b/lang/en_utf8/wiki.php @@ -106,6 +106,7 @@ $string['noadministrationaction'] = 'No administration action given.'; $string['nocandidatestoremove'] = 'No candidate pages to remove, choose \'$a\' to show all pages.'; $string['nochangestorevert'] = 'No changes to revert.'; $string['nohtml'] = 'No HTML'; +$string['nojslockwarning'] = 'Because Javascript is disabled in your browser, it is possible that somebody else could edit this page. If that happens, you won\'t be able to save your changes. Please try to make your edits quickly, or turn Javascript on and reload this page.'; $string['nolinksfound'] = 'No links found on page.'; $string['noregexp'] = 'This must be a fixed string (you cannot use * or regex), at best use the attackers` IP address or host name, but do not include the port number (because it increased with every http access).'; $string['notadministratewiki'] = 'You are not allowed to administrate this wiki !'; @@ -148,6 +149,7 @@ $string['revertpagescheck'] = 'Do you really want to revert the following change $string['revertthe'] = 'Version diving, but only purge the affected one'; $string['safehtml'] = 'Safe HTML'; $string['save'] = 'Save'; +$string['savenolock'] = 'You cannot save the page because you do not have an editing lock. This can happen if your browser has Javascript turned off, or if another user overrides your lock.

You can return to the previous screen using your browser\'s Back button and copy the text of any changes you made, then try editing again.'; $string['searchwiki'] = 'Search Wiki'; $string['setpageflags'] = 'Set page flags'; $string['showversions'] = 'Show versions:'; diff --git a/mod/wiki/lib.php b/mod/wiki/lib.php index 98b327e494..8e6188067d 100644 --- a/mod/wiki/lib.php +++ b/mod/wiki/lib.php @@ -12,11 +12,13 @@ $WIKI_TYPES = array ('teacher' => get_string('defaultcourseteacher'), define("EWIKI_ESCAPE_AT", 0); # For the algebraic filter // How long locks stay around without being confirmed (seconds) -define("WIKI_LOCK_PERSISTENCE",60); +define("WIKI_LOCK_PERSISTENCE",120); // How often to confirm that you still want a lock -define("WIKI_LOCK_RECONFIRM",30); +define("WIKI_LOCK_RECONFIRM",60); +// Session variable used to store wiki locks +define('SESSION_WIKI_LOCKS','wikilocks'); /*** Moodle 1.7 compatibility functions ***** * @@ -1562,4 +1564,82 @@ function wiki_get_post_actions() { } +/** + * Obtains an editing lock on a wiki page. + * @param int $wikiid ID of wiki object. + * @param string $pagename Name of page. + * @return array Two-element array with a boolean true (if lock has been obtained) + * or false (if lock was held by somebody else). If lock was held by someone else, + * the values of the wiki_locks entry are held in the second element. + */ +function wiki_obtain_lock($wikiid,$pagename) { + global $USER; + + // Check for lock + $alreadyownlock=false; + if($lock=get_record('wiki_locks','pagename',$pagename,'wikiid', $wikiid)) { + // Consider the page locked if the lock has been confirmed within WIKI_LOCK_PERSISTENCE seconds + if($lock->lockedby==$USER->id) { + // Cool, it's our lock, do nothing except remember it in session + $lockid=$lock->id; + $alreadyownlock=true; + } else if(time()-$lock->lockedseen < WIKI_LOCK_PERSISTENCE) { + return array(false,$lock); + } else { + // Not locked any more. Get rid of the old lock record. + if(!delete_records('wiki_locks','pagename',$pagename,'wikiid', $wikiid)) { + error('Unable to delete lock record'); + } + } + } + + // Add lock + if(!$alreadyownlock) { + // Lock page + $newlock=new stdClass; + $newlock->lockedby=$USER->id; + $newlock->lockedsince=time(); + $newlock->lockedseen=$newlock->lockedsince; + $newlock->wikiid=$wikiid; + $newlock->pagename=$pagename; + if(!$lockid=insert_record('wiki_locks',$newlock)) { + error('Unable to insert lock record'); + } + } + + // Store lock information in session so we can clear it later + if(!array_key_exists(SESSION_WIKI_LOCKS,$_SESSION)) { + $_SESSION[SESSION_WIKI_LOCKS]=array(); + } + $_SESSION[SESSION_WIKI_LOCKS][$wikiid.'_'.$pagename]=$lockid; + return array(true,null); +} + +/** + * If the user has an editing lock, releases it. Has no effect otherwise. + * Note that it doesn't matter if this isn't called (as happens if their + * browser crashes or something) since locks time out anyway. This is just + * to avoid confusion of the 'what? it says I'm editing that page but I'm + * not, I just saved it!' variety. + * @param int $wikiid ID of wiki object. + * @param string $pagename Name of page. + */ +function wiki_release_lock($wikiid,$pagename) { + if(!array_key_exists(SESSION_WIKI_LOCKS,$_SESSION)) { + // No locks at all in session + return; + } + + $key=$wikiid.'_'.$pagename; + + if(array_key_exists($key,$_SESSION[SESSION_WIKI_LOCKS])) { + $lockid=$_SESSION[SESSION_WIKI_LOCKS][$key]; + unset($_SESSION[SESSION_WIKI_LOCKS][$key]); + if(!delete_records('wiki_locks','id',$lockid)) { + error("Unable to delete lock record."); + } + } +} + + ?> \ No newline at end of file diff --git a/mod/wiki/view.php b/mod/wiki/view.php index 72381ea254..5372bb7b60 100644 --- a/mod/wiki/view.php +++ b/mod/wiki/view.php @@ -27,22 +27,14 @@ $actions = explode('/', $page,2); if(count($actions)==2) { $pagename=$actions[1]; + } else { + $pagename=$actions[0]; } } else { $actions=array(''); $pagename=''; } - // If true, we are 'really' on an editing page, not just on edit/something - $reallyedit=$actions[0]=='edit' && !$editsave && !$canceledit; - if(!$reallyedit && isset($_SESSION['lockid'])) { - if(!delete_records('wiki_locks','id',$_SESSION['lockid'])) { - unset($_SESSION['lockid']); - error("Unable to delete lock record. ".$_SESSION['lockid']); - } - unset($_SESSION['lockid']); - } - if ($id) { if (! $cm = get_coursemodule_from_id('wiki', $id)) { error("Course Module ID was incorrect"); @@ -71,8 +63,24 @@ } require_course_login($course, true, $cm); - - + + // If true, we are 'really' on an editing page, not just on edit/something + $reallyedit=$actions[0]=='edit' && !$canceledit && !$editsave; + + // Remove lock when we go to another wiki page (such as the cancel page) + if(!$reallyedit) { + wiki_release_lock($wiki->id,$pagename); + } + + // We must have the edit lock in order to be permitted to save + if(!empty($_POST['content'])) { + list($ok,$lock)=wiki_obtain_lock($wiki->id,$pagename); + if(!$ok) { + $strsavenolock=get_string('savenolock','wiki'); + error($strsavenolock,$CFG->wwwroot.'/mod/wiki/view.php?id='.$cm->id.'&page=view/'.urlencode($pagename)); + } + } + /// Add the course module 'groupmode' to the wiki object, for easy access. $wiki->groupmode = $cm->groupmode; @@ -398,33 +406,26 @@ } else if($actions[0]=='edit' && $reallyedit) { // Check the page isn't locked before printing out standard wiki content. (Locking // is implemented as a wrapper over the existing wiki.) - $goahead=true; - $alreadyownlock=false; - if($lock=get_record('wiki_locks','pagename',$pagename,'wikiid', $wiki->id)) { - // Consider the page locked if the lock has been confirmed within WIKI_LOCK_PERSISTENCE seconds - if($lock->lockedby==$USER->id) { - // Cool, it's our lock, do nothing - $alreadyownlock=true; - $lockid=$lock->id; - } else if(time()-$lock->lockedseen < WIKI_LOCK_PERSISTENCE) { - $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); - $canoverridelock = has_capability('mod/wiki:overridelock', $modcontext); - - $a=new stdClass; - $a->since=userdate($lock->lockedsince); - $a->seen=userdate($lock->lockedseen); - $user=get_record('user','id',$lock->lockedby); - $a->name=fullname($user, - has_capability('moodle/site:viewfullnames', $modcontext)); - - print_string('pagelocked','wiki',$a); + list($gotlock,$lock)=wiki_obtain_lock($wiki->id,$pagename); + if(!$gotlock) { + $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); + $canoverridelock = has_capability('mod/wiki:overridelock', $modcontext); + + $a=new stdClass; + $a->since=userdate($lock->lockedsince); + $a->seen=userdate($lock->lockedseen); + $user=get_record('user','id',$lock->lockedby); + $a->name=fullname($user, + has_capability('moodle/site:viewfullnames', $modcontext)); - if($canoverridelock) { - $pageesc=htmlspecialchars($page); - $stroverrideinfo=get_string('overrideinfo','wiki'); - $stroverridebutton=get_string('overridebutton','wiki'); - $sesskey=sesskey(); - print " + print_string('pagelocked','wiki',$a); + + if($canoverridelock) { + $pageesc=htmlspecialchars($page); + $stroverrideinfo=get_string('overrideinfo','wiki'); + $stroverridebutton=get_string('overridebutton','wiki'); + $sesskey=sesskey(); + print "
@@ -433,33 +434,12 @@
"; - } - $goahead=false; - } else { - // Not locked any more. Get rid of the lock record. - if(!delete_records('wiki_locks','pagename',$pagename,'wikiid', $wiki->id)) { - error('Unable to delete lock record'); - } - } - } - if($goahead) { - if(!$alreadyownlock) { - // Lock page - $newlock=new stdClass; - $newlock->lockedby=$USER->id; - $newlock->lockedsince=time(); - $newlock->lockedseen=$newlock->lockedsince; - $newlock->wikiid=$wiki->id; - $newlock->pagename=$pagename; - if(!$lockid=insert_record('wiki_locks',$newlock)) { - error('Unable to insert lock record'); - } } - $_SESSION['lockid']=$lockid; - - // Require AJAX library + } else { + // OK, the page is now locked to us. Put in the AJAX for keeping the lock print_require_js(array('yui_yahoo','yui_connection')); $strlockcancelled=get_string('lockcancelled','wiki'); + $strnojslockwarning=get_string('nojslockwarning','wiki'); $intervalms=WIKI_LOCK_RECONFIRM*1000; print " + "; // Print editor etc -- 2.39.5