Browse Source

Added Feature - Added Archive Feature - Archive and Featured Archive

main
PupperWoff 7 years ago
committed by discomrade
parent
commit
0ba32d3b1a
  1. 85
      ARCHIVE_UPDATE_SCRIPT.php
  2. 1
      composer.json
  3. 291
      inc/archive.php
  4. 23
      inc/config.php
  5. 48
      inc/functions.php
  6. 82
      inc/mod/pages.php
  7. 2
      mod.php
  8. 93
      stylesheets/style.css
  9. 10
      templates/archive.sql
  10. 37
      templates/mod/archive_featured_list.html
  11. 44
      templates/mod/archive_list.html
  12. 5
      tools/rebuild.php

85
ARCHIVE_UPDATE_SCRIPT.php

@ -0,0 +1,85 @@
<?php
// require 'inc/config.php';
// require 'inc/config_instance.php';
require 'inc/functions.php';
global $config;
// Check so only ADMIN can run script
check_login(true);
if (!$mod || $mod['type'] != ADMIN)
die("You need to be logged in as admin");
$page['title'] = 'Updating Database - Archiving of Threads';
$step = isset($_GET['step']) ? round($_GET['step']) : 0;
switch($step)
{
default:
case 0:
$page['body'] = '<p style="text-align:center">You are about to update the database to allow archiving of threads.</p>';
$page['body'] .= '<p style="text-align:center"><a href="?step=2">Click here to update database entries.</a></p>';
break;
case 2:
$page['body'] = '<p style="text-align:center">Database have been updated.</p>';
$sql_errors = "";
// Update posts_* table to archive function
// Get list of boards
$boards = listBoards();
foreach ($boards as &$_board) {
$query = Element('archive.sql', array('board' => $_board['uri']));
if (mysql_version() < 50503)
$query = preg_replace('/(CHARSET=|CHARACTER SET )utf8mb4/', '$1utf8', $query);
query($query) or error(db_error());
// Create Archive Folders
if (!file_exists($_board['dir'] . $config['dir']['archive']))
@mkdir($_board['dir'] . $config['dir']['archive'], 0777)
or error("Couldn't create " . $_board['dir'] . $config['dir']['archive'] . ". Check permissions.", true);
if (!file_exists($_board['dir'] . $config['dir']['archive'] . $config['dir']['img']))
@mkdir($_board['dir'] . $config['dir']['archive'] . $config['dir']['img'], 0777)
or error("Couldn't create " . $_board['dir'] . $config['dir']['archive'] . $config['dir']['img'] . ". Check permissions.", true);
if (!file_exists($_board['dir'] . $config['dir']['archive'] . $config['dir']['thumb']))
@mkdir($_board['dir'] . $config['dir']['archive'] . $config['dir']['thumb'], 0777)
or error("Couldn't create " . $_board['dir'] . $config['dir']['archive'] . $config['dir']['img'] . ". Check permissions.", true);
if (!file_exists($_board['dir'] . $config['dir']['archive'] . $config['dir']['res']))
@mkdir($_board['dir'] . $config['dir']['archive'] . $config['dir']['res'], 0777)
or error("Couldn't create " . $_board['dir'] . $config['dir']['archive'] . $config['dir']['img'] . ". Check permissions.", true);
// Create Featured threads Folders
if (!file_exists($_board['dir'] . $config['dir']['featured']))
@mkdir($_board['dir'] . $config['dir']['featured'], 0777)
or error("Couldn't create " . $_board['dir'] . $config['dir']['featured'] . ". Check permissions.", true);
if (!file_exists($_board['dir'] . $config['dir']['featured'] . $config['dir']['img']))
@mkdir($_board['dir'] . $config['dir']['featured'] . $config['dir']['img'], 0777)
or error("Couldn't create " . $_board['dir'] . $config['dir']['featured'] . $config['dir']['img'] . ". Check permissions.", true);
if (!file_exists($_board['dir'] . $config['dir']['featured'] . $config['dir']['thumb']))
@mkdir($_board['dir'] . $config['dir']['featured'] . $config['dir']['thumb'], 0777)
or error("Couldn't create " . $_board['dir'] . $config['dir']['featured'] . $config['dir']['img'] . ". Check permissions.", true);
if (!file_exists($_board['dir'] . $config['dir']['featured'] . $config['dir']['res']))
@mkdir($_board['dir'] . $config['dir']['featured'] . $config['dir']['res'], 0777)
or error("Couldn't create " . $_board['dir'] . $config['dir']['featured'] . $config['dir']['img'] . ". Check permissions.", true);
}
if (!empty($sql_errors))
$page['body'] .= '<div class="ban"><h2>SQL errors</h2><p>SQL errors were encountered when trying to update the database and hashing ip addresses.</p><p>The errors encountered were:</p><ul>' . $sql_errors . '</ul></div>';
break;
}
echo Element('page.html', $page);
?>
<!-- There is probably a much better way to do this, but eh. -->
<link rel="stylesheet" type="text/css" href="stylesheets/style.css" />

