big commit: implemented sort-of 2-phase (3-phase actually) safe commit in FP; first entry is saved as draft, then content is moved along when indices have been succesfully updated; this way if btree fails, entry is anyway kept in a safe place; added a few cosmetic changes, a bunch of new, more descriptive language strings for the entry panel; added a "you are editing a draft" notice in the panel; hope you will like it :)

This commit is contained in:
real_nowhereman 2009-02-03 16:53:52 +00:00
parent 9413e4dee7
commit d5e666a1a1
11 changed files with 232 additions and 125 deletions

View File

@ -24,6 +24,7 @@
);
var $events = array('save', 'preview', 'savecontinue');
var $draft = false;
function _makePreview($arr, $id=null) {
@ -33,24 +34,43 @@
}
if ($this->draft || $this->draft = draft_exists($this->id)) {
if (isset($arr['categories'])
&& is_array($arr['categories']) && !in_array('draft', $arr['categories']) ) {
$arr['categories'][] = 'draft';
} else {
$arr['categories'][] = 'draft';
}
}
// unfiltered content (for editing)
$this->smarty->assign('post', $arr);
if (THEME_LEGACY_MODE) {
theme_entry_filters($arr, $id);
}
$arr = array_change_key_case($arr, CASE_LOWER);
// content for preview
$this->smarty->assign('entry', $arr);
$this->smarty->assign('preview', true);
}
function makePageTitle($title, $sep) {
global $lang;
global $lang, $panel;
if ($this->draft) {
$this->smarty->append(
'warnings',
$lang['admin']['entry']['write']['msgs']['draft']
);
}
return "$title $sep {$lang['admin']['entry']['write']['head']}";
}
function draft_class($string) {
return "$string draft";
}
function _getCatsFlags() {
//$this->smarty->assign('saved_categories', entry_categories_format());
@ -90,6 +110,7 @@
$this->_getCatsFlags();
add_filter('wp_title', array(&$this, 'makePageTitle'), 10, 2);
if ($this->draft) add_filter('admin_body_class', array(&$this, 'draft_class'));
}
@ -102,11 +123,13 @@
$arr['author'] = $author['userid'];
$arr['date'] = !empty($_POST['timestamp'])?$_POST['timestamp']:date_time();
$cats = !empty($_POST['cats'])?$_POST['cats']:array();
$flags = !empty($_POST['flags'])?$_POST['flags']:array();
$catids = array_merge(array_keys($flags), array_keys($cats));
$this->draft = isset($flags['draft']);
if ($catids)
$arr['categories'] = $catids;
@ -119,25 +142,25 @@
$id = $this->id;
$data = $this->_getposteddata();
if (isset($data['categories']) && in_array('draft', $data['categories'])) {
$success=draft_save($data, $id);
if ($this->draft) {
$success=draft_save($data, $id, true);
$this->smarty->assign('success', $success? 1 : -1 );
} else {
/* anyway issued */
draft_to_entry($id);
$success=entry_save($data, $id);
$this->smarty->assign('success', is_numeric($success)? $success : 1 );
}
if ($success) sess_remove('entry');
// if ($success) sess_remove('entry');
$this->smarty->assign('success',$success? 1:-1);
if ($do_preview)
$this->_makePreview($data);
if ($success<0) {
$this->main();
return PANEL_NOREDIRECT;
}
return 1;
}
@ -151,6 +174,7 @@
$this->_getCatsFlags();
add_filter('wp_title', array(&$this, 'makePageTitle'), 10, 2);
if ($this->draft) add_filter('admin_body_class', array(&$this, 'draft_class'));
return 0;
@ -164,7 +188,7 @@
$this->_getCatsFlags();
add_filter('wp_title', array(&$this, 'makePageTitle'), 10, 2);
if ($this->draft) add_filter('admin_body_class', array(&$this, 'draft_class'));
}

View File

@ -55,7 +55,7 @@
$id=basename($file,EXT);
$arr=entry_parse($id, true);
echo "[POST] $id => {$arr['SUBJECT']}\n";
echo "[POST] $id => {$arr['subject']}\n";
$this->index->add($id, $arr);
return 0;

View File

