diff --git a/inc/api.php b/inc/api.php index 3337c613..a8ae9eed 100644 --- a/inc/api.php +++ b/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,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; } diff --git a/inc/config.php b/inc/config.php index a4d1c6d5..6ec08a09 100644 --- a/inc/config.php +++ b/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'); /* * ==================== diff --git a/inc/display.php b/inc/display.php index f7fe1620..0ae0ac05 100644 --- a/inc/display.php +++ b/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 .= ' ' . $config['mod']['link_bandelete'] . ''; // 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 .= ' ' . $config['mod']['link_bandelete'] . ''; // 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; diff --git a/inc/functions.php b/inc/functions.php index 820ee838..1db2b7f3 100644 --- a/inc/functions.php +++ b/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) { diff --git a/inc/lib/Twig/Extensions/Extension/Tinyboard.php b/inc/lib/Twig/Extensions/Extension/Tinyboard.php index 727d5d2c..4063bfd4 100644 --- a/inc/lib/Twig/Extensions/Extension/Tinyboard.php +++ b/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, ':'); +} diff --git a/inc/mod/pages.php b/inc/mod/pages.php index a247e35c..89bc6e41 100644 --- a/inc/mod/pages.php +++ b/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']); } diff --git a/install.php b/install.php index 16ef9fcb..4a5a54cb 100644 --- a/install.php +++ b/install.php @@ -1,7 +1,7 @@ execute() or error(db_error($_query)); } } - case 'v0.9.6-dev-9': + case 'v0.9.6-dev-9': case 'v0.9.6-dev-9 + vichan-devel-4.0.3': case 'v0.9.6-dev-9 + vichan-devel-4.0.4-gold': case 'v0.9.6-dev-9 + vichan-devel-4.0.5-gold': @@ -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'] .= '
I couldn\'t remove install.php. You will have to remove it manually.
File: {{ file.file }} + ( + {% 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 %} + {{ file.filename|truncate(config.max_filename_display)|bidi_cleanup }} + {% else %} + {{ file.filename|e|bidi_cleanup }} + {% endif %} + {% endif %} + {% include "post/image_identification.html" %} + ) +
+ {% include "post/image.html" with {'post':file} %} + {% endif %} +- {% if post.embed %} - {{ post.embed }} - {% elseif post.file == 'deleted' %} - - {% elseif post.file and post.file %} -
File: {{ post.file }} - ( - {% 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 %} - {{ post.filename|truncate(config.max_filename_display)|bidi_cleanup }} - {% else %} - {{ post.filename|e|bidi_cleanup }} - {% endif %} - {% endif %} - {% include "post/image_identification.html" %} - ) -
- {% include "post/image.html" %} - {% endif %} + {% include 'post/fileinfo.html' %} {{ post.postControls }} -{% trans %}File:{% endtrans %} {{ post.file }} - ( - {% 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 %} - {{ post.filename|truncate(config.max_filename_display)|bidi_cleanup }} - {% else %} - {{ post.filename|e|bidi_cleanup }} - {% endif %} - {% endif %} - {% include "post/image_identification.html" %} - ) -
- {% include "post/image.html" %} -{% endif %} -+{% include 'post/fileinfo.html' %} +