Browse Source

Update to Shadow Delete - Option to view deleted threads, and view recent shadow deleted posts and threads (threads are listed first - posts in these threads are not shown in recent list - to see these posts you need to view whole shadow deleted thread)

main
PupperWoff 7 years ago
committed by discomrade
parent
commit
45b00c76b5
  1. 18
      inc/functions.php
  2. 118
      inc/mod/pages.php
  3. 15
      inc/shadow-delete.php
  4. 2
      mod.php
  5. 6
      stylesheets/style.css
  6. 5
      templates/mod/dashboard.html
  7. 19
      templates/mod/shadow_recent_posts.html
  8. 12
      templates/post/post_controls.html
  9. 2
      templates/post_reply.html
  10. 19
      templates/post_thread.html

18
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));
}
}

118
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;

15
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;

2
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

6
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 */

5
templates/mod/dashboard.html

@ -109,9 +109,12 @@
{% if mod|hasPermission(config.mod.edit_pages) %}
<li><a href="?/edit_pages">{% trans 'Global static pages' %}</a></li>
{% endif %}
{% if mod|hasPermission(config.mod.recent) %}
{% if mod|hasPermission(config.mod.view_shadow_posts) %}
<li><a href="?/recent/25">{% trans 'Recent posts' %}</a></li>
{% endif %}
{% if mod|hasPermission(config.mod.recent) %}
<li><a href="?/shadow_recent_post/25">{% trans 'Recent shadow deleted posts' %}</a></li>
{% endif %}
{% if mod|hasPermission(config.mod.rebuild) %}
<li><a href="?/rebuild">{% trans 'Rebuild' %}</a></li>
{% endif %}

19
templates/mod/shadow_recent_posts.html

@ -0,0 +1,19 @@
<script src="{{ config.additional_javascript_url }}js/mod/recent_posts.js"></script>
{% if not posts|count %}
<p style="text-align:center" class="unimportant">({% trans 'There are no active posts.' %})</p>
{% else %}
<h4>Viewing last {{ limit|e }} posts</h4>
<p>View <a href="?/shadow_list_recent_post/25"> 25 </a>|<a href="?/shadow_list_recent_post/50"> 50 </a>|<a href="?/shadow_list_recent_post/100"> 100 </a></p>
<a href="javascript:void(0)" id="erase-local-data" style="float:right; clear:both">Erase local data</a></div>
{% for post in posts %}
{% if not post.thread %}
{% set thread = post.id %}
{% else %}
{% set thread = post.thread %}
{% endif %}
<div class="post-wrapper" data-board="{{ post.board }}"><hr/><a class="eita-link" id="eita-{{ post.board }}-{{ thread }}" href="?/{{ post.board }}/{{ config.dir.res }}{{ thread }}.html#{{ post.id }}">/{{ post.board }}/{{ post.id }}</a><br>
{{ post.built }}
</div>
{% endfor %}
{% endif %}
<a href="/mod.php?/shadow_list_recent_post/{{ limit|e }}&amp;last={{ last_time|e }}">Next {{ limit }} posts</a>

12
templates/post/post_controls.html

@ -69,11 +69,13 @@
<a title="{% trans %}Edit post{% endtrans %}" href="?/{{ board.dir }}edit{% if config.mod.raw_html_default %}_raw{% endif %}/{{ post.id }}">{{ config.mod.link_editpost }}</a>&nbsp;
{% endif %}
{% else %}
{% if mod|hasPermission(config.mod.restore_shadow_post, board.uri) %}
<a title="{% trans %}Restore Shadow Deleted Post{% endtrans %}" href="?/{{ board.dir }}sd_restore/{{ post.id }}">{{ config.mod.link_sd_restore }}</a>&nbsp;
{% endif %}
{% if mod|hasPermission(config.mod.delete_shadow_post, board.uri) %}
<a title="{% trans %}Permanent Delete Shadow Deleted Post{% endtrans %}" href="?/{{ board.dir }}sd_delete/{{ post.id }}">{{ config.mod.link_sd_delete }}</a>&nbsp;
{% if not post.no_shadow_restore %}
{% if mod|hasPermission(config.mod.restore_shadow_post, board.uri) %}
<a title="{% trans %}Restore shadow deleted post{% endtrans %}" href="?/{{ board.dir }}shadow_restore/{{ post.id }}">{{ config.mod.link_shadow_restore }}</a>&nbsp;
{% endif %}
{% if mod|hasPermission(config.mod.delete_shadow_post, board.uri) %}
<a title="{% trans %}Permanent delete shadow deleted post{% endtrans %}" href="?/{{ board.dir }}shadow_delete/{{ post.id }}">{{ config.mod.link_shadow_delete }}</a>&nbsp;
{% endif %}
{% endif %}
{% endif %}
</span>

