Skip to content

Commit

Permalink
Move page/namespace move functions into the helper
Browse files Browse the repository at this point in the history
This separates the UI (admin.php) and the functionality (helper.php) so
the main functions of the pagemove plugin can also be used by other
plugins or a possible ajax backend (for moving big namespaces).
  • Loading branch information
michitux committed Dec 29, 2012
1 parent c17ae02 commit f970892
Show file tree
Hide file tree
Showing 3 changed files with 260 additions and 255 deletions.
10 changes: 5 additions & 5 deletions _test/pagemove.test.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class PagemovePageTest extends DokuWikiTest {
var $currentNsBacklinkingId = 'parent_ns:current_ns:some_page';
var $otherBacklinkingId = 'level0:level1:other_backlinking_page';
var $subNsPage = 'parent_ns:current_ns:sub_ns:some_page';
/** @var admin_plugin_pagemove $pagemove */
/** @var helper_plugin_pagemove $pagemove */
private $pagemove = NULL;

// @todo Move page to an ID which already exists
Expand Down Expand Up @@ -163,7 +163,7 @@ function setUp() {
$references = array_keys(p_get_metadata($this->subNsPage, 'relation references', METADATA_RENDER_UNLIMITED));
idx_get_indexer()->addMetaKeys($this->subNsPage, 'relation_references', $references);

$this->pagemove = new admin_plugin_pagemove();
$this->pagemove = new helper_plugin_pagemove();
parent::setUp();
}

Expand All @@ -187,7 +187,7 @@ function test_move_page_in_same_ns() {
$opts['newns'] = $opts['ns'];
$opts['newname'] = $newPagename;
$this->movedToId = $opts['newns'].':'.$newPagename;
$this->pagemove->_pm_move_page($opts);
$this->pagemove->move_page($opts);

$newId = $opts['newns'].':'.$opts['newname'];
$newContent = rawWiki($newId);
Expand Down Expand Up @@ -305,7 +305,7 @@ function test_move_page_to_parallel_ns() {
$opts['newns'] = 'parent_ns:parallel_ns';
$opts['newname'] = $newPagename;
$this->movedToId = $opts['newns'].':'.$newPagename;
$this->pagemove->_pm_move_page($opts);
$this->pagemove->move_page($opts);

$newId = $opts['newns'].':'.$opts['newname'];
$newContent = rawWiki($newId);
Expand Down Expand Up @@ -425,7 +425,7 @@ function test_move_page_to_parent_ns() {
$newId = $opts['newns'].':'.$opts['newname'];
$this->movedToId = $opts['newns'].':'.$newPagename;

$this->pagemove->_pm_move_page($opts);
$this->pagemove->move_page($opts);

$newContent = rawWiki($newId);
$expectedContent = <<<EOT
Expand Down
258 changes: 8 additions & 250 deletions admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ function handle() {
if (isset($_POST['newnsname'])) $this->opts['newnsname'] = cleanID((string)$_POST['newnsname']);
if (isset($_POST['move_type'])) $this->opts['move_type'] = (string)$_POST['move_type'];

/** @var helper_plugin_pagemove $helper */
$helper = $this->loadHelper('pagemove', true);
if (!$helper) return;

// check the input for completeness
if( $this->opts['move_type'] == 'namespace' ) {
if ($this->opts['targetns'] == '') {
Expand All @@ -176,9 +180,9 @@ function handle() {
$this->opts['newns'] = $this->opts['targetns'].':'.$this->opts['newnsname'];
}

if ($this->_pm_move_recursive($this->opts, true) &&
$this->_pm_move_recursive($this->opts)) {
$ID = $this->getNewID($INFO['id'], $this->opts['ns'], $this->opts['newns']);
if ($helper->move_namespace($this->opts, true) &&
$helper->move_namespace($this->opts)) {
$ID = $helper->getNewID($INFO['id'], $this->opts['ns'], $this->opts['newns']);
$ACT = 'show';
} else {
return;
Expand All @@ -194,7 +198,7 @@ function handle() {
$this->opts['newns'] = $this->opts['ns_for_page'];
}

if ($this->_pm_move_page($this->opts)) {
if ($helper->move_page($this->opts)) {
// @todo if the namespace is now empty, delete it

// Set things up to display the new page.
Expand All @@ -205,250 +209,4 @@ function handle() {
}
}
}


/**
*
* @author Bastian Wolf
* @param array $opts Options for moving the page
* @param bool $checkonly If only the checks if all pages can be moved shall be executed
* @return bool if the move was executed
*/
function _pm_move_recursive(&$opts, $checkonly = false) {
global $ID;
global $conf;

$pagelist = array();
$pathToSearch = utf8_encodeFN(str_replace(':', '/', $opts['ns']));
$searchOpts = array('depth' => 0, 'skipacl' => true);
search($pagelist, $conf['datadir'], 'search_allpages', $searchOpts, $pathToSearch);

// FIXME: either use ajax for executing the queue and/or store the queue so it can be resumed when the execution
// is aborted.
foreach ($pagelist as $page) {
$ID = $page['id'];
$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->_pm_move_page($pageOpts, $checkonly)) return false;
}
return true;
}

/**
* Get the id of a page after a namespace move
*
* @param string $oldid The old id of the page
* @param string $oldns The old namespace. $oldid needs to be inside $oldns
* @param string $newns The new namespace
* @return string The new id
*/
function getNewID($oldid, $oldns, $newns) {
$newid = $oldid;
if ($oldns != '') {
$newid = substr($oldid, strlen($oldns)+1);
}

if ($newns != '') {
$newid = $newns.':'.$newid;
}

return $newid;
}


/**
* move page
*
* @author Gary Owen <[email protected]>, modified by Kay Roesler
*
* @param array $opts
* @param bool $checkonly Only execute the checks if the page can be moved
* @return bool If the move was executed
*/
function _pm_move_page(&$opts, $checkonly = false) {
global $ID;

// Check we have rights to move this document
if ( !page_exists($ID)) {
msg($this->getLang('pm_notexist'), -1);
return false;
}
if ( auth_quickaclcheck($ID) < AUTH_EDIT ) {
msg(sprintf($this->getLang('pm_norights'), hsc($ID)), -1);
return false;
}

// Check file is not locked
if (checklock($ID) !== false) {
msg( sprintf($this->getLang('pm_filelocked'), hsc($ID)), -1);
return false;
}

// Assemble fill document name and path
$opts['new_id'] = cleanID($opts['newns'].':'.$opts['newname']);
$opts['new_path'] = wikiFN($opts['new_id']);

// Has the document name and/or namespace changed?
if ( $opts['newns'] == $opts['ns'] && $opts['newname'] == $opts['name'] ) {
msg($this->getLang('pm_nochange'), -1);
return false;
}
// Check the page does not already exist
if ( @file_exists($opts['new_path']) ) {
msg(sprintf($this->getLang('pm_existing'), $opts['newname'],
($opts['newns'] == '' ? $this->getLang('pm_root') : $opts['newns'])), -1);
return false;
}

// Check if the current user can create the new page
if (auth_quickaclcheck($opts['new_id']) < AUTH_CREATE) {
msg(sprintf($this->getLang('pm_notargetperms'), $opts['new_id']), -1);
return false;
}

if ($checkonly) return true;

/**
* End of init (checks)
*/

$page_meta = p_get_metadata($ID, 'plugin_pagemove', METADATA_DONT_RENDER);
if (!$page_meta) $page_meta = array();
if (!isset($page_meta['old_ids'])) $page_meta['old_ids'] = array();
$page_meta['old_ids'][$ID] = time();

// ft_backlinks() is not used here, as it does a hidden page and acl check but we really need all pages
$affected_pages = idx_get_indexer()->lookupKey('relation_references', array_keys($page_meta['old_ids']));

$data = array('opts' => &$opts, 'old_ids' => $page_meta['old_ids'], 'affected_pages' => &$affected_pages);
// give plugins the option to add their own meta files to the list of files that need to be moved
// to the oldfiles/newfiles array or to adjust their own metadata, database, ...
// and to add other pages to the affected pages
// note that old_ids is in the form 'id' => timestamp of move and affected_pages is indexed by these ids
$event = new Doku_Event('PAGEMOVE_PAGE_RENAME', $data);
if ($event->advise_before()) {
// Open the old document and change forward links
lock($ID);
$text = rawWiki($ID);

/** @var helper_plugin_pagemove $helper */
$helper = $this->loadHelper('pagemove', true);
if (is_null($helper)) return false;

$text = $helper->rewrite_content($text, $ID, array($ID => $opts['new_id']));

// Move the Subscriptions & Indexes
$this->movemeta($opts);

// Save the updated document in its new location
if ($opts['ns'] == $opts['newns']) {
$lang_key = 'pm_renamed';
}
elseif ( $opts['name'] == $opts['newname'] ) {
$lang_key = 'pm_moved';
}
else {
$lang_key = 'pm_move_rename';
}
$summary = sprintf($this->getLang($lang_key), $ID, $opts['new_id']);
saveWikiText($opts['new_id'], $text, $summary);

// Delete the orginal file
if (@file_exists(wikiFN($opts['new_id']))) {
saveWikiText($ID, '', $this->getLang('pm_delete') );
}

// Move the old revisions
$this->moveattic($opts);

asort($page_meta['old_ids']);

// additional pages that should be considered because they were affected by moves from previous names
// if the page has been rendered in the meantime and but the new links aren't in the index yet the
// page might need information about a more recent rename even though it is not listed for this more recent link
$additional_pages = array();
foreach ($page_meta['old_ids'] as $page_id => $time) {
if (!isset($affected_pages[$page_id])) {
$affected_pages[$page_id] = $additional_pages;
} else {
$affected_pages[$page_id] = array_unique(array_merge($affected_pages[$page_id], $additional_pages));
}
foreach ($affected_pages[$page_id] as $id) {
if (!page_exists($id, '', false) || $id == $page_id || $id == $opts['new_id']) continue;
// if the page has been modified since the rename of the old page, the link in the new page is most
// probably intentionally to the old page and shouldn't be changed
if (filemtime(wikiFN($id, '', false)) > $time) continue;
$additional_pages[] = $id;
// we are only interested in persistent metadata, so no need to render anything.
$meta = p_get_metadata($id, 'plugin_pagemove', METADATA_DONT_RENDER);
if (!$meta) $meta = array('moves' => array());
if (!isset($meta['moves'])) $meta['moves'] = array();
$meta['moves'][$page_id] = $opts['new_id'];
// remove redundant moves (can happen when a page is moved back to its old id)
if ($page_id == $opts['new_id']) unset($meta['moves'][$page_id]);
if (empty($meta['moves'])) unset($meta['moves']);
p_set_metadata($id, array('plugin_pagemove' => $meta), false, true);
}
}

p_set_metadata($opts['new_id'], array('plugin_pagemove' => $page_meta), false, true);
}

$event->advise_after();
return true;
}

/**
* Move the old revisions of the page that is specified in the options.
*
* @param array $opts Pagemove options (used here: name, newname, ns, newns)
*/
function moveattic($opts) {
global $conf;

$regex = '\.\d+\.txt(?:\.gz|\.bz2)?';
$this->move_files($conf['olddir'], $opts, $regex);
}

/**
* Move the meta files of the page that is specified in the options.
*
* @param array $opts Pagemove options (used here: name, newname, ns, newns)
*/
function movemeta($opts) {
global $conf;

$regex = '\.[^.]+';
$this->move_files($conf['metadir'], $opts, $regex);
}

/**
* Internal function for moving and renaming meta/attic files between namespaces
*
* @param string $dir The root path of the files (e.g. $conf['metadir'] or $conf['olddir']
* @param array $opts Pagemove options (used here: ns, newns, name, newname)
* @param string $extregex Regular expression for matching the extension of the file that shall be moved
*/
private function move_files($dir, $opts, $extregex) {
$old_path = $dir.'/'.utf8_encodeFN(str_replace(':', '/', $opts['ns'])).'/';
$new_path = $dir.'/'.utf8_encodeFN(str_replace(':', '/', $opts['newns'])).'/';
$regex = '/^'.preg_quote(utf8_encodeFN($opts['name'])).'('.$extregex.')$/u';

$dh = @opendir($old_path);
if($dh) {
while(($file = readdir($dh)) !== false) {
if (substr($file, 0, 1) == '.') continue;
$match = array();
if (is_file($old_path.$file) && preg_match($regex, $file, $match)) {
if (!is_dir($new_path)) io_mkdir_p($new_path);
io_rename($old_path.$file, $new_path.utf8_encodeFN($opts['newname'].$match[1]));
}
}
closedir($dh);
}
}
}
Loading

0 comments on commit f970892

Please sign in to comment.