1
0
mirror of https://github.com/vichan-devel/vichan.git synced 2024-11-27 17:00:52 +01:00

multiimage posting

This commit is contained in:
copypaste 2014-04-27 15:48:47 +02:00 committed by czaks
parent 7a04150b04
commit c483e1258c
18 changed files with 366 additions and 273 deletions

View File

@ -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,31 +70,27 @@ 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;
}
}
}
private function translatePost($post, $threadsPage = false) {
$apiPost = array();
$fields = $threadsPage ? $this->threadsPageFields : $this->postFields;
$this->translateFields($fields, $post, $apiPost);
if ($threadsPage) return $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);
}
// Handle country field
if (isset($post->body_nomarkup) && $this->config['country_flags']) {
$modifiers = extract_modifiers($post->body_nomarkup);
@ -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;
}

View File

@ -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;
@ -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!');

View File

@ -339,6 +339,9 @@ class Post {
$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;
@ -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;
@ -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;

View File

@ -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 {
foreach ($files as $i => $file) {
// Delete thumbnail
file_unlink($board['dir'] . $config['dir']['thumb'] . $post['thumb']);
file_unlink($board['dir'] . $config['dir']['thumb'] . $file->thumb);
// Delete file
file_unlink($board['dir'] . $config['dir']['img'] . $post['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));
@ -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) {

View File

@ -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, ':');
}

View File

@ -1,7 +1,7 @@
<?php
// Installation/upgrade file
define('VERSION', '4.4.98');
define('VERSION', '4.9.90');
require 'inc/functions.php';
@ -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());
@ -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 Normal file
View File

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

190
post.php
View File

@ -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']))
@ -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,15 +417,38 @@ 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));
$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']))
$post['file_id'] = $config['filename_func']($post);
$file['file_id'] = $config['filename_func']($file);
else
$post['file_id'] = time() . substr(microtime(), 2, 3);
$file['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']);
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']);
@ -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']))
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']);
$is_an_image = !in_array($post['extension'], $config['allowed_ext_files']);
$file['is_an_image'] = !in_array($file['extension'], $config['allowed_ext_files']);
// Truncate filename if it is too long
$post['filename'] = mb_substr($post['filename'], 0, $config['max_filename_len']);
$file['filename'] = mb_substr($file['filename'], 0, $config['max_filename_len']);
$upload = $_FILES['file']['tmp_name'];
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']);
}
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);
$post['filesize'] = filesize($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'] || (!@$post['exif_stripped'] && $config['strip_exif'] && ($post['extension'] == 'jpg' || $post['extension'] == 'jpeg'))) {
if ($config['redraw_image'] || (!@$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 (!@rename($upload, $post['file']))
if (isset($file['file_tmp'])) {
if (!@rename($file['tmp_name'], $file['file']))
error($config['error']['nomove']);
chmod($post['file'], 0644);
} elseif (!@move_uploaded_file($upload, $post['file']))
chmod($file['file'], 0644);
} elseif (!@move_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);
@ -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;
@ -716,6 +766,10 @@ if (isset($_POST['delete'])) {
}
$post = (array)$post;
if ($post['files'])
$post['files'] = $post['files'];
$post['num_files'] = sizeof($post['files']);
$post['id'] = $id = post($post);
insertFloodPost($post);

View File

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

View File

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

View File

@ -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 %}

View File

@ -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']) }}

View File

@ -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' %}

View File

@ -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,

View File

@ -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;
}

View File

@ -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,

View File

@ -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));