2
templates/post_reply.html

@ -3,7 +3,7 @@
<div class="post reply {% if post.shadow %}shadow-post{% endif %}" id="reply_{{ post.id }}">
<p class="intro">
{% if not index %}<a id="{{ post.id }}" class="post_anchor"></a>{% endif %}
<input type="checkbox" class="delete" name="delete_{{ post.id }}" id="delete_{{ post.id }}" />
{% if not post.shadow %}<input type="checkbox" class="delete" name="delete_{{ post.id }}" id="delete_{{ post.id }}" />{% endif %}
<label for="delete_{{ post.id }}">
{% include 'post/subject.html' %}
{% include 'post/name.html' %}

19
templates/post_thread.html

@ -1,12 +1,12 @@
{% filter remove_whitespace %}
{# tabs and new lines will be ignored #}
<div class="thread" id="thread_{{ post.id }}" data-board="{{ board.uri }}">
<div class="thread {% if post.shadow %}shadow-thread{% endif %}" id="thread_{{ post.id }}" data-board="{{ board.uri }}">
{% if not index %}<a id="{{ post.id }}" class="post_anchor"></a>{% endif %}
{% include 'post/fileinfo.html' %}
<div class="post op" id="op_{{ post.id }}" {%if post.num_files > 1%}style='clear:both'{%endif%}><p class="intro">
<input type="checkbox" class="delete" name="delete_{{ post.id }}" id="delete_{{ post.id }}" />
{% if not post.shadow %}<input type="checkbox" class="delete" name="delete_{{ post.id }}" id="delete_{{ post.id }}" />{% endif %}
<label for="delete_{{ post.id }}">
{% include 'post/subject.html' %}
{% include 'post/name.html' %}
@ -48,13 +48,16 @@
{% if index %}
<a href="{{ post.root }}{{ board.dir }}{{ config.dir.res }}{{ link_for(post) }}">[{% trans %}Reply{% endtrans %}]</a>
{% endif %}
{% if isnoko50 %}
<a href="{{ post.root }}{{ board.dir }}{{ config.dir.res }}{{ link_for(post) }}">[{% trans %}View All{% endtrans %}]</a>
{% endif %}
{% if hasnoko50 and not isnoko50 %}
{% set lastcount = config.noko50_count %}
<a href="{{ post.root }}{{ board.dir }}{{ config.dir.res }}{{ link_for(post, true) }}">[{% trans %}Last 1 Post{% plural lastcount %}Last {{ count }} Posts{% endtrans %}]</a>
{% if not post.shadow %}
{% if isnoko50 %}
<a href="{{ post.root }}{{ board.dir }}{{ config.dir.res }}{{ link_for(post) }}">[{% trans %}View All{% endtrans %}]</a>
{% endif %}
{% if hasnoko50 and not isnoko50 %}
{% set lastcount = config.noko50_count %}
<a href="{{ post.root }}{{ board.dir }}{{ config.dir.res }}{{ link_for(post, true) }}">[{% trans %}Last 1 Post{% plural lastcount %}Last {{ count }} Posts{% endtrans %}]</a>
{% endif %}
{% endif %}
{% include 'post/post_controls.html' %}
</p>
<div class="body">

Loading…
Cancel
Save