Skip to content

Commit

Permalink
Avoid reading and writing the whole media/page list for each move
Browse files Browse the repository at this point in the history
This makes sure that only the necessary parts of the media/page list are
read and the file is efficiently truncated. In order to calculate the
number of remaining items this number is stored and updated in the
options.
  • Loading branch information
michitux committed Mar 31, 2013
1 parent b57a8d4 commit cb27415
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 51 deletions.
33 changes: 19 additions & 14 deletions admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ class admin_plugin_pagemove extends DokuWiki_Admin_Plugin {
private $ns_opts = false;
/** @var helper_plugin_pagemove $helper */
private $helper = null;
/** @var string $ns_move_state The state of the current namespace move (none, started, continued, error) */
private $ns_move_state = 'none';

/**
* Get the sort number that defines the position in the admin menu.
Expand Down Expand Up @@ -70,30 +72,32 @@ function html() {
ptln( $this->locale_xhtml('pagemove') );
ptln('<div class="plugin__pagemove_forms">');

if ($this->ns_opts !== false && isset($this->ns_opts['started'])) {
ptln('<p>');
ptln(sprintf($this->getLang('pm_ns_move_started'), hsc($this->ns_opts['ns']), hsc($this->ns_opts['newns']), $this->ns_opts['num_pages'], $this->ns_opts['num_media']));
ptln('</p>');
ptln($this->helper->getNSMoveButton('continue'));
ptln($this->helper->getNSMoveButton('abort'));
} elseif ($this->ns_opts !== false && isset($this->ns_opts['remaining'])) {
if ($this->ns_opts['remaining'] === false) {
switch ($this->ns_move_state) {
case 'started':
ptln('<p>');
ptln(sprintf($this->getLang('pm_ns_move_started'), hsc($this->ns_opts['ns']), hsc($this->ns_opts['newns']), $this->ns_opts['num_pages'], $this->ns_opts['num_media']));
ptln('</p>');
ptln($this->helper->getNSMoveButton('continue'));
ptln($this->helper->getNSMoveButton('abort'));
break;
case 'error':
ptln('<p>');
ptln(sprintf($this->getLang('pm_ns_move_error'), $this->ns_opts['ns'], $this->ns_opts['newns']));
ptln('</p>');
ptln($this->helper->getNSMoveButton('tryagain'));
ptln($this->helper->getNSMoveButton('skip'));
ptln($this->helper->getNSMoveButton('abort'));
} else {
break;
case 'continued':
ptln('<p>');
ptln(sprintf($this->getLang('pm_ns_move_continued'), $this->ns_opts['ns'], $this->ns_opts['newns'], $this->ns_opts['remaining']));
ptln('</p>');

ptln($this->helper->getNSMoveButton('continue'));
ptln($this->helper->getNSMoveButton('abort'));
}
} else {
$this->printForm();
break;
default:
$this->printForm();
}
ptln('</div>');
ptln('<!-- Pagemove Plugin end -->');
Expand Down Expand Up @@ -124,7 +128,7 @@ function printForm() {
$form->endFieldset();
$form->printForm();

if ($this->ns_opts !== false && !isset($this->ns_opts['remaining'])) {
if ($this->ns_opts !== false) {
ptln('<fieldset>');
ptln('<legend>');
ptln($this->getLang('pm_movens'));
Expand Down Expand Up @@ -212,6 +216,7 @@ function handle() {
$this->helper->skip_namespace_move_item();
}
$this->ns_opts['remaining'] = $this->helper->continue_namespace_move();
$this->ns_move_state = ($this->ns_opts['remaining'] === false ? 'error': 'continued');
if ($this->ns_opts['remaining'] === 0) {
$ID = $this->helper->getNewID($INFO['id'], $this->opts['ns'], $this->opts['newns']);
$ACT = 'show';
Expand Down Expand Up @@ -246,7 +251,7 @@ function handle() {
$started = $this->helper->start_namespace_move($this->opts);
if ($started !== false) {
$this->ns_opts = $this->helper->get_namespace_move_opts();
$this->ns_opts['started'] = $started;
$this->ns_move_state = 'started';
}
} else {
// check that the pagename is valid
Expand Down
124 changes: 87 additions & 37 deletions helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ public function start_namespace_move(&$opts) {

io_saveFile($files['medialist'], implode("\n", $media_files));

$opts['remaining'] = $opts['num_media'] + $opts['num_pages'];

// save the options
io_saveFile($files['opts'], serialize($opts));

Expand All @@ -109,54 +111,68 @@ public function continue_namespace_move() {
$opts = unserialize(file_get_contents($files['opts']));

if (@file_exists($files['pagelist'])) {
$pagelist = file($files['pagelist'], FILE_IGNORE_NEW_LINES);
$pagelist = fopen($files['pagelist'], 'a+');;

$limit = min(10, count($pagelist));
for ($i = 0; $i < $limit; ++$i) {
$ID = array_pop($pagelist);
for ($i = 0; $i < 10; ++$i) {
$ID = $this->get_last_id($pagelist);
if ($ID === false) {
break;
}
$newID = $this->getNewID($ID, $opts['ns'], $opts['newns']);
$pageOpts = $opts;
$pageOpts['ns'] = getNS($ID);
$pageOpts['name'] = noNS($ID);
$pageOpts['newname'] = noNS($ID);
$pageOpts['newns'] = getNS($newID);
if (!$this->move_page($pageOpts)) return false;

// save the list of pages after every move
if (empty($pagelist)) {
unlink($files['pagelist']);
} else {
io_saveFile($files['pagelist'], implode("\n", $pagelist));
if (!$this->move_page($pageOpts)) {
fclose($pagelist);
return false;
}

// update the list of pages and the options after every move
ftruncate($pagelist, ftell($pagelist));
$opts['remaining']--;
io_saveFile($files['opts'], serialize($opts));
}
return count($pagelist) + $opts['num_media'];

fclose($pagelist);
if ($ID === false) unlink($files['pagelist']);
} elseif (@file_exists($files['medialist'])) {
$medialist = file($files['medialist'], FILE_IGNORE_NEW_LINES);
$medialist = fopen($files['medialist'], 'a+');

$limit = min(10, count($medialist));
for ($i = 0; $i < $limit; ++$i) {
$ID = array_pop($medialist);
for ($i = 0; $i < 10; ++$i) {
$ID = $this->get_last_id($medialist);
if ($ID === false) {
break;
}
$newID = $this->getNewID($ID, $opts['ns'], $opts['newns']);
$pageOpts = $opts;
$pageOpts['ns'] = getNS($ID);
$pageOpts['name'] = noNS($ID);
$pageOpts['newname'] = noNS($ID);
$pageOpts['newns'] = getNS($newID);
if (!$this->move_media($pageOpts)) return false;

// save the list of media files after every move
if (empty($medialist)) {
unlink($files['medialist']);
unlink($files['opts']);
} else {
io_saveFile($files['medialist'], implode("\n", $medialist));
if (!$this->move_media($pageOpts)) {
fclose($medialist);
return false;
}

// update the list of media files and the options after every move
ftruncate($medialist, ftell($medialist));
$opts['remaining']--;
io_saveFile($files['opts'], serialize($opts));
}

fclose($medialist);
if ($ID === false) {
unlink($files['medialist']);
unlink($files['opts']);
}
return count($medialist);
} else {
unlink($files['opts']);
return 0;
}

return $opts['remaining'];
}

/**
Expand All @@ -176,33 +192,67 @@ public function skip_namespace_move_item() {
$opts = unserialize(file_get_contents($files['opts']));

if (@file_exists($files['pagelist'])) {
$pagelist = file($files['pagelist'], FILE_IGNORE_NEW_LINES);
$pagelist = fopen($files['pagelist'], 'a+');

$ID = array_pop($pagelist);
$ID = $this->get_last_id($pagelist);
// save the list of pages after every move
if (empty($pagelist)) {
if ($ID === false || ftell($pagelist) == 0) {
fclose($pagelist);
unlink($files['pagelist']);
} else {
io_saveFile($files['pagelist'], implode("\n", $pagelist));
ftruncate($pagelist, ftell($pagelist));;
fclose($pagelist);
}
return count($pagelist) + $opts['num_media'];
} elseif (@file_exists($files['medialist'])) {
$medialist = file($files['medialist'], FILE_IGNORE_NEW_LINES);
$medialist = fopen($files['medialist'], 'a+');

$ID = array_pop($medialist);
$ID = $this->get_last_id($medialist);;
// save the list of media files after every move
if (empty($medialist)) {
if ($ID === false || ftell($medialist) == 0) {
fclose($medialist);
unlink($files['medialist']);
unlink($files['opts']);
} else {
io_saveFile($files['medialist'], implode("\n", $medialist));
ftruncate($medialist, ftell($medialist));
}

return count($medialist);
} else {
unlink($files['opts']);
return 0;
}
if ($opts['remaining'] == 0) return 0;
else {
$opts['remaining']--;
// save the options
io_saveFile($files['opts'], serialize($opts));
return $opts['remaining'];
}
}

/**
* Get last file id from the list that is stored in the file that is referenced by the handle
* The handle is set to the newline before the file id
*
* @param resource $handle The file handle to read from
* @return string|bool the last id from the list or false if there is none
*/
private function get_last_id($handle) {
// begin the seek at the end of the file
fseek($handle, 0, SEEK_END);
$id = '';

// seek one backwards as long as it's possible
while (fseek($handle, -1, SEEK_CUR) >= 0) {
$c = fgetc($handle);
fseek($handle, -1, SEEK_CUR); // reset the position to the character that was read

if ($c == "\n") {
break;
}
if ($c === false) return false; // EOF, i.e. the file is empty
$id = $c.$id;
}

if ($id === '') return false; // nothing was read i.e. the file is empty
else return $id;
}

/**
Expand Down

0 comments on commit cb27415

Please sign in to comment.