Browse Source

multiimage posting

pull/40/head
copypaste 8 years ago
committed by czaks
parent
commit
c483e1258c
  1. 49
      inc/api.php
  2. 46
      inc/config.php
  3. 21
      inc/display.php
  4. 89
      inc/functions.php
  5. 6
      inc/lib/Twig/Extensions/Extension/Tinyboard.php
  6. 6
      inc/mod/pages.php
  7. 25
      install.php
  8. 30
      js/multi_image.js
  9. 224
      post.php
  10. 3
      stylesheets/style.css
  11. 1
      templates/main.js
  12. 38
      templates/post/fileinfo.html
  13. 34
      templates/post_reply.html
  14. 34
      templates/post_thread.html
  15. 12
      templates/posts.sql
  16. 5
      templates/themes/catalog/theme.php
  17. 12
      templates/themes/recent/theme.php
  18. 4
      templates/themes/ukko/theme.php

49
inc/api.php

@ -26,12 +26,6 @@ class Api {
'trip' => 'trip',
'capcode' => 'capcode',
'time' => 'time',
'thumbheight' => 'tn_w',
'thumbwidth' => 'tn_h',
'fileheight' => 'w',
'filewidth' => 'h',
'filesize' => 'fsize',
'filename' => 'filename',
'omitted' => 'omitted_posts',
'omitted_images' => 'omitted_images',
'replies' => 'replies',
@ -46,6 +40,15 @@ class Api {
'bump' => 'last_modified'
);
$this->fileFields = array(
'thumbheight' => 'tn_w',
'thumbwidth' => 'tn_h',
'height' => 'w',
'width' => 'h',
'size' => 'fsize',
'file' => 'filename',
);
if (isset($config['api']['extra_fields']) && gettype($config['api']['extra_fields']) == 'array'){
$this->postFields = array_merge($this->postFields, $config['api']['extra_fields']);
}
@ -67,30 +70,26 @@ class Api {
'last_modified' => 1
);
private function translatePost($post, $threadsPage = false) {
$apiPost = array();
$fields = $threadsPage ? $this->threadsPageFields : $this->postFields;
private function translateFields($fields, $object, &$apiPost) {
foreach ($fields as $local => $translated) {
if (!isset($post->$local))
if (!isset($object->$local))
continue;
$toInt = isset(self::$ints[$translated]);
$val = $post->$local;
$val = $object->$local;
if ($val !== null && $val !== '') {
$apiPost[$translated] = $toInt ? (int) $val : $val;
}
}
}
if ($threadsPage) return $apiPost;
private function translatePost($post, $threadsPage = false) {
$apiPost = array();
$fields = $threadsPage ? $this->threadsPageFields : $this->postFields;
$this->translateFields($fields, $post, $apiPost);
if (isset($post->filename)) {
$dotPos = strrpos($post->filename, '.');
$apiPost['filename'] = substr($post->filename, 0, $dotPos);
$apiPost['ext'] = substr($post->filename, $dotPos);
$dotPos = strrpos($post->file, '.');
$apiPost['tim'] = substr($post->file, 0, $dotPos);
}
if ($threadsPage) return $apiPost;
// Handle country field
if (isset($post->body_nomarkup) && $this->config['country_flags']) {
@ -104,6 +103,18 @@ class Api {
}
}
// Handle files
// Note: 4chan only supports one file, so only the first file is taken into account for 4chan-compatible API.
if (isset($post->files) && $post->files && !$threadsPage) {
$file = $post->files[0];
$this->translateFields($this->fileFields, $file, $apiPost);
$dotPos = strrpos($file->file, '.');
$apiPost['filename'] = substr($file->file, 0, $dotPos);
$apiPost['ext'] = substr($file->file, $dotPos);
$dotPos = strrpos($file->file, '.');
$apiPost['tim'] = substr($file->file, 0, $dotPos);
}
return $apiPost;
}

46
inc/config.php

@ -607,6 +607,17 @@
* Image settings
* ====================
*/
// Maximum number of images allowed. Increasing this number enabled multi image.
// If you make it more than 1, make sure to enable the below script for the post form to change.
// $config['additional_javascript'][] = 'js/multi_image.js';
$config['max_images'] = 1;
// Method to use for determing the max filesize.
// "split" means that your max filesize is split between the images. For example, if your max filesize
// is 2MB, the filesizes of all files must add up to 2MB for it to work.
// "each" means that each file can be 2MB, so if your max_images is 3, each post could contain 6MB of
// images. "split" is recommended.
$config['multiimage_method'] = 'split';
// For resizing, maximum thumbnail dimensions.
$config['thumb_width'] = 255;
@ -628,22 +639,22 @@
/*
* Thumbnailing method:
*
* 'gd' PHP GD (default). Only handles the most basic image formats (GIF, JPEG, PNG).
* GD is a prerequisite for Tinyboard no matter what method you choose.
* 'gd' PHP GD (default). Only handles the most basic image formats (GIF, JPEG, PNG).
* GD is a prerequisite for Tinyboard no matter what method you choose.
*
* 'imagick' PHP's ImageMagick bindings. Fast and efficient, supporting many image formats.
* A few minor bugs. http://pecl.php.net/package/imagick
* 'imagick' PHP's ImageMagick bindings. Fast and efficient, supporting many image formats.
* A few minor bugs. http://pecl.php.net/package/imagick
*
* 'convert' The command line version of ImageMagick (`convert`). Fixes most of the bugs in
* PHP Imagick. `convert` produces the best still thumbnails and is highly recommended.
* 'convert' The command line version of ImageMagick (`convert`). Fixes most of the bugs in
* PHP Imagick. `convert` produces the best still thumbnails and is highly recommended.
*
* 'gm' GraphicsMagick (`gm`) is a fork of ImageMagick with many improvements. It is more
* efficient and gets thumbnailing done using fewer resources.
* 'gm' GraphicsMagick (`gm`) is a fork of ImageMagick with many improvements. It is more
* efficient and gets thumbnailing done using fewer resources.
*
* 'convert+gifscale'
* OR 'gm+gifsicle' Same as above, with the exception of using `gifsicle` (command line application)
* instead of `convert` for resizing GIFs. It's faster and resulting animated
* thumbnails have less artifacts than if resized with ImageMagick.
* OR 'gm+gifsicle' Same as above, with the exception of using `gifsicle` (command line application)
* instead of `convert` for resizing GIFs. It's faster and resulting animated
* thumbnails have less artifacts than if resized with ImageMagick.
*/
$config['thumb_method'] = 'gd';
// $config['thumb_method'] = 'convert';
@ -693,7 +704,7 @@
// An alternative function for generating image filenames, instead of the default UNIX timestamp.
// $config['filename_func'] = function($post) {
// return sprintf("%s", time() . substr(microtime(), 2, 3));
// return sprintf("%s", time() . substr(microtime(), 2, 3));
// };
// Thumbnail to use for the non-image file uploads.
@ -986,6 +997,7 @@
$config['error']['toolong_body'] = _('The body was too long.');
$config['error']['tooshort_body'] = _('The body was too short or empty.');
$config['error']['noimage'] = _('You must upload an image.');
$config['error']['toomanyimages'] = _('You have attempted to upload too many images!');
$config['error']['nomove'] = _('The server failed to handle your upload.');
$config['error']['fileext'] = _('Unsupported image format.');
$config['error']['noboard'] = _('Invalid board!');
@ -1444,16 +1456,16 @@
$config['search']['enable'] = false;
// Maximal number of queries per IP address per minutes
$config['search']['queries_per_minutes'] = Array(15, 2);
$config['search']['queries_per_minutes'] = Array(15, 2);
// Global maximal number of queries per minutes
$config['search']['queries_per_minutes_all'] = Array(50, 2);
$config['search']['queries_per_minutes_all'] = Array(50, 2);
// Limit of search results
$config['search']['search_limit'] = 100;
$config['search']['search_limit'] = 100;
// Boards for searching
//$config['search']['boards'] = array('a', 'b', 'c', 'd', 'e');
//$config['search']['boards'] = array('a', 'b', 'c', 'd', 'e');
/*
* ====================

21
inc/display.php

@ -94,7 +94,7 @@ function error($message, $priority = true, $debug_stuff = false) {
// Return the bad request header, necessary for AJAX posts
// czaks: is it really so? the ajax errors only work when this is commented out
// better yet use it when ajax is disabled
// better yet use it when ajax is disabled
if (!isset ($_POST['json_response'])) {
header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request');
}
@ -338,6 +338,9 @@ class Post {
foreach ($post as $key => $value) {
$this->{$key} = $value;
}
if (isset($this->files))
$this->files = json_decode($this->files);
$this->subject = utf8tohtml($this->subject);
$this->name = utf8tohtml($this->name);
@ -396,7 +399,7 @@ class Post {
$built .= ' <a title="'._('Ban & Delete').'" href="?/' . $board['dir'] . 'ban&amp;delete/' . $this->id . '">' . $config['mod']['link_bandelete'] . '</a>';
// Delete file (keep post)
if (!empty($this->file) && hasPermission($config['mod']['deletefile'], $board['uri'], $this->mod))
if (!empty($this->files) && hasPermission($config['mod']['deletefile'], $board['uri'], $this->mod))
$built .= ' ' . secure_link_confirm($config['mod']['link_deletefile'], _('Delete file'), _('Are you sure you want to delete this file?'), $board['dir'] . 'deletefile/' . $this->id);
// Spoiler file (keep post)
@ -418,9 +421,6 @@ class Post {
return $built;
}
public function ratio() {
return fraction($this->filewidth, $this->fileheight, ':');
}
public function build($index=false) {
global $board, $config;
@ -439,6 +439,9 @@ class Thread {
$this->{$key} = $value;
}
if (isset($this->files))
$this->files = json_decode($this->files);
$this->subject = utf8tohtml($this->subject);
$this->name = utf8tohtml($this->name);
$this->mod = $mod;
@ -477,7 +480,7 @@ class Thread {
$this->posts[] = $post;
}
public function postCount() {
return count($this->posts) + $this->omitted;
return count($this->posts) + $this->omitted;
}
public function postControls() {
global $board, $config;
@ -506,7 +509,7 @@ class Thread {
$built .= ' <a title="'._('Ban & Delete').'" href="?/' . $board['dir'] . 'ban&amp;delete/' . $this->id . '">' . $config['mod']['link_bandelete'] . '</a>';
// Delete file (keep post)
if (!empty($this->file) && $this->file != 'deleted' && hasPermission($config['mod']['deletefile'], $board['uri'], $this->mod))
if (!empty($this->files) && $this->files[0]->file != 'deleted' && hasPermission($config['mod']['deletefile'], $board['uri'], $this->mod))
$built .= ' ' . secure_link_confirm($config['mod']['link_deletefile'], _('Delete file'), _('Are you sure you want to delete this file?'), $board['dir'] . 'deletefile/' . $this->id);
// Spoiler file (keep post)
@ -546,10 +549,6 @@ class Thread {
return $built;
}
public function ratio() {
return fraction($this->filewidth, $this->fileheight, ':');
}
public function build($index=false, $isnoko50=false) {
global $board, $config, $debug;

89
inc/functions.php

@ -96,9 +96,9 @@ function loadConfig() {
$configstr = file_get_contents('inc/instance-config.php');
if (isset($board['dir']) && file_exists($board['dir'] . '/config.php')) {
$configstr .= file_get_contents($board['dir'] . '/config.php');
}
if (isset($board['dir']) && file_exists($board['dir'] . '/config.php')) {
$configstr .= file_get_contents($board['dir'] . '/config.php');
}
$matches = array();
preg_match_all('/[^\/*#]\$config\s*\[\s*[\'"]locale[\'"]\s*\]\s*=\s*([\'"])(.*?)\1/', $configstr, $matches);
if ($matches && isset ($matches[2]) && $matches[2]) {
@ -859,7 +859,7 @@ function insertFloodPost(array $post) {
function post(array $post) {
global $pdo, $board;
$query = prepare(sprintf("INSERT INTO ``posts_%s`` VALUES ( NULL, :thread, :subject, :email, :name, :trip, :capcode, :body, :body_nomarkup, :time, :time, :thumb, :thumbwidth, :thumbheight, :file, :width, :height, :filesize, :filename, :filehash, :password, :ip, :sticky, :locked, 0, :embed)", $board['uri']));
$query = prepare(sprintf("INSERT INTO ``posts_%s`` VALUES ( NULL, :thread, :subject, :email, :name, :trip, :capcode, :body, :body_nomarkup, :time, :time, :files, :num_files, :filehash, :password, :ip, :sticky, :locked, 0, :embed)", $board['uri']));
// Basic stuff
if (!empty($post['subject'])) {
@ -919,31 +919,12 @@ function post(array $post) {
}
if ($post['has_file']) {
$query->bindValue(':thumb', $post['thumb']);
$query->bindValue(':thumbwidth', $post['thumbwidth'], PDO::PARAM_INT);
$query->bindValue(':thumbheight', $post['thumbheight'], PDO::PARAM_INT);
$query->bindValue(':file', $post['file']);
if (isset($post['width'], $post['height'])) {
$query->bindValue(':width', $post['width'], PDO::PARAM_INT);
$query->bindValue(':height', $post['height'], PDO::PARAM_INT);
} else {
$query->bindValue(':width', null, PDO::PARAM_NULL);
$query->bindValue(':height', null, PDO::PARAM_NULL);
}
$query->bindValue(':filesize', $post['filesize'], PDO::PARAM_INT);
$query->bindValue(':filename', $post['filename']);
$query->bindValue(':files', json_encode($post['files']));
$query->bindValue(':num_files', $post['num_files']);
$query->bindValue(':filehash', $post['filehash']);
} else {
$query->bindValue(':thumb', null, PDO::PARAM_NULL);
$query->bindValue(':thumbwidth', null, PDO::PARAM_NULL);
$query->bindValue(':thumbheight', null, PDO::PARAM_NULL);
$query->bindValue(':file', null, PDO::PARAM_NULL);
$query->bindValue(':width', null, PDO::PARAM_NULL);
$query->bindValue(':height', null, PDO::PARAM_NULL);
$query->bindValue(':filesize', null, PDO::PARAM_NULL);
$query->bindValue(':filename', null, PDO::PARAM_NULL);
$query->bindValue(':files', null, PDO::PARAM_NULL);
$query->bindValue(':num_files', 0);
$query->bindValue(':filehash', null, PDO::PARAM_NULL);
}
@ -974,28 +955,31 @@ function bumpThread($id) {
function deleteFile($id, $remove_entirely_if_already=true) {
global $board, $config;
$query = prepare(sprintf("SELECT `thread`,`thumb`,`file` FROM ``posts_%s`` WHERE `id` = :id LIMIT 1", $board['uri']));
$query = prepare(sprintf("SELECT `thread`, `files` FROM ``posts_%s`` WHERE `id` = :id LIMIT 1", $board['uri']));
$query->bindValue(':id', $id, PDO::PARAM_INT);
$query->execute() or error(db_error($query));
if (!$post = $query->fetch(PDO::FETCH_ASSOC))
error($config['error']['invalidpost']);
$files = json_decode($post['files']);
if ($post['file'] == 'deleted' && !$post['thread'])
if ($files[0]->file == 'deleted' && !$post['thread'])
return; // Can't delete OP's image completely.
$query = prepare(sprintf("UPDATE ``posts_%s`` SET `thumb` = NULL, `thumbwidth` = NULL, `thumbheight` = NULL, `filewidth` = NULL, `fileheight` = NULL, `filesize` = NULL, `filename` = NULL, `filehash` = NULL, `file` = :file WHERE `id` = :id", $board['uri']));
if ($post['file'] == 'deleted' && $remove_entirely_if_already) {
$query = prepare(sprintf("UPDATE ``posts_%s`` SET `files` = :file WHERE `id` = :id", $board['uri']));
if ($files[0]->file == 'deleted' && $remove_entirely_if_already) {
// Already deleted; remove file fully
$query->bindValue(':file', null, PDO::PARAM_NULL);
} else {
// Delete thumbnail
file_unlink($board['dir'] . $config['dir']['thumb'] . $post['thumb']);
foreach ($files as $i => $file) {
// Delete thumbnail
file_unlink($board['dir'] . $config['dir']['thumb'] . $file->thumb);
// Delete file
file_unlink($board['dir'] . $config['dir']['img'] . $post['file']);
// Delete file
file_unlink($board['dir'] . $config['dir']['img'] . $file->file);
}
// Set file to 'deleted'
$query->bindValue(':file', 'deleted', PDO::PARAM_INT);
$query->bindValue(':file', '[{"file":"deleted"}]', PDO::PARAM_STR);
}
$query->bindValue(':id', $id, PDO::PARAM_INT);
@ -1035,7 +1019,7 @@ function deletePost($id, $error_if_doesnt_exist=true, $rebuild_after=true) {
global $board, $config;
// Select post and replies (if thread) in one query
$query = prepare(sprintf("SELECT `id`,`thread`,`thumb`,`file` FROM ``posts_%s`` WHERE `id` = :id OR `thread` = :id", $board['uri']));
$query = prepare(sprintf("SELECT `id`,`thread`,`files` FROM ``posts_%s`` WHERE `id` = :id OR `thread` = :id", $board['uri']));
$query->bindValue(':id', $id, PDO::PARAM_INT);
$query->execute() or error(db_error($query));
@ -1065,13 +1049,12 @@ function deletePost($id, $error_if_doesnt_exist=true, $rebuild_after=true) {
// Rebuild thread
$rebuild = &$post['thread'];
}
if ($post['thumb']) {
// Delete thumbnail
file_unlink($board['dir'] . $config['dir']['thumb'] . $post['thumb']);
}
if ($post['file']) {
if ($post['files']) {
// Delete file
file_unlink($board['dir'] . $config['dir']['img'] . $post['file']);
foreach (json_decode($post['files']) as $i => $f) {
file_unlink($board['dir'] . $config['dir']['img'] . $f->file);
file_unlink($board['dir'] . $config['dir']['thumb'] . $f->thumb);
}
}
$ids[] = (int)$post['id'];
@ -1188,8 +1171,8 @@ function index($page, $mod=false) {
$num_images = 0;
foreach ($replies as $po) {
if ($po['file'])
$num_images++;
if ($po['num_files'])
$num_images+=$po['num_files'];
$thread->add(new Post($po, $mod ? '?/' : $config['root'], $mod));
}
@ -1347,7 +1330,7 @@ function checkRobot($body) {
// Returns an associative array with 'replies' and 'images' keys
function numPosts($id) {
global $board;
$query = prepare(sprintf("SELECT COUNT(*) AS `replies`, COUNT(NULLIF(`file`, 0)) AS `images` FROM ``posts_%s`` WHERE `thread` = :thread", $board['uri'], $board['uri']));
$query = prepare(sprintf("SELECT COUNT(*) AS `replies`, SUM(`num_files`) AS `images` FROM ``posts_%s`` WHERE `thread` = :thread", $board['uri'], $board['uri']));
$query->bindValue(':thread', $id, PDO::PARAM_INT);
$query->execute() or error(db_error($query));
@ -1905,7 +1888,7 @@ function markup(&$body, $track_cites = false) {
}
// replace tabs with 8 spaces
$body = str_replace("\t", ' ', $body);
$body = str_replace("\t", ' ', $body);
return $tracked_cites;
}
@ -2232,13 +2215,15 @@ function getPostByHashInThread($hash, $thread) {
}
function undoImage(array $post) {
if (!$post['has_file'])
if (!$post['has_file'] || !isset($post['files']))
return;
if (isset($post['file_path']))
file_unlink($post['file_path']);
if (isset($post['thumb_path']))
file_unlink($post['thumb_path']);
foreach ($post['files'] as $key => $file) {
if (isset($file['file_path']))
file_unlink($file['file_path']);
if (isset($file['thumb_path']))
file_unlink($file['thumb_path']);
}
}
function rDNS($ip_addr) {

6
inc/lib/Twig/Extensions/Extension/Tinyboard.php

@ -25,7 +25,7 @@ class Twig_Extensions_Extension_Tinyboard extends Twig_Extension
new Twig_SimpleFilter('until', 'until'),
new Twig_SimpleFilter('push', 'twig_push_filter'),
new Twig_SimpleFilter('bidi_cleanup', 'bidi_cleanup'),
new Twig_SimpleFilter('addslashes', 'addslashes')
new Twig_SimpleFilter('addslashes', 'addslashes'),
);
}
@ -42,6 +42,7 @@ class Twig_Extensions_Extension_Tinyboard extends Twig_Extension
new Twig_SimpleFunction('timezone', 'twig_timezone_function'),
new Twig_SimpleFunction('hiddenInputs', 'hiddenInputs'),
new Twig_SimpleFunction('hiddenInputsHash', 'hiddenInputsHash'),
new Twig_SimpleFunction('ratio', 'twig_ratio_function')
);
}
@ -100,3 +101,6 @@ function twig_truncate_filter($value, $length = 30, $preserve = false, $separato
return $value;
}
function twig_ratio_function($w, $h) {
return fraction($w, $h, ':');
}

6
inc/mod/pages.php

@ -1538,10 +1538,10 @@ function mod_deletefile($board, $post) {
function mod_spoiler_image($board, $post) {
global $config, $mod;
if (!openBoard($board))
error($config['error']['noboard']);
if (!hasPermission($config['mod']['spoilerimage'], $board))
error($config['error']['noaccess']);
@ -1572,7 +1572,7 @@ function mod_spoiler_image($board, $post) {
// Rebuild themes
rebuildThemes('post-delete', $board);
// Redirect
header('Location: ?/' . sprintf($config['board_path'], $board) . $config['file_index'], true, $config['redirect_http']);
}

25
install.php

@ -1,7 +1,7 @@
<?php
// Installation/upgrade file
define('VERSION', '4.4.98');
define('VERSION', '4.9.90');
require 'inc/functions.php';
@ -260,7 +260,7 @@ if (file_exists($config['has_installed'])) {
$_query->execute() or error(db_error($_query));
}
}
case 'v0.9.6-dev-9':
case 'v0.9.6-dev-9':
case 'v0.9.6-dev-9 + <a href="https://github.com/vichan-devel/Tinyboard/">vichan-devel-4.0.3</a>':
case 'v0.9.6-dev-9 + <a href="https://github.com/vichan-devel/Tinyboard/">vichan-devel-4.0.4-gold</a>':
case 'v0.9.6-dev-9 + <a href="https://github.com/vichan-devel/Tinyboard/">vichan-devel-4.0.5-gold</a>':
@ -521,6 +521,14 @@ if (file_exists($config['has_installed'])) {
case '4.4.98-pre':
if (!$twig) load_twig();
$twig->clearCacheFiles();
case '4.4.98':
foreach ($boards as &$board) {
query(sprintf('ALTER TABLE ``posts_%s`` ADD `files` text DEFAULT NULL AFTER `bump`;', $board['uri'])) or error(db_error());
query(sprintf('ALTER TABLE ``posts_%s`` ADD `num_files` int(11) DEFAULT 0 AFTER `files`;', $board['uri'])) or error(db_error());
query(sprintf('UPDATE ``posts_%s`` SET `files` = CONCAT(\'[{"file":"\',`filename`,\'", "size":"\',`filesize`,\'", "width":"\',`filewidth`,\'","height":"\',`fileheight`,\'","thumbwidth":"\',`thumbwidth`,\'","thumbheight":"\',`thumbheight`,\'", "file_path":"%s\/src\/\',`filename`,\'","thumb_path":"%s\/thumb\/\',`filename`,\'"}]\') WHERE `file` IS NOT NULL', $board['uri'], $board['uri'], $board['uri'])) or error(db_error());
query(sprintf('ALTER TABLE ``posts_%s`` DROP COLUMN `thumb`, DROP COLUMN `thumbwidth`, DROP COLUMN `thumbheight`, DROP COLUMN `file`, DROP COLUMN `fileheight`, DROP COLUMN `filesize`, DROP COLUMN `filename`', $board['uri'])) or error(db_error());
query(sprintf('ALTER TABLE ``posts_%s`` REBUILD', $board['uri'])) or error(db_error());
}
case false:
// TODO: enhance Tinyboard -> vichan upgrade path.
query("CREATE TABLE IF NOT EXISTS ``search_queries`` ( `ip` varchar(39) NOT NULL, `time` int(11) NOT NULL, `query` text NOT NULL) ENGINE=MyISAM DEFAULT CHARSET=utf8;") or error(db_error());
@ -586,7 +594,7 @@ if ($step == 0) {
'required' => false
)
);
$tests = array(
array(
'category' => 'PHP',
@ -679,6 +687,13 @@ if ($step == 0) {
'required' => false,
'message' => '(Optional) `gifsicle` was not found or executable; you may not use `convert+gifsicle` for better animated GIF thumbnailing.',
),
array(
'category' => 'Image processing',
'name' => '`md5sum` (quick file hashing)',
'result' => $can_exec && shell_exec('echo "vichan" | md5sum') == "141225c362da02b5c359c45b665168de -\n",
'required' => false,
'message' => '(Optional) `md5sum` was not found or executable; file hashing for multiple images will be slower.',
),
array(
'category' => 'File permissions',
'name' => getcwd(),
@ -833,9 +848,9 @@ if ($step == 0) {
}
file_write($config['has_installed'], VERSION);
if (!file_unlink(__FILE__)) {
/*if (!file_unlink(__FILE__)) {
$page['body'] .= '<div class="ban"><h2>Delete install.php!</h2><p>I couldn\'t remove <strong>install.php</strong>. You will have to remove it manually.</p></div>';
}
}*/
}
echo Element('page.html', $page);

30
js/multi_image.js

@ -0,0 +1,30 @@
/*
* multi-image.js - Add support for multiple images to the post form
*
* Copyright (c) 2014 Fredrick Brennan <admin@8chan.co>
*
* Usage:
* $config['max_images'] = 3;
* $config['additional_javascript'][] = 'js/jquery.min.js';
* $config['additional_javascript'][] = 'js/multi-image.js';
*/
function multi_image() {
$('input[type=file]').after('<a href="#" class="add_image">+</a>');
$(document).on('click', 'a.add_image', function(e) {
e.preventDefault();
$('#upload_url').remove();
var images_len = $('input[type=file]').length;
if (!(images_len >= max_images)) {
$('.add_image').after('<br/><input type="file" name="file'+(images_len+1)+'" id="upload_file'+(images_len+1)+'">');
if (typeof setup_form !== 'undefined') setup_form($('form[name="post"]'));
}
})
}
if (active_page == 'thread' || active_page == 'index' && max_images > 1) {
$(document).ready(multi_image);
}

224
post.php

@ -150,7 +150,7 @@ if (isset($_POST['delete'])) {
if (!isset($_POST['body'], $_POST['board']))
error($config['error']['bot']);
$post = array('board' => $_POST['board']);
$post = array('board' => $_POST['board'], 'files' => array());
// Check if board exists
if (!openBoard($post['board']))
@ -178,7 +178,7 @@ if (isset($_POST['delete'])) {
$post['op'] = true;
if (!(($post['op'] && $_POST['post'] == $config['button_newtopic']) ||
(!$post['op'] && $_POST['post'] == $config['button_reply'])))
(!$post['op'] && $_POST['post'] == $config['button_reply'])))
error($config['error']['bot']);
// Check the referrer
@ -328,21 +328,12 @@ if (isset($_POST['delete'])) {
);
}
// Check for a file
if ($post['op'] && !isset($post['no_longer_require_an_image_for_op'])) {
if (!isset($_FILES['file']['tmp_name']) || $_FILES['file']['tmp_name'] == '' && $config['force_image_op'])
error($config['error']['noimage']);
}
$post['name'] = $_POST['name'] != '' ? $_POST['name'] : $config['anonymous'];
$post['subject'] = $_POST['subject'];
$post['email'] = str_replace(' ', '%20', htmlspecialchars($_POST['email']));
$post['body'] = $_POST['body'];
$post['password'] = $_POST['password'];
$post['has_file'] = !isset($post['embed']) && (($post['op'] && !isset($post['no_longer_require_an_image_for_op']) && $config['force_image_op']) || (isset($_FILES['file']) && $_FILES['file']['tmp_name'] != ''));
if ($post['has_file'])
$post['filename'] = urldecode(get_magic_quotes_gpc() ? stripslashes($_FILES['file']['name']) : $_FILES['file']['name']);
$post['has_file'] = (!isset($post['embed']) && (($post['op'] && !isset($post['no_longer_require_an_image_for_op']) && $config['force_image_op']) || !empty($_FILES)));
if (!($post['has_file'] || isset($post['embed'])) || (($post['op'] && $config['force_body_op']) || (!$post['op'] && $config['force_body']))) {
$stripped_whitespace = preg_replace('/[\s]/u', '', $post['body']);
@ -367,6 +358,22 @@ if (isset($_POST['delete'])) {
}
if ($post['has_file']) {
// Determine size sanity
$size = 0;
if ($config['multiimage_method'] == 'split') {
foreach ($_FILES as $key => $file) {
$size += $file['size'];
}
} elseif ($config['multiimage_method'] == 'each') {
foreach ($_FILES as $key => $file) {
if ($file['size'] > $size) {
$size = $file['size'];
}
}
} else {
error(_('Unrecognized file size determination method.'));
}
$size = $_FILES['file']['size'];
if ($size > $config['max_filesize'])
error(sprintf3($config['error']['filesize'], array(
@ -374,6 +381,7 @@ if (isset($_POST['delete'])) {
'filesz' => number_format($size),
'maxsz' => number_format($config['max_filesize'])
)));
$post['filesize'] = $size;
}
@ -409,16 +417,39 @@ if (isset($_POST['delete'])) {
} else $noko = $config['always_noko'];
if ($post['has_file']) {
$post['extension'] = strtolower(mb_substr($post['filename'], mb_strrpos($post['filename'], '.') + 1));
if (isset($config['filename_func']))
$post['file_id'] = $config['filename_func']($post);
else
$post['file_id'] = time() . substr(microtime(), 2, 3);
$post['file'] = $board['dir'] . $config['dir']['img'] . $post['file_id'] . '.' . $post['extension'];
$post['thumb'] = $board['dir'] . $config['dir']['thumb'] . $post['file_id'] . '.' . ($config['thumb_ext'] ? $config['thumb_ext'] : $post['extension']);
$i = 0;
foreach ($_FILES as $key => $file) {
if ($file['size'] && $file['tmp_name']) {
$file['filename'] = urldecode(get_magic_quotes_gpc() ? stripslashes($file['name']) : $file['name']);
$file['extension'] = strtolower(mb_substr($file['filename'], mb_strrpos($file['filename'], '.') + 1));
if (isset($config['filename_func']))
$file['file_id'] = $config['filename_func']($file);
else
$file['file_id'] = time() . substr(microtime(), 2, 3);
if (sizeof($_FILES) > 1)
$file['file_id'] .= "-$i";
$file['file'] = $board['dir'] . $config['dir']['img'] . $file['file_id'] . '.' . $file['extension'];
$file['thumb'] = $board['dir'] . $config['dir']['thumb'] . $file['file_id'] . '.' . ($config['thumb_ext'] ? $config['thumb_ext'] : $file['extension']);
$post['files'][] = $file;
$i++;
}
}
}
if (empty($post['files'])) $post['has_file'] = false;
// Check for a file
if ($post['op'] && !isset($post['no_longer_require_an_image_for_op'])) {
if (!$post['has_file'] && $config['force_image_op'])
error($config['error']['noimage']);
}
// Check for too many files
if (sizeof($post['files']) > $config['max_images'])
error($config['error']['toomanyimages']);
if ($config['strip_combining_chars']) {
$post['name'] = strip_combining_chars($post['name']);
$post['email'] = strip_combining_chars($post['email']);
@ -475,7 +506,7 @@ if (isset($_POST['delete'])) {
$user_flag = $_POST['user_flag'];
if (!isset($config['user_flags'][$user_flag]))
error('Invalid flag selection!');
error(_('Invalid flag selection!'));
$flag_alt = isset($user_flag_alt) ? $user_flag_alt : $config['user_flags'][$user_flag];
@ -505,21 +536,38 @@ if (isset($_POST['delete'])) {
if ($post['has_file']) {
if (!in_array($post['extension'], $config['allowed_ext']) && !in_array($post['extension'], $config['allowed_ext_files']))
error($config['error']['unknownext']);
$is_an_image = !in_array($post['extension'], $config['allowed_ext_files']);
// Truncate filename if it is too long
$post['filename'] = mb_substr($post['filename'], 0, $config['max_filename_len']);
$upload = $_FILES['file']['tmp_name'];
if (!is_readable($upload))
error($config['error']['nomove']);
foreach ($post['files'] as $key => &$file) {
if (!in_array($file['extension'], $config['allowed_ext']) && !in_array($file['extension'], $config['allowed_ext_files']))
error($config['error']['unknownext']);
$file['is_an_image'] = !in_array($file['extension'], $config['allowed_ext_files']);
// Truncate filename if it is too long
$file['filename'] = mb_substr($file['filename'], 0, $config['max_filename_len']);
if (!isset($filenames)) {
$filenames = escapeshellarg($file['tmp_name']);
} else {
$filenames .= (' ' . escapeshellarg($file['tmp_name']));
}
$upload = $file['tmp_name'];
if (!is_readable($upload))
error($config['error']['nomove']);
}
$post['filehash'] = md5_file($upload);
$post['filesize'] = filesize($upload);
if ($output = shell_exec_error("cat $filenames | md5sum")) {
$hash = explode(' ', $output)[0];
$post['filehash'] = $hash;
} elseif ($config['max_images'] === 1) {
$post['filehash'] = md5_file($upload);
} else {
$str_to_hash = '';
foreach (explode(' ', $filenames) as $i => $f) {
$str_to_hash .= file_get_contents($f);
}
$post['filehash'] = md5($str_to_hash);
}
}
if (!hasPermission($config['mod']['bypass_filters'], $board['uri'])) {
@ -529,7 +577,8 @@ if (isset($_POST['delete'])) {
}
if ($post['has_file']) {
if ($is_an_image && $config['ie_mime_type_detection'] !== false) {
foreach ($post['files'] as $key => &$file) {
if ($file['is_an_image'] && $config['ie_mime_type_detection'] !== false) {
// Check IE MIME type detection XSS exploit
$buffer = file_get_contents($upload, null, null, null, 255);
if (preg_match($config['ie_mime_type_detection'], $buffer)) {
@ -540,7 +589,7 @@ if (isset($_POST['delete'])) {
require_once 'inc/image.php';
// find dimensions of an image using GD
if (!$size = @getimagesize($upload)) {
if (!$size = @getimagesize($file['tmp_name'])) {
error($config['error']['invalidimg']);
}
if ($size[0] > $config['max_width'] || $size[1] > $config['max_height']) {
@ -548,93 +597,93 @@ if (isset($_POST['delete'])) {
}
if ($config['convert_auto_orient'] && ($post['extension'] == 'jpg' || $post['extension'] == 'jpeg')) {
if ($config['convert_auto_orient'] && ($file['extension'] == 'jpg' || $file['extension'] == 'jpeg')) {
// The following code corrects the image orientation.
// Currently only works with the 'convert' option selected but it could easily be expanded to work with the rest if you can be bothered.
if (!($config['redraw_image'] || (($config['strip_exif'] && !$config['use_exiftool']) && ($post['extension'] == 'jpg' || $post['extension'] == 'jpeg')))) {
if (!($config['redraw_image'] || (($config['strip_exif'] && !$config['use_exiftool']) && ($file['extension'] == 'jpg' || $file['extension'] == 'jpeg')))) {
if (in_array($config['thumb_method'], array('convert', 'convert+gifsicle', 'gm', 'gm+gifsicle'))) {
$exif = @exif_read_data($upload);
$exif = @exif_read_data($file['tmp_name']);
$gm = in_array($config['thumb_method'], array('gm', 'gm+gifsicle'));
if (isset($exif['Orientation']) && $exif['Orientation'] != 1) {
if ($config['convert_manual_orient']) {
$error = shell_exec_error(($gm ? 'gm ' : '') . 'convert ' .
escapeshellarg($upload) . ' ' .
escapeshellarg($file['tmp_name']) . ' ' .
ImageConvert::jpeg_exif_orientation(false, $exif) . ' ' .
($config['strip_exif'] ? '+profile "*"' :
($config['use_exiftool'] ? '' : '+profile "*"')
) . ' ' .
escapeshellarg($upload));
escapeshellarg($file['tmp_name']));
if ($config['use_exiftool'] && !$config['strip_exif']) {
if ($exiftool_error = shell_exec_error(
'exiftool -overwrite_original -q -q -orientation=1 -n ' .
escapeshellarg($upload)))
error('exiftool failed!', null, $exiftool_error);
escapeshellarg($file['tmp_name'])))
error(_('exiftool failed!'), null, $exiftool_error);
} else {
// TODO: Find another way to remove the Orientation tag from the EXIF profile
// without needing `exiftool`.
}
} else {
$error = shell_exec_error(($gm ? 'gm ' : '') . 'convert ' .
escapeshellarg($upload) . ' -auto-orient ' . escapeshellarg($upload));
escapeshellarg($file['tmp_name']) . ' -auto-orient ' . escapeshellarg($upload));
}
if ($error)
error('Could not auto-orient image!', null, $error);
$size = @getimagesize($upload);
error(_('Could not auto-orient image!'), null, $error);
$size = @getimagesize($file['tmp_name']);
if ($config['strip_exif'])
$post['exif_stripped'] = true;
$file['exif_stripped'] = true;
}
}
}
}
// create image object
$image = new Image($upload, $post['extension'], $size);
$image = new Image($file['tmp_name'], $file['extension'], $size);
if ($image->size->width > $config['max_width'] || $image->size->height > $config['max_height']) {
$image->delete();
error($config['error']['maxsize']);
}
$post['width'] = $image->size->width;
$post['height'] = $image->size->height;
$file['width'] = $image->size->width;
$file['height'] = $image->size->height;
if ($config['spoiler_images'] && isset($_POST['spoiler'])) {
$post['thumb'] = 'spoiler';
$file['thumb'] = 'spoiler';
$size = @getimagesize($config['spoiler_image']);
$post['thumbwidth'] = $size[0];
$post['thumbheight'] = $size[1];
$file['thumbwidth'] = $size[0];
$file['thumbheight'] = $size[1];
} elseif ($config['minimum_copy_resize'] &&
$image->size->width <= $config['thumb_width'] &&
$image->size->height <= $config['thumb_height'] &&
$post['extension'] == ($config['thumb_ext'] ? $config['thumb_ext'] : $post['extension'])) {
$file['extension'] == ($config['thumb_ext'] ? $config['thumb_ext'] : $file['extension'])) {
// Copy, because there's nothing to resize
copy($upload, $post['thumb']);
copy($file['tmp_name'], $file['thumb']);
$post['thumbwidth'] = $image->size->width;
$post['thumbheight'] = $image->size->height;
$file['thumbwidth'] = $image->size->width;
$file['thumbheight'] = $image->size->height;
} else {
$thumb = $image->resize(
$config['thumb_ext'] ? $config['thumb_ext'] : $post['extension'],
$config['thumb_ext'] ? $config['thumb_ext'] : $file['extension'],
$post['op'] ? $config['thumb_op_width'] : $config['thumb_width'],
$post['op'] ? $config['thumb_op_height'] : $config['thumb_height']
);
$thumb->to($post['thumb']);
$thumb->to($file['thumb']);
$post['thumbwidth'] = $thumb->width;
$post['thumbheight'] = $thumb->height;
$file['thumbwidth'] = $thumb->width;
$file['thumbheight'] = $thumb->height;
$thumb->_destroy();
}
if ($config['redraw_image'] || ([email protected]$post['exif_stripped'] && $config['strip_exif'] && ($post['extension'] == 'jpg' || $post['extension'] == 'jpeg'))) {
if ($config['redraw_image'] || ([email protected]$file['exif_stripped'] && $config['strip_exif'] && ($file['extension'] == 'jpg' || $file['extension'] == 'jpeg'))) {
if (!$config['redraw_image'] && $config['use_exiftool']) {
if($error = shell_exec_error('exiftool -overwrite_original -ignoreMinorErrors -q -q -all= ' .
escapeshellarg($upload)))
error('Could not strip EXIF metadata!', null, $error);
escapeshellarg($file['tmp_name'])))
error(_('Could not strip EXIF metadata!', null, $error));
} else {
$image->to($post['file']);
$image->to($file['file']);
$dont_copy_file = true;
}
}
@ -642,26 +691,25 @@ if (isset($_POST['delete'])) {
} else {
// not an image
//copy($config['file_thumb'], $post['thumb']);
$post['thumb'] = 'file';
$file['thumb'] = 'file';
$size = @getimagesize(sprintf($config['file_thumb'],
isset($config['file_icons'][$post['extension']]) ?
$config['file_icons'][$post['extension']] : $config['file_icons']['default']));
$post['thumbwidth'] = $size[0];
$post['thumbheight'] = $size[1];
isset($config['file_icons'][$file['extension']]) ?
$config['file_icons'][$file['extension']] : $config['file_icons']['default']));
$file['thumbwidth'] = $size[0];
$file['thumbheight'] = $size[1];
}
if (!isset($dont_copy_file) || !$dont_copy_file) {
if (isset($post['file_tmp'])) {
if ([email protected]($upload, $post['file']))
if (isset($file['file_tmp'])) {
if ([email protected]($file['tmp_name'], $file['file']))
error($config['error']['nomove']);
chmod($post['file'], 0644);
} elseif ([email protected]_uploaded_file($upload, $post['file']))
chmod($file['file'], 0644);
} elseif ([email protected]_uploaded_file($file['tmp_name'], $file['file']))
error($config['error']['nomove']);
}
}
if ($post['has_file']) {
}
if ($config['image_reject_repost']) {
if ($p = getPostByHash($post['filehash'])) {
undoImage($post);
@ -689,7 +737,7 @@ if (isset($_POST['delete'])) {
));
}
}
}
}
if (!hasPermission($config['mod']['postunoriginal'], $board['uri']) && $config['robot_enable'] && checkRobot($post['body_nomarkup'])) {
undoImage($post);
@ -702,11 +750,13 @@ if (isset($_POST['delete'])) {
// Remove board directories before inserting them into the database.
if ($post['has_file']) {
$post['file_path'] = $post['file'];
$post['thumb_path'] = $post['thumb'];
$post['file'] = mb_substr($post['file'], mb_strlen($board['dir'] . $config['dir']['img']));
if ($is_an_image && $post['thumb'] != 'spoiler')
$post['thumb'] = mb_substr($post['thumb'], mb_strlen($board['dir'] . $config['dir']['thumb']));
foreach ($post['files'] as $key => &$file) {
$file['file_path'] = $file['file'];
$file['thumb_path'] = $file['thumb'];
$file['file'] = mb_substr($file['file'], mb_strlen($board['dir'] . $config['dir']['img']));
if ($file['is_an_image'] && $file['thumb'] != 'spoiler')
$file['thumb'] = mb_substr($file['thumb'], mb_strlen($board['dir'] . $config['dir']['thumb']));
}
}
$post = (object)$post;
@ -715,6 +765,10 @@ if (isset($_POST['delete'])) {
error($error);
}
$post = (array)$post;
if ($post['files'])
$post['files'] = $post['files'];
$post['num_files'] = sizeof($post['files']);
$post['id'] = $id = post($post);

3
stylesheets/style.css

@ -109,6 +109,9 @@ form table tr td div label {
.unimportant, .unimportant * {
font-size: 10px;
}
.file {
float: left;
}
p.fileinfo {
display: block;
margin: 0;

1
templates/main.js

@ -291,6 +291,7 @@ function ready() {
{% endraw %}
var post_date = "{{ config.post_date }}";
var max_images = {{ config.max_images }};
onready(init);

38
templates/post/fileinfo.html

@ -0,0 +1,38 @@
{% if post.embed %}
{{ post.embed }}
{% else %}
<div class="files">
{% for file in post.files %}
<div class="file">
{% if file.file == 'deleted' %}
<img class="post-image deleted" src="{{ config.image_deleted }}" alt="" />
{% else %}
<p class="fileinfo">File: <a href="{{ config.uri_img }}{{ file.file }}">{{ file.file }}</a> <span class="unimportant">
(
{% if file.thumb == 'spoiler' %}
{% trans %}Spoiler Image{% endtrans %},
{% endif %}
{{ file.size|filesize }}
{% if file.width and file.height %}
, {{ file.width}}x{{ file.height }}
{% if config.show_ratio %}
, {{ ratio(file.width, file.height) }}
{% endif %}
{% endif %}
{% if config.show_filename and file.filename %}
,
{% if file.filename|length > config.max_filename_display %}
<span class="postfilename" title="{{ file.filename|e }}">{{ file.filename|truncate(config.max_filename_display)|bidi_cleanup }}</span>
{% else %}
<span class="postfilename">{{ file.filename|e|bidi_cleanup }}</span>
{% endif %}
{% endif %}
{% include "post/image_identification.html" %}
)
</span></p>
{% include "post/image.html" with {'post':file} %}
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}

34
templates/post_reply.html

@ -1,7 +1,6 @@
{% filter remove_whitespace %}
{# tabs and new lines will be ignored #}
<div class="post reply" id="reply_{{ post.id }}">
<p class="intro"{% if not index %} id="{{ post.id }}"{% endif %}>
<input type="checkbox" class="delete" name="delete_{{ post.id }}" id="delete_{{ post.id }}" />
<label for="delete_{{ post.id }}">
@ -18,38 +17,9 @@
{{ post.id }}
</a>
</p>
{% if post.embed %}
{{ post.embed }}
{% elseif post.file == 'deleted' %}
<img class="post-image deleted" src="{{ config.image_deleted }}" alt="" />
{% elseif post.file and post.file %}
<p class="fileinfo">File: <a href="{{ config.uri_img }}{{ post.file }}">{{ post.file }}</a> <span class="unimportant">
(
{% if post.thumb == 'spoiler' %}
{% trans %}Spoiler Image{% endtrans %},
{% endif %}
{{ post.filesize|filesize }}
{% if post.filewidth and post.fileheight %}
, {{ post.filewidth}}x{{ post.fileheight }}
{% if config.show_ratio %}
, {{ post.ratio }}
{% endif %}
{% endif %}
{% if config.show_filename and post.filename %}
,
{% if post.filename|length > config.max_filename_display %}
<span class="postfilename" title="{{ post.filename|e }}">{{ post.filename|truncate(config.max_filename_display)|bidi_cleanup }}</span>
{% else %}
<span class="postfilename">{{ post.filename|e|bidi_cleanup }}</span>
{% endif %}
{% endif %}
{% include "post/image_identification.html" %}
)
</span></p>
{% include "post/image.html" %}
{% endif %}
{% include 'post/fileinfo.html' %}
{{ post.postControls }}
<div class="body">
<div class="body" {% if post.files|length > 1 %}style="clear:both"{% endif %}>
{% endfilter %}{% if index %}{{ post.body|truncate_body(post.link) }}{% else %}{{ post.body }}{% endif %}{% filter remove_whitespace %}
{% if post.modifiers['ban message'] %}
{{ config.mod.ban_message|sprintf(post.modifiers['ban message']) }}

34
templates/post_thread.html

@ -2,38 +2,8 @@
{# tabs and new lines will be ignored #}
<div id="thread_{{ post.id }}" data-board="{{ board.uri }}">
{% if post.embed %}
{{ post.embed }}
{% elseif post.file == 'deleted' %}
<img class="post-image deleted" src="{{ config.image_deleted }}" alt="" />
{% elseif post.file and post.file %}
<p class="fileinfo">{% trans %}File:{% endtrans %} <a href="{{ config.uri_img }}{{ post.file }}">{{ post.file }}</a> <span class="unimportant">
(
{% if post.thumb == 'spoiler' %}
{% trans %}Spoiler Image{% endtrans %},
{% endif %}
{{ post.filesize|filesize }}
{% if post.filewidth and post.fileheight %}
, {{ post.filewidth}}x{{ post.fileheight }}
{% if config.show_ratio %}
, {{ post.ratio }}
{% endif %}
{% endif %}
{% if config.show_filename and post.filename %}
,
{% if post.filename|length > config.max_filename_display %}
<span class="postfilename" title="{{ post.filename|e }}">{{ post.filename|truncate(config.max_filename_display)|bidi_cleanup }}</span>
{% else %}
<span class="postfilename">{{ post.filename|e|bidi_cleanup }}</span>
{% endif %}
{% endif %}
{% include "post/image_identification.html" %}
)
</span></p>
{% include "post/image.html" %}
{% endif %}
<div class="post op"><p class="intro"{% if not index %} id="{{ post.id }}"{% endif %}>
{% include 'post/fileinfo.html' %}
<div class="post op" {%if post.num_files > 1%}style='clear:both'{%endif%}><p class="intro"{% if not index %} id="{{ post.id }}"{% endif %}>
<input type="checkbox" class="delete" name="delete_{{ post.id }}" id="delete_{{ post.id }}" />
<label for="delete_{{ post.id }}">
{% include 'post/subject.html' %}

12
templates/posts.sql

@ -10,14 +10,8 @@ CREATE TABLE IF NOT EXISTS ``posts_{{ board }}`` (
`body_nomarkup` text,
`time` int(11) NOT NULL,
`bump` int(11) DEFAULT NULL,
`thumb` varchar(255) DEFAULT NULL,
`thumbwidth` int(11) DEFAULT NULL,
`thumbheight` int(11) DEFAULT NULL,
`file` varchar(255) DEFAULT NULL,
`filewidth` int(11) DEFAULT NULL,
`fileheight` int(11) DEFAULT NULL,
`filesize` int(11) DEFAULT NULL,
`filename` text,
`files` text DEFAULT NULL,
`num_files` int(11) DEFAULT 0,
`filehash` text CHARACTER SET ascii,
`password` varchar(20) DEFAULT NULL,
`ip` varchar(39) CHARACTER SET ascii NOT NULL,
@ -32,4 +26,4 @@ CREATE TABLE IF NOT EXISTS ``posts_{{ board }}`` (
KEY `ip` (`ip`),
KEY `list_threads` (`thread`, `sticky`, `bump`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ;

5
templates/themes/catalog/theme.php

@ -40,7 +40,10 @@
while ($post = $query->fetch(PDO::FETCH_ASSOC)) {
$post['link'] = $config['root'] . $board['dir'] . $config['dir']['res'] . sprintf($config['file_page'], ($post['thread'] ? $post['thread'] : $post['id']));
$post['board_name'] = $board['name'];
$post['file'] = $config['uri_thumb'] . $post['thumb'];
if (isset($post['files']))
$files = json_decode($post['files']);
if ($files[0]->file == 'deleted') continue;
$post['file'] = $config['uri_thumb'] . $files[0]->thumb;
$recent_posts[] = $post;
}

12
templates/themes/recent/theme.php

@ -42,7 +42,7 @@
foreach ($boards as &$_board) {
if (in_array($_board['uri'], $this->excluded))
continue;
$query .= sprintf("SELECT *, '%s' AS `board` FROM ``posts_%s`` WHERE `file` IS NOT NULL AND `file` != 'deleted' AND `thumb` != 'spoiler' UNION ALL ", $_board['uri'], $_board['uri']);
$query .= sprintf("SELECT *, '%s' AS `board` FROM ``posts_%s`` WHERE `files` IS NOT NULL UNION ALL ", $_board['uri'], $_board['uri']);
}
$query = preg_replace('/UNION ALL $/', 'ORDER BY `time` DESC LIMIT ' . (int)$settings['limit_images'], $query);
@ -54,10 +54,14 @@
while ($post = $query->fetch(PDO::FETCH_ASSOC)) {
openBoard($post['board']);
if (isset($post['files']))
$files = json_decode($post['files']);
if ($files[0]->file == 'deleted') continue;
// board settings won't be available in the template file, so generate links now
$post['link'] = $config['root'] . $board['dir'] . $config['dir']['res'] . sprintf($config['file_page'], ($post['thread'] ? $post['thread'] : $post['id'])) . '#' . $post['id'];
$post['src'] = $config['uri_thumb'] . $post['thumb'];
if ($files) $post['src'] = $config['uri_thumb'] . $files[0]->thumb;
$recent_images[] = $post;
}
@ -105,7 +109,7 @@
$stats['unique_posters'] = number_format($query->fetchColumn());
// Active content
$query = 'SELECT SUM(`filesize`) FROM (';
/*$query = 'SELECT SUM(`filesize`) FROM (';
foreach ($boards as &$_board) {
if (in_array($_board['uri'], $this->excluded))
continue;
@ -113,7 +117,7 @@
}
$query = preg_replace('/UNION ALL $/', ') AS `posts_all`', $query);
$query = query($query) or error(db_error());
$stats['active_content'] = $query->fetchColumn();
$stats['active_content'] = $query->fetchColumn();*/
return Element('themes/recent/recent.html', Array(
'settings' => $settings,

4
templates/themes/ukko/theme.php

@ -51,14 +51,14 @@
$num_images = 0;
while ($po = $posts->fetch()) {
if ($po['file'])
if ($po['files'])
$num_images++;
$thread->add(new Post($po, $mod ? '?/' : $config['root'], $mod));
}
if ($posts->rowCount() == ($post['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview'])) {
$ct = prepare(sprintf("SELECT COUNT(`id`) as `num` FROM ``posts_%s`` WHERE `thread` = :thread UNION ALL SELECT COUNT(`id`) FROM ``posts_%s`` WHERE `file` IS NOT NULL AND `thread` = :thread", $post['board'], $post['board']));
$ct = prepare(sprintf("SELECT COUNT(`id`) as `num` FROM ``posts_%s`` WHERE `thread` = :thread UNION ALL SELECT COUNT(`id`) FROM ``posts_%s`` WHERE `files` IS NOT NULL AND `thread` = :thread", $post['board'], $post['board']));
$ct->bindValue(':thread', $post['id'], PDO::PARAM_INT);
$ct->execute() or error(db_error($count));

Loading…
Cancel
Save