diff --git a/inc/config.php b/inc/config.php
index ec90df46..1b45da14 100644
--- a/inc/config.php
+++ b/inc/config.php
@@ -999,6 +999,9 @@
// persistent spammers and ban evaders. Again, a little more database load.
$config['ban_cidr'] = true;
+ // Enable the moving of single replies
+ $config['move_replies'] = false;
+
// How often (minimum) to purge the ban list of expired bans (which have been seen). Only works when
// $config['cache'] is enabled and working.
$config['purge_bans'] = 60 * 60 * 12; // 12 hours
diff --git a/inc/display.php b/inc/display.php
index e2d23215..b78639d6 100644
--- a/inc/display.php
+++ b/inc/display.php
@@ -349,9 +349,14 @@ class Post {
if (!empty($this->file) && $this->file != "deleted" && $this->file != null && $this->thumb != 'spoiler' && hasPermission($config['mod']['spoilerimage'], $board['uri'], $this->mod) && $config['spoiler_images'])
$built .= ' ' . secure_link_confirm($config['mod']['link_spoilerimage'], 'Spoiler File', 'Are you sure you want to spoiler this file?', $board['uri'] . '/spoiler/' . $this->id);
+ // Move post
+ if (hasPermission($config['mod']['move'], $board['uri'], $this->mod) && $config['move_replies'])
+ $built .= ' ' . $config['mod']['link_move'] . '';
+
// Edit post
if (hasPermission($config['mod']['editpost'], $board['uri'], $this->mod))
$built .= ' ' . $config['mod']['link_editpost'] . '';
+
if (!empty($built))
$built = '' . $built . '';
diff --git a/inc/mod/pages.php b/inc/mod/pages.php
index 8b0c400e..4f876b45 100644
--- a/inc/mod/pages.php
+++ b/inc/mod/pages.php
@@ -1002,6 +1002,99 @@ function mod_bumplock($board, $unbumplock, $post) {
header('Location: ?/' . sprintf($config['board_path'], $board) . $config['file_index'], true, $config['redirect_http']);
}
+function mod_move_reply($originBoard, $postID) {
+ global $board, $config, $mod;
+
+ if (!openBoard($originBoard))
+ error($config['error']['noboard']);
+
+ if (!hasPermission($config['mod']['move'], $originBoard))
+ error($config['error']['noaccess']);
+
+ $query = prepare(sprintf('SELECT * FROM `posts_%s` WHERE `id` = :id', $originBoard));
+ $query->bindValue(':id', $postID);
+ $query->execute() or error(db_error($query));
+ if (!$post = $query->fetch(PDO::FETCH_ASSOC))
+ error($config['error']['404']);
+
+ if (isset($_POST['board'])) {
+ $targetBoard = $_POST['board'];
+
+ if ($_POST['target_thread']) {
+ $query = prepare(sprintf('SELECT * FROM `posts_%s` WHERE `id` = :id', $targetBoard));
+ $query->bindValue(':id', $_POST['target_thread']);
+ $query->execute() or error(db_error($query)); // If it fails, thread probably does not exist
+ $post['op'] = false;
+ $post['thread'] = $_POST['target_thread'];
+ }
+ else {
+ $post['op'] = true;
+ }
+
+ if ($post['file']) {
+ $post['has_file'] = true;
+ $post['width'] = &$post['filewidth'];
+ $post['height'] = &$post['fileheight'];
+
+ $file_src = sprintf($config['board_path'], $board['uri']) . $config['dir']['img'] . $post['file'];
+ $file_thumb = sprintf($config['board_path'], $board['uri']) . $config['dir']['thumb'] . $post['thumb'];
+ } else {
+ $post['has_file'] = false;
+ }
+
+ // allow thread to keep its same traits (stickied, locked, etc.)
+ $post['mod'] = true;
+
+ if (!openBoard($targetBoard))
+ error($config['error']['noboard']);
+
+ // create the new post
+ $newID = post($post);
+
+ if ($post['has_file']) {
+ // move the image
+ rename($file_src, sprintf($config['board_path'], $board['uri']) . $config['dir']['img'] . $post['file']);
+ rename($file_thumb, sprintf($config['board_path'], $board['uri']) . $config['dir']['thumb'] . $post['thumb']);
+ }
+
+ buildIndex();
+
+ // trigger themes
+ rebuildThemes($post['op'] ? 'post-thread' : 'post');
+ // mod log
+ modLog("Moved post #${postID} to " . sprintf($config['board_abbreviation'], $targetBoard) . " (#${newID})", $originBoard);
+
+ // return to original board
+ openBoard($originBoard);
+
+ // delete original post
+ deletePost($postID);
+ buildIndex();
+
+ // open target board for redirect
+ openBoard($targetBoard);
+
+ // Find new thread on our target board
+ $query = prepare(sprintf('SELECT thread FROM `posts_%s` WHERE `id` = :id', $targetBoard));
+ $query->bindValue(':id', $newID);
+ $query->execute() or error(db_error($query));
+ $post = $query->fetch(PDO::FETCH_ASSOC);
+
+ // redirect
+ header('Location: ?/' . sprintf($config['board_path'], $board['uri']) . $config['dir']['res'] . sprintf($config['file_page'], $post['thread'] ? $post['thread'] : $newID) . '#' . $newID, true, $config['redirect_http']);
+ }
+
+ else {
+ $boards = listBoards();
+
+ $security_token = make_secure_link_token($originBoard . '/move_reply/' . $postID);
+
+ mod_page(_('Move reply'), 'mod/move_reply.html', array('post' => $postID, 'board' => $originBoard, 'boards' => $boards, 'token' => $security_token));
+
+ }
+
+}
+
function mod_move($originBoard, $postID) {
global $board, $config, $mod;
@@ -1115,8 +1208,8 @@ function mod_move($originBoard, $postID) {
if ($post['has_file']) {
// copy image
- $clone($post['file_src'], sprintf($config['board_path'], $board['uri']) . $config['dir']['img'] . $post['file']);
- $clone($post['file_thumb'], sprintf($config['board_path'], $board['uri']) . $config['dir']['thumb'] . $post['thumb']);
+ copy($post['file_src'], sprintf($config['board_path'], $board['uri']) . $config['dir']['img'] . $post['file']);
+ copy($post['file_thumb'], sprintf($config['board_path'], $board['uri']) . $config['dir']['thumb'] . $post['thumb']);
}
foreach ($post['tracked_cites'] as $cite) {
diff --git a/mod.php b/mod.php
index 0e98c4eb..f1ce6d04 100644
--- a/mod.php
+++ b/mod.php
@@ -65,6 +65,7 @@ $pages = array(
'/ban' => 'secure_POST ban', // new ban
'/(\%b)/ban(&delete)?/(\d+)' => 'secure_POST ban_post', // ban poster
'/(\%b)/move/(\d+)' => 'secure_POST move', // move thread
+ '/(\%b)/move_reply/(\d+)' => 'secure_POST move_reply', // move reply
'/(\%b)/edit(_raw)?/(\d+)' => 'secure_POST edit_post', // edit post
'/(\%b)/delete/(\d+)' => 'secure delete', // delete post
'/(\%b)/deletefile/(\d+)' => 'secure deletefile', // delete file from post