1
composer.json

@ -27,6 +27,7 @@
"inc/queue.php",
"inc/polyfill.php",
"inc/announcements.php",
"inc/archive.php",
"inc/functions.php"
]
},

291
inc/archive.php

@ -0,0 +1,291 @@
<?php
class Archive {
// Archive thread and replies
static public function archiveThread($thread_id) {
global $config, $board;
// If archiving is turned off return
if(!$config['archive']['threads'])
return;
// Check if it is a thread
$thread_query = prepare(sprintf("SELECT `thread`, `subject`, `body_nomarkup` FROM ``posts_%s`` WHERE `id` = :id", $board['uri']));
$thread_query->bindValue(':id', $thread_id, PDO::PARAM_INT);
$thread_query->execute() or error(db_error($thread_query));
$thread_data = $thread_query->fetch(PDO::FETCH_ASSOC);
if($thread_data['thread'] !== NULL)
error($config['error']['invalidpost']);
// Create Snippet of thread text
$thread_data['snippet_body'] = strtok($thread_data['body_nomarkup'], "\r\n");
$thread_data['snippet_body'] = substr($thread_data['snippet_body'], 0, $config['archive']['snippet_len'] - strlen($thread_data['subject']));
archive_list_markup($thread_data['snippet_body']);
$thread_data['snippet'] = '<b>' . $thread_data['subject'] . '</b> ';
$thread_data['snippet'] .= $thread_data['snippet_body'];
// Select thread and replies in one query
$query = prepare(sprintf("SELECT `id`,`thread`,`files`,`slug` FROM ``posts_%s`` WHERE `id` = :id OR `thread` = :id", $board['uri']));
$query->bindValue(':id', $thread_id, PDO::PARAM_INT);
$query->execute() or error(db_error($query));
// List of files associated with thread
$file_list = array();
while ($post = $query->fetch(PDO::FETCH_ASSOC)) {
// Copy Static HTML page for Thread
if (!$post['thread']) {
// Read Content of HTML
$thread_file_content = @file_get_contents($board['dir'] . $config['dir']['res'] . link_for($post));
// Replace links and posting mode to Archived
$thread_file_content = str_replace(sprintf('src="/' . $config['board_path'], $board['uri']), sprintf('src="/' . $config['board_path'] . $config['dir']['archive'], $board['uri']), $thread_file_content);
$thread_file_content = str_replace(sprintf('href="/' . $config['board_path'], $board['uri']), sprintf('href="/' . $config['board_path'] . $config['dir']['archive'], $board['uri']), $thread_file_content);
$thread_file_content = str_replace('Posting mode: Reply', 'Archived thread', $thread_file_content);
// Remove Post Form from HTML (First Form)
$thread_file_content = preg_replace("/<form name=\"post\"(.*?)<\/form>/i", "", $thread_file_content);
// Remove Form from HTML
$thread_file_content = preg_replace("/<form(.*?)>/i", "", $thread_file_content);
$thread_file_content = preg_replace("/<\/form>/i", "", $thread_file_content);
$thread_file_content = preg_replace("/<input (.*?)>/i", "", $thread_file_content);
// Remove Redundant code from HTML
$thread_file_content = preg_replace("/<div id=\"report\-fields\"(.*?)<\/div>/i", "", $thread_file_content);
$thread_file_content = preg_replace("/<div id=\"thread\-interactions\"(.*?)<\/div>/i", "", $thread_file_content);
// Write altered thread HTML to archive location
@file_put_contents($board['dir'] . $config['dir']['archive'] . $config['dir']['res'] . sprintf($config['file_page'], $thread_id), $thread_file_content, LOCK_EX);
}
// Copy Images and Files Associated with Thread
if ($post['files']) {
foreach (json_decode($post['files']) as $i => $f) {
if ($f->file !== 'deleted') {
@copy($board['dir'] . $config['dir']['img'] . $f->file, $board['dir'] . $config['dir']['archive'] . $config['dir']['img'] . $f->file);
@copy($board['dir'] . $config['dir']['thumb'] . $f->thumb, $board['dir'] . $config['dir']['archive'] . $config['dir']['thumb'] . $f->thumb);
$file_list[] = $f;
// $file_list[$i]['file'] = $f->file;
// $file_list[$i]['thumb'] = $f->thumb;
}
}
}
}
// Insert Archive Data in Database
$query = prepare(sprintf("INSERT INTO ``archive_%s`` VALUES (:thread_id, :snippet, :time, :files, 0)", $board['uri']));
$query->bindValue(':thread_id', $thread_id, PDO::PARAM_INT);
$query->bindValue(':snippet', $thread_data['snippet'], PDO::PARAM_STR);
$query->bindValue(':time', (!$config['archive']['lifetime'])?0:strtotime("+".$config['archive']['lifetime']." days"), PDO::PARAM_INT);
$query->bindValue(':files', json_encode($file_list));
$query->execute() or error(db_error($query));
// Purge Threads that have timed out
self::purgeArchive();
// Rebuild Archive Index
self::buildArchiveIndex();
return true;
}
// Removes Archived Threads that has outlived their lifetime
static public function purgeArchive() {
global $config, $board;
// Delete all static pages and files for archived threads that has timed out
$query = query(sprintf("SELECT `id`, `files` FROM ``archive_%s`` WHERE `lifetime` < %d AND `featured` = 0", $board['uri'], time())) or error(db_error());
while($thread = $query->fetch(PDO::FETCH_ASSOC)) {
// Delete Files
foreach (json_decode($thread['files']) as $f) {
@unlink($board['dir'] . $config['dir']['archive'] . $config['dir']['img'] . $f->file);
@unlink($board['dir'] . $config['dir']['archive'] . $config['dir']['img'] . $f->thumb);
}
// Delete Thread
@unlink($board['dir'] . $config['dir']['archive'] . $config['dir']['res'] . sprintf($config['file_page'], $thread['id']));
}
// Delete Archive Entries
if($query->rowCount() != 0)
$query = query(sprintf("DELETE FROM ``archive_%s`` WHERE `lifetime` < %d AND `featured` = 0", $board['uri'], time())) or error(db_error());
return $query->rowCount();
}
// Feature thread and replies
static public function featureThread($thread_id) {
global $config, $board;
// If featuring of threads is turned off return
if(!$config['feature']['threads'])
return;
$query = query(sprintf("SELECT `id`, `files` FROM ``archive_%s`` WHERE `featured` = 0", $board['uri'])) or error(db_error());
if(!$thread = $query->fetch(PDO::FETCH_ASSOC))
error($config['error']['invalidpost']);
// Read Content of HTML
$thread_file_content = @file_get_contents($board['dir'] . $config['dir']['archive'] . $config['dir']['res'] . sprintf($config['file_page'], $thread_id));
// Replace links and posting mode to Archived
$thread_file_content = str_replace(sprintf('src="/' . $config['board_path'] . $config['dir']['archive'], $board['uri']), sprintf('src="/' . $config['board_path'] . $config['dir']['featured'], $board['uri']), $thread_file_content);
$thread_file_content = str_replace(sprintf('href="/' . $config['board_path'] . $config['dir']['archive'], $board['uri']), sprintf('href="/' . $config['board_path'] . $config['dir']['featured'], $board['uri']), $thread_file_content);
$thread_file_content = str_replace('Archived thread', 'Featured thread', $thread_file_content);
// Write altered thread HTML to archive location
@file_put_contents($board['dir'] . $config['dir']['featured'] . $config['dir']['res'] . sprintf($config['file_page'], $thread_id), $thread_file_content, LOCK_EX);
foreach (json_decode($thread['files']) as $f) {
@copy($board['dir'] . $config['dir']['archive'] . $config['dir']['img'] . $f->file, $board['dir'] . $config['dir']['featured'] . $config['dir']['img'] . $f->file);
@copy($board['dir'] . $config['dir']['archive'] . $config['dir']['thumb'] . $f->thumb, $board['dir'] . $config['dir']['featured'] . $config['dir']['thumb'] . $f->thumb);
}
// Update DB entry
query(sprintf("UPDATE ``archive_%s`` SET `featured` = 1 WHERE `id` = %d", $board['uri'], (int)$thread_id)) or error(db_error());
// Rebuild Featured Index
self::buildFeaturedIndex();
return true;
}
static public function deleteFeatured($thread_id) {
global $config, $board;
$query = query(sprintf("SELECT `id`, `files`, `lifetime` FROM ``archive_%s`` WHERE `featured` = 1", $board['uri'])) or error(db_error());
if(!$thread = $query->fetch(PDO::FETCH_ASSOC))
error($config['error']['invalidpost']);
// Delete Files
foreach (json_decode($thread['files']) as $f) {
@unlink($board['dir'] . $config['dir']['featured'] . $config['dir']['img'] . $f->file);
@unlink($board['dir'] . $config['dir']['featured'] . $config['dir']['img'] . $f->thumb);
}
// Delete Thread
@unlink($board['dir'] . $config['dir']['featured'] . $config['dir']['res'] . sprintf($config['file_page'], $thread_id));
// Delete Entry in DB if it has timed out
if($thread['lifetime'] != 0 && $thread['lifetime'] < time())
query(sprintf("DELETE FROM ``archive_%s`` WHERE `id` = %d", $board['uri'], (int)$thread_id)) or error(db_error());
else
query(sprintf("UPDATE ``archive_%s`` SET `featured` = 0 WHERE `id` = %d", $board['uri'], (int)$thread_id)) or error(db_error());
// Rebuild Featured Index
self::buildFeaturedIndex();
}
static public function RebuildArchiveIndexes() {
global $config;
// If archiving is turned off return
if(!$config['archive']['threads'])
return;
// Purge Archive
self::purgeArchive();
// Rebuild Archive Index
self::buildArchiveIndex();
// Rebuild Featured Index
self::buildFeaturedIndex();
}
static public function buildArchiveIndex() {
global $config, $board;
// If archiving is turned off return
if(!$config['archive']['threads'])
return;
$query = query(sprintf("SELECT `id`, `snippet`, `featured` FROM ``archive_%s`` WHERE `lifetime` > %d ORDER BY `lifetime` DESC", $board['uri'], time())) or error(db_error());
$archive = $query->fetchAll(PDO::FETCH_ASSOC);
foreach($archive as &$thread)
$thread['archived_url'] = $config['dir']['res'] . sprintf($config['file_page'], $thread['id']);
$title = sprintf(_('Archived') . ' %s: ' . $config['board_abbreviation'], _('threads'), $board['uri']);
$archive_page = Element('page.html', array(
'config' => $config,
'mod' => false,
'hide_dashboard_link' => true,
'boardlist' => createBoardList(false),
'title' => $title,
'subtitle' => "",
'boardlist' => createBoardlist(),
'body' => Element("mod/archive_list.html", array(
'config' => $config,
'thread_count' => $query->rowCount(),
'archive' => $archive
))
));
file_write($config['dir']['home'] . $board['dir'] . $config['dir']['archive'] . $config['file_index'], $archive_page);
}
static public function buildFeaturedIndex() {
global $config, $board;
// If featuring of threads is turned off return
if(!$config['feature']['threads'])
return;
$query = query(sprintf("SELECT `id`, `snippet`, `featured` FROM ``archive_%s`` WHERE `featured` = 1 ORDER BY `lifetime` DESC", $board['uri'])) or error(db_error());
$archive = $query->fetchAll(PDO::FETCH_ASSOC);
foreach($archive as &$thread)
$thread['featured_url'] = $config['dir']['res'] . sprintf($config['file_page'], $thread['id']);
$title = sprintf(_('Featured') . ' %s: ' . $config['board_abbreviation'], _('threads'), $board['uri']);
$archive_page = Element('page.html', array(
'config' => $config,
'mod' => false,
'hide_dashboard_link' => true,
'boardlist' => createBoardList(false),
'title' => $title,
'subtitle' => "",
'boardlist' => createBoardlist(),
'body' => Element("mod/archive_featured_list.html", array(
'config' => $config,
'archive' => $archive
))
));
file_write($config['dir']['home'] . $board['dir'] . $config['dir']['featured'] . $config['file_index'], $archive_page);
}
}
?>

