forked from leftypol/leftypol
		
	
		
			
				
	
	
		
			421 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			421 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
|     require 'info.php';
 | |
| 
 | |
|     function catalog_build($action, $settings, $board) {
 | |
|         global $config;
 | |
| 
 | |
|         $b = new Catalog($settings);
 | |
|         $boards = explode(' ', $settings['boards']);
 | |
| 
 | |
|         if (isset($settings['has_overboard']) && $settings['has_overboard']) {
 | |
|             // Include overboard settings so that we can find them all and process exclusions
 | |
|             require "templates/themes/overboards/overboards.php";
 | |
|         }
 | |
| 
 | |
|         // Possible values for $action:
 | |
|         //	- all (rebuild everything, initialization)
 | |
|         //	- news (news has been updated)
 | |
|         //	- boards (board list changed)
 | |
|         //	- post (a reply has been made)
 | |
|         //	- post-thread (a thread has been made)
 | |
|         if ($action === 'all') {
 | |
|             foreach ($boards as $board) {
 | |
|                 $action = generation_strategy("sb_catalog", array($board));
 | |
|                 if ($action == 'delete') {
 | |
|                     file_unlink($config['dir']['home'] . $board . '/catalog.html');
 | |
|                     file_unlink($config['dir']['home'] . $board . '/index.rss');
 | |
|                 }
 | |
|                 elseif ($action == 'rebuild') {
 | |
|                     $b->build($board);
 | |
|                 }
 | |
|             }
 | |
|             if (isset($settings['has_overboard']) && $settings['has_overboard']) {
 | |
|                 foreach ($overboards_config as &$overboard) {
 | |
|                     $included_boards = array_diff(listBoards(true), $overboard['exclude']);
 | |
|                     $action = generation_strategy("sb_catalog", array($overboard));
 | |
|                     if ($action == 'delete') {
 | |
|                         file_unlink($config['dir']['home'] . $overboard . '/catalog.html');
 | |
|                         file_unlink($config['dir']['home'] . $overboard . '/index.rss');
 | |
|                     }
 | |
|                     elseif ($action == 'rebuild') {
 | |
|                         $b->buildOverboard($overboard['uri'], $settings, $included_boards);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         } elseif ($action == 'post-thread' || ($settings['update_on_posts'] && $action == 'post') || ($settings['update_on_posts'] && $action == 'post-delete')
 | |
|         || $action == 'sticky' || ($action == 'lock' && in_array($board, $boards))) {
 | |
|             $b = new Catalog($settings);
 | |
| 
 | |
|             $action = generation_strategy("sb_catalog", array($board));
 | |
|             if ($action == 'delete') {
 | |
|                 file_unlink($config['dir']['home'] . $board . '/catalog.html');
 | |
|                 file_unlink($config['dir']['home'] . $board . '/index.rss');
 | |
|             }
 | |
|             elseif ($action == 'rebuild') {
 | |
|                 $b->build($board);
 | |
| 
 | |
|                 if (isset($settings['has_overboard']) && $settings['has_overboard']) {
 | |
|                     foreach ($overboards_config as &$overboard) {
 | |
|                         $included_boards = array_diff(listBoards(true), $overboard['exclude']);
 | |
|                         $b->buildOverboard($overboard['uri'], $settings, $included_boards);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // FIXME: Check that Ukko is actually enabled
 | |
|         if ($settings['enable_ukko'] && (
 | |
|             $action === 'all' || $action === 'post' ||
 | |
|             $action === 'post-thread' || $action === 'post-delete' || $action === 'rebuild'))
 | |
|         {
 | |
|             $b->buildUkko();
 | |
|         }
 | |
| 
 | |
|         // FIXME: Check that Rand is actually enabled
 | |
|         if ($settings['enable_rand'] && (
 | |
|             $action === 'all' || $action === 'post' ||
 | |
|             $action === 'post-thread' || $action === 'post-delete' || $action === 'rebuild'))
 | |
|         {
 | |
|             $b->buildRand();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Wrap functions in a class so they don't interfere with normal Tinyboard operations
 | |
|     class Catalog {
 | |
|         private $settings;
 | |
|         // Cache for threads from boards that have already been processed
 | |
|         private $threadsCache = array();
 | |
| 
 | |
|         public function __construct($settings) {
 | |
|             $this->settings = $settings;
 | |
|         }
 | |
| 
 | |
|         /**
 | |
|          * Build and save the HTML of the catalog for the Ukko theme
 | |
|          */
 | |
|         public function buildUkko($mod = false) {
 | |
|             global $board, $config;
 | |
| 
 | |
|             $ukkoSettings = themeSettings('ukko');
 | |
|              $queries = array();
 | |
|             $threads = array();
 | |
| 
 | |
|             $exclusions = explode(' ', $ukkoSettings['exclude']);
 | |
|             $boards = array_diff(listBoards(true), $exclusions);
 | |
| 
 | |
|             foreach ($boards as $b) {
 | |
|                 if (array_key_exists($b, $this->threadsCache)) {
 | |
|                     $threads = array_merge($threads, $this->threadsCache[$b]);
 | |
|                 } else {
 | |
|                     $queries[] = $this->buildThreadsQuery($b);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // Fetch threads from boards that haven't beenp processed yet
 | |
|             if (!empty($queries)) {
 | |
|                 $sql = implode(' UNION ALL ', $queries);
 | |
|                 $res = query($sql) or error(db_error());
 | |
|                 $threads = array_merge($threads, $res->fetchAll(PDO::FETCH_ASSOC));
 | |
|             }
 | |
| 
 | |
|             // Sort in bump order
 | |
|             usort($threads, function($a, $b) {
 | |
|                 return strcmp($b['bump'], $a['bump']);
 | |
|             });
 | |
|             // Generate data for the template
 | |
|             $recent_posts = $this->generateRecentPosts($threads, $mod);
 | |
| 
 | |
|             // Generate board data for building
 | |
|             $board_original = $board;
 | |
|             $board = [];
 | |
|             $board['uri'] = $ukkoSettings['uri'];
 | |
|             $board['title'] = $ukkoSettings['title'];
 | |
|             $board['subtitle'] = $ukkoSettings['subtitle'];
 | |
| 
 | |
|             $ret = $this->saveForBoard($ukkoSettings['uri'], $recent_posts,
 | |
|                 $config['root'] . $ukkoSettings['uri'], $mod);
 | |
| 
 | |
|             $board = $board_original;
 | |
| 
 | |
|             return $ret;
 | |
|         }
 | |
| 
 | |
|         /**
 | |
|          * Build and save the HTML of the catalog for the Rand theme
 | |
|          */
 | |
|         public function buildRand($mod = false) {
 | |
|             global $board, $config;
 | |
| 
 | |
|             $randSettings = themeSettings('rand');
 | |
|              $queries = array();
 | |
|             $threads = array();
 | |
| 
 | |
|             $exclusions = explode(' ', $randSettings['exclude']);
 | |
|             $boards = array_diff(listBoards(true), $exclusions);
 | |
| 
 | |
|             foreach ($boards as $b) {
 | |
|                 if (array_key_exists($b, $this->threadsCache)) {
 | |
|                     $threads = array_merge($threads, $this->threadsCache[$b]);
 | |
|                 } else {
 | |
|                     $queries[] = $this->buildThreadsQuery($b);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // Fetch threads from boards that haven't beenp processed yet
 | |
|             if (!empty($queries)) {
 | |
|                 $sql = implode(' UNION ALL ', $queries);
 | |
|                 $res = query($sql) or error(db_error());
 | |
|                 $threads = array_merge($threads, $res->fetchAll(PDO::FETCH_ASSOC));
 | |
|             }
 | |
| 
 | |
|             // Randomize order
 | |
|             shuffle($threads);
 | |
|             // Generate data for the template
 | |
|             $recent_posts = $this->generateRecentPosts($threads, $mod);
 | |
| 
 | |
|             // Generate board data for building
 | |
|             $board_original = $board;
 | |
|             $board = [];
 | |
|             $board['uri'] = $randSettings['uri'];
 | |
|             $board['title'] = $randSettings['title'];
 | |
|             $board['subtitle'] = $randSettings['subtitle'];
 | |
| 
 | |
|             $ret = $this->saveForBoard($randSettings['uri'], $recent_posts,
 | |
|                 $config['root'] . $randSettings['uri'], $mod);
 | |
| 
 | |
|             $board = $board_original;
 | |
| 
 | |
|             return $ret;
 | |
|         }
 | |
| 
 | |
|         /**
 | |
|          * Builds the overboard.
 | |
|          *
 | |
|          * @param string $board_name The name of the overboard.
 | |
|          * @param array $settings The settings array.
 | |
|          * @param array $boards The array with all the board to generate the catalog from. Will use all the boards if null.
 | |
|          * @param bool $mod If the board is to be generate for mod view.
 | |
|          * @return mixed Viewable html.
 | |
|          */
 | |
|         public function buildOverboard($board_name, $settings, $boards, $mod = false) {
 | |
|             global $config;
 | |
| 
 | |
|             if (array_key_exists($board_name, $this->threadsCache)) {
 | |
|                 $threads = $this->threadsCache[$board_name];
 | |
|             } else {
 | |
|                 $sql = '';
 | |
|                 foreach ($boards as $board) {
 | |
|                     $sql .= '('. $this->buildThreadsQuery($board) . ')';
 | |
|                     $sql .= " UNION ALL ";
 | |
|                 }
 | |
|                 $sql = preg_replace('/UNION ALL $/', 'ORDER BY `bump` DESC LIMIT :limit', $sql);
 | |
|                 $query = prepare($sql);
 | |
|                 $query->bindValue(':limit', $settings['overboard_limit'], PDO::PARAM_INT);
 | |
|                 $query->execute() or error(db_error($query));
 | |
| 
 | |
|                 $threads = $query->fetchAll(PDO::FETCH_ASSOC);
 | |
|                 // Save for posterity
 | |
|                 $this->threadsCache[$board_name] = $threads;
 | |
|             }
 | |
|             // Generate data for the template
 | |
|             $recent_posts = $this->generateRecentPosts($threads);
 | |
| 
 | |
|             $ret = $this->saveForBoard($board_name, $recent_posts, '/' . $board_name, $mod, true);
 | |
| 
 | |
|             // Build the overboard JSON outputs
 | |
|             if ($config['api']['enabled']) {
 | |
|                 $api = new Api();
 | |
| 
 | |
|                 // Separate the threads into pages
 | |
|                 $pages = array(array());
 | |
|                 $totalThreads = count($recent_posts);
 | |
|                 $page = 0;
 | |
|                 for ($i = 1; $i <= $totalThreads; $i++) {
 | |
|                     $pages[$page][] = new Thread($recent_posts[$i-1]);
 | |
| 
 | |
|                     // If we have not yet visited all threads,
 | |
|                     // and we hit the limit on the current page,
 | |
|                     // skip to the next page
 | |
|                     if ($i < $totalThreads && ($i % $config['threads_per_page'] == 0)) {
 | |
|                         $page++;
 | |
|                         $pages[$page] = array();
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 $json = json_encode($api->translateCatalog($pages));
 | |
|                 file_write($config['dir']['home'] . $board_name . '/catalog.json', $json);
 | |
| 
 | |
|                 $json = json_encode($api->translateCatalog($pages, true));
 | |
|                 file_write($config['dir']['home'] . $board_name . '/threads.json', $json);
 | |
|             }
 | |
| 
 | |
|             return $ret;
 | |
|         }
 | |
| 
 | |
|         /**
 | |
|          * Build and save the HTML of the catalog for the given board
 | |
|          */
 | |
|         public function build($board_name, $mod = false) {
 | |
|             if (!openBoard($board_name)) {
 | |
|                 error(sprintf(_("Board %s doesn't exist"), $board_name));
 | |
|             }
 | |
| 
 | |
|             if (array_key_exists($board_name, $this->threadsCache)) {
 | |
|                 $threads = $this->threadsCache[$board_name];
 | |
|             } else {
 | |
|                 $sql = $this->buildThreadsQuery($board_name);
 | |
|                 $query = query($sql . ' ORDER BY `sticky` DESC,`bump` DESC') or error(db_error());
 | |
|                 $threads = $query->fetchAll(PDO::FETCH_ASSOC);
 | |
|                 // Save for posterity
 | |
|                 $this->threadsCache[$board_name] = $threads;
 | |
|             }
 | |
| 
 | |
|             // Generate data for the template
 | |
|             $recent_posts = $this->generateRecentPosts($threads, $mod);
 | |
| 
 | |
|             return $this->saveForBoard($board_name, $recent_posts, null, $mod);
 | |
|         }
 | |
| 
 | |
|         private function buildThreadsQuery($board) {
 | |
|             $sql  = "SELECT *, `id` AS `thread_id`, " .
 | |
|                 "(SELECT COUNT(`id`) FROM ``posts_$board`` WHERE `thread` = `thread_id`) AS `reply_count`, " .
 | |
|                 "(SELECT SUM(`num_files`) FROM ``posts_$board`` WHERE `thread` = `thread_id` AND `num_files` IS NOT NULL) AS `image_count`, " .
 | |
|                 "'$board' AS `board` FROM ``posts_$board`` WHERE `thread` IS NULL";
 | |
| 
 | |
|             return $sql;
 | |
|         }
 | |
| 
 | |
|         private function filepathForThumb($thumb_or_special, $path_when_file) {
 | |
|             global $config;
 | |
| 
 | |
|             if ($thumb_or_special === 'deleted') {
 | |
|                 return $config['root'] . $config['image_deleted'];
 | |
|             } else if ($thumb_or_special === 'spoiler') {
 | |
|                 return $config['root'] . $config['spoiler_image'];
 | |
|             } else if ($thumb_or_special === 'file') {
 | |
|                 // see twig_extension_filter
 | |
|                 $ext   = mb_strtolower(mb_substr($path_when_file, mb_strrpos($path_when_file, '.') + 1));
 | |
|                 $icons = $config['file_icons'];
 | |
|                 // see templates/post/image.html
 | |
|                 if (isset($icons[$ext])) {
 | |
|                     return $config['root'] . sprintf($config['file_thumb'], $icons[$ext]);
 | |
|                 } else {
 | |
|                     return $config['root'] . sprintf($config['file_thumb'], $icons['default']);
 | |
|                 }
 | |
|             } else {
 | |
|                 return $config['uri_thumb'] . $thumb_or_special;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private function generateRecentPosts($threads, $mod = false) {
 | |
|             global $config, $board;
 | |
| 
 | |
|             $posts = array();
 | |
|             foreach ($threads as $post) {
 | |
|                 if ($board['uri'] !== $post['board']) {
 | |
|                     openBoard($post['board']);
 | |
|                 }
 | |
| 
 | |
|                 if ($mod)
 | |
|                     $post['link'] = $config['root'] . $config['file_mod'] . '?/'. $board['dir'] . $config['dir']['res'] . link_for($post);
 | |
|                 else
 | |
|                     $post['link'] = $config['root'] . $board['dir'] . $config['dir']['res'] . link_for($post);
 | |
|                 $post['board_name'] = $board['name'];
 | |
| 
 | |
|                 if ($post['embed'] && preg_match('/^https?:\/\/(\w+\.)?(?:youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9\-_]{10,11})(&.+)?$/i', $post['embed'], $matches)) {
 | |
|                     $post['youtube'] = $matches[2];
 | |
|                 }
 | |
| 
 | |
|                 if (isset($post['files']) && $post['files']) {
 | |
|                     $files = json_decode($post['files']);
 | |
| 
 | |
|                     if (isset($files[0]) && $files[0]) {
 | |
|                         $foundone = false;
 | |
|                         foreach ($files as $file) {
 | |
|                             if ($file->file != 'deleted') {
 | |
|                                 $post['file'] = $this->filepathForThumb($file->thumb, $file->file);
 | |
|                                 $foundone = true;
 | |
|                                 break;
 | |
|                             }
 | |
|                         }
 | |
|                         if (!$foundone) {
 | |
|                             $post['file'] = $this->filepathForThumb('deleted', null);
 | |
|                         }
 | |
|                     } else {
 | |
|                         $post['file'] = $this->filepathForThumb('deleted', null);
 | |
|                     }
 | |
|                 } else {
 | |
|                     $post['file'] = $this->filepathForThumb('deleted', null);
 | |
|                 }
 | |
| 
 | |
|                 if (empty($post['image_count']))
 | |
|                     $post['image_count'] = 0;
 | |
|                 $post['pubdate'] = date('r', $post['time']);
 | |
|                 $posts[] = $post;
 | |
|             }
 | |
| 
 | |
|             return $posts;
 | |
|         }
 | |
| 
 | |
|         private function saveForBoard($board_name, $recent_posts, $board_link = null, $mod = false, $is_overboard = false) {
 | |
|             global $board, $config;
 | |
| 
 | |
|             $required_scripts = array('js/jquery.min.js', 'js/jquery.mixitup.min.js',
 | |
|                 'js/catalog.js');
 | |
| 
 | |
|             // Include scripts that haven't been yet included
 | |
|             foreach($required_scripts as $i => $s) {
 | |
|                 if (!in_array($s, $config['additional_javascript']))
 | |
|                     $config['additional_javascript'][] = $s;
 | |
|             }
 | |
| 
 | |
|             $antibot = create_antibot($board_name);
 | |
|             $antibot->reset();
 | |
| 
 | |
|             if ($board_link === null) {
 | |
|                 $board_link = ($mod) ? $config['root'] . $config['file_mod'] . '?/' . $board['dir'] : $config['root'] . $board['dir'];
 | |
|             }
 | |
| 
 | |
|             $template_config = Array(
 | |
|                 'settings' => $this->settings,
 | |
|                 'config' => $config,
 | |
|                 'boardlist' => createBoardlist($mod),
 | |
|                 'recent_images' => array(),
 | |
|                 'recent_posts' => $recent_posts,
 | |
|                 'stats' => array(),
 | |
|                 'board_name' => $board_name,
 | |
|                 'board' => $board,
 | |
|                 'is_overboard' => $is_overboard,
 | |
|                 'antibot' => $antibot,
 | |
|                 'link' => $board_link,
 | |
|                 'mod' => $mod
 | |
|             );
 | |
| 
 | |
|             // Fake board, I vomit and patch the template configuration.
 | |
|             if ($is_overboard) {
 | |
|                 // Redefine 'board' and disable posting.
 | |
|                 $template_config['board'] = Array(
 | |
|                     'uri' => $board_name,
 | |
|                     'title' => $board_name,
 | |
|                     'name' => $board_name,
 | |
|                     'dir' => $board_name . '/',
 | |
|                     'url' => '/' . $board_name . '/'
 | |
|                 );
 | |
|                 $template_config['no_post_form'] = true;
 | |
|             }
 | |
| 
 | |
|             $element = Element('themes/catalog/catalog.html', $template_config);
 | |
| 
 | |
|             if ($mod) {
 | |
|                 return $element;
 | |
|             } else {
 | |
|                 file_write($config['dir']['home'] . $board_name . '/catalog.html', $element);
 | |
| 
 | |
|                 file_write($config['dir']['home'] . $board_name . '/index.rss', Element('themes/catalog/index.rss', Array(
 | |
|                     'config' => $config,
 | |
|                     'recent_posts' => $recent_posts,
 | |
|                     'board' => $board
 | |
|                 )));
 | |
|             }
 | |
|         }
 | |
|     }
 | 
