2021-06-20 13:40:00 -04:00
< ? php
require 'info.php' ;
/**
* Generate the board ' s HTML and move it and its JavaScript in place , whence
* it ' s served
*/
function overboards_build ( $action , $settings ) {
global $config ;
if ( $action !== 'all' && $action !== 'post' && $action !== 'post-thread' &&
$action !== 'post-delete' )
{
return ;
}
if ( $config [ 'smart_build' ]) {
file_unlink ( $settings [ 'uri' ] . '/index.html' );
} else {
require 'overboards.php' ;
$overboards = new overboards ( $overboards_config );
foreach ( $overboards_config as & $overboard ) {
if ( ! file_exists ( $overboard [ 'uri' ])) {
@ mkdir ( $overboard [ 'uri' ], 0777 ) or error ( " Couldn't create { $overboard [ 'uri' ] } . Check permissions. " , true );
}
// Copy the generated board HTML to its place
file_write ( $overboard [ 'uri' ] . '/index.html' , $overboards -> build ( $overboard ));
file_write ( $overboard [ 'uri' ] . '/overboard.js' ,
2024-10-20 11:48:05 +02:00
Element ( 'themes/overboards/overboard.js' , []));
2021-06-20 13:40:00 -04:00
}
}
}
/**
* Encapsulation of the theme ' s internals
*/
class overboards {
private $settings ;
function __construct ( $settings ) {
$this -> settings = $this -> parseSettings ( $settings );
}
/**
* Parse and validate configuration parameters passed from the UI
*/
private function parseSettings ( & $settings ) {
foreach ( $settings as & $overboard ) {
if ( ! is_int ( $overboard [ 'thread_limit' ]) || $overboard [ 'thread_limit' ] < 1 )
{
error ( 'thread_limit must be an integer above 1.' , true );
}
2024-08-22 00:09:28 +02:00
if ( ! is_array ( $overboard [ 'exclude' ]))
2021-06-20 13:40:00 -04:00
{
error ( 'Exclude list must be array of strings.' , true );
}
foreach ( $overboard [ 'exclude' ] as & $board ) {
if ( ! is_string ( $board )){
error ( 'Exclude list must be array of strings.' , true );
}
}
2024-08-22 00:09:28 +02:00
2021-06-20 13:40:00 -04:00
}
return $settings ;
}
/**
* Obtain list of all threads from all non - excluded boards
*/
private function fetchThreads ( $overboard ) {
$query = '' ;
$boards = listBoards ( true );
foreach ( $boards as $b ) {
if ( in_array ( $b , $overboard [ 'exclude' ]))
continue ;
// Threads are those posts that have no parent thread
$query .= " SELECT *, ' $b ' AS `board` FROM ``posts_ $b `` " .
" WHERE `thread` IS NULL UNION ALL " ;
}
$query = preg_replace ( '/UNION ALL $/' , 'ORDER BY `bump` DESC' , $query );
$result = query ( $query ) or error ( db_error ());
return $result -> fetchAll ( PDO :: FETCH_ASSOC );
}
/**
* Retrieve all replies to a given thread
*/
private function fetchReplies ( $board , $thread_id , $preview_count ) {
$query = prepare ( " SELECT * FROM (SELECT * FROM ``posts_ $board `` WHERE `thread` = :id ORDER BY `time` DESC LIMIT :limit) as
t ORDER BY t . time ASC " );
$query -> bindValue ( ':id' , $thread_id , PDO :: PARAM_INT );
$query -> bindValue ( ':limit' , $preview_count , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ( $query ));
return $query -> fetchAll ( PDO :: FETCH_ASSOC );
}
/**
* Retrieve count of images and posts in a thread
*/
private function fetchThreadCount ( $board , $thread_id , $preview_count ) {
$query = prepare ( " SELECT SUM(t.num_files) as file_count, COUNT(t.id) as post_count FROM (SELECT * FROM ``posts_ $board `` WHERE `thread` = :id ORDER BY `time` DESC LIMIT :offset , 18446744073709551615) as t; " );
$query -> bindValue ( ':id' , $thread_id , PDO :: PARAM_INT );
$query -> bindValue ( ':offset' , $preview_count , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ( $query ));
return $query -> fetch ( PDO :: FETCH_ASSOC );
}
/**
* Build the HTML of a single thread in the catalog
*/
private function buildOne ( $post , $mod = false ) {
global $config ;
openBoard ( $post [ 'board' ]);
$thread = new Thread ( $post , $mod ? '?/' : $config [ 'root' ], $mod );
// Number of replies to a thread that are displayed beneath it
$preview_count = $post [ 'sticky' ] ? $config [ 'threads_preview_sticky' ] :
$config [ 'threads_preview' ];
$replies = $this -> fetchReplies ( $post [ 'board' ], $post [ 'id' ], $preview_count );
$disp_replies = $replies ;
foreach ( $disp_replies as $reply ) {
// Append the reply to the thread as it's being built
$thread -> add ( new Post ( $reply , $mod ? '?/' : $config [ 'root' ], $mod ));
}
$threadCount = $this -> fetchThreadCount ( $post [ 'board' ], $post [ 'id' ], $preview_count );
$thread -> omitted = $threadCount [ 'post_count' ];
$thread -> omitted_images = $threadCount [ 'file_count' ];
// Board name and link
$html = '<h2><a href="' . $config [ 'root' ] . $post [ 'board' ] . '/">/' .
$post [ 'board' ] . '/</a></h2>' ;
// The thread itself
$html .= $thread -> build ( true );
return $html ;
}
/**
* Query the required information and generate the HTML
*/
public function build ( $overboard , $mod = false ) {
if ( ! isset ( $this -> settings )) {
error ( 'Theme is not configured properly.' );
}
global $config ;
$html = '' ;
2024-10-20 11:48:05 +02:00
$overflow = [];
2021-06-20 13:40:00 -04:00
// Fetch threads from all boards and chomp the first 'n' posts, depending
// on the setting
$threads = $this -> fetchThreads ( $overboard );
$total_count = count ( $threads );
// Top threads displayed on load
$top_threads = array_splice ( $threads , 0 , $overboard [ 'thread_limit' ]);
// Number of processed threads by board
2024-10-20 11:48:05 +02:00
$counts = [];
2021-06-20 13:40:00 -04:00
// Output threads up to the specified limit
foreach ( $top_threads as $post ) {
if ( array_key_exists ( $post [ 'board' ], $counts )) {
++ $counts [ $post [ 'board' ]];
} else {
$counts [ $post [ 'board' ]] = 1 ;
}
$html .= $this -> buildOne ( $post , $mod );
}
foreach ( $threads as $post ) {
if ( array_key_exists ( $post [ 'board' ], $counts )) {
++ $counts [ $post [ 'board' ]];
} else {
$counts [ $post [ 'board' ]] = 1 ;
}
$page = 'index' ;
$board_page = floor ( $counts [ $post [ 'board' ]] / $config [ 'threads_per_page' ]);
if ( $board_page > 0 ) {
$page = $board_page + 1 ;
}
$overflow [] = array (
'id' => $post [ 'id' ],
'board' => $post [ 'board' ],
'page' => $page . '.html'
);
}
$html .= '<script>var ukko_overflow = ' . json_encode ( $overflow ) . '</script>' ;
2024-08-22 00:09:28 +02:00
$html .= '<script type="text/javascript" src="/' . $overboard [ 'uri' ] . '/overboard.js?=' . $config [ 'resource_version' ] . '"></script>' ;
2021-06-20 13:40:00 -04:00
2024-10-20 11:48:05 +02:00
return Element ( 'index.html' , [
2021-06-20 13:40:00 -04:00
'config' => $config ,
2024-10-20 11:48:05 +02:00
'board' => [
2021-06-20 13:40:00 -04:00
'dir' => $overboard [ 'uri' ] . " / " ,
'url' => $overboard [ 'uri' ],
'title' => $overboard [ 'title' ],
2024-10-20 11:48:05 +02:00
'subtitle' => str_replace ( '%s' , $overboard [ 'thread_limit' ], strval ( min ( $overboard [ 'subtitle' ], $total_count ))),
],
2021-06-20 13:40:00 -04:00
'no_post_form' => true ,
'body' => $html ,
'mod' => $mod ,
'boardlist' => createBoardlist ( $mod ),
2024-10-20 11:48:05 +02:00
]);
2021-06-20 13:40:00 -04:00
}
};
if ( ! function_exists ( 'array_column' )) {
/**
* Pick out values from subarrays by given key
*/
function array_column ( $array , $key ) {
$result = [];
foreach ( $array as $val ) {
$result [] = $val [ $key ];
}
return $result ;
}
}