23
inc/config.php

@ -1300,6 +1300,25 @@
$config['dir']['thumb'] = 'thumb/';
$config['dir']['res'] = 'res/';
// Directory for archived threads
$config['dir']['archive'] = 'archive/';
// Directory for "Featured Threads" (threads makred for permanent storage)
$config['dir']['featured'] = 'featured/';
// Indicate if threads should be archived
$config['archive']['threads'] = true;
// Indicate if it is possible to mark threads as featured (stored forever)
$config['feature']['threads'] = true;
// Days to keep archived threads before deletion (if set to false all archived threads are kept forever)
$config['archive']['lifetime'] = 3;
// Number of chars in snippet
$config['archive']['snippet_len'] = 400;
// For load balancing, having a seperate server (and domain/subdomain) for serving static content is
// possible. This can either be a directory or a URL. Defaults to $config['root'] . 'static/'.
// $config['dir']['static'] = 'http://static.example.org/';
@ -1707,6 +1726,10 @@
$config['mod']['news_custom'] = ADMIN;
// Delete news entries
$config['mod']['news_delete'] = ADMIN;
// Feature archived threads
$config['mod']['feature_archived_threads'] = JANITOR;
// Delete featured archived threads
$config['mod']['delete_featured_archived_threads'] = MOD;
// Execute un-filtered SQL queries on the database (?/debug/sql)
$config['mod']['debug_sql'] = DISABLED;
// Look through all cache values for debugging when APC is enabled (?/debug/apc)

