summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--conf/dokuwiki.php1
-rw-r--r--inc/actions.php77
-rw-r--r--inc/common.php14
-rw-r--r--inc/html.php47
-rw-r--r--inc/io.php3
-rw-r--r--inc/lang/en/lang.php10
-rw-r--r--inc/template.php14
-rw-r--r--lib/exe/ajax.php38
-rw-r--r--lib/exe/js.php4
-rw-r--r--lib/scripts/edit.js41
-rw-r--r--lib/scripts/script.js10
-rw-r--r--lib/tpl/default/design.css14
12 files changed, 241 insertions, 32 deletions
diff --git a/conf/dokuwiki.php b/conf/dokuwiki.php
index 539da08e8..411d2be7d 100644
--- a/conf/dokuwiki.php
+++ b/conf/dokuwiki.php
@@ -65,6 +65,7 @@ $conf['profileconfirm'] = '1'; //Require current password to confirm c
/* Advanced Options */
$conf['userewrite'] = 0; //this makes nice URLs: 0: off 1: .htaccess 2: internal
$conf['useslash'] = 0; //use slash instead of colon? only when rewrite is on
+$conf['usedraft'] = 1; //automatically save a draft while editing (0|1)
$conf['sepchar'] = '_'; //word separator character in page names; may be a
// letter, a digit, '_', '-', or '.'.
$conf['canonical'] = 0; //Should all URLs use full canonical http://... style?
diff --git a/inc/actions.php b/inc/actions.php
index e92e366d0..963be86a9 100644
--- a/inc/actions.php
+++ b/inc/actions.php
@@ -62,6 +62,14 @@ function act_dispatch(){
if($ACT == 'save')
$ACT = act_save($ACT);
+ //draft deletion
+ if($ACT == 'draftdel')
+ $ACT = act_draftdel($ACT);
+
+ //draft saving on preview
+ if($ACT == 'preview')
+ $ACT = act_draftsave($ACT);
+
//edit
if(($ACT == 'edit' || $ACT == 'preview') && $INFO['editable']){
$ACT = act_edit($ACT);
@@ -116,10 +124,18 @@ function act_clean($act){
global $lang;
global $conf;
+ // check if the action was given as array key
+ if(is_array($act)){
+ list($act) = array_keys($act);
+ }
+
//handle localized buttons
- if($act == $lang['btn_save']) $act = 'save';
- if($act == $lang['btn_preview']) $act = 'preview';
- if($act == $lang['btn_cancel']) $act = 'show';
+ if($act == $lang['btn_save']) $act = 'save';
+ if($act == $lang['btn_preview']) $act = 'preview';
+ if($act == $lang['btn_cancel']) $act = 'show';
+ if($act == $lang['btn_recover']) $act = 'recover';
+ if($act == $lang['btn_draftdel']) $act = 'draftdel';
+
//remove all bad chars
$act = strtolower($act);
@@ -136,12 +152,12 @@ function act_clean($act){
return 'show';
}
- if(array_search($act,array('login','logout','register','save','edit',
- 'preview','search','show','check','index','revisions',
- 'diff','recent','backlink','admin','subscribe',
- 'unsubscribe','profile','resendpwd',)) === false
- && substr($act,0,7) != 'export_' ) {
- msg('Unknown command: '.htmlspecialchars($act),-1);
+ if(!in_array($act,array('login','logout','register','save','edit','draft',
+ 'preview','search','show','check','index','revisions',
+ 'diff','recent','backlink','admin','subscribe',
+ 'unsubscribe','profile','resendpwd','recover',
+ 'draftdel',)) && substr($act,0,7) != 'export_' ) {
+ msg('Command unknown: '.htmlspecialchars($act),-1);
return 'show';
}
return $act;
@@ -156,7 +172,7 @@ function act_permcheck($act){
global $INFO;
global $conf;
- if(in_array($act,array('save','preview','edit'))){
+ if(in_array($act,array('save','preview','edit','recover'))){
if($INFO['exists']){
if($act == 'edit'){
//the edit function will check again and do a source show
@@ -187,6 +203,43 @@ function act_permcheck($act){
}
/**
+ * Handle 'draftdel'
+ *
+ * Deletes the draft for the current page and user
+ */
+function act_draftdel($act){
+ global $INFO;
+ @unlink($INFO['draft']);
+ $INFO['draft'] = null;
+ return 'show';
+}
+
+/**
+ * Saves a draft on preview
+ *
+ * @todo this currently duplicates code from ajax.php :-/
+ */
+function act_draftsave($act){
+ global $INFO;
+ global $ID;
+ global $conf;
+ if($conf['usedraft'] && $_POST['wikitext']){
+ $draft = array('id' => $ID,
+ 'prefix' => $_POST['prefix'],
+ 'text' => $_POST['wikitext'],
+ 'suffix' => $_POST['suffix'],
+ 'date' => $_POST['date'],
+ 'client' => $INFO['client'],
+ );
+ $cname = getCacheName($draft['client'].$ID,'.draft');
+ if(io_saveFile($cname,serialize($draft))){
+ $INFO['draft'] = $cname;
+ }
+ }
+ return $act;
+}
+
+/**
* Handle 'save'
*
* Checks for spam and conflicts and saves the page.
@@ -215,6 +268,9 @@ function act_save($act){
//unlock it
unlock($ID);
+ //delete draft
+ act_draftdel($act);
+
//show it
session_write_close();
header("Location: ".wl($ID,'',true));
@@ -259,6 +315,7 @@ function act_auth($act){
*/
function act_edit($act){
global $ID;
+ global $INFO;
//check if locked by anyone - if not lock for my self
$lockedby = checklock($ID);
diff --git a/inc/common.php b/inc/common.php
index ffb78f432..a2facee90 100644
--- a/inc/common.php
+++ b/inc/common.php
@@ -36,14 +36,17 @@ function pageinfo(){
$info['userinfo'] = $USERINFO;
$info['perm'] = auth_quickaclcheck($ID);
$info['subscribed'] = is_subscribed($ID,$_SERVER['REMOTE_USER']);
+ $info['client'] = $_SERVER['REMOTE_USER'];
// if some outside auth were used only REMOTE_USER is set
if(!$info['userinfo']['name']){
$info['userinfo']['name'] = $_SERVER['REMOTE_USER'];
}
+
}else{
$info['perm'] = auth_aclcheck($ID,'',null);
$info['subscribed'] = false;
+ $info['client'] = clientIP(true);
}
$info['namespace'] = getNS($ID);
@@ -86,6 +89,17 @@ function pageinfo(){
$info['editor'] = $revinfo['ip'];
}
+ // draft
+ $draft = getCacheName($info['client'].$ID,'.draft');
+ if(@file_exists($draft)){
+ if(@filemtime($draft) < @filemtime(wikiFN($ID))){
+ // remove stale draft
+ @unlink($draft);
+ }else{
+ $info['draft'] = $draft;
+ }
+ }
+
return $info;
}
diff --git a/inc/html.php b/inc/html.php
index c0ce8527f..0cc52fbd1 100644
--- a/inc/html.php
+++ b/inc/html.php
@@ -98,7 +98,7 @@ function html_login(){
}
/**
- * shows the edit/source/show button dependent on current mode
+ * shows the edit/source/show/draft button dependent on current mode
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
@@ -110,10 +110,14 @@ function html_editbutton(){
if($ACT == 'show' || $ACT == 'search'){
if($INFO['writable']){
- if($INFO['exists']){
- $r = html_btn('edit',$ID,'e',array('do' => 'edit','rev' => $REV),'post');
+ if($INFO['draft']){
+ $r = html_btn('draft',$ID,'e',array('do' => 'draft'),'post');
}else{
- $r = html_btn('create',$ID,'e',array('do' => 'edit','rev' => $REV),'post');
+ if($INFO['exists']){
+ $r = html_btn('edit',$ID,'e',array('do' => 'edit','rev' => $REV),'post');
+ }else{
+ $r = html_btn('create',$ID,'e',array('do' => 'edit','rev' => $REV),'post');
+ }
}
}else{
$r = html_btn('source',$ID,'v',array('do' => 'edit','rev' => $REV),'post');
@@ -287,6 +291,36 @@ function html_show($txt=''){
}
/**
+ * ask the user about how to handle an exisiting draft
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+function html_draft(){
+ global $INFO;
+ global $ID;
+ global $lang;
+ global $conf;
+ $draft = unserialize(io_readFile($INFO['draft'],false));
+ $text = cleanText(con($draft['prefix'],$draft['text'],$draft['suffix'],true));
+
+ echo p_locale_xhtml('draft');
+ ?>
+ <form id="dw__editform" method="post" action="<?php echo script()?>"
+ accept-charset="<?php echo $lang['encoding']?>"><div class="no">
+ <input type="hidden" name="id" value="<?php echo $ID?>" />
+ <input type="hidden" name="date" value="<?php echo $draft['date']?>" /></div>
+ <textarea name="wikitext" id="wiki__text" readonly="readonly" cols="80" rows="10" class="edit"><?php echo "\n".formText($text)?></textarea>
+
+ <div id="draft__status"><?php echo $lang['draftdate'].' '.date($conf['dformat'],filemtime($INFO['draft']))?></div>
+
+ <input class="button" type="submit" name="do" value="<?php echo $lang['btn_recover']?>" tabindex="1" />
+ <input class="button" type="submit" name="do" value="<?php echo $lang['btn_draftdel']?>" tabindex="2" />
+ <input class="button" type="submit" name="do" value="<?php echo $lang['btn_cancel']?>" tabindex="3" />
+ </form>
+ <?php
+}
+
+/**
* Highlights searchqueries in HTML code
*
* @author Andreas Gohr <andi@splitbrain.org>
@@ -999,6 +1033,7 @@ function html_edit($text=null,$include='edit'){ //FIXME: include needed?
<div style="width:99%;">
<form id="dw__editform" method="post" action="<?php echo script()?>" accept-charset="<?php echo $lang['encoding']?>"><div class="no">
<div class="toolbar">
+ <div id="draft__status"><?php if($INFO['draft']) echo $lang['draftdate'].' '.date($conf['dformat']);?></div>
<div id="tool__bar"></div>
<input type="hidden" name="id" value="<?php echo $ID?>" />
<input type="hidden" name="rev" value="<?php echo $REV?>" />
@@ -1012,8 +1047,8 @@ function html_edit($text=null,$include='edit'){ //FIXME: include needed?
textChanged = <?php ($pr) ? print 'true' : print 'false' ?>;
</script>
<span id="spell__action"></span>
- <?php } ?>
<div id="spell__suggest"></div>
+ <?php } ?>
</div>
<div id="spell__result"></div>
@@ -1025,7 +1060,7 @@ function html_edit($text=null,$include='edit'){ //FIXME: include needed?
<div class="editButtons">
<input class="button" id="edbtn__save" type="submit" name="do" value="<?php echo $lang['btn_save']?>" accesskey="s" title="[ALT+S]" tabindex="4" />
<input class="button" id="edbtn__preview" type="submit" name="do" value="<?php echo $lang['btn_preview']?>" accesskey="p" title="[ALT+P]" tabindex="5" />
- <input class="button" type="submit" name="do" value="<?php echo $lang['btn_cancel']?>" tabindex="5" />
+ <input class="button" type="submit" name="do[draftdel]" value="<?php echo $lang['btn_cancel']?>" tabindex="5" />
</div>
<?php } ?>
<?php if($wr){ ?>
diff --git a/inc/io.php b/inc/io.php
index 1d458ace9..ae1a3611c 100644
--- a/inc/io.php
+++ b/inc/io.php
@@ -33,6 +33,9 @@ function io_sweepNS($id,$basedir='datadir'){
*
* Uses gzip if extension is .gz
*
+ * If you want to use the returned value in unserialize
+ * be sure to set $clean to false!
+ *
* @author Andreas Gohr <andi@splitbrain.org>
*/
function io_readFile($file,$clean=true){
diff --git a/inc/lang/en/lang.php b/inc/lang/en/lang.php
index 1a4308125..c1aa6bd6c 100644
--- a/inc/lang/en/lang.php
+++ b/inc/lang/en/lang.php
@@ -36,9 +36,12 @@ $lang['btn_backlink'] = "Backlinks";
$lang['btn_backtomedia'] = 'Back to Mediafile Selection';
$lang['btn_subscribe'] = 'Subscribe Changes';
$lang['btn_unsubscribe'] = 'Unsubscribe Changes';
-$lang['btn_profile'] = 'Update Profile';
-$lang['btn_reset'] = 'Reset';
-$lang['btn_resendpwd'] = 'Send new password';
+$lang['btn_profile'] = 'Update Profile';
+$lang['btn_reset'] = 'Reset';
+$lang['btn_resendpwd'] = 'Send new password';
+$lang['btn_draft'] = 'Edit draft';
+$lang['btn_recover'] = 'Recover draft';
+$lang['btn_draftdel'] = 'Delete draft';
$lang['loggedinas'] = 'Logged in as';
$lang['user'] = 'Username';
@@ -53,6 +56,7 @@ $lang['register'] = 'Register';
$lang['profile'] = 'User Profile';
$lang['badlogin'] = 'Sorry, username or password was wrong.';
$lang['minoredit'] = 'Minor Changes';
+$lang['draftdate'] = 'Draft autosaved on'; // full dformat date will be added
$lang['regmissing'] = 'Sorry, you must fill in all fields.';
$lang['reguexists'] = 'Sorry, a user with this login already exists.';
diff --git a/inc/template.php b/inc/template.php
index 091cd2e56..a74067f8b 100644
--- a/inc/template.php
+++ b/inc/template.php
@@ -53,8 +53,8 @@ function template($tpl){
* (defined by the global $ACT var) by calling the appropriate
* outputfunction(s) from html.php
*
- * Everything that doesn't use the default template isn't
- * handled by this function. ACL stuff is not done either.
+ * Everything that doesn't use the main template file isn't
+ * handled by this function. ACL stuff is not done here either.
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
@@ -74,9 +74,15 @@ function tpl_content(){
html_edit($TEXT);
html_show($TEXT);
break;
+ case 'recover':
+ html_edit($TEXT);
+ break;
case 'edit':
html_edit();
break;
+ case 'draft':
+ html_draft();
+ break;
case 'wordblock':
html_edit($TEXT,'wordblock');
break;
@@ -203,7 +209,7 @@ function tpl_metaheaders($alt=true){
ptln('<link rel="stylesheet" media="print" type="text/css" href="'.DOKU_BASE.'lib/exe/css.php?print=1" />',$it);
// load javascript
- $js_edit = ($ACT=='edit' || $ACT=='preview') ? 1 : 0;
+ $js_edit = ($ACT=='edit' || $ACT=='preview' || $ACT=='recover') ? 1 : 0;
$js_write = ($INFO['writable']) ? 1 : 0;
if($js_edit && $js_write){
ptln('<script type="text/javascript" charset="utf-8">',$it);
@@ -283,7 +289,7 @@ function tpl_getparent($ID){
*
* Available Buttons are
*
- * edit - edit/create/show button
+ * edit - edit/create/show/draft button
* history - old revisions
* recent - recent changes
* login - login/logout button - if ACL enabled
diff --git a/lib/exe/ajax.php b/lib/exe/ajax.php
index e52d5d378..886e9829d 100644
--- a/lib/exe/ajax.php
+++ b/lib/exe/ajax.php
@@ -61,18 +61,52 @@ function ajax_qsearch(){
}
/**
- * Refresh a page lock
+ * Refresh a page lock and save draft
*
* Andreas Gohr <andi@splitbrain.org>
*/
function ajax_lock(){
+ global $conf;
+ global $lang;
$id = cleanID($_POST['id']);
if(empty($id)) return;
if(!checklock($id)){
lock($id);
- print 1;
+ echo 1;
}
+
+ if($conf['usedraft'] && $_POST['wikitext']){
+ $client = $_SERVER['REMOTE_USER'];
+ if(!$client) $client = clientIP(true);
+
+ $draft = array('id' => $ID,
+ 'prefix' => $_POST['prefix'],
+ 'text' => $_POST['wikitext'],
+ 'suffix' => $_POST['suffix'],
+ 'date' => $_POST['date'],
+ 'client' => $client,
+ );
+ $cname = getCacheName($draft['client'].$id,'.draft');
+ if(io_saveFile($cname,serialize($draft))){
+ echo $lang['draftdate'].' '.date($conf['dformat']);
+ }
+ }
+
+}
+
+/**
+ * Delete a draft
+ */
+function ajax_draftdel(){
+ $id = cleanID($_POST['id']);
+ if(empty($id)) return;
+
+ $client = $_SERVER['REMOTE_USER'];
+ if(!$client) $client = clientIP(true);
+
+ $cname = getCacheName($client.$id,'.draft');
+ @unlink($cname);
}
//Setup VIM: ex: et ts=2 enc=utf-8 :
diff --git a/lib/exe/js.php b/lib/exe/js.php
index 5147f1be3..9cc4a863c 100644
--- a/lib/exe/js.php
+++ b/lib/exe/js.php
@@ -93,7 +93,7 @@ function js_out(){
js_runonstart("initChangeCheck('".js_escape($lang['notsavedyet'])."')");
// add lock timer
- js_runonstart("locktimer.init(".($conf['locktime'] - 60).",'".js_escape($lang['willexpire'])."')");
+ js_runonstart("locktimer.init(".($conf['locktime'] - 60).",'".js_escape($lang['willexpire'])."',".$conf['usedraft'].")");
// load spell checker
if($conf['spellchecker']){
@@ -195,7 +195,7 @@ function js_escape($string){
* @author Andreas Gohr <andi@splitbrain.org>
*/
function js_runonstart($func){
- echo "addInitEvent(function(){ $func; });";
+ echo "addInitEvent(function(){ $func; });\n";
}
/**
diff --git a/lib/scripts/edit.js b/lib/scripts/edit.js
index d39835526..48acc542a 100644
--- a/lib/scripts/edit.js
+++ b/lib/scripts/edit.js
@@ -328,7 +328,24 @@ var textChanged = false;
*/
function changeCheck(msg){
if(textChanged){
- return confirm(msg);
+ var ok = confirm(msg);
+ if(ok){
+ // remove a possibly saved draft using ajax
+ var dwform = $('dw__editform');
+ if(dwform){
+ var params = 'call=draftdel';
+ params += '&id='+dwform.elements.id.value;
+ params += '&user='+encodeURI(USERNAME);
+
+ var sackobj = new sack(DOKU_BASE + 'lib/exe/ajax.php');
+ sackobj.AjaxFailedAlert = '';
+ sackobj.encodeURIString = false;
+ sackobj.runAJAX(params);
+ // we send this request blind without waiting for
+ // and handling the returned data
+ }
+ }
+ return ok;
}else{
return true;
}
@@ -408,10 +425,11 @@ function locktimer_class(){
this.pageid = '';
};
var locktimer = new locktimer_class();
- locktimer.init = function(timeout,msg){
+ locktimer.init = function(timeout,msg,draft){
// init values
locktimer.timeout = timeout*1000;
locktimer.msg = msg;
+ locktimer.draft = draft;
locktimer.lasttime = new Date();
if(!$('dw__editform')) return;
@@ -465,8 +483,16 @@ var locktimer = new locktimer_class();
locktimer.refresh = function(){
var now = new Date();
// refresh every minute only
- if(now.getTime() - locktimer.lasttime.getTime() > 60*1000){
- locktimer.sack.runAJAX('call=lock&id='+encodeURI(locktimer.pageid));
+ if(now.getTime() - locktimer.lasttime.getTime() > 30*1000){ //FIXME decide on time
+ var params = 'call=lock&id='+encodeURI(locktimer.pageid);
+ if(locktimer.draft){
+ var dwform = $('dw__editform');
+ params += '&prefix='+encodeURI(dwform.elements.prefix.value);
+ params += '&wikitext='+encodeURI(dwform.elements.wikitext.value);
+ params += '&suffix='+encodeURI(dwform.elements.suffix.value);
+ params += '&date='+encodeURI(dwform.elements.date.value);
+ }
+ locktimer.sack.runAJAX(params);
locktimer.lasttime = now;
}
};
@@ -476,7 +502,12 @@ var locktimer = new locktimer_class();
* Callback. Resets the warning timer
*/
locktimer.refreshed = function(){
- if(this.response != '1') return; // locking failed
+ var data = this.response;
+ var error = data.charAt(0);
+ data = data.substring(1);
+
+ $('draft__status').innerHTML=data;
+ if(error != '1') return; // locking failed
locktimer.reset();
};
// end of locktimer class functions
diff --git a/lib/scripts/script.js b/lib/scripts/script.js
index e05aeb0fe..87fd8e503 100644
--- a/lib/scripts/script.js
+++ b/lib/scripts/script.js
@@ -41,6 +41,16 @@ function $() {
}
/**
+ * Simple function to check if a global var is defined
+ *
+ * @author Kae Verens
+ * @link http://verens.com/archives/2005/07/25/isset-for-javascript/#comment-2835
+ */
+function isset(varname){
+ return(typeof(window[varname])!='undefined');
+}
+
+/**
* Get the X offset of the top left corner of the given object
*
* @link http://www.quirksmode.org/index.html?/js/findpos.html
diff --git a/lib/tpl/default/design.css b/lib/tpl/default/design.css
index 0f3502c98..70b429bc3 100644
--- a/lib/tpl/default/design.css
+++ b/lib/tpl/default/design.css
@@ -115,6 +115,16 @@ div.dokuwiki input.missing {
display: inline;
}
+/* disabled style - not understood by IE */
+div.dokuwiki textarea.edit[disabled],
+div.dokuwiki textarea.edit[readonly],
+div.dokuwiki input.edit[disabled],
+div.dokuwiki input.edit[readonly],
+div.dokuwiki select.edit[disabled] {
+ background-color: #f5f5f5!important;
+ color: #666!important;
+}
+
/* edit form */
div.dokuwiki div.toolbar, div.dokuwiki div#wiki__editbar {
margin:2px 0;
@@ -138,6 +148,10 @@ div.dokuwiki div#wiki__editbar div.summary {
div.dokuwiki .nowrap {
white-space:nowrap;
}
+div.dokuwiki div#draft__status {
+ float: right;
+ color: __dark__;
+}
/* --------- buttons ------------------- */