diff --git a/inc/Data/Driver/ErrorLogLogDriver.php b/inc/Data/Driver/ErrorLogLogDriver.php deleted file mode 100644 index e2050606..00000000 --- a/inc/Data/Driver/ErrorLogLogDriver.php +++ /dev/null @@ -1,28 +0,0 @@ -name = $name; - $this->level = $level; - } - - public function log(int $level, string $message): void { - if ($level <= $this->level) { - $lv = $this->levelToString($level); - $line = "{$this->name} $lv: $message"; - \error_log($line, 0, null, null); - } - } -} diff --git a/inc/Data/Driver/FileLogDriver.php b/inc/Data/Driver/FileLogDriver.php deleted file mode 100644 index 2c9f14a0..00000000 --- a/inc/Data/Driver/FileLogDriver.php +++ /dev/null @@ -1,61 +0,0 @@ -fd = \fopen($file_path, 'a'); - if ($this->fd === false) { - throw new \RuntimeException("Unable to open log file at $file_path"); - } - - $this->name = $name; - $this->level = $level; - - // In some cases PHP does not run the destructor. - \register_shutdown_function([$this, 'close']); - } - - public function __destruct() { - $this->close(); - } - - public function log(int $level, string $message): void { - if ($level <= $this->level) { - $lv = $this->levelToString($level); - $line = "{$this->name} $lv: $message\n"; - \flock($this->fd, LOCK_EX); - \fwrite($this->fd, $line); - \fflush($this->fd); - \flock($this->fd, LOCK_UN); - } - } - - public function close() { - \flock($this->fd, LOCK_UN); - \fclose($this->fd); - } -} diff --git a/inc/Data/Driver/LogDriver.php b/inc/Data/Driver/LogDriver.php deleted file mode 100644 index fddc3f27..00000000 --- a/inc/Data/Driver/LogDriver.php +++ /dev/null @@ -1,22 +0,0 @@ -name = $name; - $this->level = $level; - } - - public function log(int $level, string $message): void { - if ($level <= $this->level) { - $lv = $this->levelToString($level); - \fwrite(\STDERR, "{$this->name} $lv: $message\n"); - } - } -} diff --git a/inc/Data/Driver/SyslogLogDriver.php b/inc/Data/Driver/SyslogLogDriver.php deleted file mode 100644 index c0df5304..00000000 --- a/inc/Data/Driver/SyslogLogDriver.php +++ /dev/null @@ -1,35 +0,0 @@ -level = $level; - } - - public function log(int $level, string $message): void { - if ($level <= $this->level) { - if (isset($_SERVER['REMOTE_ADDR'], $_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'])) { - // CGI - \syslog($level, "$message - client: {$_SERVER['REMOTE_ADDR']}, request: \"{$_SERVER['REQUEST_METHOD']} {$_SERVER['REQUEST_URI']}\""); - } else { - \syslog($level, $message); - } - } - } -} diff --git a/inc/Data/IpNoteQueries.php b/inc/Data/IpNoteQueries.php deleted file mode 100644 index ba6fdb15..00000000 --- a/inc/Data/IpNoteQueries.php +++ /dev/null @@ -1,76 +0,0 @@ -pdo = $pdo; - $this->cache = $cache; - } - - /** - * Get all the notes relative to an IP. - * - * @param string $ip The IP of the notes. THE STRING IS NOT VALIDATED. - * @return array Returns an array of notes sorted by the most recent. Includes the username of the mods. - */ - public function getByIp(string $ip) { - $ret = $this->cache->get("ip_note_queries_$ip"); - if ($ret !== null) { - return $ret; - } - - $query = $this->pdo->prepare('SELECT `ip_notes`.*, `username` FROM `ip_notes` LEFT JOIN `mods` ON `mod` = `mods`.`id` WHERE `ip` = :ip ORDER BY `time` DESC'); - $query->bindValue(':ip', $ip); - $query->execute(); - $ret = $query->fetchAll(\PDO::FETCH_ASSOC); - - $this->cache->set("ip_note_queries_$ip", $ret); - return $ret; - } - - /** - * Creates a new note relative to the given ip. - * - * @param string $ip The IP of the note. THE STRING IS NOT VALIDATED. - * @param int $mod_id The id of the mod who created the note. - * @param string $body The text of the note. - * @return void - */ - public function add(string $ip, int $mod_id, string $body) { - $query = $this->pdo->prepare('INSERT INTO `ip_notes` (`ip`, `mod`, `time`, `body`) VALUES (:ip, :mod, :time, :body)'); - $query->bindValue(':ip', $ip); - $query->bindValue(':mod', $mod_id); - $query->bindValue(':time', time()); - $query->bindValue(':body', $body); - $query->execute(); - - $this->cache->delete("ip_note_queries_$ip"); - } - - /** - * Delete a note only if it's of a particular IP address. - * - * @param int $id The id of the note. - * @param int $ip The expected IP of the note. THE STRING IS NOT VALIDATED. - * @return bool True if any note was deleted. - */ - public function deleteWhereIp(int $id, string $ip): bool { - $query = $this->pdo->prepare('DELETE FROM `ip_notes` WHERE `ip` = :ip AND `id` = :id'); - $query->bindValue(':ip', $ip); - $query->bindValue(':id', $id); - $query->execute(); - $any = $query->rowCount() != 0; - - if ($any) { - $this->cache->delete("ip_note_queries_$ip"); - } - return $any; - } -} diff --git a/inc/Data/UserPostQueries.php b/inc/Data/UserPostQueries.php index 1c203431..8f803b93 100644 --- a/inc/Data/UserPostQueries.php +++ b/inc/Data/UserPostQueries.php @@ -44,20 +44,19 @@ class UserPostQueries { $posts_count = \count($posts); - // By fetching one extra post bellow and/or above the limit, we know if there are any posts beside the current page. if ($posts_count === $page_size + 2) { $has_extra_prev_post = true; $has_extra_end_post = true; - } else { - /* - * If the id we start fetching from is also the first id fetched from the DB, then we exclude it from - * the results, noting that we fetched 1 more posts than we needed, and it was before the current page. - * Hence, we have no extra post at the end and no next page. - */ + } elseif ($posts_count === $page_size + 1) { $has_extra_prev_post = $start_id !== null && $start_id === (int)$posts[0]['id']; - $has_extra_end_post = !$has_extra_prev_post && $posts_count > $page_size; + $has_extra_end_post = !$has_extra_prev_post; + } else { + $has_extra_prev_post = false; + $has_extra_end_post = false; } + // Since we fetched one post bellow and/or above the limit, we always know if there are any posts after the current page. + // Get the previous cursor, if any. if ($has_extra_prev_post) { \array_shift($posts); diff --git a/inc/config.php b/inc/config.php index 71b0fbf4..def2cd27 100644 --- a/inc/config.php +++ b/inc/config.php @@ -63,29 +63,9 @@ // been generated. This keeps the script from querying the database and causing strain when not needed. $config['has_installed'] = '.installed'; - // Deprecated, use 'log_system'. + // Use syslog() for logging all error messages and unauthorized login attempts. $config['syslog'] = false; - $config['log_system'] = [ - /* - * Log all error messages and unauthorized login attempts. - * Can be "syslog", "error_log" (default), "file", or "stderr". - */ - 'type' => 'error_log', - // The application name used by the logging system. Defaults to "tinyboard" for backwards compatibility. - 'name' => 'tinyboard', - /* - * Only relevant if 'log_system' is set to "syslog". If true, double print the logs also in stderr. Defaults to - * false. - */ - 'syslog_stderr' => false, - /* - * Only relevant if "log_system" is set to `file`. Sets the file that vichan will log to. Defaults to - * '/var/log/vichan.log'. - */ - 'file_path' => '/var/log/vichan.log', - ]; - // Use `host` via shell_exec() to lookup hostnames, avoiding query timeouts. May not work on your system. // Requires safe_mode to be disabled. $config['dns_system'] = false; @@ -943,6 +923,10 @@ // Location of thumbnail to use for deleted images. $config['image_deleted'] = 'static/deleted.png'; + // When a thumbnailed image is going to be the same (in dimension), just copy the entire file and use + // that as a thumbnail instead of resizing/redrawing. + $config['minimum_copy_resize'] = false; + // Maximum image upload size in bytes. $config['max_filesize'] = 10 * 1024 * 1024; // 10MB // Maximum image dimensions. @@ -981,6 +965,15 @@ // Set this to true if you're using Linux and you can execute `md5sum` binary. $config['gnu_md5'] = false; + // Use Tesseract OCR to retrieve text from images, so you can use it as a spamfilter. + $config['tesseract_ocr'] = false; + + // Tesseract parameters + $config['tesseract_params'] = ''; + + // Tesseract preprocess command + $config['tesseract_preprocess_command'] = 'convert -monochrome %s -'; + // Number of posts in a "View Last X Posts" page $config['noko50_count'] = 50; // Number of posts a thread needs before it gets a "View Last X Posts" page. diff --git a/inc/context.php b/inc/context.php index 11a153ec..c744f9d8 100644 --- a/inc/context.php +++ b/inc/context.php @@ -1,8 +1,8 @@ $config, - LogDriver::class => function($c) { - $config = $c->get('config'); - - $name = $config['log_system']['name']; - $level = $config['debug'] ? LogDriver::DEBUG : LogDriver::NOTICE; - $backend = $config['log_system']['type']; - - $legacy_syslog = isset($config['syslog']) && $config['syslog']; - - // Check 'syslog' for backwards compatibility. - if ($legacy_syslog || $backend === 'syslog') { - $log_driver = new SyslogLogDriver($name, $level, $config['log_system']['syslog_stderr']); - if ($legacy_syslog) { - $log_driver->log(LogDriver::NOTICE, 'The configuration setting \'syslog\' is deprecated. Please use \'log_system\' instead'); - } - return $log_driver; - } elseif ($backend === 'file') { - return new FileLogDriver($name, $level, $config['log_system']['file_path']); - } elseif ($backend === 'stderr') { - return new StderrLogDriver($name, $level); - } elseif ($backend === 'error_log') { - return new ErrorLogLogDriver($name, $level); - } else { - $log_driver = new ErrorLogLogDriver($name, $level); - $log_driver->log(LogDriver::ERROR, "Unknown 'log_system' value '$backend', using 'error_log' default"); - return $log_driver; - } - }, CacheDriver::class => function($c) { // Use the global for backwards compatibility. return \cache::getCache(); @@ -76,7 +48,6 @@ function build_context(array $config): Context { }, UserPostQueries::class => function($c) { return new UserPostQueries($c->get(\PDO::class)); - }, - IpNoteQueries::class => fn($c) => new IpNoteQueries($c->get(\PDO::class), $c->get(CacheDriver::class)), + } ]); } diff --git a/inc/filters.php b/inc/filters.php index 97cbc524..2a66cd2a 100644 --- a/inc/filters.php +++ b/inc/filters.php @@ -4,26 +4,23 @@ * Copyright (c) 2010-2013 Tinyboard Development Group */ -use Vichan\Context; -use Vichan\Data\IpNoteQueries; - defined('TINYBOARD') or exit; class Filter { public $flood_check; private $condition; private $post; - + public function __construct(array $arr) { foreach ($arr as $key => $value) - $this->$key = $value; + $this->$key = $value; } - + public function match($condition, $match) { $condition = strtolower($condition); $post = &$this->post; - + switch($condition) { case 'custom': if (!is_callable($match)) @@ -32,11 +29,11 @@ class Filter { case 'flood-match': if (!is_array($match)) error('Filter condition "flood-match" must be an array.'); - + // Filter out "flood" table entries which do not match this filter. - + $flood_check_matched = array(); - + foreach ($this->flood_check as $flood_post) { foreach ($match as $flood_match_arg) { switch ($flood_match_arg) { @@ -72,10 +69,10 @@ class Filter { } $flood_check_matched[] = $flood_post; } - + // is there any reason for this assignment? $this->flood_check = $flood_check_matched; - + return !empty($this->flood_check); case 'flood-time': foreach ($this->flood_check as $flood_post) { @@ -138,42 +135,46 @@ class Filter { error('Unknown filter condition: ' . $condition); } } - - public function action(Context $ctx) { + + public function action() { global $board; $this->add_note = isset($this->add_note) ? $this->add_note : false; if ($this->add_note) { - $note_queries = $ctx->get(IpNoteQueries::class); - $note_queries->add($_SERVER['REMOTE_ADDR'], -1, 'Autoban message: ' . $this->post['body']); - } + $query = prepare('INSERT INTO ``ip_notes`` VALUES (NULL, :ip, :mod, :time, :body)'); + $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']); + $query->bindValue(':mod', -1); + $query->bindValue(':time', time()); + $query->bindValue(':body', "Autoban message: ".$this->post['body']); + $query->execute() or error(db_error($query)); + } if (isset ($this->action)) switch($this->action) { case 'reject': error(isset($this->message) ? $this->message : 'Posting blocked by filter.'); case 'ban': if (!isset($this->reason)) error('The ban action requires a reason.'); - + $this->expires = isset($this->expires) ? $this->expires : false; $this->reject = isset($this->reject) ? $this->reject : true; $this->all_boards = isset($this->all_boards) ? $this->all_boards : false; - + Bans::new_ban($_SERVER['REMOTE_ADDR'], $this->reason, $this->expires, $this->all_boards ? false : $board['uri'], -1); if ($this->reject) { if (isset($this->message)) error($message); - + checkBan($board['uri']); exit; } - + break; default: error('Unknown filter action: ' . $this->action); } } - + public function check(array $post) { $this->post = $post; foreach ($this->condition as $condition => $value) { @@ -183,7 +184,7 @@ class Filter { } else { $NOT = false; } - + if ($this->match($condition, $value) == $NOT) return false; } @@ -193,11 +194,11 @@ class Filter { function purge_flood_table() { global $config; - + // Determine how long we need to keep a cache of posts for flood prevention. Unfortunately, it is not // aware of flood filters in other board configurations. You can solve this problem by settings the // config variable $config['flood_cache'] (seconds). - + if (isset($config['flood_cache'])) { $max_time = &$config['flood_cache']; } else { @@ -207,18 +208,18 @@ function purge_flood_table() { $max_time = max($max_time, $filter['condition']['flood-time']); } } - + $time = time() - $max_time; - + query("DELETE FROM ``flood`` WHERE `time` < $time") or error(db_error()); } -function do_filters(Context $ctx, array $post) { +function do_filters(array $post) { global $config; if (!isset($config['filters']) || empty($config['filters'])) return; - + foreach ($config['filters'] as $filter) { if (isset($filter['condition']['flood-match'])) { $has_flood = true; @@ -231,15 +232,15 @@ function do_filters(Context $ctx, array $post) { } else { $flood_check = false; } - + foreach ($config['filters'] as $filter_array) { $filter = new Filter($filter_array); $filter->flood_check = $flood_check; if ($filter->check($post)) { - $filter->action($ctx); + $filter->action(); } } - + purge_flood_table(); } diff --git a/inc/functions.php b/inc/functions.php index def00287..66cd6fa7 100644 --- a/inc/functions.php +++ b/inc/functions.php @@ -2069,7 +2069,7 @@ function remove_modifiers($body) { return preg_replace('@(.+?)@usm', '', $body); } -function markup(&$body, $track_cites = false) { +function markup(&$body, $track_cites = false, $op = false) { global $board, $config, $markup_urls; $modifiers = extract_modifiers($body); @@ -2168,15 +2168,12 @@ function markup(&$body, $track_cites = false) { link_for(array('id' => $cite, 'thread' => $cited_posts[$cite])) . '#' . $cite . '">' . '>>' . $cite . ''; - } else { - $replacement = ">>$cite"; - } - $body = mb_substr_replace($body, $matches[1][0] . $replacement . $matches[3][0], $matches[0][1] + $skip_chars, mb_strlen($matches[0][0])); - $skip_chars += mb_strlen($matches[1][0] . $replacement . $matches[3][0]) - mb_strlen($matches[0][0]); + $body = mb_substr_replace($body, $matches[1][0] . $replacement . $matches[3][0], $matches[0][1] + $skip_chars, mb_strlen($matches[0][0])); + $skip_chars += mb_strlen($matches[1][0] . $replacement . $matches[3][0]) - mb_strlen($matches[0][0]); - if ($track_cites && $config['track_cites']) { - $tracked_cites[] = array($board['uri'], $cite); + if ($track_cites && $config['track_cites']) + $tracked_cites[] = array($board['uri'], $cite); } } } diff --git a/inc/mod/pages.php b/inc/mod/pages.php index 155c19a6..380a5627 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -3,20 +3,17 @@ * Copyright (c) 2010-2013 Tinyboard Development Group */ use Vichan\Context; -use Vichan\Data\{IpNoteQueries, UserPostQueries, ReportQueries}; -use Vichan\Data\Driver\LogDriver; +use Vichan\Data\{UserPostQueries, ReportQueries}; use Vichan\Functions\Net; defined('TINYBOARD') or exit; -function _link_or_copy_factory(Context $ctx): callable { - return function(string $target, string $link) use ($ctx) { - if (!\link($target, $link)) { - $ctx->get(LogDriver::class)->log(LogDriver::NOTICE, "Failed to link() $target to $link. FAlling back to copy()"); - return \copy($target, $link); - } - return true; - }; +function _link_or_copy(string $target, string $link): bool { + if (!link($target, $link)) { + error_log("Failed to link() $target to $link. FAlling back to copy()"); + return copy($target, $link); + } + return true; } function mod_page($title, $template, $args, $subtitle = false) { @@ -57,7 +54,8 @@ function mod_login(Context $ctx, $redirect = false) { if (!isset($_POST['username'], $_POST['password']) || $_POST['username'] == '' || $_POST['password'] == '') { $args['error'] = $config['error']['invalid']; } elseif (!login($_POST['username'], $_POST['password'])) { - $ctx->get(LogDriver::class)->log(LogDriver::INFO, 'Unauthorized login attempt!'); + if ($config['syslog']) + _syslog(LOG_WARNING, 'Unauthorized login attempt!'); $args['error'] = $config['error']['invalid']; } else { @@ -853,26 +851,18 @@ function mod_view_thread50(Context $ctx, $boardName, $thread) { } function mod_ip_remove_note(Context $ctx, $ip, $id) { - $config = $ctx->get('config'); + global $config; - if (!hasPermission($config['mod']['remove_notes'])) { - error($config['error']['noaccess']); - } + if (!hasPermission($config['mod']['remove_notes'])) + error($config['error']['noaccess']); - if (filter_var($ip, \FILTER_VALIDATE_IP) === false) { + if (filter_var($ip, FILTER_VALIDATE_IP) === false) error('Invalid IP address'); - } - if (!is_numeric($id)) { - error('Invalid note ID'); - } - - $queries = $ctx->get(IpNoteQueries::class); - $deleted = $queries->deleteWhereIp((int)$id, $ip); - - if (!$deleted) { - error("Note $id does not exist for $ip"); - } + $query = prepare('DELETE FROM ``ip_notes`` WHERE `ip` = :ip AND `id` = :id'); + $query->bindValue(':ip', $ip); + $query->bindValue(':id', $id); + $query->execute() or error(db_error($query)); modLog("Removed a note for {$ip}"); @@ -880,17 +870,14 @@ function mod_ip_remove_note(Context $ctx, $ip, $id) { } function mod_ip(Context $ctx, $ip, string $encoded_cursor = null) { - global $mod; - $config = $ctx->get('config'); + global $config, $mod; - if (filter_var($ip, FILTER_VALIDATE_IP) === false) { + if (filter_var($ip, FILTER_VALIDATE_IP) === false) error('Invalid IP address'); - } if (isset($_POST['ban_id'], $_POST['unban'])) { - if (!hasPermission($config['mod']['unban'])) { + if (!hasPermission($config['mod']['unban'])) error($config['error']['noaccess']); - } Bans::delete($_POST['ban_id'], true, $mod['boards']); @@ -903,15 +890,17 @@ function mod_ip(Context $ctx, $ip, string $encoded_cursor = null) { } if (isset($_POST['note'])) { - if (!hasPermission($config['mod']['create_notes'])) { + if (!hasPermission($config['mod']['create_notes'])) error($config['error']['noaccess']); - } $_POST['note'] = escape_markup_modifiers($_POST['note']); markup($_POST['note']); - - $note_queries = $ctx->get(IpNoteQueries::class); - $note_queries->add($ip, $mod['id'], $_POST['note']); + $query = prepare('INSERT INTO ``ip_notes`` VALUES (NULL, :ip, :mod, :time, :body)'); + $query->bindValue(':ip', $ip); + $query->bindValue(':mod', $mod['id']); + $query->bindValue(':time', time()); + $query->bindValue(':body', $_POST['note']); + $query->execute() or error(db_error($query)); Cache::delete("mod_page_ip_view_notes_$ip"); @@ -963,8 +952,15 @@ function mod_user_posts_by_ip(Context $ctx, string $ip, string $encoded_cursor = } if (hasPermission($config['mod']['view_notes'])) { - $note_queries = $ctx->get(IpNoteQueries::class); - $args['notes'] = $note_queries->getByIp($ip); + $ret = Cache::get("mod_page_ip_view_notes_$ip"); + if (!$ret) { + $query = prepare("SELECT ``ip_notes``.*, `username` FROM ``ip_notes`` LEFT JOIN ``mods`` ON `mod` = ``mods``.`id` WHERE `ip` = :ip ORDER BY `time` DESC"); + $query->bindValue(':ip', $ip); + $query->execute() or error(db_error($query)); + $ret = $query->fetchAll(PDO::FETCH_ASSOC); + Cache::set("mod_page_ip_view_notes_$ip", $ret, 900); + } + $args['notes'] = $ret; } if (hasPermission($config['mod']['modlog_ip'])) { @@ -1091,6 +1087,12 @@ function mod_user_posts_by_passwd(Context $ctx, string $passwd, string $encoded_ $args['boards'] = $boards; $args['token'] = make_secure_link_token('ban'); + if (empty($encoded_cursor)) { + $args['security_token'] = make_secure_link_token("user_posts/passwd/$passwd"); + } else { + $args['security_token'] = make_secure_link_token("user_posts/passwd/$passwd/cursor/$encoded_cursor"); + } + mod_page(\sprintf('%s: %s', _('Password'), \htmlspecialchars($passwd)), 'mod/view_passwd.html', $args); } @@ -1491,9 +1493,8 @@ function mod_move(Context $ctx, $originBoard, $postID) { if ($targetBoard === $originBoard) error(_('Target and source board are the same.')); - $_link_or_copy = _link_or_copy_factory($ctx); // link() if leaving a shadow thread behind; else, rename(). - $clone = $shadow ? $_link_or_copy : 'rename'; + $clone = $shadow ? '_link_or_copy' : 'rename'; // indicate that the post is a thread $post['op'] = true; @@ -1787,8 +1788,7 @@ function mod_merge(Context $ctx, $originBoard, $postID) { $op = $post; $op['id'] = $newID; - $_link_or_copy = _link_or_copy_factory($ctx); - $clone = $shadow ? $_link_or_copy : 'rename'; + $clone = $shadow ? '_link_or_copy' : 'rename'; if ($post['has_file']) { // copy image @@ -2004,9 +2004,13 @@ function mod_ban_post(Context $ctx, $board, $delete, $post, $token = false) { $autotag .= "/${board}/" . " " . $filehash . " " . $filename ."\r\n"; $autotag .= $body . "\r\n"; $autotag = escape_markup_modifiers($autotag); - - $note_queries = $ctx->get(IpNoteQueries::class); - $note_queries->add($ip, $mod['id'], $autotag); + markup($autotag); + $query = prepare('INSERT INTO ``ip_notes`` VALUES (NULL, :ip, :mod, :time, :body)'); + $query->bindValue(':ip', $ip); + $query->bindValue(':mod', $mod['id']); + $query->bindValue(':time', time()); + $query->bindValue(':body', $autotag); + $query->execute() or error(db_error($query)); modLog("Added a note for {$ip}"); } } @@ -2112,9 +2116,12 @@ function mod_warning_post(Context $ctx, $board, $post, $token = false) { $autotag .= $body . "\r\n"; $autotag = escape_markup_modifiers($autotag); markup($autotag); - - $note_queries = $ctx->get(IpNoteQueries::class); - $note_queries->add($ip, $mod['id'], $autotag); + $query = prepare('INSERT INTO ``ip_notes`` VALUES (NULL, :ip, :mod, :time, :body)'); + $query->bindValue(':ip', $ip); + $query->bindValue(':mod', $mod['id']); + $query->bindValue(':time', time()); + $query->bindValue(':body', $autotag); + $query->execute() or error(db_error($query)); modLog("Added a note for {$ip}"); } } @@ -2220,7 +2227,7 @@ function mod_edit_post(Context $ctx, $board, $edit_raw_html, $postID) { } function mod_delete(Context $ctx, $board, $post) { - global $config, $mod; + global $config; if (!openBoard($board)) error($config['error']['noboard']); @@ -2261,9 +2268,12 @@ function mod_delete(Context $ctx, $board, $post) { $autotag .= $body . "\r\n"; $autotag = escape_markup_modifiers($autotag); markup($autotag); - - $note_queries = $ctx->get(IpNoteQueries::class); - $note_queries->add($ip, $mod['id'], $autotag); + $query = prepare('INSERT INTO ``ip_notes`` VALUES (NULL, :ip, :mod, :time, :body)'); + $query->bindValue(':ip', $ip); + $query->bindValue(':mod', $mod['id']); + $query->bindValue(':time', time()); + $query->bindValue(':body', $autotag); + $query->execute() or error(db_error($query)); modLog("Added a note for {$ip}"); } } @@ -2351,7 +2361,7 @@ function mod_spoiler_image(Context $ctx, $board, $post, $file) { } function mod_deletebyip(Context $ctx, $boardName, $post, $global = false) { - global $config, $board, $mod; + global $config, $board; $global = (bool)$global; @@ -2429,9 +2439,12 @@ function mod_deletebyip(Context $ctx, $boardName, $post, $global = false) { $autotag .= $body . "\r\n"; $autotag = escape_markup_modifiers($autotag); markup($autotag); - - $note_queries = $ctx->get(IpNoteQueries::class); - $note_queries->add($ip, $mod['id'], $autotag); + $query2 = prepare('INSERT INTO ``ip_notes`` VALUES (NULL, :ip, :mod, :time, :body)'); + $query2->bindValue(':ip', $ip); + $query2->bindValue(':mod', $mod['id']); + $query2->bindValue(':time', time()); + $query2->bindValue(':body', $autotag); + $query2->execute() or error(db_error($query2)); modLog("Added a note for {$ip}"); } } diff --git a/js/ajax.js b/js/ajax.js index 3cb06bf1..f65c6c96 100644 --- a/js/ajax.js +++ b/js/ajax.js @@ -111,7 +111,7 @@ $(window).ready(function() { $(form).find('input[type="submit"]').val(submit_txt); $(form).find('input[type="submit"]').removeAttr('disabled'); $(form).find('input[name="subject"],input[name="file_url"],\ - textarea[name="body"],input[type="file"],input[name="embed"]').val('').change(); + textarea[name="body"],input[type="file"]').val('').change(); }, cache: false, contentType: false, @@ -123,7 +123,7 @@ $(window).ready(function() { $(form).find('input[type="submit"]').val(submit_txt); $(form).find('input[type="submit"]').removeAttr('disabled'); $(form).find('input[name="subject"],input[name="file_url"],\ - textarea[name="body"],input[type="file"],input[name="embed"]').val('').change(); + textarea[name="body"],input[type="file"]').val('').change(); } else { alert(_('An unknown error occured when posting!')); $(form).find('input[type="submit"]').val(submit_txt); diff --git a/js/inline-expanding.js b/js/inline-expanding.js index c44843b0..41625d2d 100644 --- a/js/inline-expanding.js +++ b/js/inline-expanding.js @@ -17,10 +17,6 @@ $(document).ready(function() { // Default maximum image loads. const DEFAULT_MAX = 5; - if (localStorage.inline_expand_fit_height !== 'false') { - $('').appendTo($('head')); - } - let inline_expand_post = function() { let link = this.getElementsByTagName('a'); @@ -60,12 +56,12 @@ $(document).ready(function() { }, add: function(ele) { ele.deferred = $.Deferred(); - ele.deferred.done(function() { + ele.deferred.done(function () { let $loadstart = $.Deferred(); let thumb = ele.childNodes[0]; let img = ele.childNodes[1]; - let onLoadStart = function(img) { + let onLoadStart = function (img) { if (img.naturalWidth) { $loadstart.resolve(img, thumb); } else { @@ -73,15 +69,15 @@ $(document).ready(function() { } }; - $(img).one('load', function() { - $.when($loadstart).done(function() { - // once fully loaded, update the waiting queue + $(img).one('load', function () { + $.when($loadstart).done(function () { + // Once fully loaded, update the waiting queue. --loading; $(ele).data('imageLoading', 'false'); update(); }); }); - $loadstart.done(function(img, thumb) { + $loadstart.done(function (img, thumb) { thumb.style.display = 'none'; img.style.display = ''; }); @@ -206,8 +202,6 @@ $(document).ready(function() { Options.extend_tab('general', '' + _('Number of simultaneous image downloads (0 to disable): ') + ''); - Options.extend_tab('general', ''); - $('#inline-expand-max input') .css('width', '50px') .val(localStorage.inline_expand_max || DEFAULT_MAX) @@ -218,21 +212,6 @@ $(document).ready(function() { localStorage.inline_expand_max = val; }); - - $('#inline-expand-fit-height input').on('change', function() { - if (localStorage.inline_expand_fit_height !== 'false') { - localStorage.inline_expand_fit_height = 'false'; - $('#expand-fit-height-style').remove(); - } - else { - localStorage.inline_expand_fit_height = 'true'; - $('').appendTo($('head')); - } - }); - - if (localStorage.inline_expand_fit_height !== 'false') { - $('#inline-expand-fit-height input').prop('checked', true); - } } if (window.jQuery) { diff --git a/js/options/fav.js b/js/options/fav.js index 17ccb337..3236e70e 100644 --- a/js/options/fav.js +++ b/js/options/fav.js @@ -23,20 +23,20 @@ function addBoard(){ add_favorites(); } //This adds the text inside the textbox to favorites, localStorage.favorites and the page -var favorites = JSON.parse(localStorage.favorites); +var favorites = localStorage.favorites ? JSON.parse(localStorage.favorites) : []; Options.add_tab('fav-tab','star',_("Favorites")); -//Pregenerating list of boards +//Pregenerating list of boards var favList = $('
'); for(var i=0; i'+favorites[i]+'
') ); -} +} //Creating list of minus symbols to remove unwanted boards var minusList = $('
'); for(var i=0; i-
').on('click', function(e){removeBoard($(this).data('board'));}) ); -} +} //Help message so people understand how sorting boards works $(""+_("Drag the boards to sort them.")+"