48
inc/functions.php

@ -552,6 +552,33 @@ function setupBoard($array) {
if (!file_exists($board['dir'] . $config['dir']['res']))
@mkdir($board['dir'] . $config['dir']['res'], 0777)
or error("Couldn't create " . $board['dir'] . $config['dir']['img'] . ". Check permissions.", true);
// Create Archive Folders
if (!file_exists($board['dir'] . $config['dir']['archive']))
@mkdir($board['dir'] . $config['dir']['archive'], 0777)
or error("Couldn't create " . $board['dir'] . $config['dir']['archive'] . ". Check permissions.", true);
if (!file_exists($board['dir'] . $config['dir']['archive'] . $config['dir']['img']))
@mkdir($board['dir'] . $config['dir']['archive'] . $config['dir']['img'], 0777)
or error("Couldn't create " . $board['dir'] . $config['dir']['archive'] . $config['dir']['img'] . ". Check permissions.", true);
if (!file_exists($board['dir'] . $config['dir']['archive'] . $config['dir']['thumb']))
@mkdir($board['dir'] . $config['dir']['archive'] . $config['dir']['thumb'], 0777)
or error("Couldn't create " . $board['dir'] . $config['dir']['archive'] . $config['dir']['img'] . ". Check permissions.", true);
if (!file_exists($board['dir'] . $config['dir']['archive'] . $config['dir']['res']))
@mkdir($board['dir'] . $config['dir']['archive'] . $config['dir']['res'], 0777)
or error("Couldn't create " . $board['dir'] . $config['dir']['archive'] . $config['dir']['img'] . ". Check permissions.", true);
// Create Featured threads Folders
if (!file_exists($board['dir'] . $config['dir']['featured']))
@mkdir($board['dir'] . $config['dir']['featured'], 0777)
or error("Couldn't create " . $board['dir'] . $config['dir']['featured'] . ". Check permissions.", true);
if (!file_exists($board['dir'] . $config['dir']['featured'] . $config['dir']['img']))
@mkdir($board['dir'] . $config['dir']['featured'] . $config['dir']['img'], 0777)
or error("Couldn't create " . $board['dir'] . $config['dir']['featured'] . $config['dir']['img'] . ". Check permissions.", true);
if (!file_exists($board['dir'] . $config['dir']['featured'] . $config['dir']['thumb']))
@mkdir($board['dir'] . $config['dir']['featured'] . $config['dir']['thumb'], 0777)
or error("Couldn't create " . $board['dir'] . $config['dir']['featured'] . $config['dir']['img'] . ". Check permissions.", true);
if (!file_exists($board['dir'] . $config['dir']['featured'] . $config['dir']['res']))
@mkdir($board['dir'] . $config['dir']['featured'] . $config['dir']['res'], 0777)
or error("Couldn't create " . $board['dir'] . $config['dir']['featured'] . $config['dir']['img'] . ". Check permissions.", true);
}
function openBoard($uri) {
@ -1500,6 +1527,8 @@ function clean($pid = false) {
$query->execute() or error(db_error($query));
while ($post = $query->fetch(PDO::FETCH_ASSOC)) {
Archive::archiveThread($post['id']);
if ($pid) modLog("Automatically archived thread #{$post['id']} due to new thread #{$pid}");
deletePost($post['id'], false, false);
if ($pid) modLog("Automatically deleting thread #{$post['id']} due to new thread #{$pid}");
}
@ -2026,6 +2055,10 @@ function buildIndex($global_api = "yes") {
}
}
Announcements::buildAnnouncementPages();
Archive::RebuildArchiveIndexes();
if ($config['try_smarter'])
$build_pages = array();
}
@ -2508,6 +2541,21 @@ function markup(&$body, $track_cites = false, $op = false) {
return $tracked_cites;
}
function archive_list_markup(&$body) {
$body = str_replace("\r", '', $body);
$body = utf8tohtml($body);
$body = preg_replace("/^\s*&gt;.*$/m", '<span class="quote">$0</span>', $body);
// replace tabs with 8 spaces
$body = str_replace("\t", ' ', $body);
}
function escape_markup_modifiers($string) {
return preg_replace('@<(tinyboard) ([\w\s]+)>@mi', '<$1 escape $2>', $string);
}

