diff --git a/inc/functions.php b/inc/functions.php index 7e33a9c5..63b33869 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -2666,30 +2666,20 @@ function buildThread($id, $return = false, $mod = false, $shadow = false) { if ($action == 'rebuild' || $return || $mod) { $query = prepare( sprintf("SELECT ``posts_%s``.*, 0 AS `shadow` FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id", $board['uri'], $board['uri']) . - ($shadow?" UNION ALL " . sprintf("SELECT ``shadow_posts_%s``.*, 1 AS `shadow` FROM ``shadow_posts_%s`` WHERE `thread` = :id", $board['uri'], $board['uri']):"") . + ($shadow?" UNION ALL " . sprintf("SELECT ``shadow_posts_%s``.*, 1 AS `shadow` FROM ``shadow_posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id", $board['uri'], $board['uri']):"") . " ORDER BY `thread`,`id`"); $query->bindValue(':id', $id, PDO::PARAM_INT); $query->execute() or error(db_error($query)); while ($post = $query->fetch(PDO::FETCH_ASSOC)) { // Fix Filenames if shadow copy - if($post['shadow'] && $post['files']) { - $files_new = array(); - // Move files to temp storage - foreach (json_decode($post['files']) as $i => $f) { - if ($f->file !== 'deleted') { - // Add file to array of all files - $f->file = ShadowDelete::hashShadowDelFilename($f->file); - $f->thumb = ShadowDelete::hashShadowDelFilename($f->thumb); - $files_new[] = $f; - } - } - $post['files'] = json_encode($files_new); - } + if($post['shadow'] && $post['files']) + $post['files'] = Shadowdelete::hashShadowDelFilenamesDBJSON($post['files']); if (!isset($thread)) { $thread = new Thread($post, $mod ? '?/' : $config['root'], $mod); } else { + $post['no_shadow_restore'] = true; $thread->add(new Post($post, $mod ? '?/' : $config['root'], $mod)); } } diff --git a/inc/mod/pages.php b/inc/mod/pages.php index 97f30fd0..2186a13c 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -2128,6 +2128,85 @@ function mod_edit_post($board, $edit_raw_html, $postID) { +function mod_recent_shadow_posts($lim) { + global $config, $mod, $pdo; + + if (!hasPermission($config['mod']['view_shadow_posts'])) + error($config['error']['noaccess']); + + $limit = (is_numeric($lim))? $lim : 25; + $last_time = (isset($_GET['last']) && is_numeric($_GET['last'])) ? $_GET['last'] : 0; + + $mod_boards = array(); + $boards = listBoards(); + + //if not all boards + if ($mod['boards'][0]!='*') { + foreach ($boards as $board) { + if (in_array($board['uri'], $mod['boards'])) + $mod_boards[] = $board; + } + } else { + $mod_boards = $boards; + } + + // Manually build an SQL query + $query = 'SELECT * FROM ('; + foreach ($mod_boards as $board) { + $query .= sprintf('SELECT *, %s AS `board`, 1 AS `shadow` FROM ``shadow_posts_%s`` UNION ALL ', $pdo->quote($board['uri']), $board['uri']); + } + // Remove the last "UNION ALL" seperator and complete the query + $query = preg_replace('/UNION ALL $/', ') AS `all_posts` WHERE (`time` < :last_time OR NOT :last_time) ORDER BY `thread` IS NULL DESC, `time` DESC LIMIT ' . $limit, $query); + $query = prepare($query); + $query->bindValue(':last_time', $last_time); + $query->execute() or error(db_error($query)); + $posts = $query->fetchAll(PDO::FETCH_ASSOC); + + // List of threads + $thread_ids = array(); + // List of posts in thread + $posts_in_thread_ids = array(); + + foreach ($posts as $key => &$post) { + openBoard($post['board']); + + // Fix Filenames if shadow copy + if($post['shadow'] && $post['files']) + $post['files'] = Shadowdelete::hashShadowDelFilenamesDBJSON($post['files']); + + if (!$post['thread']) { + // Still need to fix this: + $po = new Thread($post, '?/', $mod, false); + $post['built'] = $po->build(true); + + // Add to list of threads + $thread_ids[] = $post['id']; + } else { + // If post belong to deleted thread don't list it + if(in_array($post['thread'], $thread_ids)) { + $posts_in_thread_ids[] = $key; + } else { + $po = new Post($post, '?/', $mod); + $post['built'] = $po->build(true); + } + } + $last_time = $post['time']; + } + + foreach($posts_in_thread_ids as $id) + unset($posts[$id]); + + echo mod_page(_('Shadow Deleted Posts'), 'mod/shadow_recent_posts.html', array( + 'posts' => $posts, + 'limit' => $limit, + 'last_time' => $last_time + ) + ); + +} + + + function mod_shadow_restore_post($board, $post) { global $config, $mod; @@ -2145,8 +2224,6 @@ function mod_shadow_restore_post($board, $post) { modLog("Restored Shadow Deleted post #{$post}"); // Rebuild board buildIndex(); - // Rebuild thread - buildThread($thread_id ? $thread_id : $post); // Rebuild themes rebuildThemes('post-delete', $board); @@ -2155,8 +2232,40 @@ function mod_shadow_restore_post($board, $post) { // If we got a thread id number as response reload to thread header('Location: ?/' . sprintf($config['board_path'], $board) . $config['dir']['res'] . sprintf($config['file_page'], $thread_id), true, $config['redirect_http']); } else { - // Reload to board index - header('Location: ?/' . sprintf($config['board_path'], $board) . $config['file_index'], true, $config['redirect_http']); + // We restored a thread so we reload to it + header('Location: ?/' . sprintf($config['board_path'], $board) . $config['dir']['res'] . sprintf($config['file_page'], $post), true, $config['redirect_http']); + } +} + + + + +function mod_view_shadow_thread($board, $thread_id) { + global $config, $mod; + + if (!openBoard($board)) + error($config['error']['noboard']); + + if (!hasPermission($config['mod']['restore_shadow_post'], $board)) + error($config['error']['noaccess']); + + // Restore Post + $thread_id = ShadowDelete::restorePost($post); + + // Record the action + modLog("Restored Shadow Deleted post #{$post}"); + // Rebuild board + buildIndex(); + // Rebuild themes + rebuildThemes('post-delete', $board); + + // Redirect + if($thread_id !== true) { + // If we got a thread id number as response reload to thread + header('Location: ?/' . sprintf($config['board_path'], $board) . $config['dir']['res'] . sprintf($config['file_page'], $thread_id), true, $config['redirect_http']); + } else { + // We restored a thread so we reload to it + header('Location: ?/' . sprintf($config['board_path'], $board) . $config['dir']['res'] . sprintf($config['file_page'], $post), true, $config['redirect_http']); } } @@ -2164,6 +2273,7 @@ function mod_shadow_restore_post($board, $post) { + function mod_shadow_delete_post($board, $post) { global $config, $mod; diff --git a/inc/shadow-delete.php b/inc/shadow-delete.php index a5e744ee..db8ca4f4 100644 --- a/inc/shadow-delete.php +++ b/inc/shadow-delete.php @@ -9,6 +9,21 @@ class ShadowDelete { return sha1($file['filename'] . $config['shadow_del']['filename_seed']) . "." . $file['extension']; } + static public function hashShadowDelFilenamesDBJSON($files_db_json) + { + // Fix Filenames if shadow copy + $files_new = array(); + foreach (json_decode($files_db_json) as $i => $f) { + if ($f->file !== 'deleted') { + // Add file to array of all files + $f->file = self::hashShadowDelFilename($f->file); + $f->thumb = self::hashShadowDelFilename($f->thumb); + $files_new[] = $f; + } + } + return json_encode($files_new); + } + // Delete a post (reply or thread) static public function deletePost($id, $error_if_doesnt_exist=true, $rebuild_after=true) { global $board, $config; diff --git a/mod.php b/mod.php index 6630f9b1..712cf527 100644 --- a/mod.php +++ b/mod.php @@ -79,6 +79,8 @@ $pages = array( '/(\%b)/archive/' => 'secure_POST view_archive', // view archive '/(\%b)/featured/' => 'secure_POST view_archive_featured', // view featured archive + '/shadow_recent_post/(\d+)' => 'recent_shadow_posts', // view recent posts shadow deleted + '/(\%b)/shadow_view/(\d+)' => 'view_shadow_thread', // view shadow deleted thread '/(\%b)/shadow_restore/(\d+)' => 'secure_POST shadow_restore_post', // restore shadow deleted post '/(\%b)/shadow_delete/(\d+)' => 'secure_POST shadow_delete_post', // permanent delete shadow deleted post '/(\%b)/shadow_purge/(\d+)' => 'secure_POST shadow_purge', // permanent delete all shadow deleted post that have timed out diff --git a/stylesheets/style.css b/stylesheets/style.css index 4319d804..c0ba8237 100644 --- a/stylesheets/style.css +++ b/stylesheets/style.css @@ -377,6 +377,12 @@ span.spoiler { padding: 0 1px; } +.shadow-thread { + filter: grayscale(20%) !important; + opacity: 0.7 !important; /* Real browsers */ + filter: alpha(opacity = 70) !important; /* MSIE */ +} + .shadow-post { filter: grayscale(80%) !important; opacity: 0.6 !important; /* Real browsers */ diff --git a/templates/mod/dashboard.html b/templates/mod/dashboard.html index 88199e64..7f17fe7c 100644 --- a/templates/mod/dashboard.html +++ b/templates/mod/dashboard.html @@ -109,9 +109,12 @@ {% if mod|hasPermission(config.mod.edit_pages) %}
  • {% trans 'Global static pages' %}
  • {% endif %} - {% if mod|hasPermission(config.mod.recent) %} + {% if mod|hasPermission(config.mod.view_shadow_posts) %}
  • {% trans 'Recent posts' %}
  • {% endif %} + {% if mod|hasPermission(config.mod.recent) %} +
  • {% trans 'Recent shadow deleted posts' %}
  • + {% endif %} {% if mod|hasPermission(config.mod.rebuild) %}
  • {% trans 'Rebuild' %}
  • {% endif %} diff --git a/templates/mod/shadow_recent_posts.html b/templates/mod/shadow_recent_posts.html new file mode 100644 index 00000000..b7db0213 --- /dev/null +++ b/templates/mod/shadow_recent_posts.html @@ -0,0 +1,19 @@ + +{% if not posts|count %} +

    ({% trans 'There are no active posts.' %})

    +{% else %} +

    Viewing last {{ limit|e }} posts

    +

    View 25 | 50 | 100

    + Erase local data + {% for post in posts %} + {% if not post.thread %} + {% set thread = post.id %} + {% else %} + {% set thread = post.thread %} + {% endif %} +

    /{{ post.board }}/{{ post.id }}
    + {{ post.built }} +
    + {% endfor %} +{% endif %} +Next {{ limit }} posts diff --git a/templates/post/post_controls.html b/templates/post/post_controls.html index d44b6859..472bb403 100644 --- a/templates/post/post_controls.html +++ b/templates/post/post_controls.html @@ -69,11 +69,13 @@ {{ config.mod.link_editpost }}  {% endif %} {% else %} - {% if mod|hasPermission(config.mod.restore_shadow_post, board.uri) %} - {{ config.mod.link_sd_restore }}  - {% endif %} - {% if mod|hasPermission(config.mod.delete_shadow_post, board.uri) %} - {{ config.mod.link_sd_delete }}  + {% if not post.no_shadow_restore %} + {% if mod|hasPermission(config.mod.restore_shadow_post, board.uri) %} + {{ config.mod.link_shadow_restore }}  + {% endif %} + {% if mod|hasPermission(config.mod.delete_shadow_post, board.uri) %} + {{ config.mod.link_shadow_delete }}  + {% endif %} {% endif %} {% endif %} diff --git a/templates/post_reply.html b/templates/post_reply.html index c4521199..1cfe1305 100644 --- a/templates/post_reply.html +++ b/templates/post_reply.html @@ -3,7 +3,7 @@

    {% if not index %}{% endif %} - + {% if not post.shadow %}{% endif %}

    +
    {% if not index %}{% endif %} {% include 'post/fileinfo.html' %}
    1%}style='clear:both'{%endif%}>

    - + {% if not post.shadow %}{% endif %}