").appendTo(Options.get_tab('fav-tab').content); @@ -65,7 +65,7 @@ addDiv.appendTo(Options.get_tab('fav-tab').content); //Adding the plus button favList.sortable(); //Making boards with sortable id use the sortable jquery function favList.on('sortstop', function() { - favorites = generateList(); + favorites = generateList(); localStorage.favorites = JSON.stringify(favorites); add_favorites(); }); diff --git a/js/options/general.js b/js/options/general.js index 6715ae1d..c0652269 100644 --- a/js/options/general.js +++ b/js/options/general.js @@ -43,6 +43,9 @@ $(function(){ document.location.reload(); } }); + + + $("#style-select").detach().css({float:"none","margin-bottom":0}).appendTo(tab.content); }); }(); diff --git a/js/post-menu.js b/js/post-menu.js index c2155c00..79cfd868 100644 --- a/js/post-menu.js +++ b/js/post-menu.js @@ -104,10 +104,8 @@ function buildMenu(e) { function addButton(post) { var $ele = $(post); - // Use unicode code with ascii variant selector - // https://stackoverflow.com/questions/37906969/how-to-prevent-ios-from-converting-ascii-into-emoji $ele.find('input.delete').after( - $('', {href: '#', class: 'post-btn', title: 'Post menu'}).text('\u{25B6}\u{fe0e}') + $('', {href: '#', class: 'post-btn', title: 'Post menu'}).text('►') ); } diff --git a/js/show-backlinks.js b/js/show-backlinks.js index 607c24ab..f0671e80 100644 --- a/js/show-backlinks.js +++ b/js/show-backlinks.js @@ -15,7 +15,7 @@ $(document).ready(function() { let showBackLinks = function() { - let replyId = $(this).attr('id').split('_')[1]; + let replyId = $(this).attr('id').replace(/^reply_/, ''); $(this).find('div.body a:not([rel="nofollow"])').each(function() { let id = $(this).text().match(/^>>(\d+)$/); diff --git a/js/youtube.js b/js/youtube.js index 4a5a5afe..4c87b0aa 100644 --- a/js/youtube.js +++ b/js/youtube.js @@ -22,7 +22,7 @@ $(document).ready(function() { function makeEmbedNode(embedHost, videoId, width, height) { return $(`