82
inc/mod/pages.php

@ -372,6 +372,9 @@ function mod_edit_board($boardName) {
// Delete posting table
$query = query(sprintf('DROP TABLE IF EXISTS ``posts_%s``', $board['uri'])) or error(db_error());
// Delete archive table
$query = query(sprintf('DROP TABLE IF EXISTS ``archive_%s``', $board['uri'])) or error(db_error());
// Clear reports
$query = prepare('DELETE FROM ``reports`` WHERE `board` = :id');
$query->bindValue(':id', $board['uri'], PDO::PARAM_STR);
@ -501,6 +504,13 @@ function mod_new_board() {
query($query) or error(db_error());
// Create Archive Table in DB
$query = Element('archive.sql', array('board' => $board['uri']));
if (mysql_version() < 50503)
$query = preg_replace('/(CHARSET=|CHARACTER SET )utf8mb4/', '$1utf8', $query);
query($query) or error(db_error());
if ($config['cache']['enabled'])
cache::delete('all_boards');
@ -3581,3 +3591,75 @@ function mod_debug_apc() {
mod_page(_('Debug: APC'), 'mod/debug/apc.html', array('cached_vars' => $cached_vars));
}
function mod_view_archive($boardName) {
global $board, $config;
// If archiving is turned off return
if(!$config['archive']['threads'])
return;
if (!openBoard($boardName))
error($config['error']['noboard']);
if(isset($_POST['feature'], $_POST['id'])) {
if(!hasPermission($config['mod']['feature_archived_threads'], $board['uri']))
error($config['error']['noaccess']);
Archive::featureThread($_POST['id']);
}
// // Purge Threads that have timed out, and rebuild index if anyone was purged
// if(Archive::purgeArchive() != 0)
// Archive::buildArchiveIndex();
$query = query(sprintf("SELECT `id`, `snippet`, `featured` FROM ``archive_%s`` WHERE `lifetime` > %d ORDER BY `lifetime` DESC", $board['uri'], time())) or error(db_error());
$archive = $query->fetchAll(PDO::FETCH_ASSOC);
foreach($archive as &$thread)
$thread['archived_url'] = sprintf($config['board_path'], $board['uri']) . $config['dir']['archive'] . $config['dir']['res'] . sprintf($config['file_page'], $thread['id']);
mod_page(sprintf(_('Archived') . ' %s: ' . $config['board_abbreviation'], _('threads'), $board['uri']), 'mod/archive_list.html', array(
'archive' => $archive,
'thread_count' => $query->rowCount(),
'token' => make_secure_link_token($board['uri']. '/archive/')
));
}
function mod_view_archive_featured($boardName) {
global $board, $config;
// If archiving is turned off return
if(!$config['feature']['threads'])
return;
if (!openBoard($boardName))
error($config['error']['noboard']);
if(isset($_POST['delete'], $_POST['id'])) {
if(!hasPermission($config['mod']['delete_featured_archived_threads'], $board['uri']))
error($config['error']['noaccess']);
Archive::deleteFeatured($_POST['id']);
}
$query = query(sprintf("SELECT `id`, `snippet`, `featured` FROM ``archive_%s`` WHERE `featured` = 1 ORDER BY `lifetime` DESC", $board['uri'])) or error(db_error());
$archive = $query->fetchAll(PDO::FETCH_ASSOC);
foreach($archive as &$thread)
$thread['featured_url'] = sprintf($config['board_path'], $board['uri']) . $config['dir']['featured'] . $config['dir']['res'] . sprintf($config['file_page'], $thread['id']);
mod_page(sprintf(_('Featured') . ' %s: ' . $config['board_abbreviation'], _('threads'), $board['uri']), 'mod/archive_featured_list.html', array(
'archive' => $archive,
'token' => make_secure_link_token($board['uri']. '/featured/')
));
}

2
mod.php

@ -76,6 +76,8 @@ $pages = array(
'/search/(posts|IP_notes|bans|log)/(.+)/(\d+)' => 'search', // search
'/search/(posts|IP_notes|bans|log)/(.+)' => 'search', // search
'/(\%b)/archive/' => 'secure_POST view_archive', // view archive
'/(\%b)/featured/' => 'secure_POST view_archive_featured', // view featured Archive
'/(\%b)/warning/(\d+)' => 'secure_POST warning_post', // issue warning for post
'/(\%b)/ban(&delete)?/(\d+)' => 'secure_POST ban_post', // ban poster
'/(\%b)/move/(\d+)' => 'secure_POST move', // move thread

93
stylesheets/style.css

@ -539,23 +539,116 @@ table.announcements {
margin-right: auto;
font-size: 0.8em;
}
table.announcements thead td {
border-bottom: 1px solid rgb(63, 63, 63);
border-bottom: 1px solid rgba(0, 0, 0, .4);
-webkit-background-clip: padding-box; /* for Safari */
background-clip: padding-box; /* for IE9+, Firefox 4+, Opera, Chrome */
}
table.announcements tbody {
text-align: left;
}
table.announcements tbody td:first-child {
text-align: right;
vertical-align: top;
}
table.announcements tfoot td {
text-align: right;
}
table.announcements-list {
text-align: center;
margin-left: auto;
margin-right: auto;
width: 80% !important;
border-collapse: collapse;
border-spacing: 0;
}
table.announcements-list tr td, table.announcements-list tr th {
border: 1px solid rgb(63, 63, 63);
border: 1px solid rgba(0, 0, 0, .4);
-webkit-background-clip: padding-box; /* for Safari */
background-clip: padding-box; /* for IE9+, Firefox 4+, Opera, Chrome */
padding: 3px;
}
table.announcements-list tr th {
font-weight: bold;
text-align: center;
}
table.announcements-list tbody {
text-align: left;
vertical-align: top;
background: rgb(250, 250, 250);
color: black;
}
table.announcements-list td:first-child, table.announcements-list th:first-child {
text-align: center;
}
table.announcements-list tbody tr td:nth-child(4) :first-child {
float: left;
}
table.announcements-list tbody tr td:nth-child(4) :last-child {
float: right;
}
.announcementForm input[type=text] {
box-sizing: border-box;
width: 100%;
}
table.archive-list {
text-align: center;
margin-left: auto;
margin-right: auto;
width: 80% !important;
border-collapse: collapse;
border-spacing: 0;
font-size: 0.8em;
}
table.archive-list tr td, table.archive-list tr th {
border: 1px solid rgb(63, 63, 63);
border: 1px solid rgba(0, 0, 0, .4);
-webkit-background-clip: padding-box; /* for Safari */
background-clip: padding-box; /* for IE9+, Firefox 4+, Opera, Chrome */
padding: 3px;
}
table.archive-list tr th {
font-weight: bold;
text-align: center;
}
table.archive-list tbody {
text-align: left;
vertical-align: top;
background: rgb(250, 250, 250);
color: black;
}
table.archive-list tbody tr td:first-child{
text-align: right;
}
table.archive-list tbody tr td:nth-child(3),
table.archive-list tbody tr td:nth-child(4){
text-align: center;
}
.archiveForm {
margin-bottom: inherit !important;
}
table.mod.config-editor {
font-size: 9pt;
width: 100%;

10
templates/archive.sql

@ -0,0 +1,10 @@
CREATE TABLE IF NOT EXISTS ``archive_{{ board }}`` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`snippet` text NOT NULL,
`lifetime` int(11) NOT NULL,
`files` text NOT NULL,
`featured` int(1) NOT NULL,
UNIQUE KEY `id` (`id`),
KEY `lifetime` (`lifetime`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

37
templates/mod/archive_featured_list.html

@ -0,0 +1,37 @@
<table id="archive-list" class="archive-list modlog">
<thead>
<tr>
<th width='80px'>Post #</th>
<th>Snippet</th>
<th width='50px'>&nbsp;</th>
{% if mod and mod|hasPermission(config.mod.delete_featured_archived_threads, board.uri) %}
<th width='80px'>&nbsp;</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for thread in archive %}
<tr>
<td>{{ thread.id }}</td>
<td>{{ thread.snippet }}</td>
<td><a href="{{ thread.featured_url }}">[{% trans 'View' %}]</a></td>
{% if mod and mod|hasPermission(config.mod.delete_featured_archived_threads, board.uri) %}
<td>
<form action="" method="post" class="archiveForm">
<input type="hidden" name="token" value="{{ token }}">
<input type="hidden" name="id" value="{{ thread.id }}">
<input type="hidden" name="delete" value="1">
<a href="#" onclick="return confirm('Are you sure you want to permanently delete this thread from featured archive?')?this.parentNode.submit():false;">[{% trans 'Delete' %}]</a>
</form>
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>

44
templates/mod/archive_list.html

@ -0,0 +1,44 @@
<p style="text-align: center">
<b>{% trans 'Displaying' %} {{ thread_count }} {% trans 'expired threads' %}{% if config.archive.lifetime %} {% trans 'from the past' %} {{ config.archive.lifetime }} {% trans 'days' %}{% endif %}</b>
</p>
<table id="archive-list" class="archive-list modlog">
<thead>
<tr>
<th width='80px'>Post #</th>
<th>Snippet</th>
<th width='50px'>&nbsp;</th>
{% if mod and mod|hasPermission(config.mod.feature_archived_threads, board.uri) %}
<th width='80px'>&nbsp;</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for thread in archive %}
<tr>
<td>{{ thread.id }}</td>
<td>{{ thread.snippet }}</td>
<td><a href="{{ thread.archived_url }}">[{% trans 'View' %}]</a></td>
{% if mod and mod|hasPermission(config.mod.feature_archived_threads, board.uri) %}
<td>
{% if not thread.featured %}
<form action="" method="post" class="archiveForm">
<input type="hidden" name="token" value="{{ token }}">
<input type="hidden" name="id" value="{{ thread.id }}">
<input type="hidden" name="feature" value="1">
<a href="#" onclick="confirm('Are you sure you want to add thread to featured archive?')?this.parentNode.submit():false;">[{% trans 'Feature' %}]</a>
</form>
{% else %}
<b>{% trans 'Featured' %}</b>
{% endif %}
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>

5
tools/rebuild.php

@ -22,6 +22,7 @@
require dirname(__FILE__) . '/inc/cli.php';
require_once("inc/announcements.php");
require_once("inc/archive.php");
require_once("inc/bans.php");
$start = microtime(true);
@ -98,11 +99,15 @@ foreach($boards as &$board) {
echo "Rebuilding #{$post['id']}...\n";
buildThread($post['id']);
}
// Rebuild Archive Index for Board
Archive::RebuildArchiveIndexes();
}
// Generate announcement files
Announcements::buildAnnouncements();
if(!$options['quiet'])
printf("Complete! Took %g seconds\n", microtime(true) - $start);

Loading…
Cancel
Save