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;
@@ -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');
/*
* ====================

View File

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

View File

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

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

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