@ -985,7 +985,7 @@ class BPlusTree_Node {
#d(implode(",",$this->keys));
#$place = array_search($key, $this->keys);
$place = BPT_bisect($this->keys, $key, 0, $this->validkeys);
if (@$this->keys[$place-1] == $key) {
if ($this->keys[$place-1] == $key) {
return $this->indices[$place-1];
} else {
if ($loose) {
@ -2539,28 +2539,48 @@ class SBPlusTree extends BPlusTree {
$this->maxstring = $maxstring;
}
function startup() {
fwrite($this->stringfile, 'BPTSTRINGS');
return parent::startup();
}
function getstring($seek) {
fseek($this->stringfile, $seek);
$s = fread($this->stringfile, $this->maxstring);
return rtrim($s);
}
function setstring($s) {
function setstring($s, $key) {
$seek = $this->has_key($key);
if (!is_numeric($seek)) {
fseek($this->stringfile, 0, SEEK_END);
$seek = ftell($this->stringfile);
} else {
fseek($this->stringfile, $seek);
}
// nul-pad string
if (strlen($s>$this->maxstring))
$x = substr($s, 0, $this->maxstring);
$x = str_pad($s, $this->maxstring, chr(0));
fwrite($this->stringfile, $x);
return $seek;
}
function getitem(&$key, $loose=false) {
$seek = parent::getitem($key, $loose);
return $seek!==false? $this->getstring($seek) : false;
$seek = $this->has_key($key, $loose);
return is_numeric($seek)? $this->getstring($seek) : false;
}
/**
* @param $key target key
* @returns int seek point if key exists, 0 otherwise
*/
function has_key($key, $loose=false) {
return @parent::getitem($key, $loose);
}
function setitem($key, $val) {
$seek = $this->setstring($val);
$seek = $this->setstring($val, $key);
parent::setitem($key, $seek);
return $seek;
}

View File

@ -64,7 +64,7 @@
$entry = io_load_file($fname);
$entry = array_change_key_case(utils_kexplode($entry));
$entry = utils_kexplode($entry);
if (!isset($entry['categories']))
$entry['categories'] = array();
else
@ -76,7 +76,7 @@
}
function draft_save($entry, $id=null, $update_date=false) {
function draft_save($entry, $id=null, $update_index = false, $update_date=false) {
if (!$id) {
$id = bdb_idfromtime('entry', $entry['date']);
@ -90,27 +90,21 @@
// move collateral files
@rename($ed, $dd);
if ($update_index) {
// delete normal entry
fs_delete($ed.EXT);
// remove from normal flow
$o =& entry_init();
$o->delete($id);
$o->delete($id, null);
}
}
$entry['content'] = apply_filters('content_save_pre', $entry['content']);
$entry['subject'] = apply_filters('title_save_pre', $entry['subject']);
$entry = array_change_key_case($entry, CASE_UPPER);
if (isset($entry['CATEGORIES'])) {
if (is_array($entry['CATEGORIES']))
$entry['CATEGORIES'] = implode(',',$entry['CATEGORIES']);
else
trigger_error("Failed saving draft. Expected 'categories' to be
an array, found " . gettype($entry['CATEGORIES']), E_USER_ERROR);
}
$entry = entry_prepare($entry);
if ($entry['categories'])
$entry['categories']=implode(',', $entry['categories']);
else unset($entry['categories']);
$string = utils_kimplode($entry);
@ -136,9 +130,6 @@
function draft_exists($id) {
if (!user_loggedin())
return false;
$dir = draft_dir($id);
if (!$dir)
return false;

View File

@ -100,15 +100,19 @@
return $this->indices[$cat];
}
function add($id, $entry, $del = array()) {
function add($id, $entry, $del = array(), $update_title = true) {
$key = entry_idtokey($id);
$val = $entry['SUBJECT'];
$val = $entry['subject'];
$main =& $this->get_index();
$seek = $main->setitem($key, $val);
$seek = null;
if (!$update_title)
$seek = $main->has_key($key, $val);
if (isset($entry['CATEGORIES']) && is_array($entry['CATEGORIES'])) {
foreach ($entry['CATEGORIES'] as $cat) {
if (is_numeric($seek))
$seek = $main->setitem($key, $val);
if (isset($entry['categories']) && is_array($entry['categories'])) {
foreach ($entry['categories'] as $cat) {
if (!is_numeric($cat)) continue;
$this_index =& $this->get_index($cat);
$this_index->setitem($key, $seek);
@ -117,6 +121,7 @@
if ($del) {
foreach($del as $cat) {
// echo 'DEL '. $cat,"\n";
$this_index =& $this->get_index($cat);
$this_index->delitem($key);
}
@ -126,17 +131,18 @@
}
function delete($id) {
function delete($id, $entry) {
$key = entry_idtokey($id);
$main =& $this->get_index();
$main->delitem($key);
$entry = entry_parse($id);
if (isset($entry['categories']) && is_array($entry['categories'])) {
foreach ($entry['categories'] as $cat) {
if (!is_numeric($cat)) continue;
$this_index =& $this->get_index($cat);
if ($this_index->has_key($key))
$this_index->delitem($key);
}
}
@ -202,10 +208,10 @@
function add($id, $val) {
$this->_list[$id]=array('subject' => $val['SUBJECT'],
$this->_list[$id]=array('subject' => $val['subject'],
'categories' =>
(isset($val['CATEGORIES'])?
$val['CATEGORIES'] : array()));
(isset($val['categories'])?
$val['categories'] : array()));
return $this->save();
}
@ -424,10 +430,13 @@
return file_exists($f)? $f : false;
}
function entry_dir($id) {
function entry_dir($id, $month_only = false) {
if (!preg_match('|^entry[0-9]{6}-[0-9]{6}$|', $id))
return false;
$date = date_from_id($id);
if ($month_only)
$f = CONTENT_DIR . "{$date['y']}/{$date['m']}/";
else
$f = CONTENT_DIR . "{$date['y']}/{$date['m']}/$id";
return $f;
@ -450,18 +459,18 @@
// propagates the error if entry does not exist
if (isset($arr['CATEGORIES']) && // fix to bad old behaviour:
(trim($arr['CATEGORIES']) != '')) {
if (isset($arr['categories']) && // fix to bad old behaviour:
(trim($arr['categories']) != '')) {
$cats = (array)explode(',',$arr['CATEGORIES']);
$arr['CATEGORIES'] = (array) $cats;
$cats = (array)explode(',',$arr['categories']);
$arr['categories'] = (array) $cats;
} else $arr['CATEGORIES'] = array();
} else $arr['categories'] = array();
// if (!is_array($arr['CATEGORIES'])) die();
// if (!is_array($arr['categories'])) die();
if (!isset($arr['AUTHOR'])) {
global $fp_config;
@ -469,7 +478,7 @@
}
if ($raw) return $arr;
return array_change_key_case($arr, CASE_LOWER);
return $arr;
}
@ -621,61 +630,121 @@
}
function entry_save($entry_cont, $id=null, $update_index = true) {
// @TODO : check against schema ?
function entry_prepare($entry) { // prepare for serialization
global $post;
$obj =& entry_init();
if (!isset($entry_cont['date'])) {
$entry_cont['date']=date_time();
// fill in missing value
if (!isset($entry['date'])) {
$entry['date']=date_time();
}
$post = $entry_cont;
// import into global scope
$post = $entry;
$entry = array_change_key_case($entry_cont, CASE_UPPER);
// apply *_pre filters
$entry['content'] = apply_filters('content_save_pre', $entry['content']);
$entry['subject'] = apply_filters('title_save_pre', $entry['subject']);
// prepare for serialization
if (isset($entry['categories'])) {
if (!is_array($entry['categories'])) {
trigger_error("Expected 'categories' to be an array, found "
. gettype($entry['categories']), E_USER_WARNING);
$entry['categories'] = array();
}
} else { $entry['categories'] = array(); }
return $entry;
}
function entry_save($entry, $id=null, $update_index = true) {
// PHASE 1 : prepare entry
if (!$id) {
$id = bdb_idfromtime(BDB_ENTRY, $entry['DATE']);
}
do_action('publish_post', $id, $entry_cont);
$f = bdb_idtofile($id);
$entry['CONTENT'] = apply_filters('content_save_pre', $entry['CONTENT']);
$entry['SUBJECT'] = apply_filters('title_save_pre', $entry['SUBJECT']);
$del = array();
if ($arr = entry_parse($id)) {
if (isset($entry['CATEGORIES']) && is_array($entry['CATEGORIES']))
$del = array_diff($arr['categories'], $entry['CATEGORIES']);
if (!@$entry['date']) $entry['date'] = date_time();
$id = bdb_idfromtime(BDB_ENTRY, $entry['date']);
}
$ok = ($update_index) ? $obj->add($id, $entry, $del) : true;
do_action('publish_post', $id, $entry);
// PHASE 2 : Store
// secure data as DRAFT
$ret = draft_save($entry, $id);
if ($ret === false) {
return -1; // FAILURE: ABORT
}
// PHASE 3 : Update index
$delete_cats = array();
$all_cats = @$entry['categories'];
$update_title = false;
if ($old_entry = entry_parse($id)) {
if ($all_cats) {
$delete_cats = array_diff($old_entry['categories'], $all_cats);
}
$all_cats = $all_cats? array_merge($all_cats, $old_entry['categories']) : $old_entry['categories'];
$update_title = $entry['subject'] != $old_entry['subject'];
}
/*
echo 'old';
print_r($old_entry['categories']);
echo 'new';
print_r($entry['categories']);
echo 'del';
print_r($delete_cats);
echo 'all';
print_r($all_cats);
*/
$INDEX =& entry_init();
$ok = ($update_index) ? $INDEX->add($id, $entry, $delete_cats, $update_title) : true;
// PHASE 4 : index updated; let's move back the entry
if ($ok) {
if (isset($entry['CATEGORIES'])) {
if (is_array($entry['CATEGORIES']))
$entry['CATEGORIES'] = implode(',',$entry['CATEGORIES']);
else
trigger_error("Failed saving entry. Expected 'categories' to be
an array, found " . gettype($entry['CATEGORIES']), E_USER_ERROR);
$entryd = entry_dir($id, true);
$entryf = $entryd.$id.EXT;
$draftf = draft_exists($id);
if ($draftf === false) { // this should never happen!
if ($update_index) {
$INDEX->delete($id, $all_cats);
}
return -2;
}
fs_delete($entryf);
fs_mkdir($entryd);
$ret = rename($draftf, $entryf);
$str = utils_kimplode($entry);
if (!io_write_file($f, $str)) {
if ($update_index)
$obj->delete($id, $entry);
return false;
} else return $id;
if (!$ret) {
if (draft_exists($id)) {
// rollback changes in the index
// (keep the draft file)
if ($update_index) {
$INDEX->delete($id, $all_cats);
}
return -3;
} else { echo 'zomg bacon';
return -2;
}
} else {
// SUCCESS : delete draft, move comments along
draft_to_entry($id);
return $id;
}
}
return false;
return -4;
}

View File

@ -773,7 +773,7 @@
if (isset($params['content']) && is_array($params['content']) && $params['content']) {
//foreach ($params['entry'] as $k => $val)
$smarty->assign(array_change_key_case($params['content'], CASE_LOWER));
$smarty->assign($params['content']);
return $content;
}

View File

@ -26,7 +26,7 @@
function static_parse($id) {
if ($fname=static_exists($id)) {
$entry = io_load_file($fname);
return array_change_key_case(utils_kexplode($entry));
return (utils_kexplode($entry));
}
return array();
}
@ -35,7 +35,6 @@
function static_save($entry, $id, $oldid=null) {
$fname = STATIC_DIR . $id . EXT;
$entry = array_change_key_case($entry, CASE_UPPER);
$entry['CONTENT'] = apply_filters('content_save_pre', $entry['CONTENT']);
$entry['SUBJECT'] = apply_filters('title_save_pre', $entry['SUBJECT']);
@ -106,7 +105,7 @@
if (isset($params['content']) && is_array($params['content']) && $params['content']) {
//foreach ($params['entry'] as $k => $val)
$smarty->assign(array_change_key_case($params['content'], CASE_LOWER));
$smarty->assign($params['content']);
return $content;
}

View File

@ -86,7 +86,8 @@ if (!function_exists('fnmatch')) {
$arr = array();
$string = trim($string);
$arr[strtok($string, $delim)] = strtok($delim);
$k = strtolower(strtok($string, $delim));
$arr[$k] = strtok($delim);
while (( $k = strtok($delim) ) !== false) {
if ($keyupper && !preg_match('/[A-Z-_]/',$k)){
/*
@ -100,7 +101,7 @@ if (!function_exists('fnmatch')) {
continue;
}
$arr[$k] = strtok($delim);
$arr[strtolower($k)] = strtok($delim);
}
return $arr;
@ -156,10 +157,11 @@ if (!function_exists('fnmatch')) {
// $arr['key1'] = 'value1'; $arr['key2'] = 'value2'; etc.
function utils_kimplode($arr, $delim='|') {
$string = "";
foreach ($arr as $k => $val) {
if ($val)
$string .= $k . $delim . $val . $delim;
$string .= strtoupper($k) . $delim . ($val) . $delim;
}
return $string;
}

View File

@ -73,8 +73,10 @@
$lang['admin']['entry']['write']['msgs'] = array(
1 => 'Entry has been saved successfully',
-1 => 'An error occurred while trying to save
the entry',
-1 => 'An error occurred: your entry could not be saved successfully',
-2 => 'An error occurred: your entry has not been saved; index might have become corrupt',
-3 => 'An error occurred: your entry has been saved as draft',
-4 => 'An error occurred: your entry has been saved as draft; index might have become corrupt',
'draft'=> 'You are editing a <strong>draft</strong> entry'
);

View File

@ -7,7 +7,7 @@
{action hook=admin_head}
</head>
<body class="{"admin-$panel-$action"|tag:body_class:adminpanel}">
<body class="{"admin-$panel-$action"|tag:admin_body_class}">
<div id="body-container">
<div id="outer-container">

View File

@ -554,7 +554,7 @@ a.link-general:hover, .main-cell a:hover {
cursor: help;
}
.draft { background-color: lightgrey }
.draft { background-color: #333 }
/* (already defined in common, here we put just some tuning settings) */
#admin-content ul.msgs {