From 0df4afe9171aff099654f8336b796338bc13b623 Mon Sep 17 00:00:00 2001 From: Forkless Date: Mon, 10 Nov 2014 04:49:26 -0600 Subject: [PATCH 01/86] Security fix: Added defaults to the banned boards list to protect core folders of the codebase. Added banned boards restriction to the mod_new_board function. --- inc/config.php | 17 ++++++++++++++--- inc/mod/pages.php | 10 +++++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/inc/config.php b/inc/config.php index 10c9faa5..ebbe31a2 100644 --- a/inc/config.php +++ b/inc/config.php @@ -602,6 +602,17 @@ // How many ban appeals can be made for a single ban? $config['ban_appeals_max'] = 1; + + // Blacklisted board names. Default values to protect existing folders in the core codebase. + $config['banned_boards'] = array( + '.git', + 'inc', + 'js', + 'static', + 'stylesheets', + 'templates', + 'tools' + ); // Show moderator name on ban page. $config['show_modname'] = false; @@ -1326,8 +1337,8 @@ // Capcode permissions. $config['mod']['capcode'] = array( // JANITOR => array('Janitor'), - MOD => array('Mod'), - ADMIN => true + MOD => array('Mod'), + ADMIN => true ); // Example: Allow mods to post with "## Moderator" as well @@ -1410,7 +1421,7 @@ $config['mod']['view_banlist'] = MOD; // View the username of the mod who made a ban $config['mod']['view_banstaff'] = MOD; - // If the moderator doesn't fit the $config['mod']['view_banstaff''] (previous) permission, show him just + // If the moderator doesn't fit the $config['mod']['view_banstaff'] (previous) permission, show him just // a "?" instead. Otherwise, it will be "Mod" or "Admin". $config['mod']['view_banquestionmark'] = false; // Show expired bans in the ban list (they are kept in cache until the culprit returns) diff --git a/inc/mod/pages.php b/inc/mod/pages.php index 1a56c99c..61920b46 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -495,7 +495,15 @@ function mod_new_board() { if (openBoard($_POST['uri'])) { error(sprintf($config['error']['boardexists'], $board['url'])); } - + foreach ($config['banned_boards'] as $i => $w) { + if ($w[0] !== '/') { + if (strpos($_POST['uri'],$w) !== false) + error(_("Cannot create board with banned word $w")); + } else { + if (preg_match($w,$_POST['uri'])) + error(_("Cannot create board matching banned pattern $w")); + } + } $query = prepare('INSERT INTO ``boards`` (``uri``, ``title``, ``subtitle``) VALUES (:uri, :title, :subtitle)'); $query->bindValue(':uri', $_POST['uri']); $query->bindValue(':title', $_POST['title']); From fdfd64357a6f2cad825d713940190ab821db75a5 Mon Sep 17 00:00:00 2001 From: Forkless Date: Tue, 11 Nov 2014 01:21:55 -0600 Subject: [PATCH 02/86] Added missing column to reports table --- install.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/install.sql b/install.sql index 70bb2260..42216549 100644 --- a/install.sql +++ b/install.sql @@ -227,6 +227,7 @@ CREATE TABLE IF NOT EXISTS `reports` ( `board` varchar(58) CHARACTER SET utf8 DEFAULT NULL, `post` int(11) NOT NULL, `reason` text NOT NULL, + `local` tinyint(1) NOT NULL DEFAULT '0', `global` tinyint(1) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ; From b28fc414b0ebf91bf8743c9cc3adc7bdb8725264 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 12 Nov 2014 01:16:42 -0800 Subject: [PATCH 03/86] Make removing board from favorites on 404 actually work --- 404.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/404.php b/404.php index a9a29c36..0e1b634f 100644 --- a/404.php +++ b/404.php @@ -35,7 +35,7 @@ $page = << Date: Wed, 12 Nov 2014 01:17:34 -0800 Subject: [PATCH 04/86] lol --- faq.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/faq.php b/faq.php index 6c039741..cc4dbd93 100644 --- a/faq.php +++ b/faq.php @@ -79,7 +79,7 @@ $body = <<There isn't one yet and there will never be an official archive.

How do I donate?

-

Donations can be sent to 1NpQaXqmCBji6gfX8UgaQEmEstvVY7U32C (Bitcoin) or LUPgSCJt3iGeJXUETVhmnbQ89Riaq1yjZm (Litecoin). PayPal is also accepted @ fredrick.brennan1@gmail.com .

+

Donations can be sent to 1NpQaXqmCBji6gfX8UgaQEmEstvVY7U32C (Bitcoin) or LUPgSCJt3iGeJXUETVhmnbQ89Riaq1yjZm (Litecoin).

You may also donate monthly via Patreon at http://www.patreon.com/user?u=162165.

Are you really a cripple?

From 802dd12c7ccd079851404415a6a3472e40da8004 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 12 Nov 2014 01:19:02 -0800 Subject: [PATCH 05/86] Have to load onready --- js/thread-stats.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/js/thread-stats.js b/js/thread-stats.js index 59fed912..4050fb67 100644 --- a/js/thread-stats.js +++ b/js/thread-stats.js @@ -8,8 +8,9 @@ * $config['additional_javascript'][] = 'js/thread-stats.js'; */ if (active_page == 'thread') { +$(document).ready(function(){ //check if page uses unique ID - var IDsupport = ($('.poster_id').length > 0); + var IDsupport = ($('.poster_id').length > 0); var thread_id = (document.location.pathname + document.location.search).split('/'); thread_id = thread_id[thread_id.length -1].split('+')[0].split('.')[0]; @@ -100,10 +101,9 @@ if (active_page == 'thread') { if (!found) $('#thread_stats_page').css('color','red'); }); },30000); - $(document).ready(function(){ $('body').append(''); update_thread_stats(); $('#update_thread').click(update_thread_stats); $(document).on('new_post',update_thread_stats); - }); -} \ No newline at end of file +}); +} From 2f1526a1a6f698f29c5a07a9f46c808b59cced2e Mon Sep 17 00:00:00 2001 From: czaks Date: Wed, 12 Nov 2014 10:45:25 +0100 Subject: [PATCH 06/86] add missing tablesorter library --- js/jquery.tablesorter.min.js | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 js/jquery.tablesorter.min.js diff --git a/js/jquery.tablesorter.min.js b/js/jquery.tablesorter.min.js new file mode 100644 index 00000000..b8605df1 --- /dev/null +++ b/js/jquery.tablesorter.min.js @@ -0,0 +1,4 @@ + +(function($){$.extend({tablesorter:new +function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",cssChildRow:"expand-child",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,sortLocaleCompare:true,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'/\.|\,/g',onRenderHeader:null,selectorHeaders:'thead th',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}if(table.tBodies.length==0)return;var rows=table.tBodies[0].rows;if(rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function checkHeaderOptionsSortingLocked(table,i){if((table.config.headers[i])&&(table.config.headers[i].lockedOrder))return table.config.headers[i].lockedOrder;return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i b["+i+"]) ? 1 : 0));";};function makeSortTextDesc(i){return"((b["+i+"] < a["+i+"]) ? -1 : ((b["+i+"] > a["+i+"]) ? 1 : 0));";};function makeSortNumeric(i){return"a["+i+"]-b["+i+"];";};function makeSortNumericDesc(i){return"b["+i+"]-a["+i+"];";};function sortText(a,b){if(table.config.sortLocaleCompare)return a.localeCompare(b);return((ab)?1:0));};function sortTextDesc(a,b){if(table.config.sortLocaleCompare)return b.localeCompare(a);return((ba)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$.data(this,"tablesorter",config);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){$this.trigger("sortStart");var $cell=$(this);var i=this.column;this.order=this.count++%2;if(this.lockedOrder)this.order=this.lockedOrder;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i Date: Wed, 12 Nov 2014 21:04:58 -0800 Subject: [PATCH 07/86] Bug: Banners weren't displaying if board was over 10 chars long --- banners.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/banners.php b/banners.php index 10604ea8..2707ca5c 100644 --- a/banners.php +++ b/banners.php @@ -8,7 +8,7 @@ header("Expires: 0"); function get_custom_banner(&$b) { # Validate the board name - if (!(isset($b) && preg_match('/^[a-z0-9]{1,10}$/', $b))) + if (!(isset($b) && preg_match('/^[a-z0-9]{1,30}$/', $b))) return null; # Check if directory exists From 4f2398f7b88f9418db3c35876daca4fd18b988fa Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 12 Nov 2014 21:06:58 -0800 Subject: [PATCH 08/86] Make deletePost more tolerant of errors Sometimes thumbnail generation fails but the post is still there, sometimes post deletion is aborted halfway through Ignore errors from file_unlink and don't fail if $f->file, $f->thumb not set --- inc/functions.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 3504cc38..2ffdc222 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1091,9 +1091,9 @@ function deletePost($id, $error_if_doesnt_exist=true, $rebuild_after=true) { if (!$post['thread']) { // Delete thread HTML page - file_unlink($board['dir'] . $config['dir']['res'] . sprintf($config['file_page'], $post['id'])); - file_unlink($board['dir'] . $config['dir']['res'] . sprintf($config['file_page50'], $post['id'])); - file_unlink($board['dir'] . $config['dir']['res'] . sprintf('%d.json', $post['id'])); + @file_unlink($board['dir'] . $config['dir']['res'] . sprintf($config['file_page'], $post['id'])); + @file_unlink($board['dir'] . $config['dir']['res'] . sprintf($config['file_page50'], $post['id'])); + @file_unlink($board['dir'] . $config['dir']['res'] . sprintf('%d.json', $post['id'])); $antispam_query = prepare('DELETE FROM ``antispam`` WHERE `board` = :board AND `thread` = :thread'); $antispam_query->bindValue(':board', $board['uri']); @@ -1106,9 +1106,9 @@ function deletePost($id, $error_if_doesnt_exist=true, $rebuild_after=true) { if ($post['files']) { // Delete file foreach (json_decode($post['files']) as $i => $f) { - if ($f->file !== 'deleted') { - file_unlink($config['dir']['img_root'] . $board['dir'] . $config['dir']['img'] . $f->file); - file_unlink($config['dir']['img_root'] . $board['dir'] . $config['dir']['thumb'] . $f->thumb); + if (isset($f->file, $f->thumb) && $f->file !== 'deleted') { + @file_unlink($config['dir']['img_root'] . $board['dir'] . $config['dir']['img'] . $f->file); + @file_unlink($config['dir']['img_root'] . $board['dir'] . $config['dir']['thumb'] . $f->thumb); } } } From 1267c60a2c5755c35ad7132e7462a224fbc08328 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 12 Nov 2014 21:08:32 -0800 Subject: [PATCH 09/86] SECURITY: CSRF in post.php --- post.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/post.php b/post.php index 881c7e01..650b431b 100644 --- a/post.php +++ b/post.php @@ -232,15 +232,16 @@ elseif (isset($_POST['post'])) { } } - if (!(($post['op'] && $_POST['post'] == $config['button_newtopic']) || - (!$post['op'] && $_POST['post'] == $config['button_reply']))) + //if (!(($post['op'] && $_POST['post'] == $config['button_newtopic']) || + //(!$post['op'] && $_POST['post'] == $config['button_reply']))) //error($config['error']['bot']); // Check the referrer if ($config['referer_match'] !== false && - (!isset($_SERVER['HTTP_REFERER']) || !preg_match($config['referer_match'], rawurldecode($_SERVER['HTTP_REFERER'])))) + (!isset($_SERVER['HTTP_REFERER']) || !preg_match($config['referer_match'], rawurldecode($_SERVER['HTTP_REFERER'])))) { error($config['error']['referer']); - + } + checkDNSBL(); // Check if banned From f944116a7caa54bed003d052b251485536efe94c Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 12 Nov 2014 21:08:48 -0800 Subject: [PATCH 10/86] Enabe thread stats --- inc/instance-config.php | 1 + 1 file changed, 1 insertion(+) diff --git a/inc/instance-config.php b/inc/instance-config.php index d888b71f..aa45f960 100644 --- a/inc/instance-config.php +++ b/inc/instance-config.php @@ -135,6 +135,7 @@ $config['additional_javascript'][] = 'js/youtube.js'; $config['additional_javascript'][] = 'js/comment-toolbar.js'; $config['additional_javascript'][] = 'js/catalog-search.js'; + $config['additional_javascript'][] = 'js/thread-stats.js'; //$config['font_awesome_css'] = '/netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css'; From 6f5d2bb62ced28a2bc195c038a1def23dcee9e02 Mon Sep 17 00:00:00 2001 From: theBladeee Date: Fri, 14 Nov 2014 04:45:34 -0500 Subject: [PATCH 11/86] Fix for install.sql dank memes --- install.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.sql b/install.sql index ec912520..e1d5279f 100644 --- a/install.sql +++ b/install.sql @@ -80,7 +80,7 @@ CREATE TABLE IF NOT EXISTS `board_create` ( -- INSERT INTO `boards` VALUES -('b', 'Random', NULL); +('b', 'Random', NULL, TRUE, TRUE); -- -------------------------------------------------------- From dbda783b9252f525c2d6aaa37227b4ed98cbd55f Mon Sep 17 00:00:00 2001 From: 8chan Date: Fri, 14 Nov 2014 04:15:04 -0800 Subject: [PATCH 12/86] Better install documentation --- README.md | 140 ++++------------------------------------------------ install.sql | 7 --- 2 files changed, 9 insertions(+), 138 deletions(-) diff --git a/README.md b/README.md index 96e008cc..4292844d 100644 --- a/README.md +++ b/README.md @@ -3,145 +3,23 @@ About ------------ -8chan is a free light-weight, fast, highly configurable and user-friendly -imageboard software package. It is written in PHP and has few dependencies. +8chan is a fork of vichan, with the difference that 8chan is geared towards allowing users to create their own boards. -8chan is a fork of [vichan](https://github.com/vichan-devel/vichan), which is a fork of [Tinyboard](http://tinyboard.org/), a great imageboard package, actively -building on it and adding a lot of features and other improvements. +Most things (other than installation) that apply to upstream vichan also apply to 8chan. See their readme for a detailed FAQ: https://github.com/vichan-devel/vichan/blob/master/README.md -Support and announcements: https://int.vichan.net/devel/ +If you are not interested in letting your users make their own boards, install vichan instead of 8chan. -Requirements ------------- -1. PHP >= 5.3 -2. MySQL/MariaDB server -3. [mbstring](http://www.php.net/manual/en/mbstring.installation.php) -4. [PHP GD](http://www.php.net/manual/en/intro.image.php) -5. [PHP PDO](http://www.php.net/manual/en/intro.pdo.php) - -We try to make sure vichan is compatible with all major web servers and -operating systems. vichan does not include an Apache ```.htaccess``` file nor does -it need one. - -### Recommended -1. MySQL/MariaDB server >= 5.5.3 -2. ImageMagick (command-line ImageMagick or GraphicsMagick preferred). -3. [APC (Alternative PHP Cache)](http://php.net/manual/en/book.apc.php), - [XCache](http://xcache.lighttpd.net/) or - [Memcached](http://www.php.net/manual/en/intro.memcached.php) - -Contributing ------------- -You can contribute to vichan by: -* Developing patches/improvements/translations and using GitHub to submit pull requests -* Providing feedback and suggestions -* Writing/editing documentation - -If you need help developing a patch, please join our IRC channel. - -Installation -------------- -1. Download and extract Tinyboard to your web directory or get the latest - development version with: - - git clone git://github.com/ctrlcctrlv/8chan.git - -2. Copy secrets.example.php to secrets.php and edit it. - -3. Navigate to ```install.php``` in your web browser and follow the - prompts. -4. vichan should now be installed. Log in to ```mod.php``` with the - default username and password combination: **admin / password**. - -Please remember to change the administrator account password. - -See also: [Configuration Basics](http://tinyboard.org/docs/?p=Config). - -Upgrade -------- -To upgrade from any version of Tinyboard or vichan: - -Either run ```git pull``` to update your files, if you used git, or -backup your ```inc/instance-config.php```, replace all your files in place -(don't remove boards etc.), then put ```inc/instance-config.php``` back and -finally run ```install.php```. - -Support --------- -vichan is still beta software -- there are bound to be bugs. If you find a -bug, please report it. - -If you need assistance with installing, configuring, or using vichan, you may -find support from a variety of sources: - -* If you're unsure about how to enable or configure certain features, make - sure you have read the comments in ```inc/config.php```. -* Check out an [official vichan board](http://int.vichan.net/devel/). -* You can join vichan's IRC channel for support - [irc.6irc.net #vichan-devel](irc://irc.6irc.net/vichan-devel) - -### Tinyboard support -8chan is based on a Tinyboard, so both engines have very much in common. These -links may be helpful for you as well: - -* Tinyboard documentation can be found [here](http://tinyboard.org/docs/). -* You can join Tinyboard's IRC channel for support and general queries: - [irc.datnode.net #tinyboard](irc://irc.datnode.net/tinyboard). -* You may find help at [tinyboard.org](http://tinyboard.org/#help). - -Donations ---------- -Do you like our work? You can motivate us financially to do better ;) -* Bitcoin: [![tip for next commit](http://tip4commit.com/projects/708.svg)](http://tip4commit.com/projects/708) - -You can also ask us to develop some feature specially for you <3. Join our IRC -channel and ask for a quote (there are a few of us, who work with the codebase -and are skilled enough to develop such features pretty quickly). - -CLI tools ------------------ -There are a few command line interface tools, based on Tinyboard-Tools. These need -to be launched from a Unix shell account (SSH, or something). They are located in a ```tools/``` -directory. - -You actually don't need these tools for your imageboard functioning, they are aimed -at the power users. You won't be able to run these from shared hosting accounts -(i.e. all free web servers). - -Localisation ------------- -Want to have vichan/8chan in your language? You can contribute your translations at this URL: - -https://www.transifex.com/projects/p/tinyboard-vichan-devel/ - -Oekaki ------- -vichan makes use of [wPaint](https://github.com/websanova/wPaint) for oekaki. After you pull the repository, however, you will need to download wPaint separately using git's `submodule` feature. Use the following commands: +Because I cannot be bothered to maintain `install.php`, the install process is as such: ``` -git submodule init -git submodule update +mysql -uroot 8chan < install.sql +echo '8chan' > .installed ``` -To enable oekaki, add all the scripts listed in `js/wpaint.js` to your `instance-config.php`. +Here's my install script as of 11/14/2014 for the 8chan servers which run Ubuntu 14.04: -WebM support ------------- -Read `inc/lib/webm/README.md` for information about enabling webm. - -Static Pages ------------- -Some pages like `/faq.html` need to be pre-generated: ``` -$ php faq.php > faq.html +apt-get install graphicsmagick gifsicle php5-fpm mysql-client php5-mysql php5-cli php-pear php5-apcu; add-apt-repository ppa:jon-severinsson/ffmpeg; add-apt-repository ppa:nginx/stable; apt-get update; apt-get install nginx ffmpeg; pear install Net_DNS2 ``` -vichan API ----------- -vichan provides by default a 4chan-compatible JSON API. For documentation on this, see: -https://github.com/vichan-devel/vichan-API/ . - -License --------- -See [LICENSE.md](http://github.com/vichan-devel/vichan/blob/master/LICENSE.md). - +Have fun! diff --git a/install.sql b/install.sql index 812349c1..9af84787 100644 --- a/install.sql +++ b/install.sql @@ -75,13 +75,6 @@ CREATE TABLE IF NOT EXISTS `board_create` ( `uri` text NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; --- --- Dumping data for table `boards` --- - -INSERT INTO `boards` VALUES -('b', 'Random', NULL); - -- -------------------------------------------------------- -- From 62a6dac022cb338f7b719d0c35a64ab3efc64658 Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 14 Nov 2014 15:09:52 +0100 Subject: [PATCH 13/86] custom captcha integration --- inc/8chan-mod-pages.php | 11 +++++++++- inc/config.php | 15 +++++++++++++ inc/instance-config.php | 1 + js/captcha.js | 44 +++++++++++++++++++++++++++++++++++++ js/quick-reply.js | 6 ++--- post.php | 15 +++++++++++++ templates/mod/settings.html | 2 ++ templates/post_form.html | 11 ++++++++++ 8 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 js/captcha.js diff --git a/inc/8chan-mod-pages.php b/inc/8chan-mod-pages.php index 1474cf20..3375820a 100644 --- a/inc/8chan-mod-pages.php +++ b/inc/8chan-mod-pages.php @@ -317,6 +317,9 @@ FLAGS; $code_tags = isset($_POST['code_tags']) ? '$config[\'additional_javascript\'][] = \'js/code_tags/run_prettify.js\';$config[\'markup\'][] = array("/\[code\](.+?)\[\/code\]/ms", "
\$1
");' : ''; $katex = isset($_POST['katex']) ? '$config[\'katex\'] = true;$config[\'additional_javascript\'][] = \'js/katex/katex.min.js\'; $config[\'markup\'][] = array("/\[tex\](.+?)\[\/tex\]/ms", "\$1"); $config[\'additional_javascript\'][] = \'js/katex-enable.js\';' : ''; $user_flags = isset($_POST['user_flags']) ? "if (file_exists('$b/flags.php')) { include 'flags.php'; }\n" : ''; + $captcha = isset($_POST['captcha']) ? 'true' : 'false'; + $captcha_charset = base64_encode(isset($_POST['captcha_charset']) && $_POST['captcha_charset'] ? $_POST['captcha_charset'] : 'abcdefghijklmnopqrstuvwxyz'); + $oekaki_js = <<fetch(PDO::FETCH_ASSOC)) { diff --git a/inc/config.php b/inc/config.php index ebbe31a2..3adabcc9 100644 --- a/inc/config.php +++ b/inc/config.php @@ -272,6 +272,8 @@ 'embed', 'recaptcha_challenge_field', 'recaptcha_response_field', + 'captcha_cookie', + 'captcha_text', 'spoiler', 'page', 'file_url', @@ -300,6 +302,19 @@ $config['recaptcha_public'] = '6LcXTcUSAAAAAKBxyFWIt2SO8jwx4W7wcSMRoN3f'; $config['recaptcha_private'] = '6LcXTcUSAAAAAOGVbVdhmEM1_SyRF4xTKe8jbzf_'; + $config['captcha'] = array(); + + // Enable custom captcha provider + $config['captcha']['enabled'] = false; + + // Custom captcha provider path + $config['captcha']['provider_get'] = 'http://8chan.vichan.net/captcha/entrypoint.php'; + $config['captcha']['provider_check'] = 'http://8chan.vichan.net/captcha/entrypoint.php'; + + // Custom captcha extra field (eg. charset) + $config['captcha']['extra'] = 'abcdefghijklmnopqrstuvwxyz'; + + /* * Custom filters detect certain posts and reject/ban accordingly. They are made up of a condition and an * action (for when ALL conditions are met). As every single post has to be put through each filter, diff --git a/inc/instance-config.php b/inc/instance-config.php index aa45f960..da57903c 100644 --- a/inc/instance-config.php +++ b/inc/instance-config.php @@ -91,6 +91,7 @@ $config['additional_javascript'][] = 'js/jquery.min.js'; $config['additional_javascript'][] = 'js/jquery.mixitup.min.js'; $config['additional_javascript'][] = 'js/catalog.js'; + $config['additional_javascript'][] = 'js/captcha.js'; $config['additional_javascript'][] = 'js/jquery.tablesorter.min.js'; $config['additional_javascript'][] = 'js/options.js'; $config['additional_javascript'][] = 'js/style-select.js'; diff --git a/js/captcha.js b/js/captcha.js new file mode 100644 index 00000000..1824214b --- /dev/null +++ b/js/captcha.js @@ -0,0 +1,44 @@ +var tout; + +function redo_events(provider, extra) { + $('.captcha .captcha_text, textarea[id="body"]').off("focus").one("focus", function() { actually_load_captcha(provider, extra); }); +} + +function actually_load_captcha(provider, extra) { + $('.captcha .captcha_text, textarea[id="body"]').off("focus"); + + if (tout !== undefined) { + clearTimeout(tout); + } + + $.getJSON(provider, {mode: 'get', extra: extra}, function(json) { + $(".captcha .captcha_cookie").val(json.cookie); + $(".captcha .captcha_html").html(json.captchahtml); + + setTimeout(function() { + redo_events(provider, extra); + }, json.expires_in * 1000); + }); +} + +function load_captcha(provider, extra) { + $(function() { + $(".captcha>td").html(""+ + ""+ + "
"); + + $("#quick-reply .captcha .captcha_text").prop("placeholder", _("Verification")); + + $(".captcha .captcha_html").on("click", function() { actually_load_captcha(provider, extra); }); + $(document).on("ajax_after_post", function() { actually_load_captcha(provider, extra); }); + redo_events(provider, extra); + + $(window).on("quick-reply", function() { + redo_events(provider, extra); + $("#quick-reply .captcha .captcha_html").html($("form:not(#quick-reply) .captcha .captcha_html").html()); + $("#quick-reply .captcha .captcha_cookie").val($("form:not(#quick-reply) .captcha .captcha_cookie").html()); + $("#quick-reply .captcha .captcha_html").on("click", function() { actually_load_captcha(provider, extra); }); + }); + }); +} + diff --git a/js/quick-reply.js b/js/quick-reply.js index 07fe4aef..85503dd8 100644 --- a/js/quick-reply.js +++ b/js/quick-reply.js @@ -149,14 +149,14 @@ .removeAttr('size') .attr('placeholder', $th.clone().children().remove().end().text()); } - + // Move anti-spam nonsense and remove $th.contents().filter(function() { return this.nodeType == 3; // Node.TEXT_NODE }).remove(); $th.contents().appendTo($dummyStuff); $th.remove(); - + if ($td.find('input[name="password"]').length) { // Hide password field $(this).hide(); @@ -280,7 +280,7 @@ $postForm.find('textarea[name="body"]').removeAttr('id').removeAttr('cols').attr('placeholder', _('Comment')); - $postForm.find('textarea:not([name="body"]),input[type="hidden"]').removeAttr('id').appendTo($dummyStuff); + $postForm.find('textarea:not([name="body"]),input[type="hidden"]:not(.captcha_cookie)').removeAttr('id').appendTo($dummyStuff); $postForm.find('br').remove(); $postForm.find('table').prepend('\ diff --git a/post.php b/post.php index 650b431b..bffc91c0 100644 --- a/post.php +++ b/post.php @@ -232,6 +232,21 @@ elseif (isset($_POST['post'])) { } } + // Same, but now with our custom captcha provider + if ($config['captcha']['enabled']) { + $resp = file_get_contents($config['captcha']['provider_check'] . "?" . http_build_query([ + 'mode' => 'check', + 'text' => $_POST['captcha_text'], + 'extra' => $config['captcha']['extra'], + 'cookie' => $_POST['captcha_cookie'] + ])); + + if ($resp !== '1') { + error($config['error']['captcha'] . + ''); + } + } + //if (!(($post['op'] && $_POST['post'] == $config['button_newtopic']) || //(!$post['op'] && $_POST['post'] == $config['button_reply']))) //error($config['error']['bot']); diff --git a/templates/mod/settings.html b/templates/mod/settings.html index 487f82d8..74319aa4 100644 --- a/templates/mod/settings.html +++ b/templates/mod/settings.html @@ -44,6 +44,8 @@ {% trans %}Enable dice rolling{% endtrans %} {% trans %}Don't allow users to repost images{% endtrans %} {% trans %}Allow a poster to delete his own posts{% endtrans %} + {% trans %}Enable captcha{% endtrans %} + {% trans %}Captcha charset{% endtrans %} {% trans %}Language{% endtrans %}
{% trans %}To contribute translations, register at Transifex{% endtrans %} '); - $replacement.attr('name', $(this).attr('name')); - $replacement.val(submit_txt); - $(this) - .after($replacement) - .replaceWith($('').val(submit_txt)); - }); - $(form).submit(); + alert(_('The server returned an error or truncated response -- your post was probably still submitted. If it wasn\'t, 8chan.co might be experiencing issues right now -- please try your post again later.')); }, data: formData, cache: false, From 71c89de6defcc103f4f335717c78259aaa845dea Mon Sep 17 00:00:00 2001 From: 8chan Date: Mon, 17 Nov 2014 05:27:00 -0800 Subject: [PATCH 18/86] >letting board owners change the charset yeah, nah. thanks anyway though @czaks lmao --- inc/8chan-mod-pages.php | 2 -- templates/mod/settings.html | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/inc/8chan-mod-pages.php b/inc/8chan-mod-pages.php index 3375820a..6e792572 100644 --- a/inc/8chan-mod-pages.php +++ b/inc/8chan-mod-pages.php @@ -318,7 +318,6 @@ FLAGS; $katex = isset($_POST['katex']) ? '$config[\'katex\'] = true;$config[\'additional_javascript\'][] = \'js/katex/katex.min.js\'; $config[\'markup\'][] = array("/\[tex\](.+?)\[\/tex\]/ms", "\$1"); $config[\'additional_javascript\'][] = \'js/katex-enable.js\';' : ''; $user_flags = isset($_POST['user_flags']) ? "if (file_exists('$b/flags.php')) { include 'flags.php'; }\n" : ''; $captcha = isset($_POST['captcha']) ? 'true' : 'false'; - $captcha_charset = base64_encode(isset($_POST['captcha_charset']) && $_POST['captcha_charset'] ? $_POST['captcha_charset'] : 'abcdefghijklmnopqrstuvwxyz'); $oekaki_js = <<{% trans %}Enable dice rolling{% endtrans %} {% trans %}Don't allow users to repost images{% endtrans %} {% trans %}Allow a poster to delete his own posts{% endtrans %} - {% trans %}Enable captcha{% endtrans %} - {% trans %}Captcha charset{% endtrans %} + {% trans %}Enable CAPTCHA{% endtrans %}
This is not ReCAPTCHA, it is custom to 8chan {% trans %}Language{% endtrans %}
{% trans %}To contribute translations, register at Transifex{% endtrans %} ' + ' Disable board specific style sheets' + '') + $(element).find('input').prop('checked', disableStyles) + } + + $(element).on(event, function() { + if(disableStyles) { + delete localStorage.disablestylesheet; + } else { + localStorage.disablestylesheet = true; + } + disableStyles =! disableStyles; + document.styleSheets[sheet].disabled = disableStyles; + }) +} +$(document).ready(disableStyleSheet()); From 8fa14518e2de990bb66f11296d3691ef17ba5b5a Mon Sep 17 00:00:00 2001 From: wholelotofhs Date: Thu, 20 Nov 2014 14:50:01 -0500 Subject: [PATCH 23/86] only disable stylesheets on Applicable pages --- js/disable-styles.js | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/js/disable-styles.js b/js/disable-styles.js index aff6f034..c44510e4 100644 --- a/js/disable-styles.js +++ b/js/disable-styles.js @@ -4,17 +4,21 @@ var disableStyleSheet = function () { var disableStyles = localStorage['disablestylesheet'] ? true : false; - var i = 0 - while(i Date: Thu, 20 Nov 2014 15:12:40 -0500 Subject: [PATCH 24/86] clean up and added comments --- js/disable-styles.js | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/js/disable-styles.js b/js/disable-styles.js index c44510e4..823d023a 100644 --- a/js/disable-styles.js +++ b/js/disable-styles.js @@ -2,8 +2,9 @@ /* Adds a checkbox in the General options tab to disable and enable board style sheets. */ -var disableStyleSheet = function () { +$(document).ready(function () { var disableStyles = localStorage['disablestylesheet'] ? true : false; + /* only search for and disable board stylesheets if the user is on a page that uses them */ if(active_page == 'ukko' || active_page == 'thread' || active_page == 'index' || active_page == 'catalog') { var i = 0 @@ -18,22 +19,19 @@ var disableStyleSheet = function () { i++ } } - + /* add the option on all pages so that the user doesn't need to goto a board to toggle it */ if (window.Options && Options.get_tab('general')){ - element = '#disablestyle' - event = 'change' Options.extend_tab('general','') - $(element).find('input').prop('checked', disableStyles) + $('#disablestyle').find('input').prop('checked', disableStyles) } - $(element).on(event, function() { + $('#disablestyle').on('change', function() { if(disableStyles) { - delete localStorage.disablestylesheet; + delete localStorage.disablestylesheet } else { - localStorage.disablestylesheet = true; + localStorage.disablestylesheet = true } - disableStyles =! disableStyles; - if(active_page == 'ukko' || active_page == 'thread' || active_page == 'index' || active_page == 'catalog') document.styleSheets[sheet].disabled = disableStyles; + disableStyles =! disableStyles + if(active_page == 'ukko' || active_page == 'thread' || active_page == 'index' || active_page == 'catalog') document.styleSheets[sheet].disabled = disableStyles }) -} -$(document).ready(disableStyleSheet()); +}) From 345b0b848fdafdf74b39da485b294edd281252a6 Mon Sep 17 00:00:00 2001 From: wholelotofhs Date: Thu, 20 Nov 2014 15:23:19 -0500 Subject: [PATCH 25/86] Update disable-styles.js --- js/disable-styles.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/js/disable-styles.js b/js/disable-styles.js index 823d023a..c69f4e2d 100644 --- a/js/disable-styles.js +++ b/js/disable-styles.js @@ -1,5 +1,3 @@ -/* This file is dedicated to the public domain; you may do as you wish with it. */ - /* Adds a checkbox in the General options tab to disable and enable board style sheets. */ $(document).ready(function () { From faa51accaa860e543c5c2d97661925cb79eb606a Mon Sep 17 00:00:00 2001 From: 8chan Date: Fri, 21 Nov 2014 04:05:20 -0800 Subject: [PATCH 26/86] We dynamic pages now, read.php --- 404.php | 3 +- inc/config.php | 7 ++ inc/display.php | 8 +- inc/functions.php | 155 +++++++++++++++--------- inc/mod/pages.php | 3 + js/ajax.js | 4 +- js/thread-stats.js | 2 +- post.php | 6 +- read.php | 183 +++++++++++++++++++++++++++++ templates/themes/catalog/theme.php | 19 ++- 10 files changed, 323 insertions(+), 67 deletions(-) create mode 100644 read.php diff --git a/404.php b/404.php index 0e1b634f..cee2ece6 100644 --- a/404.php +++ b/404.php @@ -1,6 +1,7 @@ '. ''. ''; + + // Use read.php? + // read.php is a file that dynamically displays pages to users instead of the build on demand system in use in Tinyboard since 2010. + // + // read.php is basically a watered down mod.php -- if coupled with caching, it improves performance and allows for easier replication + // across machines. + $config['use_read_php'] = false; diff --git a/inc/display.php b/inc/display.php index a58d92f4..9561c4b8 100644 --- a/inc/display.php +++ b/inc/display.php @@ -406,9 +406,13 @@ class Post { } public function getClean( ) { - global $board; + global $board, $config; if( !isset( $this->clean ) ) { + if ($config['cache']['enabled'] && $this->clean = cache::get("post_clean_{$board['uri']}_{$this->id}")) { + return $this->clean; + } + $query = prepare("SELECT * FROM `post_clean` WHERE `post_id` = :post AND `board_id` = :board"); $query->bindValue( ':board', $board['uri'] ); $query->bindValue( ':post', $this->id ); @@ -424,6 +428,8 @@ class Post { 'clean_local_mod_id' => null, 'clean_global_mod_id' => null, ); + + cache::set("post_clean_{$board['uri']}_{$this->id}", $this->clean); } } diff --git a/inc/functions.php b/inc/functions.php index 2ffdc222..bf28009d 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1099,6 +1099,11 @@ function deletePost($id, $error_if_doesnt_exist=true, $rebuild_after=true) { $antispam_query->bindValue(':board', $board['uri']); $antispam_query->bindValue(':thread', $post['id']); $antispam_query->execute() or error(db_error($antispam_query)); + + cache::delete("thread_index_{$board['uri']}_{$post['id']}"); + cache::delete("thread_index_display_{$board['uri']}_{$post['id']}"); + cache::delete("thread_{$board['uri']}_{$post['id']}"); + cache::delete("catalog_{$board['uri']}"); } elseif ($query->rowCount() == 1) { // Rebuild thread $rebuild = &$post['thread']; @@ -1190,57 +1195,62 @@ function index($page, $mod=false) { return false; $threads = array(); - + while ($th = $query->fetch(PDO::FETCH_ASSOC)) { - $thread = new Thread($th, $mod ? '?/' : $config['root'], $mod); + if ($config['cache']['enabled'] && !($thread = cache::get("thread_index_display_{$board['uri']}_{$th['id']}"))) { + $thread = new Thread($th, $mod ? '?/' : $config['root'], $mod); - if ($config['cache']['enabled']) { - $cached = cache::get("thread_index_{$board['uri']}_{$th['id']}"); - if (isset($cached['replies'], $cached['omitted'])) { - $replies = $cached['replies']; - $omitted = $cached['omitted']; - } else { - unset($cached); + if ($config['cache']['enabled']) { + $cached = cache::get("thread_index_{$board['uri']}_{$th['id']}"); + if (isset($cached['replies'], $cached['omitted'])) { + $replies = $cached['replies']; + $omitted = $cached['omitted']; + } else { + unset($cached); + } } - } - if (!isset($cached)) { - $posts = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `thread` = :id ORDER BY `id` DESC LIMIT :limit", $board['uri'])); - $posts->bindValue(':id', $th['id']); - $posts->bindValue(':limit', ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']), PDO::PARAM_INT); - $posts->execute() or error(db_error($posts)); + if (!isset($cached)) { + $posts = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `thread` = :id ORDER BY `id` DESC LIMIT :limit", $board['uri'])); + $posts->bindValue(':id', $th['id']); + $posts->bindValue(':limit', ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']), PDO::PARAM_INT); + $posts->execute() or error(db_error($posts)); - $replies = array_reverse($posts->fetchAll(PDO::FETCH_ASSOC)); + $replies = array_reverse($posts->fetchAll(PDO::FETCH_ASSOC)); - if (count($replies) == ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview'])) { - $count = numPosts($th['id']); - $omitted = array('post_count' => $count['replies'], 'image_count' => $count['images']); - } else { - $omitted = false; + if (count($replies) == ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview'])) { + $count = numPosts($th['id']); + $omitted = array('post_count' => $count['replies'], 'image_count' => $count['images']); + } else { + $omitted = false; + } + + if ($config['cache']['enabled']) + cache::set("thread_index_{$board['uri']}_{$th['id']}", array( + 'replies' => $replies, + 'omitted' => $omitted, + )); } + $num_images = 0; + foreach ($replies as $po) { + if ($po['num_files']) + $num_images+=$po['num_files']; + + $thread->add(new Post($po, $mod ? '?/' : $config['root'], $mod)); + } + + $thread->images = $num_images; + $thread->replies = isset($omitted['post_count']) ? $omitted['post_count'] : count($replies); + + if ($omitted) { + $thread->omitted = $omitted['post_count'] - ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']); + $thread->omitted_images = $omitted['image_count'] - $num_images; + } + if ($config['cache']['enabled']) - cache::set("thread_index_{$board['uri']}_{$th['id']}", array( - 'replies' => $replies, - 'omitted' => $omitted, - )); + cache::set("thread_index_display_{$board['uri']}_{$th['id']}", $thread); + } - - $num_images = 0; - foreach ($replies as $po) { - if ($po['num_files']) - $num_images+=$po['num_files']; - - $thread->add(new Post($po, $mod ? '?/' : $config['root'], $mod)); - } - - $thread->images = $num_images; - $thread->replies = isset($omitted['post_count']) ? $omitted['post_count'] : count($replies); - - if ($omitted) { - $thread->omitted = $omitted['post_count'] - ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']); - $thread->omitted_images = $omitted['image_count'] - $num_images; - } - $threads[] = $thread; $body .= $thread->build(true); } @@ -1458,6 +1468,10 @@ function checkMute() { function buildIndex() { global $board, $config, $build_pages; + if ($config['use_read_php']) { + cache::delete("catalog_{$board['uri']}"); + return; + } $pages = getPages(); if (!$config['try_smarter']) @@ -1535,6 +1549,17 @@ function buildIndex() { function buildJavascript() { global $config; + if ($config['cache']['enabled']) { + if (false === strpos($config['file_script'], '/')) { + $cache_name = 'main_js'; + } else { + $b = explode('/', $config['file_script'])[0]; + $cache_name = "board_{$b}_js"; + } + + cache::delete($cache_name); + } + $script = Element('main.js', array( 'config' => $config, )); @@ -1556,7 +1581,14 @@ function buildJavascript() { $script = JSMin::minify($script); } - file_write($config['file_script'], $script); + if ($config['cache']['enabled']) + cache::set($cache_name, $script); + + if (!$config['use_read_php']) { + file_write($config['file_script'], $script); + } else { + return $script; + } } function checkDNSBL() { @@ -1990,27 +2022,34 @@ function strip_combining_chars($str) { function buildThread($id, $return = false, $mod = false) { global $board, $config, $build_pages; + if (!$return && $config['use_read_php']) { + cache::delete("thread_index_{$board['uri']}_{$id}"); + cache::delete("thread_index_display_{$board['uri']}_{$id}"); + cache::delete("thread_{$board['uri']}_{$id}"); + cache::delete("catalog_{$board['uri']}"); + return; + } + $id = round($id); if (event('build-thread', $id)) return; - if ($config['cache']['enabled'] && !$mod) { - // Clear cache - cache::delete("thread_index_{$board['uri']}_{$id}"); - cache::delete("thread_{$board['uri']}_{$id}"); - } + if (!($thread = cache::get("thread_{$board['uri']}_{$id}"))) { + unset($thread); + $query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id`", $board['uri'])); + $query->bindValue(':id', $id, PDO::PARAM_INT); + $query->execute() or error(db_error($query)); - $query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id`", $board['uri'])); - $query->bindValue(':id', $id, PDO::PARAM_INT); - $query->execute() or error(db_error($query)); - - while ($post = $query->fetch(PDO::FETCH_ASSOC)) { - if (!isset($thread)) { - $thread = new Thread($post, $mod ? '?/' : $config['root'], $mod); - } else { - $thread->add(new Post($post, $mod ? '?/' : $config['root'], $mod)); + while ($post = $query->fetch(PDO::FETCH_ASSOC)) { + if (!isset($thread)) { + $thread = new Thread($post, $mod ? '?/' : $config['root'], $mod); + } else { + $thread->add(new Post($post, $mod ? '?/' : $config['root'], $mod)); + } } + + if (isset($thread)) cache::set("thread_{$board['uri']}_{$id}", $thread); } // Check if any posts were found @@ -2038,7 +2077,7 @@ function buildThread($id, $return = false, $mod = false) { $build_pages[] = thread_find_page($id); // json api - if ($config['api']['enabled']) { + if ($config['api']['enabled'] && !$config['use_read_php']) { $api = new Api(); $json = json_encode($api->translateThread($thread)); $jsonFilename = $board['dir'] . $config['dir']['res'] . $id . '.json'; diff --git a/inc/mod/pages.php b/inc/mod/pages.php index 61920b46..0f059f05 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -2995,6 +2995,9 @@ function mod_report_clean( $global_reports, $board, $unclean, $post, $global, $l $log_action = ($unclean ? "Closed" : "Re-opened" ); $log_scope = ($local && $global ? "local and global" : ($local ? "local" : "global" ) ); modLog( "{$log_action} reports for post #{$post} in {$log_scope}.", $board); + if ($config['cache']['enabled']) { + cache::delete("post_clean_{$board}_{$post}"); + } rebuildPost( $post ); } diff --git a/js/ajax.js b/js/ajax.js index 1b2bf2c0..a80ae541 100644 --- a/js/ajax.js +++ b/js/ajax.js @@ -55,7 +55,7 @@ $(window).ready(function() { } return xhr; }, - success: function(post_response) { + success: function(post_response, textStatus, xhr) { if (post_response.error) { if (post_response.banned) { // You are banned. Must post the form normally so the user can see the ban message. @@ -109,12 +109,14 @@ $(window).ready(function() { $(form).find('input[type="submit"]').val(_('Posted...')); $(document).trigger("ajax_after_post", post_response); } else { + console.log(xhr); alert(_('An unknown error occured when posting!')); $(form).find('input[type="submit"]').val(submit_txt); $(form).find('input[type="submit"]').removeAttr('disabled'); } }, error: function(xhr, status, er) { + console.log(xhr); alert(_('The server returned an error or truncated response -- your post was probably still submitted. If it wasn\'t, 8chan.co might be experiencing issues right now -- please try your post again later.')); }, data: formData, diff --git a/js/thread-stats.js b/js/thread-stats.js index 7c13d66c..52176ae8 100644 --- a/js/thread-stats.js +++ b/js/thread-stats.js @@ -1,4 +1,4 @@ -/* +/* * thread-stats.js * - Adds statistics of the thread below the posts area * - Shows ID post count beside each postID on hover diff --git a/post.php b/post.php index bffc91c0..50c5c8bd 100644 --- a/post.php +++ b/post.php @@ -852,8 +852,10 @@ elseif (isset($_POST['post'])) { bumpThread($post['thread']); } - buildThread($post['op'] ? $id : $post['thread']); - + $pid = $post['op'] ? $id : $post['thread']; + + buildThread($pid); + if ($config['try_smarter'] && $post['op']) $build_pages = range(1, $config['max_pages']); diff --git a/read.php b/read.php new file mode 100644 index 00000000..f1589ee3 --- /dev/null +++ b/read.php @@ -0,0 +1,183 @@ + 'view_board', + '/(\%b)/(\d+)\.json' => 'view_api_index', + '/(\%b)/catalog\.json' => 'view_api_catalog', + '/(\%b)/threads\.json' => 'view_api_threads', + '/(\%b)/main\.js' => 'view_js', + '/main\.js' => 'view_js', + '/(\%b)/catalog(\.html)?' => 'view_catalog', + '/(\%b)/' . preg_quote($config['file_index'], '!') => 'view_board', + '/(\%b)/' . str_replace('%d', '(\d+)', preg_quote($config['file_page'], '!')) => 'view_board', + '/(\%b)/' . preg_quote($config['dir']['res'], '!') . + str_replace('%d', '(\d+)', preg_quote($config['file_page50'], '!')) => 'view_thread50', + '/(\%b)/' . preg_quote($config['dir']['res'], '!') . + str_replace('%d', '(\d+)', '%d(\.html)?') => 'view_thread', +); + +$new_pages = array(); +foreach ($pages as $key => $callback) { + if (is_string($callback) && preg_match('/^secure /', $callback)) + $key .= '(/(?P[a-f0-9]{8}))?'; + $key = str_replace('\%b', '?P' . sprintf(substr($config['board_path'], 0, -1), $config['board_regex']), $key); + $new_pages[@$key[0] == '!' ? $key : '!^' . $key . '(?:&[^&=]+=[^&]*)*$!u'] = $callback; +} +$pages = $new_pages; + +function view_thread50($boardName, $thread) { + global $config, $mod; + + if (!openBoard($boardName)) { + include '404.php'; + return; + } + + $page = buildThread50($thread, true, false); + echo $page; +} + +function view_thread($boardName, $thread) { + global $config, $mod; + + if (!openBoard($boardName)) { + include '404.php'; + return; + } + + $page = buildThread($thread, true, false); + echo $page; +} + +function view_api_index($boardName, $page) { + global $config, $board; + + $api = new Api(); + + if (!openBoard($boardName)) { + include '404.php'; + return; + } + + $content = index($page+1); + + if (!$content) + return; + + if ($config['api']['enabled']) { + $threads = $content['threads']; + $json = json_encode($api->translatePage($threads)); + header('Content-Type: text/json'); + + echo $json; + } +} + +function APICatalog($boardName, $gen_threads = false) { + global $config; + if (!openBoard($boardName)) { + include '404.php'; + return; + } + + header('Content-Type: text/json'); + + $catalog = array(); + $api = new Api(); + + for ($page = 1; $page <= $config['max_pages']; $page++) { + $content = index($page); + + if (!$content) + break; + + $catalog[$page-1] = $content['threads']; + } + + echo json_encode($api->translateCatalog($catalog, $gen_threads)); +} + +function view_api_catalog($boardName) { + APICatalog($boardName, false); +} + +function view_api_threads($boardName) { + APICatalog($boardName, true); +} + +function view_board($boardName, $page_no = 1) { + global $config, $mod; + + if (!openBoard($boardName)) { + include '404.php'; + return; + } + + if (!$page = index($page_no, $mod)) { + error($config['error']['404']); + } + + $page['pages'] = getPages(false); + $page['pages'][$page_no-1]['selected'] = true; + $page['btn'] = getPageButtons($page['pages'], false); + $page['mod'] = false; + $page['config'] = $config; + + echo Element('index.html', $page); +} + +function view_js($boardName = false) { + global $config; + + if ($boardName && !openBoard($boardName)) + error($config['error']['noboard']); + + if (!$boardName) { + $cache_name = 'main_js'; + } else { + $cache_name = "board_{$boardName}_js"; + } + + if (!($script = cache::get($cache_name))) { + $script = buildJavascript(); + } + + echo $script; +} + +function view_catalog($boardName) { + global $board, $config; + $_theme = 'catalog'; + + $theme = loadThemeConfig($_theme); + + if (!openBoard($boardName)) { + include '404.php'; + return; + } + + require_once $config['dir']['themes'] . '/' . $_theme . '/theme.php'; + + $catalog = $theme['build_function']('read_php', themeSettings($_theme), $board['uri']); + echo $catalog; +} + +$found = false; +foreach ($pages as $uri => $handler) { + if (preg_match($uri, $query, $matches)) { + $matches = array_slice($matches, 2); + if (is_callable($handler)) { + $found = true; + call_user_func_array($handler, $matches); + } + } +} + +if (!$found) + include '404.php'; diff --git a/templates/themes/catalog/theme.php b/templates/themes/catalog/theme.php index 5b940167..72e43c14 100644 --- a/templates/themes/catalog/theme.php +++ b/templates/themes/catalog/theme.php @@ -25,14 +25,18 @@ } elseif ($action == 'post-thread' || ($settings['update_on_posts'] && $action == 'post') || ($settings['update_on_posts'] && $action == 'post-delete') && (in_array($board, $boards) | $settings['all'])) { $b = new Catalog(); $b->build($settings, $board); + } elseif ($action == 'read_php') { + $b = new Catalog(); + return $b->build($settings, $board, true); } } // Wrap functions in a class so they don't interfere with normal Tinyboard operations class Catalog { - public function build($settings, $board_name) { + public function build($settings, $board_name, $return = false) { global $config, $board; + if (!($config['cache']['enabled'] && $catalog_out = cache::get("catalog_{$board['uri']}"))) {; openBoard($board_name); $recent_images = array(); @@ -90,7 +94,7 @@ $config['additional_javascript'][] = $s; } - file_write($config['dir']['home'] . $board_name . '/catalog.html', Element('themes/catalog/catalog.html', Array( + $catalog_out = Element('themes/catalog/catalog.html', Array( 'settings' => $settings, 'config' => $config, 'boardlist' => createBoardlist(), @@ -99,6 +103,15 @@ 'stats' => $stats, 'board' => $board_name, 'link' => $config['root'] . $board['dir'] - ))); + )); + + cache::set("catalog_{$board['uri']}", $catalog_out); + } + + if ($return) { + return $catalog_out; + } else { + file_write($config['dir']['home'] . $board_name . '/catalog.html', $catalog_out); + } } }; From 7d11c7a8842b36df9555d6d3569b30a248fdb4a5 Mon Sep 17 00:00:00 2001 From: 8chan Date: Fri, 21 Nov 2014 16:17:00 -0800 Subject: [PATCH 27/86] Fix mod mode for read.php --- inc/functions.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index bf28009d..521d994a 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1197,7 +1197,7 @@ function index($page, $mod=false) { $threads = array(); while ($th = $query->fetch(PDO::FETCH_ASSOC)) { - if ($config['cache']['enabled'] && !($thread = cache::get("thread_index_display_{$board['uri']}_{$th['id']}"))) { + if (($config['cache']['enabled'] && !($thread = cache::get("thread_index_display_{$board['uri']}_{$th['id']}")) || $mod)) { $thread = new Thread($th, $mod ? '?/' : $config['root'], $mod); if ($config['cache']['enabled']) { @@ -1247,7 +1247,7 @@ function index($page, $mod=false) { $thread->omitted_images = $omitted['image_count'] - $num_images; } - if ($config['cache']['enabled']) + if ($config['cache']['enabled'] && !$mod) cache::set("thread_index_display_{$board['uri']}_{$th['id']}", $thread); } @@ -2035,7 +2035,7 @@ function buildThread($id, $return = false, $mod = false) { if (event('build-thread', $id)) return; - if (!($thread = cache::get("thread_{$board['uri']}_{$id}"))) { + if (!($thread = cache::get("thread_{$board['uri']}_{$id}")) || $mod) { unset($thread); $query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id`", $board['uri'])); $query->bindValue(':id', $id, PDO::PARAM_INT); From d8c385176ceaff6ced9b74cecc3750c45caa5d01 Mon Sep 17 00:00:00 2001 From: 8chan Date: Fri, 21 Nov 2014 23:32:22 -0800 Subject: [PATCH 28/86] New claim.html page --- claim.php | 60 ++++++++++++++++++++++++++++++++++++-- faq.php | 6 ++-- inc/8chan-mod-pages.php | 32 ++++++++++++++++++++ templates/8chan/index.html | 2 +- 4 files changed, 94 insertions(+), 6 deletions(-) diff --git a/claim.php b/claim.php index edc6669a..4c733180 100644 --- a/claim.php +++ b/claim.php @@ -1,5 +1,61 @@ execute(); + $row = $query->fetch(); + $ago = (new DateTime)->sub(new DateInterval('P1W')); + $mod_ago = (new DateTime)->sub(new DateInterval('P2W')); -error('Automatic claiming is no longer available. To claim a board, send your request to admin@8chan.co along with the IP you used to post on that board.'); + $last_activity_date = new DateTime(); + $last_mod_date = new DateTime(); + + $last_activity_date->setTimestamp($row['time']); + + $query = query("SELECT id, username FROM mods WHERE boards = '$board'"); + $mods = $query->fetchAll(); + + if ($mods) { + $mod = $mods[0]['id']; + $query = query("SELECT MAX(time) AS time FROM modlogs WHERE `mod` = $mod"); + $a = $query->fetchAll(PDO::FETCH_COLUMN); + + if ($a[0]) { + $last_mod_date->setTimestamp($a[0]); + if (!$row['time']) + $last_activity_date->setTimestamp($a[0]); + } else {// no one ever logged in, try board creation time + $query = query("SELECT UNIX_TIMESTAMP(time) AS time FROM board_create WHERE uri = '$board'"); + $crt = $query->fetchAll(PDO::FETCH_COLUMN); + $last_activity_date->setTimestamp($crt[0]); + $last_mod_date = false; + } + } + + if ($mods and ($last_activity_date < $ago or ($last_mod_date and $last_mod_date < $mod_ago))) { + return array($last_activity_date, $last_mod_date, $mods); + } + else { + return false; + } +} +$q = query("SELECT uri FROM boards"); +$boards = $q->fetchAll(PDO::FETCH_COLUMN); +$delete = array(); +foreach($boards as $board) { + $last_activity = last_activity($board); + + if ($last_activity) { + list($last_activity_date, $last_mod_date, $mods) = $last_activity; + + $last_mod_f = $last_mod_date ? $last_mod_date->format('Y-m-d H:i:s') : 'never'; + $last_activity_f = $last_activity_date ? $last_activity_date->format('Y-m-d H:i:s') : 'never'; + $delete[] = array('board' => $board, 'last_activity_date' => $last_activity_f, 'last_mod' => $last_mod_date, 'last_mod_f' => $last_mod_f); + } +} +$body = Element("8chan/claim.html", array("config" => $config, "delete" => $delete)); +echo Element("page.html", array("config" => $config, "body" => $body, "title" => _("Claim"), "subtitle" => _("Take deserted boards back from their owners"))); diff --git a/faq.php b/faq.php index cc4dbd93..2ea68f72 100644 --- a/faq.php +++ b/faq.php @@ -34,9 +34,9 @@ $body = <<The admin can be reached at admin at 8chan dot co.

Help! My board has been deleted!

-

Were you inactive for longer than one week? Were there no posts on the board for 72 hours?

+

As of November 13th, 2014, board expiration no longer occurs.

-

If either of those is true, the board was deleted automatically. You are free to recreate it. I cannot restore it, so don't bother emailing me about it.

+

You still may lose access to your board, however, if you fail to log in for two weeks or it receives no posts for a week. See here for a list of boards that are available for reclaiming.

How do I post as a volunteer on my board?

Make sure you are using the volunteer interface to view your board. The URL of your browser should be https://8chan.co/mod.php?/yourboard.

@@ -47,7 +47,7 @@ $body = <<If they are doing something illegal, email me.

Can you give me X board?

-

If the owner of the board is inactive or the board is broken due to bad CSS, sure. Send me an email.

+

If the owner of the board is inactive or the board is broken due to bad CSS, sure. Send me an email. You can see a list of boards that qualify for being taken over here.

Can you add some new feature?

Open a Github issue. Better yet, write it yourself and open a pull request. diff --git a/inc/8chan-mod-pages.php b/inc/8chan-mod-pages.php index 6b7f97b8..1073f3ba 100644 --- a/inc/8chan-mod-pages.php +++ b/inc/8chan-mod-pages.php @@ -49,6 +49,38 @@ $config['mod']['ban_appeals'] = BOARDVOLUNTEER; $config['mod']['view_ban_appeals'] = BOARDVOLUNTEER; $config['mod']['view_ban'] = BOARDVOLUNTEER; + $config['mod']['reassign_board'] = ADMIN; + + $config['mod']['custom_pages']['/reassign/(\%b)'] = function($b) { + global $board, $config; + + if (!openBoard($b)) + error("Could not open board!"); + + if (!hasPermission($config['mod']['reassign_board'], $b)) + error($config['error']['noaccess']); + + $query = query("SELECT id, username FROM mods WHERE boards = '$b' AND type = 20"); + $mods = $query->fetchAll(); + + if (!$mods) { + error('No mods?'); + } + + $password = base64_encode(openssl_random_pseudo_bytes(9)); + $salt = generate_salt(); + $hashed = hash('sha256', $salt . sha1($password)); + + $query = prepare('UPDATE ``mods`` SET `password` = :hashed, `salt` = :salt WHERE BINARY username = :mod'); + $query->bindValue(':hashed', $hashed); + $query->bindValue(':salt', $salt); + $query->bindValue(':mod', $mods[0]['username']); + $query->execute(); + + $body = "Thanks for your interest in this board. Kindly find the username and password below. You can login at 8chan.co/mod.php.
Username: {$mods[0]['username']}
Password: {$password}
Thanks for using 8chan.co!"; + + mod_page(_('Edit reassign'), 'blank.html', array('board'=>$board,'token'=>make_secure_link_token('reassign/'.$board['uri']),'body'=>$body)); + }; $config['mod']['custom_pages']['/volunteers/(\%b)'] = function($b) { global $board, $config, $pdo; diff --git a/templates/8chan/index.html b/templates/8chan/index.html index dc120787..61cd051c 100644 --- a/templates/8chan/index.html +++ b/templates/8chan/index.html @@ -295,7 +295,7 @@

-

{% trans %}On ∞chan, you can create your own imageboard for free with no experience or programming knowledge needed. As long as the admin logs in once per week and it gets one new post every 72 hours, the board will be hosted forever.{% endtrans %}

+

{% trans %}On ∞chan, you can create your own imageboard for free with no experience or programming knowledge needed. As long as the board has unobtrusive CSS, the owner doesn't ban more than half the users, the owner logs in once every two weeks and it gets one new post every week, you will retain the board forever.{% endtrans %}

{% trans %}Warning: Some boards on this site might contain content of an adult or offensive nature. Please cease use of this site if it is illegal for you to view such content. The boards on this site are made entirely by the users and do not represent the opinions of the administration of 8chan.co. In the interest of free speech, only content that directly violates the DMCA or other US laws is deleted.{% endtrans %}

{% trans %}Create your board{% endtrans %} From 8a608ab62365e7798c02e06f551bf26e6e76cdd5 Mon Sep 17 00:00:00 2001 From: 8chan Date: Fri, 21 Nov 2014 23:33:05 -0800 Subject: [PATCH 29/86] Fix hide-threads.js interaction in no-animated-gif.js --- js/no-animated-gif.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/no-animated-gif.js b/js/no-animated-gif.js index 45a55f97..1b644ade 100644 --- a/js/no-animated-gif.js +++ b/js/no-animated-gif.js @@ -13,6 +13,8 @@ */ function unanimate_gif(e) { + if ($(e).closest('.thread').children('.thread-hidden').length > 0) return; + if (active_page === "catalog") var c = $(''); else From 5daa887a92db34b6598153bd65ba25437ab15228 Mon Sep 17 00:00:00 2001 From: 8chan Date: Fri, 21 Nov 2014 23:33:42 -0800 Subject: [PATCH 30/86] No need to display pruned in two places...especially if server times out / errors --- js/thread-stats.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/thread-stats.js b/js/thread-stats.js index 7c13d66c..1785b79a 100644 --- a/js/thread-stats.js +++ b/js/thread-stats.js @@ -65,7 +65,7 @@ $(document).ready(function(){ $('#thread_stats_uids').text(size(ids)); } $.getJSON('//'+ document.location.host +'/'+ board_name +'/threads.json').success(function(data){ - var found, page = 'Pruned or Deleted'; + var found, page = '???'; for (var i=0;data[i];i++){ var threads = data[i].threads; for (var j=0; threads[j]; j++){ @@ -86,7 +86,7 @@ $(document).ready(function(){ // uses ajax call so it gets loaded on a delay (depending on network resources available) var thread_stats_page_timer = setInterval(function(){ $.getJSON('//'+ document.location.host +'/'+ board_name +'/threads.json').success(function(data){ - var found, page = 'Pruned or Deleted'; + var found, page = '???'; for (var i=0;data[i];i++){ var threads = data[i].threads; for (var j=0; threads[j]; j++){ From 467eee88c697a327604fd98aaac97a3325fec510 Mon Sep 17 00:00:00 2001 From: 8chan Date: Fri, 21 Nov 2014 23:34:03 -0800 Subject: [PATCH 31/86] Make bottom watchlist-toggle work --- js/thread-watcher.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/js/thread-watcher.js b/js/thread-watcher.js index b66dc657..7f20e028 100644 --- a/js/thread-watcher.js +++ b/js/thread-watcher.js @@ -147,15 +147,16 @@ $(document).ready(function(){ } //Append the watchlist toggle button. - $('.boardlist').append('[ watchlist ]'); + $('.boardlist').append('[ watchlist ]'); //Append a watch thread button after every OP. - $('.op>.intro').append('[Watch Thread]'); + $('.op>.intro').append('[Watch Thread]'); //Draw the watchlist, hidden. watchlist.render(); //Show or hide the watchlist. - $('#watchlist-toggle').on('click', function(e) { + $('.watchlist-toggle').on('click', function(e) { + e.preventDefault(); //if ctrl+click, reset the watchlist. if (e.ctrlKey) { watchlist.render(true); @@ -169,7 +170,8 @@ $(document).ready(function(){ //Trigger the watchlist add function. //The selector is passed as an argument in case the page is not a thread. - $('.watchThread').on('click', function() { + $('.watchThread').on('click', function(e) { + e.preventDefault(); watchlist.add(this).render(); }); From 89f0d4f3bcc631e59252846e9363ec537ea36b52 Mon Sep 17 00:00:00 2001 From: 8chan Date: Fri, 21 Nov 2014 23:34:23 -0800 Subject: [PATCH 32/86] This exclamation mark was confusing people --- templates/8chan/create_success.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/8chan/create_success.html b/templates/8chan/create_success.html index b129787c..25fe4177 100644 --- a/templates/8chan/create_success.html +++ b/templates/8chan/create_success.html @@ -1,5 +1,5 @@

{% trans %}Your new board is created and is live at{% endtrans %} /{{ uri }}/.

-

{% trans %}Make sure you don't forget your password, {{ password }}!{% endtrans %}

+

{% trans %}Make sure you don't forget your password, {{ password }}{% endtrans %}

{% trans %}You can manage your board at http://8chan.co/mod.php?/.{% endtrans %}

From ef0ab8bb0039173f3c5b9b5883c0f72d94c63df7 Mon Sep 17 00:00:00 2001 From: 8chan Date: Sat, 22 Nov 2014 00:15:32 -0800 Subject: [PATCH 33/86] Isnt set if first write --- inc/8chan-mod-pages.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/8chan-mod-pages.php b/inc/8chan-mod-pages.php index 6b7f97b8..6a325742 100644 --- a/inc/8chan-mod-pages.php +++ b/inc/8chan-mod-pages.php @@ -421,7 +421,7 @@ EOT; || $_config['captcha']['extra'] != $config['captcha']['extra'] || $_config['blotter'] != $config['blotter'] || $_config['field_disable_name'] != $config['field_disable_name'] - || $_config['show_sages'] != $config['show_sages']) { + || $_config['show_sages'] != (isset($config['show_sages']) && $config['show_sages'])) { buildIndex(); $query = query(sprintf("SELECT `id` FROM ``posts_%s`` WHERE `thread` IS NULL", $b)) or error(db_error()); while ($post = $query->fetch(PDO::FETCH_ASSOC)) { From 6d54ae52622661623d7dadc51ba0655866a26418 Mon Sep 17 00:00:00 2001 From: 8chan Date: Sat, 22 Nov 2014 00:44:57 -0800 Subject: [PATCH 34/86] read.php +50 fix --- inc/functions.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/inc/functions.php b/inc/functions.php index 521d994a..f02abd3d 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1103,6 +1103,7 @@ function deletePost($id, $error_if_doesnt_exist=true, $rebuild_after=true) { cache::delete("thread_index_{$board['uri']}_{$post['id']}"); cache::delete("thread_index_display_{$board['uri']}_{$post['id']}"); cache::delete("thread_{$board['uri']}_{$post['id']}"); + cache::delete("thread_50_{$board['uri']}_{$post['id']}"); cache::delete("catalog_{$board['uri']}"); } elseif ($query->rowCount() == 1) { // Rebuild thread @@ -2024,6 +2025,7 @@ function buildThread($id, $return = false, $mod = false) { global $board, $config, $build_pages; if (!$return && $config['use_read_php']) { cache::delete("thread_index_{$board['uri']}_{$id}"); + cache::delete("thread_50_{$board['uri']}_{$id}"); cache::delete("thread_index_display_{$board['uri']}_{$id}"); cache::delete("thread_{$board['uri']}_{$id}"); cache::delete("catalog_{$board['uri']}"); @@ -2103,7 +2105,8 @@ function buildThread50($id, $return = false, $mod = false, $thread = null, $anti if ($antibot) $antibot->reset(); - if (!$thread) { + if (!$thread && ($config['cache']['enabled'] && !($thread = cache::get("thread_50_{$board['uri']}_{$id}")))) { + unset($thread); $query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id` DESC LIMIT :limit", $board['uri'])); $query->bindValue(':id', $id, PDO::PARAM_INT); $query->bindValue(':limit', $config['noko50_count']+1, PDO::PARAM_INT); @@ -2140,6 +2143,8 @@ function buildThread50($id, $return = false, $mod = false, $thread = null, $anti } $thread->posts = array_reverse($thread->posts); + if ($config['cache']['enabled']) + cache::set("thread_50_{$board['uri']}_{$id}", $thread); } else { $allPosts = $thread->posts; From a7d4614a3eeed5f147678458a58204c4564bbd5f Mon Sep 17 00:00:00 2001 From: 8chan Date: Sat, 22 Nov 2014 01:46:21 -0800 Subject: [PATCH 35/86] Better read.php +50 regex --- read.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/read.php b/read.php index f1589ee3..64fee794 100644 --- a/read.php +++ b/read.php @@ -17,7 +17,7 @@ $pages = array( '/(\%b)/' . preg_quote($config['file_index'], '!') => 'view_board', '/(\%b)/' . str_replace('%d', '(\d+)', preg_quote($config['file_page'], '!')) => 'view_board', '/(\%b)/' . preg_quote($config['dir']['res'], '!') . - str_replace('%d', '(\d+)', preg_quote($config['file_page50'], '!')) => 'view_thread50', + str_replace('%d', '(\d+)', '%d\+50(\.html)?') => 'view_thread50', '/(\%b)/' . preg_quote($config['dir']['res'], '!') . str_replace('%d', '(\d+)', '%d(\.html)?') => 'view_thread', ); From f8318d3425124ee30fb0ffcd3e9fc82e856afd17 Mon Sep 17 00:00:00 2001 From: 8chan Date: Sat, 22 Nov 2014 01:46:37 -0800 Subject: [PATCH 36/86] Dont write catalogs if use_read_php --- templates/themes/catalog/theme.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/templates/themes/catalog/theme.php b/templates/themes/catalog/theme.php index 72e43c14..2f3c61f1 100644 --- a/templates/themes/catalog/theme.php +++ b/templates/themes/catalog/theme.php @@ -17,15 +17,18 @@ $boards = explode(' ', $settings['boards']); } - if ($action == 'all') { - foreach ($boards as $board) { + if (!$config['use_read_php']) { + if ($action == 'all') { + foreach ($boards as $board) { + $b = new Catalog(); + $b->build($settings, $board); + } + } elseif ($action == 'post-thread' || ($settings['update_on_posts'] && $action == 'post') || ($settings['update_on_posts'] && $action == 'post-delete') && (in_array($board, $boards) | $settings['all'])) { $b = new Catalog(); $b->build($settings, $board); } - } elseif ($action == 'post-thread' || ($settings['update_on_posts'] && $action == 'post') || ($settings['update_on_posts'] && $action == 'post-delete') && (in_array($board, $boards) | $settings['all'])) { - $b = new Catalog(); - $b->build($settings, $board); - } elseif ($action == 'read_php') { + } + if ($action == 'read_php') { $b = new Catalog(); return $b->build($settings, $board, true); } From 81e5f9f0cb0ea6955240ddf0402ffb91c66dae88 Mon Sep 17 00:00:00 2001 From: Forkless Date: Sat, 22 Nov 2014 19:41:23 -0600 Subject: [PATCH 37/86] Fix thread stats so post count hover works on the OP's id. --- js/thread-stats.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/js/thread-stats.js b/js/thread-stats.js index c787bbdb..c66916f1 100644 --- a/js/thread-stats.js +++ b/js/thread-stats.js @@ -50,9 +50,12 @@ $(document).ready(function(){ ids[opID] = 0; } ids[opID]++; + var cur = op.find('>.intro >.poster_id'); + cur.find('+.posts_by_id').remove(); + cur.after(' ('+ ids[cur.text()] +')'); replies.each(function(){ - var cur = $(this).find('> .intro > .poster_id'); - cur.find('+ .posts_by_id').remove(); + cur = $(this).find('>.intro >.poster_id'); + cur.find('+.posts_by_id').remove(); cur.after(' ('+ ids[cur.text()] +')'); }); var size = function(obj) { From af305de33688fdf207e0560ecdec4ed5e2dfd04a Mon Sep 17 00:00:00 2001 From: topkek Date: Sun, 23 Nov 2014 22:48:00 +0000 Subject: [PATCH 38/86] add space between backlinks --- js/show-backlinks.js | 3 ++- stylesheets/style.css | 6 +----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/js/show-backlinks.js b/js/show-backlinks.js index fa4865fd..4056119e 100644 --- a/js/show-backlinks.js +++ b/js/show-backlinks.js @@ -38,7 +38,8 @@ onready(function(){ var $link = $('>>' + reply_id + ''); - $link.appendTo($mentioned) + $link.appendTo($mentioned); + $link.after(" "); if (window.init_hover) { $link.each(init_hover); diff --git a/stylesheets/style.css b/stylesheets/style.css index bebd4813..5f88551a 100644 --- a/stylesheets/style.css +++ b/stylesheets/style.css @@ -257,7 +257,7 @@ p.intro span.capcode,p.intro a.capcode,p.intro a.nametag { } p.intro a { - margin-left: 8px; + margin-left: 5px; } div.delete { @@ -939,10 +939,6 @@ pre { margin-top: 0px; } -.mentioned { - word-wrap: break-word; -} - .poster_id { cursor: pointer; } From a2c20cda6540697af3ae312f6a02cce462b431ad Mon Sep 17 00:00:00 2001 From: Markerov Date: Sat, 29 Nov 2014 11:01:30 +0800 Subject: [PATCH 39/86] Fix bug with inline expansion with Unanimate Gif Issue #273 --- js/inline-expanding.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/js/inline-expanding.js b/js/inline-expanding.js index cf8cab81..8da66898 100644 --- a/js/inline-expanding.js +++ b/js/inline-expanding.js @@ -19,7 +19,7 @@ onready(function(){ for (var i = 0; i < link.length; i++) { if (typeof link[i] == "object" && link[i].childNodes && typeof link[i].childNodes[0] !== 'undefined' && link[i].childNodes[0].src && link[i].childNodes[0].className.match(/post-image/) && !link[i].className.match(/file/)) { link[i].onclick = function(e) { - var img, post_body, still_open; + var img, post_body, still_open, canvas; var thumb = this.childNodes[0]; var padding = 5; var boardlist = $('.boardlist')[0]; @@ -43,8 +43,10 @@ onready(function(){ this.dataset.expanded = 'true'; if (thumb.tagName === 'CANVAS') { - this.removeChild(thumb); - thumb.style.display = 'block'; + canvas = thumb; + thumb = thumb.nextSibling; + this.removeChild(canvas); + canvas.style.display = 'block'; } thumb.style.opacity = '0.4'; From c2e9be776b67b4be98dd6ebbb95a265265ced9f9 Mon Sep 17 00:00:00 2001 From: b4x Date: Mon, 1 Dec 2014 20:36:11 +0100 Subject: [PATCH 40/86] Changed moderate to volunteer on --- inc/locale/en/LC_MESSAGES/tinyboard.po | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/inc/locale/en/LC_MESSAGES/tinyboard.po b/inc/locale/en/LC_MESSAGES/tinyboard.po index e1b8f64b..97fc01ac 100644 --- a/inc/locale/en/LC_MESSAGES/tinyboard.po +++ b/inc/locale/en/LC_MESSAGES/tinyboard.po @@ -689,7 +689,7 @@ msgstr "" msgid "You seem to have mistyped the verification." msgstr "" -#. Moderator errors +#. Volunteer errors #: ../../../../inc/config.php:906 ../../../../inc/config.php:1011 #: ../../../../inc/config.php:1002 ../../../../inc/config.php:1004 #: ../../../../inc/config.php:1006 ../../../../inc/config.php:1022 @@ -879,7 +879,7 @@ msgstr "" #: ../../../../inc/mod/pages.php:671 ../../../../inc/mod/pages.php:698 #: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:300 #: ../../../../templates/cache/73/f8/5e3142a8a6f8d7e40422ff577e83b0dedf55a7cb9bc7082839b24f653545.php:75 -msgid "Moderation log" +msgid "Volunteer log" msgstr "" #. line 104 @@ -3489,7 +3489,7 @@ msgid "There are no active posts." msgstr "" #: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:49 -msgid "Show only bans from boards I moderate" +msgid "Show only bans from boards I volunteer on" msgstr "" #: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:55 From 666a7f760d48ea4ae017a425f101a11cb11d2cd4 Mon Sep 17 00:00:00 2001 From: topkek Date: Wed, 26 Nov 2014 21:10:27 +0000 Subject: [PATCH 41/86] add global option for tree view --- js/treeview.js | 71 +++++++++++++++++++++++++++---------------- stylesheets/style.css | 3 +- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/js/treeview.js b/js/treeview.js index 78ad620b..c9bd55f8 100644 --- a/js/treeview.js +++ b/js/treeview.js @@ -11,35 +11,54 @@ * */ +if (active_page == 'thread' || active_page == 'ukko' || active_page == 'index') +$(function() { + if (window.Options && Options.get_tab('general')) { + var selector = '#treeview-global>input'; + Options.extend_tab("general", ""); + } + $(selector).on('change', function () { + if (localStorage.treeview === 'true') { + localStorage.treeview = 'false'; + } else { + localStorage.treeview = 'true'; + } + }); + if (localStorage.treeview === 'true') { + $(selector).attr('checked', 'checked'); + } +}); + if (active_page == 'thread') $(function() { + var treeview = function() { + $('.post.reply').each(function(){ + var references = []; + $(this).find('.body a').each(function(){ + if ($(this).html().match('^>>[0-9]+$')) { + references.push(parseInt($(this).html().replace('>>', ''))); + } + }); + var maxref = references.reduce(function(a,b) { return a > b ? a : b; }, 0); + + var parent_post = $("#reply_"+maxref); + if (parent_post.length == 0) return; + + var margin = parseInt(parent_post.css("margin-left"))+32; + + var post = $(this); + var br = post.next(); + + post.detach().css("margin-left", margin).insertAfter(parent_post.next()); + br.detach().insertAfter(post); + }); + } + if (localStorage.treeview === 'true') { + treeview(); + } + $('hr:first').before('
'); $('div#treeview a') .text(_('Tree view')) - .click(function(e) { - e.preventDefault(); - - $('.post.reply').each(function(){ - var references = []; - $(this).find('.body a').each(function(){ - if ($(this).html().match('^>>[0-9]+$')) { - references.push(parseInt($(this).html().replace('>>', ''))); - } - }); - - var maxref = references.reduce(function(a,b) { return a > b ? a : b; }, 0); - - var parent_post = $("#reply_"+maxref); - if (parent_post.length == 0) return; - - var margin = parseInt(parent_post.css("margin-left"))+32; - - var post = $(this); - var br = post.next(); - - post.detach().css("margin-left", margin).insertAfter(parent_post.next()); - br.detach().insertAfter(post); - - }); - }); + .click(function(e) { treeview(); e.preventDefault(); }); }); diff --git a/stylesheets/style.css b/stylesheets/style.css index bebd4813..f86f0021 100644 --- a/stylesheets/style.css +++ b/stylesheets/style.css @@ -858,7 +858,7 @@ pre { #options_div { width: 600px; - height: 300px; + height: 320px; } #alert_div { @@ -925,6 +925,7 @@ pre { right: 0px; text-align: left; font-size: 12px; + overflow-y: auto; } .options_tab h2 { From 2da17bfebeabd2c85b5102d66bec8ea733f846f6 Mon Sep 17 00:00:00 2001 From: topkek Date: Wed, 26 Nov 2014 22:48:54 +0000 Subject: [PATCH 42/86] toggle tree view --- js/treeview.js | 52 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/js/treeview.js b/js/treeview.js index c9bd55f8..937ea6ff 100644 --- a/js/treeview.js +++ b/js/treeview.js @@ -31,27 +31,41 @@ $(function() { if (active_page == 'thread') $(function() { + var treeview_on = false; var treeview = function() { - $('.post.reply').each(function(){ - var references = []; - $(this).find('.body a').each(function(){ - if ($(this).html().match('^>>[0-9]+$')) { - references.push(parseInt($(this).html().replace('>>', ''))); - } - }); - var maxref = references.reduce(function(a,b) { return a > b ? a : b; }, 0); - - var parent_post = $("#reply_"+maxref); - if (parent_post.length == 0) return; - - var margin = parseInt(parent_post.css("margin-left"))+32; - - var post = $(this); - var br = post.next(); + if (!treeview_on) { + treeview_on = true; + $('.post.reply').each(function(){ + var references = []; + $(this).find('.body a').each(function(){ + if ($(this).html().match('^>>[0-9]+$')) { + references.push(parseInt($(this).html().replace('>>', ''))); + } + }); + var maxref = references.reduce(function(a,b) { return a > b ? a : b; }, 0); - post.detach().css("margin-left", margin).insertAfter(parent_post.next()); - br.detach().insertAfter(post); - }); + var parent_post = $("#reply_"+maxref); + if (parent_post.length == 0) return; + + var margin = parseInt(parent_post.css("margin-left"))+32; + + var post = $(this); + var br = post.next(); + + post.detach().css("margin-left", margin).insertAfter(parent_post.next()); + br.detach().insertAfter(post); + }); + } else { + treeview_on = false; + $('.post.reply').sort(function(a,b) { + return parseInt(a.id.replace('reply_', '')) > parseInt(b.id.replace('reply_', '')); + }).each(function () { + var post = $(this); + var br = post.next(); + post.detach().css('margin-left', '0').appendTo('.thread'); + br.detach().insertAfter(post); + }); + } } if (localStorage.treeview === 'true') { treeview(); From 191347b92e79b03457ff3c1d8581181bd8f912cc Mon Sep 17 00:00:00 2001 From: topkek Date: Thu, 27 Nov 2014 00:36:51 +0000 Subject: [PATCH 43/86] chrome fix --- js/treeview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/treeview.js b/js/treeview.js index 937ea6ff..046a9796 100644 --- a/js/treeview.js +++ b/js/treeview.js @@ -58,7 +58,7 @@ $(function() { } else { treeview_on = false; $('.post.reply').sort(function(a,b) { - return parseInt(a.id.replace('reply_', '')) > parseInt(b.id.replace('reply_', '')); + return parseInt(a.id.replace('reply_', '')) - parseInt(b.id.replace('reply_', '')); }).each(function () { var post = $(this); var br = post.next(); From 2a2f64c09805a4f6dfe8ba298fd8570b21518661 Mon Sep 17 00:00:00 2001 From: topkek Date: Thu, 27 Nov 2014 18:14:12 +0000 Subject: [PATCH 44/86] don't indent previews --- js/post-hover.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/post-hover.js b/js/post-hover.js index eefb3875..657c4f1d 100644 --- a/js/post-hover.js +++ b/js/post-hover.js @@ -73,6 +73,7 @@ onready(function(){ .css('font-style', 'normal') .css('z-index', '100') .css('left', '0') + .css('margin-left', '0') .addClass('reply').addClass('post') .appendTo(link.closest('div.post')) From 82fc6c241b6b1ba7c5c80a4771101dc12a633a90 Mon Sep 17 00:00:00 2001 From: topkek Date: Fri, 28 Nov 2014 01:15:10 +0000 Subject: [PATCH 45/86] checkbox for tree view --- js/favorites.js | 1 - js/post-hover.js | 2 +- js/treeview.js | 21 +++++++++------------ 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/js/favorites.js b/js/favorites.js index 39bae783..acef9c7e 100644 --- a/js/favorites.js +++ b/js/favorites.js @@ -56,7 +56,6 @@ if (active_page == 'thread' || active_page == 'index' || active_page == 'catalog $(document).ready(function(){ var favorites = JSON.parse(localStorage.favorites); var is_board_favorite = ~$.inArray(board_name, favorites); - console.log(is_board_favorite); $('header>h1').append('\u2605'); add_favorites(); diff --git a/js/post-hover.js b/js/post-hover.js index 657c4f1d..94841cc5 100644 --- a/js/post-hover.js +++ b/js/post-hover.js @@ -73,7 +73,7 @@ onready(function(){ .css('font-style', 'normal') .css('z-index', '100') .css('left', '0') - .css('margin-left', '0') + .css('margin-left', '') .addClass('reply').addClass('post') .appendTo(link.closest('div.post')) diff --git a/js/treeview.js b/js/treeview.js index 046a9796..dcd87e1e 100644 --- a/js/treeview.js +++ b/js/treeview.js @@ -31,10 +31,8 @@ $(function() { if (active_page == 'thread') $(function() { - var treeview_on = false; - var treeview = function() { - if (!treeview_on) { - treeview_on = true; + var treeview = function(enable) { + if (enable === true) { $('.post.reply').each(function(){ var references = []; $(this).find('.body a').each(function(){ @@ -56,23 +54,22 @@ $(function() { br.detach().insertAfter(post); }); } else { - treeview_on = false; $('.post.reply').sort(function(a,b) { return parseInt(a.id.replace('reply_', '')) - parseInt(b.id.replace('reply_', '')); }).each(function () { var post = $(this); var br = post.next(); - post.detach().css('margin-left', '0').appendTo('.thread'); + post.detach().css('margin-left', '').appendTo('.thread'); br.detach().insertAfter(post); }); } } + + $('hr:first').before('
'); + $('input#treeview').on('change', function(e) { treeview($(this).is(':checked')); }); + if (localStorage.treeview === 'true') { - treeview(); + treeview(true); + $('input#treeview').attr('checked', true); } - - $('hr:first').before('
'); - $('div#treeview a') - .text(_('Tree view')) - .click(function(e) { treeview(); e.preventDefault(); }); }); From 74a6ad7e52d043227f7bae279c766100d4e7dca7 Mon Sep 17 00:00:00 2001 From: topkek Date: Fri, 28 Nov 2014 21:59:05 +0000 Subject: [PATCH 46/86] clean up options code --- js/treeview.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/js/treeview.js b/js/treeview.js index dcd87e1e..1709debb 100644 --- a/js/treeview.js +++ b/js/treeview.js @@ -16,16 +16,16 @@ $(function() { if (window.Options && Options.get_tab('general')) { var selector = '#treeview-global>input'; Options.extend_tab("general", ""); - } - $(selector).on('change', function () { + $(selector).on('change', function() { + if (localStorage.treeview === 'true') { + localStorage.treeview = 'false'; + } else { + localStorage.treeview = 'true'; + } + }); if (localStorage.treeview === 'true') { - localStorage.treeview = 'false'; - } else { - localStorage.treeview = 'true'; + $(selector).attr('checked', 'checked'); } - }); - if (localStorage.treeview === 'true') { - $(selector).attr('checked', 'checked'); } }); From fa9a3da9137457083be12f7c8409e4aafa246d28 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:10:28 -0800 Subject: [PATCH 47/86] New boards.html page now with tags --- boards.php | 33 +++++- inc/functions.php | 172 ++++++++++----------------- install.sql | 15 +++ read.php | 183 ----------------------------- templates/8chan/boards-tags.html | 157 +++++++++++++++++++++++++ templates/themes/catalog/theme.php | 30 ++--- 6 files changed, 274 insertions(+), 316 deletions(-) delete mode 100644 read.php create mode 100644 templates/8chan/boards-tags.html diff --git a/boards.php b/boards.php index f22cb524..de7f15e8 100644 --- a/boards.php +++ b/boards.php @@ -10,10 +10,17 @@ if (php_sapi_name() == 'fpm-fcgi' && !$admin) { error('Cannot be run directly.'); } $boards = listBoards(); - +$all_tags = array(); $total_posts_hour = 0; $total_posts = 0; +function to_tag($str) { + $str = trim($str); + $str = strtolower($str); + $str = str_replace(['_', ' '], '-', $str); + return $str; +} + foreach ($boards as $i => $board) { //$query = prepare(sprintf("SELECT (SELECT MAX(id) from ``posts_%s``) AS max, (SELECT MAX(id) FROM ``posts_%s`` WHERE FROM_UNIXTIME(time) < DATE_SUB(NOW(), INTERVAL 1 HOUR)) AS oldmax, (SELECT MAX(id) from ``posts_%s``) AS max_d, (SELECT MAX(id) FROM ``posts_%s`` WHERE FROM_UNIXTIME(time) < DATE_SUB(NOW(), INTERVAL 1 DAY)) AS oldmax_d, (SELECT count(id) FROM ``posts_%s``) AS count;", $board['uri'], $board['uri'], $board['uri'], $board['uri'], $board['uri'])); @@ -28,6 +35,23 @@ SELECT MAX(id) max, (SELECT COUNT(*) FROM ``posts_%s`` WHERE FROM_UNIXTIME(time) $query->execute() or error(db_error($query)); $r = $query->fetch(PDO::FETCH_ASSOC); + $tquery = prepare("SELECT `tag` FROM ``board_tags`` WHERE `uri` = :uri"); + $tquery->execute([":uri" => $board['uri']]) or error(db_error($tquery)); + $r2 = $tquery->fetchAll(PDO::FETCH_ASSOC); + + $tags = array(); + if ($r2) { + foreach ($r2 as $ii => $t) { + $tag=to_tag($t['tag']); + $tags[] = $tag; + if (!isset($all_tags[$tag])) { + $all_tags[$tag] = (int)$r['uniq_ip']; + } else { + $all_tags[$tag] += $r['uniq_ip']; + } + } + } + $pph = $r['pph']; $ppd = $r['ppd']; @@ -38,6 +62,7 @@ SELECT MAX(id) max, (SELECT COUNT(*) FROM ``posts_%s`` WHERE FROM_UNIXTIME(time) $boards[$i]['ppd'] = $ppd; $boards[$i]['max'] = $r['max']; $boards[$i]['uniq_ip'] = $r['uniq_ip']; + $boards[$i]['tags'] = $tags; } usort($boards, @@ -86,14 +111,18 @@ foreach ($boards as $i => &$board) { $n_boards = sizeof($boards); $t_boards = $hidden_boards_total + $n_boards; +$boards = array_values($boards); +arsort($all_tags); + $config['additional_javascript'] = array('js/jquery.min.js', 'js/jquery.tablesorter.min.js'); -$body = Element("8chan/boards.html", array("config" => $config, "n_boards" => $n_boards, "t_boards" => $t_boards, "hidden_boards_total" => $hidden_boards_total, "total_posts" => $total_posts, "total_posts_hour" => $total_posts_hour, "boards" => $boards, "last_update" => date('r'), "uptime_p" => shell_exec('uptime -p'))); +$body = Element("8chan/boards-tags.html", array("config" => $config, "n_boards" => $n_boards, "t_boards" => $t_boards, "hidden_boards_total" => $hidden_boards_total, "total_posts" => $total_posts, "total_posts_hour" => $total_posts_hour, "boards" => $boards, "last_update" => date('r'), "uptime_p" => shell_exec('uptime -p'), 'tags' => $all_tags)); $html = Element("page.html", array("config" => $config, "body" => $body, "title" => "Boards on ∞chan")); if ($admin) { echo $html; } else { file_write("boards.json", json_encode($boards)); + file_write("tags.json", json_encode($all_tags)); foreach ($boards as $i => $b) { if (in_array($b['uri'], $config['no_top_bar_boards'])) { unset($boards[$i]); diff --git a/inc/functions.php b/inc/functions.php index f02abd3d..2ffdc222 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1099,12 +1099,6 @@ function deletePost($id, $error_if_doesnt_exist=true, $rebuild_after=true) { $antispam_query->bindValue(':board', $board['uri']); $antispam_query->bindValue(':thread', $post['id']); $antispam_query->execute() or error(db_error($antispam_query)); - - cache::delete("thread_index_{$board['uri']}_{$post['id']}"); - cache::delete("thread_index_display_{$board['uri']}_{$post['id']}"); - cache::delete("thread_{$board['uri']}_{$post['id']}"); - cache::delete("thread_50_{$board['uri']}_{$post['id']}"); - cache::delete("catalog_{$board['uri']}"); } elseif ($query->rowCount() == 1) { // Rebuild thread $rebuild = &$post['thread']; @@ -1196,62 +1190,57 @@ function index($page, $mod=false) { return false; $threads = array(); - + while ($th = $query->fetch(PDO::FETCH_ASSOC)) { - if (($config['cache']['enabled'] && !($thread = cache::get("thread_index_display_{$board['uri']}_{$th['id']}")) || $mod)) { - $thread = new Thread($th, $mod ? '?/' : $config['root'], $mod); + $thread = new Thread($th, $mod ? '?/' : $config['root'], $mod); - if ($config['cache']['enabled']) { - $cached = cache::get("thread_index_{$board['uri']}_{$th['id']}"); - if (isset($cached['replies'], $cached['omitted'])) { - $replies = $cached['replies']; - $omitted = $cached['omitted']; - } else { - unset($cached); - } + if ($config['cache']['enabled']) { + $cached = cache::get("thread_index_{$board['uri']}_{$th['id']}"); + if (isset($cached['replies'], $cached['omitted'])) { + $replies = $cached['replies']; + $omitted = $cached['omitted']; + } else { + unset($cached); } - if (!isset($cached)) { - $posts = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `thread` = :id ORDER BY `id` DESC LIMIT :limit", $board['uri'])); - $posts->bindValue(':id', $th['id']); - $posts->bindValue(':limit', ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']), PDO::PARAM_INT); - $posts->execute() or error(db_error($posts)); - - $replies = array_reverse($posts->fetchAll(PDO::FETCH_ASSOC)); - - if (count($replies) == ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview'])) { - $count = numPosts($th['id']); - $omitted = array('post_count' => $count['replies'], 'image_count' => $count['images']); - } else { - $omitted = false; - } - - if ($config['cache']['enabled']) - cache::set("thread_index_{$board['uri']}_{$th['id']}", array( - 'replies' => $replies, - 'omitted' => $omitted, - )); - } - - $num_images = 0; - foreach ($replies as $po) { - if ($po['num_files']) - $num_images+=$po['num_files']; - - $thread->add(new Post($po, $mod ? '?/' : $config['root'], $mod)); - } - - $thread->images = $num_images; - $thread->replies = isset($omitted['post_count']) ? $omitted['post_count'] : count($replies); - - if ($omitted) { - $thread->omitted = $omitted['post_count'] - ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']); - $thread->omitted_images = $omitted['image_count'] - $num_images; - } - - if ($config['cache']['enabled'] && !$mod) - cache::set("thread_index_display_{$board['uri']}_{$th['id']}", $thread); - } + if (!isset($cached)) { + $posts = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `thread` = :id ORDER BY `id` DESC LIMIT :limit", $board['uri'])); + $posts->bindValue(':id', $th['id']); + $posts->bindValue(':limit', ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']), PDO::PARAM_INT); + $posts->execute() or error(db_error($posts)); + + $replies = array_reverse($posts->fetchAll(PDO::FETCH_ASSOC)); + + if (count($replies) == ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview'])) { + $count = numPosts($th['id']); + $omitted = array('post_count' => $count['replies'], 'image_count' => $count['images']); + } else { + $omitted = false; + } + + if ($config['cache']['enabled']) + cache::set("thread_index_{$board['uri']}_{$th['id']}", array( + 'replies' => $replies, + 'omitted' => $omitted, + )); + } + + $num_images = 0; + foreach ($replies as $po) { + if ($po['num_files']) + $num_images+=$po['num_files']; + + $thread->add(new Post($po, $mod ? '?/' : $config['root'], $mod)); + } + + $thread->images = $num_images; + $thread->replies = isset($omitted['post_count']) ? $omitted['post_count'] : count($replies); + + if ($omitted) { + $thread->omitted = $omitted['post_count'] - ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']); + $thread->omitted_images = $omitted['image_count'] - $num_images; + } + $threads[] = $thread; $body .= $thread->build(true); } @@ -1469,10 +1458,6 @@ function checkMute() { function buildIndex() { global $board, $config, $build_pages; - if ($config['use_read_php']) { - cache::delete("catalog_{$board['uri']}"); - return; - } $pages = getPages(); if (!$config['try_smarter']) @@ -1550,17 +1535,6 @@ function buildIndex() { function buildJavascript() { global $config; - if ($config['cache']['enabled']) { - if (false === strpos($config['file_script'], '/')) { - $cache_name = 'main_js'; - } else { - $b = explode('/', $config['file_script'])[0]; - $cache_name = "board_{$b}_js"; - } - - cache::delete($cache_name); - } - $script = Element('main.js', array( 'config' => $config, )); @@ -1582,14 +1556,7 @@ function buildJavascript() { $script = JSMin::minify($script); } - if ($config['cache']['enabled']) - cache::set($cache_name, $script); - - if (!$config['use_read_php']) { - file_write($config['file_script'], $script); - } else { - return $script; - } + file_write($config['file_script'], $script); } function checkDNSBL() { @@ -2023,35 +1990,27 @@ function strip_combining_chars($str) { function buildThread($id, $return = false, $mod = false) { global $board, $config, $build_pages; - if (!$return && $config['use_read_php']) { - cache::delete("thread_index_{$board['uri']}_{$id}"); - cache::delete("thread_50_{$board['uri']}_{$id}"); - cache::delete("thread_index_display_{$board['uri']}_{$id}"); - cache::delete("thread_{$board['uri']}_{$id}"); - cache::delete("catalog_{$board['uri']}"); - return; - } - $id = round($id); if (event('build-thread', $id)) return; - if (!($thread = cache::get("thread_{$board['uri']}_{$id}")) || $mod) { - unset($thread); - $query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id`", $board['uri'])); - $query->bindValue(':id', $id, PDO::PARAM_INT); - $query->execute() or error(db_error($query)); + if ($config['cache']['enabled'] && !$mod) { + // Clear cache + cache::delete("thread_index_{$board['uri']}_{$id}"); + cache::delete("thread_{$board['uri']}_{$id}"); + } - while ($post = $query->fetch(PDO::FETCH_ASSOC)) { - if (!isset($thread)) { - $thread = new Thread($post, $mod ? '?/' : $config['root'], $mod); - } else { - $thread->add(new Post($post, $mod ? '?/' : $config['root'], $mod)); - } + $query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id`", $board['uri'])); + $query->bindValue(':id', $id, PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + + while ($post = $query->fetch(PDO::FETCH_ASSOC)) { + if (!isset($thread)) { + $thread = new Thread($post, $mod ? '?/' : $config['root'], $mod); + } else { + $thread->add(new Post($post, $mod ? '?/' : $config['root'], $mod)); } - - if (isset($thread)) cache::set("thread_{$board['uri']}_{$id}", $thread); } // Check if any posts were found @@ -2079,7 +2038,7 @@ function buildThread($id, $return = false, $mod = false) { $build_pages[] = thread_find_page($id); // json api - if ($config['api']['enabled'] && !$config['use_read_php']) { + if ($config['api']['enabled']) { $api = new Api(); $json = json_encode($api->translateThread($thread)); $jsonFilename = $board['dir'] . $config['dir']['res'] . $id . '.json'; @@ -2105,8 +2064,7 @@ function buildThread50($id, $return = false, $mod = false, $thread = null, $anti if ($antibot) $antibot->reset(); - if (!$thread && ($config['cache']['enabled'] && !($thread = cache::get("thread_50_{$board['uri']}_{$id}")))) { - unset($thread); + if (!$thread) { $query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id` DESC LIMIT :limit", $board['uri'])); $query->bindValue(':id', $id, PDO::PARAM_INT); $query->bindValue(':limit', $config['noko50_count']+1, PDO::PARAM_INT); @@ -2143,8 +2101,6 @@ function buildThread50($id, $return = false, $mod = false, $thread = null, $anti } $thread->posts = array_reverse($thread->posts); - if ($config['cache']['enabled']) - cache::set("thread_50_{$board['uri']}_{$id}", $thread); } else { $allPosts = $thread->posts; diff --git a/install.sql b/install.sql index 9e15396a..9a03736f 100644 --- a/install.sql +++ b/install.sql @@ -67,6 +67,8 @@ CREATE TABLE IF NOT EXISTS `boards` ( `subtitle` tinytext, `indexed` boolean default true, `public_bans` boolean default true, + `8archive` boolean default false, + `sfw` boolean default false, PRIMARY KEY (`uri`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; @@ -316,6 +318,19 @@ CREATE TABLE `post_clean` ( UNIQUE KEY `clean_id_UNIQUE` (`clean_id`) ); +-- -------------------------------------------------------- + +-- +-- Table structure for table `board_tags` +-- + +CREATE TABLE `board_tags` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `uri` varchar(30) DEFAULT NULL, + `tag` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) +); + /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/read.php b/read.php deleted file mode 100644 index 64fee794..00000000 --- a/read.php +++ /dev/null @@ -1,183 +0,0 @@ - 'view_board', - '/(\%b)/(\d+)\.json' => 'view_api_index', - '/(\%b)/catalog\.json' => 'view_api_catalog', - '/(\%b)/threads\.json' => 'view_api_threads', - '/(\%b)/main\.js' => 'view_js', - '/main\.js' => 'view_js', - '/(\%b)/catalog(\.html)?' => 'view_catalog', - '/(\%b)/' . preg_quote($config['file_index'], '!') => 'view_board', - '/(\%b)/' . str_replace('%d', '(\d+)', preg_quote($config['file_page'], '!')) => 'view_board', - '/(\%b)/' . preg_quote($config['dir']['res'], '!') . - str_replace('%d', '(\d+)', '%d\+50(\.html)?') => 'view_thread50', - '/(\%b)/' . preg_quote($config['dir']['res'], '!') . - str_replace('%d', '(\d+)', '%d(\.html)?') => 'view_thread', -); - -$new_pages = array(); -foreach ($pages as $key => $callback) { - if (is_string($callback) && preg_match('/^secure /', $callback)) - $key .= '(/(?P[a-f0-9]{8}))?'; - $key = str_replace('\%b', '?P' . sprintf(substr($config['board_path'], 0, -1), $config['board_regex']), $key); - $new_pages[@$key[0] == '!' ? $key : '!^' . $key . '(?:&[^&=]+=[^&]*)*$!u'] = $callback; -} -$pages = $new_pages; - -function view_thread50($boardName, $thread) { - global $config, $mod; - - if (!openBoard($boardName)) { - include '404.php'; - return; - } - - $page = buildThread50($thread, true, false); - echo $page; -} - -function view_thread($boardName, $thread) { - global $config, $mod; - - if (!openBoard($boardName)) { - include '404.php'; - return; - } - - $page = buildThread($thread, true, false); - echo $page; -} - -function view_api_index($boardName, $page) { - global $config, $board; - - $api = new Api(); - - if (!openBoard($boardName)) { - include '404.php'; - return; - } - - $content = index($page+1); - - if (!$content) - return; - - if ($config['api']['enabled']) { - $threads = $content['threads']; - $json = json_encode($api->translatePage($threads)); - header('Content-Type: text/json'); - - echo $json; - } -} - -function APICatalog($boardName, $gen_threads = false) { - global $config; - if (!openBoard($boardName)) { - include '404.php'; - return; - } - - header('Content-Type: text/json'); - - $catalog = array(); - $api = new Api(); - - for ($page = 1; $page <= $config['max_pages']; $page++) { - $content = index($page); - - if (!$content) - break; - - $catalog[$page-1] = $content['threads']; - } - - echo json_encode($api->translateCatalog($catalog, $gen_threads)); -} - -function view_api_catalog($boardName) { - APICatalog($boardName, false); -} - -function view_api_threads($boardName) { - APICatalog($boardName, true); -} - -function view_board($boardName, $page_no = 1) { - global $config, $mod; - - if (!openBoard($boardName)) { - include '404.php'; - return; - } - - if (!$page = index($page_no, $mod)) { - error($config['error']['404']); - } - - $page['pages'] = getPages(false); - $page['pages'][$page_no-1]['selected'] = true; - $page['btn'] = getPageButtons($page['pages'], false); - $page['mod'] = false; - $page['config'] = $config; - - echo Element('index.html', $page); -} - -function view_js($boardName = false) { - global $config; - - if ($boardName && !openBoard($boardName)) - error($config['error']['noboard']); - - if (!$boardName) { - $cache_name = 'main_js'; - } else { - $cache_name = "board_{$boardName}_js"; - } - - if (!($script = cache::get($cache_name))) { - $script = buildJavascript(); - } - - echo $script; -} - -function view_catalog($boardName) { - global $board, $config; - $_theme = 'catalog'; - - $theme = loadThemeConfig($_theme); - - if (!openBoard($boardName)) { - include '404.php'; - return; - } - - require_once $config['dir']['themes'] . '/' . $_theme . '/theme.php'; - - $catalog = $theme['build_function']('read_php', themeSettings($_theme), $board['uri']); - echo $catalog; -} - -$found = false; -foreach ($pages as $uri => $handler) { - if (preg_match($uri, $query, $matches)) { - $matches = array_slice($matches, 2); - if (is_callable($handler)) { - $found = true; - call_user_func_array($handler, $matches); - } - } -} - -if (!$found) - include '404.php'; diff --git a/templates/8chan/boards-tags.html b/templates/8chan/boards-tags.html new file mode 100644 index 00000000..7ac4bcc3 --- /dev/null +++ b/templates/8chan/boards-tags.html @@ -0,0 +1,157 @@ + + +

{% trans %}There are currently {{n_boards}} boards + {{hidden_boards_total}} unindexed boards = {{t_boards}} total boards. Site-wide, {{total_posts_hour}} posts have been made in the last hour, with {{total_posts}} being made on all active boards since October 23, 2013.{% endtrans %}

+ +
+ Tags:  + {% for tag, pop in tags %} + {% if pop > 1000 %} + {{ tag }} + {% elseif pop > 500 %} + {{ tag }} + {% elseif pop > 100 %} + {{ tag }} + {% else %} + {{ tag }} + {% endif %} + {% endfor %} +
+ + + + + + + + + + + +{% for board in boards %} + + + + + + + + +{% endfor %} +
B{% trans %}Board{% endtrans %}{% trans %}Title{% endtrans %}{% trans %}PPH{% endtrans %}{% trans %}Total posts{% endtrans %}{% trans %}Active users{% endtrans %}{% trans %}Tags{% endtrans %}
{{ board.img|raw }} {% if board['sfw'] %}{% else %}{% endif %}
/{{board['uri']}}/{{lock|raw}}
{{ board['title'] }}
{{board['pph']}}{{board['max']}}{{board['uniq_ip']}}
{% for tag in board.tags %}{{ tag }} {% endfor %}
+

Page last updated: {{last_update}}

+

{{uptime_p}} without interruption (read)

+ + diff --git a/templates/themes/catalog/theme.php b/templates/themes/catalog/theme.php index 2f3c61f1..5b940167 100644 --- a/templates/themes/catalog/theme.php +++ b/templates/themes/catalog/theme.php @@ -17,29 +17,22 @@ $boards = explode(' ', $settings['boards']); } - if (!$config['use_read_php']) { - if ($action == 'all') { - foreach ($boards as $board) { - $b = new Catalog(); - $b->build($settings, $board); - } - } elseif ($action == 'post-thread' || ($settings['update_on_posts'] && $action == 'post') || ($settings['update_on_posts'] && $action == 'post-delete') && (in_array($board, $boards) | $settings['all'])) { + if ($action == 'all') { + foreach ($boards as $board) { $b = new Catalog(); $b->build($settings, $board); } - } - if ($action == 'read_php') { + } elseif ($action == 'post-thread' || ($settings['update_on_posts'] && $action == 'post') || ($settings['update_on_posts'] && $action == 'post-delete') && (in_array($board, $boards) | $settings['all'])) { $b = new Catalog(); - return $b->build($settings, $board, true); + $b->build($settings, $board); } } // Wrap functions in a class so they don't interfere with normal Tinyboard operations class Catalog { - public function build($settings, $board_name, $return = false) { + public function build($settings, $board_name) { global $config, $board; - if (!($config['cache']['enabled'] && $catalog_out = cache::get("catalog_{$board['uri']}"))) {; openBoard($board_name); $recent_images = array(); @@ -97,7 +90,7 @@ $config['additional_javascript'][] = $s; } - $catalog_out = Element('themes/catalog/catalog.html', Array( + file_write($config['dir']['home'] . $board_name . '/catalog.html', Element('themes/catalog/catalog.html', Array( 'settings' => $settings, 'config' => $config, 'boardlist' => createBoardlist(), @@ -106,15 +99,6 @@ 'stats' => $stats, 'board' => $board_name, 'link' => $config['root'] . $board['dir'] - )); - - cache::set("catalog_{$board['uri']}", $catalog_out); - } - - if ($return) { - return $catalog_out; - } else { - file_write($config['dir']['home'] . $board_name . '/catalog.html', $catalog_out); - } + ))); } }; From b9dace75633e73cbf8f4ea565786e6a1988326bf Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:11:48 -0800 Subject: [PATCH 48/86] New mod.php pages: ?/tags and ?/reassign --- inc/8chan-mod-pages.php | 85 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/inc/8chan-mod-pages.php b/inc/8chan-mod-pages.php index dfd51658..86d91ef8 100644 --- a/inc/8chan-mod-pages.php +++ b/inc/8chan-mod-pages.php @@ -20,6 +20,8 @@ $config['mod']['noticeboard_post'] = GLOBALVOLUNTEER; $config['mod']['search'] = GLOBALVOLUNTEER; $config['mod']['clean_global'] = GLOBALVOLUNTEER; + $config['mod']['view_notes'] = GLOBALVOLUNTEER; + $config['mod']['create_notes'] = GLOBALVOLUNTEER; $config['mod']['debug_recent'] = ADMIN; $config['mod']['debug_antispam'] = ADMIN; $config['mod']['noticeboard_post'] = ADMIN; @@ -29,6 +31,7 @@ $config['mod']['edit_flags'] = MOD; $config['mod']['edit_settings'] = MOD; $config['mod']['edit_volunteers'] = MOD; + $config['mod']['edit_tags'] = MOD; $config['mod']['clean'] = BOARDVOLUNTEER; // new perms @@ -51,6 +54,55 @@ $config['mod']['view_ban'] = BOARDVOLUNTEER; $config['mod']['reassign_board'] = ADMIN; + $config['mod']['custom_pages']['/tags/(\%b)'] = function ($b) { + global $board, $config; + + if (!openBoard($b)) + error("Could not open board!"); + + if (!hasPermission($config['mod']['edit_tags'], $b)) + error($config['error']['noaccess']); + + if (isset($_POST['tags'])) { + if (sizeof($_POST['tags']) > 5) + error(_('Too many tags.')); + + $delete = prepare('DELETE FROM ``board_tags`` WHERE uri = :uri'); + $delete->bindValue(':uri', $b); + $delete->execute(); + + foreach ($_POST['tags'] as $i => $tag) { + if ($tag) { + if (strlen($tag) > 255) + continue; + + $insert = prepare('INSERT INTO ``board_tags``(uri, tag) VALUES (:uri, :tag)'); + $insert->bindValue(':uri', $b); + $insert->bindValue(':tag', utf8tohtml($tag)); + $insert->execute(); + } + } + + $update = prepare('UPDATE ``boards`` SET sfw = :sfw WHERE uri = :uri'); + $update->bindValue(':uri', $b); + $update->bindValue(':sfw', isset($_POST['sfw'])); + $update->execute(); + } + $query = prepare('SELECT * FROM ``board_tags`` WHERE uri = :uri'); + $query->bindValue(':uri', $b); + $query->execute(); + + $tags = $query->fetchAll(); + + $query = prepare('SELECT `sfw` FROM ``boards`` WHERE uri = :uri'); + $query->bindValue(':uri', $b); + $query->execute(); + + $sfw = $query->fetchColumn(); + + mod_page(_('Edit tags'), 'mod/tags.html', array('board'=>$board,'token'=>make_secure_link_token('reassign/'.$board['uri']), 'tags'=>$tags, 'sfw'=>$sfw)); + }; + $config['mod']['custom_pages']['/reassign/(\%b)'] = function($b) { global $board, $config; @@ -435,10 +487,39 @@ $locale $add_to_config EOT; + // Clean up our CSS...no more expression() or off-site URLs. + $clean_css = preg_replace('/expression\s*\(/', '', $_POST['css']); + + // URL matcher from SO: + $match_urls = '(?xi)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))'; + + $matched = array(); + + preg_match_all("#$match_urls#im", $clean_css, $matched); + + $allowed_urls = array('https://i.imgur.com/', 'https://media.8chan.co/', 'https://a.pomf.se/', 'https://fonts.googleapis.com/', 'http://8ch.net/'); + $error = false; + + if (isset($matched[0])) { + foreach ($matched[0] as $i => $v) { + $error = true; + foreach ($allowed_urls as $ii => $url) { + if (strpos($v, $url) === 0) { + $error = false; + break; + } + } + } + } + + if ($error) { + error(_('Off-site links are not allowed in board stylesheets!')); + } + $query = query('SELECT `uri`, `title`, `subtitle` FROM ``boards`` WHERE `8archive` = TRUE'); file_write('8archive.json', json_encode($query->fetchAll(PDO::FETCH_ASSOC))); file_write($b.'/config.php', $config_file); - file_write('stylesheets/board/'.$b.'.css', $_POST['css']); + file_write('stylesheets/board/'.$b.'.css', $clean_css); file_write($b.'/rules.html', Element('page.html', array('title'=>'Rules', 'subtitle'=>'', 'config'=>$config, 'body'=>'
'.purify($_POST['rules']).'
'))); file_write($b.'/rules.txt', $_POST['rules']); @@ -446,7 +527,7 @@ EOT; // Faster than openBoard and bypasses cache...we're trusting the PHP output // to be safe enough to run with every request, we can eval it here. - eval(preg_replace('/^\<\?php$/m', '', $config_file)); + eval(str_replace('flags.php', "$b/flags.php", preg_replace('/^\<\?php$/m', '', $config_file))); // be smarter about rebuilds...only some changes really require us to rebuild all threads if ($_config['captcha']['enabled'] != $config['captcha']['enabled'] From 8cc378eaf186310f101e38e2a9f32f9aedcaafe4 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:12:38 -0800 Subject: [PATCH 49/86] claim.html wrong account bug fix --- claim.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/claim.php b/claim.php index 4c733180..a07ccadf 100644 --- a/claim.php +++ b/claim.php @@ -16,7 +16,7 @@ function last_activity($board) { $last_activity_date->setTimestamp($row['time']); - $query = query("SELECT id, username FROM mods WHERE boards = '$board'"); + $query = query("SELECT id, username FROM mods WHERE boards = '$board' AND type = 20"); $mods = $query->fetchAll(); if ($mods) { From dd1c160d35e778aff154e059183d262f7d4c9b89 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:15:24 -0800 Subject: [PATCH 50/86] Hash masked IPs option, use less_ip in inc/bans --- inc/bans.php | 10 ++-------- inc/config.php | 1 - inc/functions.php | 12 +++++++++++- inc/instance-config.php | 1 + 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/inc/bans.php b/inc/bans.php index 14b7f939..215ff279 100644 --- a/inc/bans.php +++ b/inc/bans.php @@ -218,14 +218,8 @@ class Bans { } unset($ban['type']); if ($filter_ips || ($board_access !== false && !in_array($ban['board'], $board_access))) { - @list($ban['mask'], $subnet) = explode("/", $ban['mask']); - $ban['mask'] = preg_split("/[\.:]/", $ban['mask']); - $ban['mask'] = array_slice($ban['mask'], 0, 2); - $ban['mask'] = implode(".", $ban['mask']); - $ban['mask'] .= ".x.x"; - if (isset ($subnet)) { - $ban['mask'] .= "/$subnet"; - } + $ban['mask'] = @less_ip($ban['mask']); + $ban['masked'] = true; } diff --git a/inc/config.php b/inc/config.php index 89eaac69..056ec77c 100644 --- a/inc/config.php +++ b/inc/config.php @@ -753,7 +753,6 @@ // Allowed image file extensions. $config['allowed_ext'][] = 'jpg'; $config['allowed_ext'][] = 'jpeg'; - $config['allowed_ext'][] = 'bmp'; $config['allowed_ext'][] = 'gif'; $config['allowed_ext'][] = 'png'; // $config['allowed_ext'][] = 'svg'; diff --git a/inc/functions.php b/inc/functions.php index 2ffdc222..0ff32e37 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -2427,6 +2427,8 @@ function diceRoller($post) { } function less_ip($ip) { + global $config; + $ipv6 = (strstr($ip, ':') !== false); $has_range = (strstr($ip, '/') !== false); @@ -2446,7 +2448,15 @@ function less_ip($ip) { } $final = inet_ntop($in_addr & $mask); - return str_replace(array(':0', '.0'), array(':x', '.x'), $final) . (isset($range) ? '/'.$range : ''); + $masked = str_replace(array(':0', '.0'), array(':x', '.x'), $final); + + if ($config['hash_masked_ip']) { + $masked = substr(sha1(sha1($masked) . $config['secure_trip_salt']), 0, 10); + } + + $masked .= (isset($range) ? '/'.$range : ''); + + return $masked; } function less_hostmask($hostmask) { diff --git a/inc/instance-config.php b/inc/instance-config.php index 9c442fef..bd51e227 100644 --- a/inc/instance-config.php +++ b/inc/instance-config.php @@ -192,6 +192,7 @@ ); $config['gzip_static'] = false; +$config['hash_masked_ip'] = true; // 8chan specific mod pages require '8chan-mod-pages.php'; From 58c13bd2a75e5b32c83a96d298b954c0e167457d Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:16:01 -0800 Subject: [PATCH 51/86] Better post.php allow_delete error msg --- post.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/post.php b/post.php index 50c5c8bd..1a7c5da2 100644 --- a/post.php +++ b/post.php @@ -49,7 +49,7 @@ if (isset($_POST['delete'])) { // Check if deletion enabled if (!$config['allow_delete']) - error(_('Post deletion is not allowed!')); + error(_('Users are not allowed to delete their own posts on this board.')); if (empty($delete)) error($config['error']['nodelete']); From 6413bc78f6db8a1ec1eb876e09223d80ebeb64e8 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:16:25 -0800 Subject: [PATCH 52/86] Make this footer message optional --- inc/config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/config.php b/inc/config.php index 056ec77c..b54d0e0f 100644 --- a/inc/config.php +++ b/inc/config.php @@ -884,7 +884,7 @@ $config['thread_subject_in_title'] = false; // Additional lines added to the footer of all pages. - $config['footer'][] = _('All trademarks, copyrights, comments, and images on this page are owned by and are the responsibility of their respective parties.'); + // $config['footer'][] = _('All trademarks, copyrights, comments, and images on this page are owned by and are the responsibility of their respective parties.'); // Characters used to generate a random password (with Javascript). $config['genpassword_chars'] = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+'; From 8d34866636a85c555e29c29dbd35a09355d0993d Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:17:24 -0800 Subject: [PATCH 53/86] Fix locale bugs, init_locale was not calling setlocale!? --- inc/functions.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 0ff32e37..7d0cc9a3 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -29,14 +29,16 @@ mb_internal_encoding('UTF-8'); loadConfig(); function init_locale($locale, $error='error') { - if (_setlocale(LC_ALL, $locale) === false) { - $error('The specified locale (' . $locale . ') does not exist on your platform!'); - } + if ($locale === 'en') + $locale = 'en_US.utf8'; + if (extension_loaded('gettext')) { + setlocale(LC_ALL, $locale); bindtextdomain('tinyboard', './inc/locale'); bind_textdomain_codeset('tinyboard', 'UTF-8'); textdomain('tinyboard'); } else { + _setlocale(LC_ALL, $locale); _bindtextdomain('tinyboard', './inc/locale'); _bind_textdomain_codeset('tinyboard', 'UTF-8'); _textdomain('tinyboard'); From a564bb8ecc6f5753b7d3a4660fd088c46d77a0e6 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:18:24 -0800 Subject: [PATCH 54/86] Display some important information under the post form --- stylesheets/style.css | 2 +- templates/post_form.html | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/stylesheets/style.css b/stylesheets/style.css index bebd4813..4a038483 100644 --- a/stylesheets/style.css +++ b/stylesheets/style.css @@ -819,7 +819,7 @@ pre { margin-top: 1em; } -.new-threads { +.new-threads, .board-settings { text-align: center; } diff --git a/templates/post_form.html b/templates/post_form.html index 93e6465f..1b01b93b 100644 --- a/templates/post_form.html +++ b/templates/post_form.html @@ -178,3 +178,15 @@ + +

+{% if not config.disable_images %} +Allowed file types: {{ config.allowed_ext|join(', ') }}{% if config.allowed_ext_files %}, {{ config.allowed_ext_files|join(', ') }}{% endif %}
+Max filesize is {{ config.max_filesize|filesize }}.
+Max image dimensions are {{ config.max_height }} x {{ config.max_width }}.
+You may upload {{ config.max_images }} per post.
+{% endif %} +{% if config.allow_roll %} +You may roll dice on this board, type "dice XdY+Z" in the email field where X is number of dice, Y is max roll and Z is modifier.
+{% endif %} +

From e7fbb7e509e92b48af8c11a68178afd9cbb896d3 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:19:02 -0800 Subject: [PATCH 55/86] Update footer, warrant canary, quote US law --- inc/instance-config.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/inc/instance-config.php b/inc/instance-config.php index bd51e227..2c183707 100644 --- a/inc/instance-config.php +++ b/inc/instance-config.php @@ -138,6 +138,7 @@ $config['additional_javascript'][] = 'js/comment-toolbar.js'; $config['additional_javascript'][] = 'js/catalog-search.js'; $config['additional_javascript'][] = 'js/thread-stats.js'; + $config['additional_javascript'][] = 'js/quote-selection.js'; //$config['font_awesome_css'] = '/netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css'; @@ -153,12 +154,13 @@ $config['boards'] = array(array('' => '/', '' => '/boards.html', '' => '/faq.html', '' => '/random.php', '' => '/create.php', '' => '/bans.html', '' => '/search.php', '' => '/mod.php', '' => 'https://qchat.rizon.net/?channels=#8chan'), array('b', 'meta'), array(''=>'https://twitter.com/infinitechan')); //$config['boards'] = array(array('' => '/', '' => '/boards.html', '' => '/faq.html', '' => '/random.php', '' => '/create.php', '' => '/search.php', '' => '/mod.php', '' => 'https://qchat.rizon.net/?channels=#8chan'), array('b', 'meta', 'int'), array('v', 'a', 'tg', 'fit', 'pol', 'tech', 'mu', 'co', 'sp', 'boards'), array(''=>'https://twitter.com/infinitechan')); + $config['footer'][] = 'All posts on 8chan.co are the responsibility of the individual poster and not the administration of 8chan.co, pursuant to 47 U.S.C. § 230.'; + $config['footer'][] = 'We have not been served any secret court orders and are not under any gag orders.'; $config['footer'][] = 'Contribute to 8chan.co development at github'; - $config['footer'][] = 'To make a DMCA request or report illegal content, please email admin@8chan.co or use the "Global Report" functionality on every page.'; + $config['footer'][] = 'To make a DMCA request or report illegal content, please email admin@8chan.co.'; $config['search']['enable'] = true; -//$config['debug'] = true; $config['syslog'] = true; $config['wordfilters'][] = array('\rule', ''); // 'true' means it's a regular expression From a86b07282fe4c55d49d346ee23bed1b57d836ff0 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:19:27 -0800 Subject: [PATCH 56/86] Featured -> Top 15 --- templates/8chan/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/8chan/index.html b/templates/8chan/index.html index 61cd051c..406c7fbe 100644 --- a/templates/8chan/index.html +++ b/templates/8chan/index.html @@ -235,7 +235,7 @@
{% trans %}Welcome to ∞chan, the infinitely expanding imageboard.{% endtrans %}
- {% trans %}Featured boards:{% endtrans %} + {% trans %}Top 15 boards:{% endtrans %}
@@ -295,7 +295,7 @@
-

{% trans %}On ∞chan, you can create your own imageboard for free with no experience or programming knowledge needed. As long as the board has unobtrusive CSS, the owner doesn't ban more than half the users, the owner logs in once every two weeks and it gets one new post every week, you will retain the board forever.{% endtrans %}

+

{% trans %}On ∞chan, you can create your own imageboard for free with no experience or programming knowledge needed.{% endtrans %}

{% trans %}Warning: Some boards on this site might contain content of an adult or offensive nature. Please cease use of this site if it is illegal for you to view such content. The boards on this site are made entirely by the users and do not represent the opinions of the administration of 8chan.co. In the interest of free speech, only content that directly violates the DMCA or other US laws is deleted.{% endtrans %}

{% trans %}Create your board{% endtrans %} From 3df5b86235298b6d966638d4a3d14d8f91b1d3f3 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:19:50 -0800 Subject: [PATCH 57/86] Return some important info from listBoards() --- inc/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/functions.php b/inc/functions.php index 7d0cc9a3..2d505173 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -682,7 +682,7 @@ function listBoards($just_uri = false, $indexed_only = false) { return $boards; if (!$just_uri) { - $query = query("SELECT ``boards``.`uri` uri, ``boards``.`title` title, ``boards``.`subtitle` subtitle, ``board_create``.`time` time, ``boards``.`indexed` indexed FROM ``boards``" . ( $indexed_only ? " WHERE `indexed` = 1 " : "" ) . "LEFT JOIN ``board_create`` ON ``boards``.`uri` = ``board_create``.`uri` ORDER BY ``boards``.`uri`") or error(db_error()); + $query = query("SELECT ``boards``.`uri` uri, ``boards``.`title` title, ``boards``.`subtitle` subtitle, ``board_create``.`time` time, ``boards``.`indexed` indexed, ``boards``.`sfw` sfw FROM ``boards``" . ( $indexed_only ? " WHERE `indexed` = 1 " : "" ) . "LEFT JOIN ``board_create`` ON ``boards``.`uri` = ``board_create``.`uri` ORDER BY ``boards``.`uri`") or error(db_error()); $boards = $query->fetchAll(PDO::FETCH_ASSOC); } else { $boards = array(); From 5aa40669a7b8c0e1170e538a035879800a656461 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:20:20 -0800 Subject: [PATCH 58/86] tags in settings.html --- templates/mod/settings.html | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/mod/settings.html b/templates/mod/settings.html index 4d29cea7..f0f16032 100644 --- a/templates/mod/settings.html +++ b/templates/mod/settings.html @@ -60,6 +60,7 @@

{% trans %}Edit board banners{% endtrans %}

{% trans %}Edit board flags{% endtrans %}

{% trans %}Edit board volunteers{% endtrans %}

+

{% trans %}Edit board tags{% endtrans %}

From 05933f23d4a81fe5b28734f2754429f1a02a2c49 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:20:36 -0800 Subject: [PATCH 59/86] Make claim.html more discoverable --- templates/8chan/create.html | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/8chan/create.html b/templates/8chan/create.html index f99fa727..91ed9262 100644 --- a/templates/8chan/create.html +++ b/templates/8chan/create.html @@ -1,3 +1,4 @@ +

Did you know? Many boards with popular names have been abandoned by their owners and can be claimed. See here for a full list.

{% trans %}Default poster name{% endtrans %}
From 7e7cb6117d9e0355cd572bd58d976c74ed25c0e1 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:21:11 -0800 Subject: [PATCH 60/86] we dont even use this file anymore --- expire.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/expire.php b/expire.php index e24b69cf..8ca0789f 100644 --- a/expire.php +++ b/expire.php @@ -8,8 +8,8 @@ $protected = array('burgers', 'cow', 'wilno', 'cute', 'yoga'); $q = query("SELECT uri FROM boards"); $boards = $q->fetchAll(PDO::FETCH_COLUMN); $now = new DateTime(); -$ago = (new DateTime)->sub(new DateInterval('P3D')); -$mod_ago = (new DateTime)->sub(new DateInterval('P7D')); +$ago = (new DateTime)->sub(new DateInterval('P7D')); +$mod_ago = (new DateTime)->sub(new DateInterval('P14D')); // Find out the last activity for our board $delete = array(); From 27cb60ebc59eacb500eee1beeb56eac3040e425d Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 01:15:02 -0800 Subject: [PATCH 61/86] Disable notes completely until per-board notes --- inc/8chan-mod-pages.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inc/8chan-mod-pages.php b/inc/8chan-mod-pages.php index 86d91ef8..4c8e3b90 100644 --- a/inc/8chan-mod-pages.php +++ b/inc/8chan-mod-pages.php @@ -20,8 +20,8 @@ $config['mod']['noticeboard_post'] = GLOBALVOLUNTEER; $config['mod']['search'] = GLOBALVOLUNTEER; $config['mod']['clean_global'] = GLOBALVOLUNTEER; - $config['mod']['view_notes'] = GLOBALVOLUNTEER; - $config['mod']['create_notes'] = GLOBALVOLUNTEER; + $config['mod']['view_notes'] = DISABLED; + $config['mod']['create_notes'] = DISABLED; $config['mod']['debug_recent'] = ADMIN; $config['mod']['debug_antispam'] = ADMIN; $config['mod']['noticeboard_post'] = ADMIN; From 96d69c52f2935a72516f8a15b2fd97289004f911 Mon Sep 17 00:00:00 2001 From: topkek Date: Tue, 9 Dec 2014 02:04:53 +0000 Subject: [PATCH 62/86] Scroll to thumbnail when shrinking an expanded image --- js/inline-expanding.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/inline-expanding.js b/js/inline-expanding.js index 8da66898..05300138 100644 --- a/js/inline-expanding.js +++ b/js/inline-expanding.js @@ -74,10 +74,10 @@ onready(function(){ if (still_open > 1) { if (e.target.getBoundingClientRect().top - padding < 0) - $('body').scrollTop($(e.target).parent().parent().offset().top - padding); + $(document).scrollTop($(e.target).parent().parent().offset().top - padding); } else { if (post_body[0].getBoundingClientRect().top - padding < 0) - $('body').scrollTop(post_body.offset().top - padding); + $(document).scrollTop(post_body.offset().top - padding); } } From 57619d766fec5ce1fdba7aa20d594f17f56d442a Mon Sep 17 00:00:00 2001 From: 8chan Date: Sun, 14 Dec 2014 03:19:11 -0800 Subject: [PATCH 63/86] Let's not use this file. --- banned.php | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 banned.php diff --git a/banned.php b/banned.php deleted file mode 100644 index 57e4a9bc..00000000 --- a/banned.php +++ /dev/null @@ -1,7 +0,0 @@ -"._("Banned?").""; - print "

"._("You are not banned.")."

"; - print ""; -?> From 9db6895c98c7080176d2b2367e8b0105919f151c Mon Sep 17 00:00:00 2001 From: 8chan Date: Sun, 14 Dec 2014 03:25:43 -0800 Subject: [PATCH 64/86] Allow + in board names, add some new config vars (news+) --- banners.php | 2 +- inc/config.php | 3 ++- inc/functions.php | 5 ++++- inc/instance-config.php | 9 +++++++-- post.php | 11 ++++++++++- templates/index.html | 2 +- templates/main.js | 2 +- 7 files changed, 26 insertions(+), 8 deletions(-) diff --git a/banners.php b/banners.php index 2707ca5c..fd345cc9 100644 --- a/banners.php +++ b/banners.php @@ -8,7 +8,7 @@ header("Expires: 0"); function get_custom_banner(&$b) { # Validate the board name - if (!(isset($b) && preg_match('/^[a-z0-9]{1,30}$/', $b))) + if (!(isset($b) && preg_match('/^[a-z0-9+]{1,30}$/', $b))) return null; # Check if directory exists diff --git a/inc/config.php b/inc/config.php index b54d0e0f..1769ed6a 100644 --- a/inc/config.php +++ b/inc/config.php @@ -1083,6 +1083,7 @@ $config['error']['youaremuted'] = _('You are muted! Expires in %d seconds.'); $config['error']['dnsbl'] = _('Your IP address is listed in %s.'); $config['error']['toomanylinks'] = _('Too many links; flood detected.'); + $config['error']['notenoughlinks'] = _('OPs are required to have at least %d links on this board.'); $config['error']['toomanycites'] = _('Too many cites; post discarded.'); $config['error']['toomanycross'] = _('Too many cross-board links; post discarded.'); $config['error']['nodelete'] = _('You didn\'t select anything to delete.'); @@ -1680,7 +1681,7 @@ // Regex for board URIs. Don't add "`" character or any Unicode that MySQL can't handle. 58 characters // is the absolute maximum, because MySQL cannot handle table names greater than 64 characters. - $config['board_regex'] = '[0-9a-zA-Z$_\x{0080}-\x{FFFF}]{1,58}'; + $config['board_regex'] = '[0-9a-zA-Z\+$_\x{0080}-\x{FFFF}]{1,58}'; // Youtube.js embed HTML code $config['youtube_js_html'] = '
'. diff --git a/inc/functions.php b/inc/functions.php index 2d505173..93206d5f 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1703,7 +1703,7 @@ function extract_modifiers($body) { return $modifiers; } -function markup(&$body, $track_cites = false) { +function markup(&$body, $track_cites = false, $op = false) { global $board, $config, $markup_urls; $modifiers = extract_modifiers($body); @@ -1741,6 +1741,9 @@ function markup(&$body, $track_cites = false) { if ($num_links > $config['max_links']) error($config['error']['toomanylinks']); + + if ($num_links < $config['min_links'] && $op) + error(sprintf($config['error']['notenoughlinks'], $config['min_links'])); } if ($config['markup_repair_tidy']) diff --git a/inc/instance-config.php b/inc/instance-config.php index 2c183707..896eb3a1 100644 --- a/inc/instance-config.php +++ b/inc/instance-config.php @@ -74,7 +74,7 @@ $config['mod']['capcode'][MOD] = array('Board Owner'); $config['mod']['capcode'][GLOBALVOLUNTEER] = array('Global Volunteer'); $config['custom_capcode']['Admin'] = array( - ' %s', + ' 8chan.co Administrator', ); //$config['mod']['view_banlist'] = GLOBALVOLUNTEER; $config['mod']['recent_reports'] = 65535; @@ -91,6 +91,7 @@ //$config['default_stylesheet'] = array('Notsuba', 'notsuba.css'); $config['additional_javascript'][] = 'js/jquery.min.js'; $config['additional_javascript'][] = 'js/jquery.mixitup.min.js'; + $config['additional_javascript'][] = 'js/jquery-ui.custom.min.js'; $config['additional_javascript'][] = 'js/catalog.js'; $config['additional_javascript'][] = 'js/captcha.js'; $config['additional_javascript'][] = 'js/jquery.tablesorter.min.js'; @@ -150,8 +151,9 @@ $config['markup'][] = array("/\[spoiler\](.+?)\[\/spoiler\]/", "\$1"); $config['markup'][] = array("/~~(.+?)~~/", "\$1"); $config['markup'][] = array("/__(.+?)__/", "\$1"); + $config['markup'][] = array("/###([^\s']+)###/", "###\$1###"); - $config['boards'] = array(array('' => '/', '' => '/boards.html', '' => '/faq.html', '' => '/random.php', '' => '/create.php', '' => '/bans.html', '' => '/search.php', '' => '/mod.php', '' => 'https://qchat.rizon.net/?channels=#8chan'), array('b', 'meta'), array(''=>'https://twitter.com/infinitechan')); + $config['boards'] = array(array('' => '/', '' => '/boards.html', '' => '/faq.html', '' => '/random.php', '' => '/create.php', '' => '/bans.html', '' => '/search.php', '' => '/mod.php', '' => 'https://qchat.rizon.net/?channels=#8chan'), array('b', 'meta', 'news+'), array(''=>'https://twitter.com/infinitechan')); //$config['boards'] = array(array('' => '/', '' => '/boards.html', '' => '/faq.html', '' => '/random.php', '' => '/create.php', '' => '/search.php', '' => '/mod.php', '' => 'https://qchat.rizon.net/?channels=#8chan'), array('b', 'meta', 'int'), array('v', 'a', 'tg', 'fit', 'pol', 'tech', 'mu', 'co', 'sp', 'boards'), array(''=>'https://twitter.com/infinitechan')); $config['footer'][] = 'All posts on 8chan.co are the responsibility of the individual poster and not the administration of 8chan.co, pursuant to 47 U.S.C. § 230.'; @@ -195,6 +197,9 @@ $config['gzip_static'] = false; $config['hash_masked_ip'] = true; +$config['force_subject_op'] = false; +$config['min_links'] = 0; +$config['min_body'] = 0; // 8chan specific mod pages require '8chan-mod-pages.php'; diff --git a/post.php b/post.php index 1a7c5da2..37092bba 100644 --- a/post.php +++ b/post.php @@ -399,6 +399,13 @@ elseif (isset($_POST['post'])) { error($config['error']['tooshort_body']); } } + + if ($config['force_subject_op'] && $post['op']) { + $stripped_whitespace = preg_replace('/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $post['subject']); + if ($stripped_whitespace == '') { + error(_('It is required to enter a subject when starting a new thread on this board.')); + } + } if (!$post['op']) { // Check if thread is locked @@ -523,6 +530,8 @@ elseif (isset($_POST['post'])) { error(sprintf($config['error']['toolong'], 'subject')); if (!$mod && mb_strlen($post['body']) > $config['max_body']) error($config['error']['toolong_body']); + if (mb_strlen($post['body']) < $config['min_body'] && $post['op']) + error(_(sprintf('OP must be at least %d chars on this board.', $config['min_body']))); if (mb_strlen($post['password']) > 20) error(sprintf($config['error']['toolong'], 'password')); @@ -588,7 +597,7 @@ elseif (isset($_POST['post'])) { } } - $post['tracked_cites'] = markup($post['body'], true); + $post['tracked_cites'] = markup($post['body'], true, $post['op']); diff --git a/templates/index.html b/templates/index.html index 5638fd65..e422b8bc 100644 --- a/templates/index.html +++ b/templates/index.html @@ -18,7 +18,7 @@ {{ boardlist.top }} {% if pm %}
You have an unread PM{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.

{% endif %} - {% if config.url_banner %}{% endif %} + {% if config.url_banner %}{% endif %}

{{ board.url }} - {{ board.title|e }}

diff --git a/templates/main.js b/templates/main.js index 0156e23b..44b3dd96 100644 --- a/templates/main.js +++ b/templates/main.js @@ -188,7 +188,7 @@ function changeStyle(styleName, link) { {% endraw %} function init_stylechooser() { - var matches = document.URL.match(/\/(\w+)\/($|{{ config.dir.res|replace({'/': '\\/'}) }}{{ config.file_page|replace({'%d': '\\d+', '.': '\\.'}) }}|{{ config.file_index|replace({'.': '\\.'}) }}|{{ config.dir.res|replace({'/': '\\/'}) }}{{ config.file_page50|replace({'+': '\\+', '%d': '\\d+', '.': '\\.'}) }}|{{ config.file_page|replace({'%d': '\\d+', '.': '\\.'}) }}|{{ config.catalog_link|replace({'.': '\\.'}) }})/); + var matches = document.URL.match(/\/([0-9a-zA-Z\+$_\u0080-\uFFFF]{1,58})\/($|{{ config.dir.res|replace({'/': '\\/'}) }}{{ config.file_page|replace({'%d': '\\d+', '.': '\\.'}) }}|{{ config.file_index|replace({'.': '\\.'}) }}|{{ config.dir.res|replace({'/': '\\/'}) }}{{ config.file_page50|replace({'+': '\\+', '%d': '\\d+', '.': '\\.'}) }}|{{ config.file_page|replace({'%d': '\\d+', '.': '\\.'}) }}|{{ config.catalog_link|replace({'.': '\\.'}) }})/); var newElement = document.createElement('div'); newElement.className = 'styles'; From 8ff796cf8031b3f8e5d5e39cd7b4b0089b82fbb7 Mon Sep 17 00:00:00 2001 From: 8chan Date: Sun, 14 Dec 2014 03:26:00 -0800 Subject: [PATCH 65/86] Update FAQ --- faq.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/faq.php b/faq.php index 2ea68f72..4644d501 100644 --- a/faq.php +++ b/faq.php @@ -65,9 +65,9 @@ $body = <<

How are featured boards chosen?

-

Top fifteen boards excluding /meta/, /b/ and /int/.

+

Top fifteen boards excluding /meta/, /b/ and /pol/.

-

Who owns /meta/, /b/, and /int/?

+

Who owns /meta/ and /b/?

No one, so they are de facto property of the administration.

Where's the mobile app?

@@ -76,12 +76,22 @@ $body = <<I don't provide support for this app, ask the developer of it if you have a problem with it.

Where's the archive?

-

There isn't one yet and there will never be an official archive.

+

There isn't one yet and there will never be an official archive.

+

Given that archives are inevitable and will be created anyway via archive.today, Google cache, and anyone who installs Asagi, I'm softening my stance on this. Currently, 8archive.moe provides our archive, and I may set up an official one. All archives officially partnered with us will be opt-in by our board owners, not opt-out. Archives who archive boards that have not opted in will be considered pirate archives, and legal action may be taken.

+ +

I got an email from an @8chan.co email address, is that you?

+

8chan.co uses cock.li to manage our domain's email. cock.li allows anyone to create an email account @8chan.co.

+

That said, we have quite a few official 8chan.co email addresses. They are:

+
    +
  • admin at 8chan dot co
  • +
  • dmca at 8chan dot co
  • +
  • claim at 8chan dot co
  • +

How do I donate?

Donations can be sent to 1NpQaXqmCBji6gfX8UgaQEmEstvVY7U32C (Bitcoin) or LUPgSCJt3iGeJXUETVhmnbQ89Riaq1yjZm (Litecoin).

+

I am also a big fan of Monero (XMR). You can send XMR to our OpenAlias in the simplewallet client, or simply send to 49dBJhGhYFxJEfydS6hH6GRyg1W4cDgupdNVtw7j1WtcUY7xPXwNLw6fUVay644viaCcEhMFG1Z7SjjxRXEFDdNWJdvH9kS.

You may also donate monthly via Patreon at http://www.patreon.com/user?u=162165. -

Are you really a cripple?

Yes.

From 6ed13f3fb0cc8921bb8dcbf87626cb734e90ef8f Mon Sep 17 00:00:00 2001 From: 8chan Date: Sun, 14 Dec 2014 03:26:45 -0800 Subject: [PATCH 66/86] SECURITY: Board Owners could brick accounts by creating account names of matching users with different capitalizations --- inc/8chan-mod-pages.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/inc/8chan-mod-pages.php b/inc/8chan-mod-pages.php index 4c8e3b90..33a6e752 100644 --- a/inc/8chan-mod-pages.php +++ b/inc/8chan-mod-pages.php @@ -22,6 +22,7 @@ $config['mod']['clean_global'] = GLOBALVOLUNTEER; $config['mod']['view_notes'] = DISABLED; $config['mod']['create_notes'] = DISABLED; + $config['mod']['edit_config'] = DISABLED; $config['mod']['debug_recent'] = ADMIN; $config['mod']['debug_antispam'] = ADMIN; $config['mod']['noticeboard_post'] = ADMIN; @@ -155,14 +156,16 @@ error(sprintf($config['error']['required'], 'username')); if ($_POST['password'] == '') error(sprintf($config['error']['required'], 'password')); + if (!preg_match('/^[a-zA-Z0-9._]{1,30}$/', $_POST['username'])) + error(_('Invalid username')); if ($count > 10) { error(_('Too many board volunteers!')); } foreach ($volunteers as $i => $v) { - if ($_POST['username'] == $v['username']) { - error(_('Refusing to create a volunteer with the same username as an existing one.')); + if (strtolower($_POST['username']) == strtolower($v['username'])) { + error(_('Refusing to create a volunteer with the same username as an existing one.')); } } From d70560672922b3c35cbe70867282e121c1ffefd5 Mon Sep 17 00:00:00 2001 From: 8chan Date: Sun, 14 Dec 2014 03:27:26 -0800 Subject: [PATCH 67/86] Allow a board called news to exist --- inc/mod/pages.php | 8 ++++---- mod.php | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/inc/mod/pages.php b/inc/mod/pages.php index 0f059f05..a933f95a 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -633,7 +633,7 @@ function mod_news($page_no = 1) { rebuildThemes('news'); - header('Location: ?/news#' . $pdo->lastInsertId(), true, $config['redirect_http']); + header('Location: ?/edit_news#' . $pdo->lastInsertId(), true, $config['redirect_http']); } $query = prepare("SELECT * FROM ``news`` ORDER BY `id` DESC LIMIT :offset, :limit"); @@ -646,14 +646,14 @@ function mod_news($page_no = 1) { error($config['error']['404']); foreach ($news as &$entry) { - $entry['delete_token'] = make_secure_link_token('news/delete/' . $entry['id']); + $entry['delete_token'] = make_secure_link_token('edit_news/delete/' . $entry['id']); } $query = prepare("SELECT COUNT(*) FROM ``news``"); $query->execute() or error(db_error($query)); $count = $query->fetchColumn(); - mod_page(_('News'), 'mod/news.html', array('news' => $news, 'count' => $count, 'token' => make_secure_link_token('news'))); + mod_page(_('News'), 'mod/news.html', array('news' => $news, 'count' => $count, 'token' => make_secure_link_token('edit_news'))); } function mod_news_delete($id) { @@ -668,7 +668,7 @@ function mod_news_delete($id) { modLog('Deleted a news entry'); - header('Location: ?/news', true, $config['redirect_http']); + header('Location: ?/edit_news', true, $config['redirect_http']); } function mod_log($page_no = 1) { diff --git a/mod.php b/mod.php index 45c836ac..18dffec3 100644 --- a/mod.php +++ b/mod.php @@ -42,9 +42,9 @@ $pages = array( '/log/(\d+)' => 'log', // modlog '/log:([^/]+)' => 'user_log', // modlog '/log:([^/]+)/(\d+)' => 'user_log', // modlog - '/news' => 'secure_POST news', // view news - '/news/(\d+)' => 'secure_POST news', // view news - '/news/delete/(\d+)' => 'secure news_delete', // delete from news + '/edit_news' => 'secure_POST news', // view news + '/edit_news/(\d+)' => 'secure_POST news', // view news + '/edit_news/delete/(\d+)' => 'secure news_delete', // delete from news '/noticeboard' => 'secure_POST noticeboard', // view noticeboard '/noticeboard/(\d+)' => 'secure_POST noticeboard', // view noticeboard From b9b1935e08a2561b5dae15ed0408646a079fc6b3 Mon Sep 17 00:00:00 2001 From: 8chan Date: Sun, 14 Dec 2014 03:28:04 -0800 Subject: [PATCH 68/86] Bug fix: Boards were always unindexed in search engines --- inc/functions.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/inc/functions.php b/inc/functions.php index 93206d5f..dd02ce79 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -432,7 +432,8 @@ function setupBoard($array) { $board = array( 'uri' => $array['uri'], 'title' => $array['title'], - 'subtitle' => $array['subtitle'] + 'subtitle' => $array['subtitle'], + 'indexed' => $array['indexed'] ); // older versions From e8f061029936411adfe179575241c9375860de94 Mon Sep 17 00:00:00 2001 From: 8chan Date: Sun, 14 Dec 2014 03:28:19 -0800 Subject: [PATCH 69/86] update update_boards.js --- js/update_boards.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/update_boards.js b/js/update_boards.js index 127d0874..439ddb14 100644 --- a/js/update_boards.js +++ b/js/update_boards.js @@ -4,7 +4,7 @@ window.boards = new Array(); function handle_boards(data) { $.each(data, function(k, v) { if (v.uri != 'meta' && v.uri != 'b') { - boards.push(''+v.uri+''); + boards.push(''+v.uri+''); } }) From 1cab7bc74f3052de570603935692d4c2e2cf6a1d Mon Sep 17 00:00:00 2001 From: 8chan Date: Sun, 14 Dec 2014 03:28:38 -0800 Subject: [PATCH 70/86] Prevent one type of blank-posting --- post.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/post.php b/post.php index 37092bba..d28f50d6 100644 --- a/post.php +++ b/post.php @@ -394,7 +394,8 @@ elseif (isset($_POST['post'])) { $post['has_file'] = (!isset($post['embed']) && (($post['op'] && !isset($post['no_longer_require_an_image_for_op']) && $config['force_image_op']) || !empty($_FILES['file']['name']))); 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']); + // http://stackoverflow.com/a/4167053 + $stripped_whitespace = preg_replace('/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $post['body']); if ($stripped_whitespace == '') { error($config['error']['tooshort_body']); } From 1c207c0aa5dc5490ce2c93186d112a622a1b6e2a Mon Sep 17 00:00:00 2001 From: 8chan Date: Sun, 14 Dec 2014 03:29:00 -0800 Subject: [PATCH 71/86] Bug fix: fix highlightReply() with the new floating boardlist --- templates/main.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/templates/main.js b/templates/main.js index 44b3dd96..23af9447 100644 --- a/templates/main.js +++ b/templates/main.js @@ -255,9 +255,10 @@ function get_cookie(cookie_name) { } function highlightReply(id) { - if (typeof window.event != "undefined" && event.which == 2) { + if (typeof window.event != "undefined") { // don't highlight on middle click - return true; + if (event.which == 2) return true; + window.event.preventDefault(); } var divs = document.getElementsByTagName('div'); @@ -268,9 +269,18 @@ function highlightReply(id) { } if (id) { var post = document.getElementById('reply_'+id); - if (post) + if (post) { post.className += ' highlighted'; window.location.hash = id; + + // Better offset to keep in mind new hovering boardlist + var post_top = post.getBoundingClientRect().top; + var body_top = document.body.getBoundingClientRect().top; + var boardlist_height = document.getElementsByClassName('boardlist')[0].getBoundingClientRect().height; + var offset = (post_top - body_top) - boardlist_height; + + window.scrollTo(0, offset); + } } return true; } From 968fab9420b5b7f747e279193fc27f567bf52307 Mon Sep 17 00:00:00 2001 From: 8chan Date: Sun, 14 Dec 2014 03:29:34 -0800 Subject: [PATCH 72/86] + signs in banner board names --- templates/thread.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/thread.html b/templates/thread.html index 0a78f274..61b6e152 100644 --- a/templates/thread.html +++ b/templates/thread.html @@ -13,7 +13,7 @@ {{ boardlist.top }} {% if pm %}
You have an unread PM{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.

{% endif %} - {% if config.url_banner %}{% endif %} + {% if config.url_banner %}{% endif %}

{{ board.url }} - {{ board.title|e }}

From d457cb57b82c9115360d862c4e0940ca9be8c1c6 Mon Sep 17 00:00:00 2001 From: Forkless Date: Sun, 14 Dec 2014 22:46:11 -0600 Subject: [PATCH 73/86] Added tripcode removal to post editing --- inc/mod/pages.php | 7 ++++--- templates/mod/edit_post_form.html | 8 ++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/inc/mod/pages.php b/inc/mod/pages.php index 0f059f05..8f7a54bb 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -1599,12 +1599,13 @@ function mod_edit_post($board, $edit_raw_html, $postID) { error($config['error']['404']); if (isset($_POST['name'], $_POST['email'], $_POST['subject'], $_POST['body'])) { + $trip = isset($_POST['remove_trip']) ? ' `trip` = NULL,' : ''; if ($edit_raw_html) - $query = prepare(sprintf('UPDATE ``posts_%s`` SET `name` = :name, `email` = :email, `subject` = :subject, `body` = :body, `body_nomarkup` = :body_nomarkup, `edited_at` = NOW() WHERE `id` = :id', $board)); + $query = prepare(sprintf('UPDATE ``posts_%s`` SET `name` = :name,'. $trip .' `email` = :email, `subject` = :subject, `body` = :body, `body_nomarkup` = :body_nomarkup, `edited_at` = NOW() WHERE `id` = :id', $board)); else - $query = prepare(sprintf('UPDATE ``posts_%s`` SET `name` = :name, `email` = :email, `subject` = :subject, `body_nomarkup` = :body, `edited_at` = NOW() WHERE `id` = :id', $board)); + $query = prepare(sprintf('UPDATE ``posts_%s`` SET `name` = :name,'. $trip .' `email` = :email, `subject` = :subject, `body_nomarkup` = :body, `edited_at` = NOW() WHERE `id` = :id', $board)); $query->bindValue(':id', $postID); - $query->bindValue('name', $_POST['name']); + $query->bindValue(':name', $_POST['name'] ? $_POST['name'] : $config['anonymous']); $query->bindValue(':email', $_POST['email']); $query->bindValue(':subject', $_POST['subject']); $query->bindValue(':body', $_POST['body']); diff --git a/templates/mod/edit_post_form.html b/templates/mod/edit_post_form.html index a2980d35..09dc3fcd 100644 --- a/templates/mod/edit_post_form.html +++ b/templates/mod/edit_post_form.html @@ -10,6 +10,14 @@ +
+ + + + From 025713547c1406af16452a267baf275b56055215 Mon Sep 17 00:00:00 2001 From: 8chan Date: Sat, 20 Dec 2014 07:01:40 -0800 Subject: [PATCH 82/86] Tweak flag size requirements --- inc/8chan-mod-pages.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/8chan-mod-pages.php b/inc/8chan-mod-pages.php index 0fe9255f..aa9bc376 100644 --- a/inc/8chan-mod-pages.php +++ b/inc/8chan-mod-pages.php @@ -262,7 +262,7 @@ error($config['error']['invalidimg']); } - if ($size[0] > 20 or $size[0] < 11 or $size[1] != 11){ + if ($size[0] > 20 or $size[0] < 11 or $size[1] > 16 or $size[1] < 11){ error(_('Image wrong size!')); } if (sizeof($banners) > 256) { From e843f81f3cfa94852a61665881aafcf798ee1436 Mon Sep 17 00:00:00 2001 From: 8chan Date: Sat, 20 Dec 2014 07:02:00 -0800 Subject: [PATCH 83/86] Customize embed sizes --- js/youtube.js | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/js/youtube.js b/js/youtube.js index c4ef77fb..b40bbb66 100644 --- a/js/youtube.js +++ b/js/youtube.js @@ -23,11 +23,39 @@ */ -onready(function(){ +$(document).ready(function(){ + if (window.Options && Options.get_tab('general')) { + Options.extend_tab("general", "" + _('YouTube size') + ": x"); + + if (typeof localStorage.youtube_size === 'undefined') { + localStorage.youtube_size = '{"width":360,"height":270}'; + var our_yt = JSON.parse(localStorage.youtube_size); + } else { + var our_yt = JSON.parse(localStorage.youtube_size); + $('#youtube-height').val(our_yt.height); + $('#youtube-width').val(our_yt.width); + } + + + $('#youtube-width, #youtube-height').on('change', function() { + if ($(this).attr('id') === 'youtube-height') { + our_yt.height = $(this).val(); + } else { + our_yt.width = $(this).val(); + } + + localStorage.youtube_size = JSON.stringify(our_yt); + }); + } + var do_embed_yt = function(tag) { + if (typeof our_yt === "undefined") { + our_yt = {"width":360,"height":270}; + } + $('div.video-container a', tag).click(function() { $(this.parentNode).html('
+ {% trans %}Tripcode{% endtrans %} + + Remove? +
{% trans %}Email{% endtrans %} From f8c1d7c80fd072fba64513815c76ab16e73bf3f2 Mon Sep 17 00:00:00 2001 From: 8chan Date: Mon, 15 Dec 2014 17:50:22 -0800 Subject: [PATCH 74/86] what the fuck were you even thinking this is an insane default BUGFIX: all-Japanese, all-Arabic, etc posts no longer trip the filter if not robot_enable --- inc/functions.php | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index dd02ce79..e72708fd 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1332,23 +1332,26 @@ function getPages($mod=false) { // Stolen with permission from PlainIB (by Frank Usrs) function make_comment_hex($str) { + global $config; // remove cross-board citations // the numbers don't matter - $str = preg_replace('!>>>/[A-Za-z0-9]+/!', '', $str); + $str = preg_replace("!>>>/[A-Za-z0-9]+/!", '', $str); - if (function_exists('iconv')) { - // remove diacritics and other noise - // FIXME: this removes cyrillic entirely - $oldstr = $str; - $str = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $str); - if (!$str) $str = $oldstr; + if ($config['robot_enable']) { + if (function_exists('iconv')) { + // remove diacritics and other noise + // FIXME: this removes cyrillic entirely + $oldstr = $str; + $str = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $str); + if (!$str) $str = $oldstr; + } + + $str = strtolower($str); + + // strip all non-alphabet characters + $str = preg_replace('/[^a-z]/', '', $str); } - $str = strtolower($str); - - // strip all non-alphabet characters - $str = preg_replace('/[^a-z]/', '', $str); - return md5($str); } From cccc1bff79723951f0a9f89326d26f399ee4c487 Mon Sep 17 00:00:00 2001 From: 8chan Date: Mon, 15 Dec 2014 17:52:31 -0800 Subject: [PATCH 75/86] Fix new highlightReply in Gecko --- templates/main.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/templates/main.js b/templates/main.js index 23af9447..74cf40e6 100644 --- a/templates/main.js +++ b/templates/main.js @@ -1,5 +1,4 @@ {% raw %} - /* gettext-compatible _ function, example of usage: * * > // Loading pl_PL.json here (containing polish translation strings generated by tools/i18n_compile.php) @@ -258,7 +257,7 @@ function highlightReply(id) { if (typeof window.event != "undefined") { // don't highlight on middle click if (event.which == 2) return true; - window.event.preventDefault(); + if (active_page == 'thread' && typeof window.event.preventDefault != "undefined") window.event.preventDefault(); } var divs = document.getElementsByTagName('div'); @@ -434,6 +433,9 @@ function ready() { var post_date = "{{ config.post_date }}"; var max_images = {{ config.max_images }}; +if (typeof active_page === "undefined") { + active_page = "page"; +} {% if config.google_analytics %}{% raw %} From 9dbc4e93be260b86d51e809e9f4e67f1270e5fd5 Mon Sep 17 00:00:00 2001 From: 8chan Date: Mon, 15 Dec 2014 23:35:39 -0800 Subject: [PATCH 76/86] Show backlinks on OP --- js/show-backlinks.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/js/show-backlinks.js b/js/show-backlinks.js index fa4865fd..6f4cafa9 100644 --- a/js/show-backlinks.js +++ b/js/show-backlinks.js @@ -15,7 +15,7 @@ onready(function(){ var showBackLinks = function() { - var reply_id = $(this).attr('id').replace(/^reply_/, ''); + var reply_id = $(this).attr('id').replace(/^(reply|op)_/, ''); $(this).find('div.body a:not([rel="nofollow"])').each(function() { var id, post, $mentioned; @@ -26,6 +26,9 @@ onready(function(){ return; $post = $('#reply_' + id); + if($post.length == 0) + $post = $('#op_' + id); + if($post.length == 0) return; From d0d2abe8ec2379d4fab4b974e2b7893804e760d9 Mon Sep 17 00:00:00 2001 From: 8chan Date: Tue, 16 Dec 2014 00:33:20 -0800 Subject: [PATCH 77/86] Why not let users resize the QR horizontally? --- js/quick-reply.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/quick-reply.js b/js/quick-reply.js index 85503dd8..7156b224 100644 --- a/js/quick-reply.js +++ b/js/quick-reply.js @@ -87,7 +87,7 @@ -webkit-box-sizing:border-box;\ -moz-box-sizing: border-box;\ font-size: 10pt;\ - resize: vertical;\ + resize: both;\ }\ #quick-reply input, #quick-reply select, #quick-reply textarea {\ margin: 0 0 1px 0;\ From 8b701ef10a79df3126ebb8e4a048d756c4a8d2b1 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 17 Dec 2014 05:29:56 -0800 Subject: [PATCH 78/86] Add news+ to excluded in FAQ --- faq.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/faq.php b/faq.php index 4644d501..6803b8d5 100644 --- a/faq.php +++ b/faq.php @@ -65,7 +65,7 @@ $body = <<

How are featured boards chosen?

-

Top fifteen boards excluding /meta/, /b/ and /pol/.

+

Top fifteen boards excluding /meta/, /b/ and /news+/.

Who owns /meta/ and /b/?

No one, so they are de facto property of the administration.

From 13c75110b997697f63f11b8ed873f8981f04d19d Mon Sep 17 00:00:00 2001 From: 8chan Date: Sat, 20 Dec 2014 07:00:26 -0800 Subject: [PATCH 79/86] Fix board removal on 404, splice not pop @bui --- 404.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/404.php b/404.php index cee2ece6..b28dbd25 100644 --- a/404.php +++ b/404.php @@ -37,7 +37,7 @@ $page = << Date: Sat, 20 Dec 2014 07:00:43 -0800 Subject: [PATCH 80/86] Obscenity.html link in FAQ --- faq.php | 1 + 1 file changed, 1 insertion(+) diff --git a/faq.php b/faq.php index 6803b8d5..6261af42 100644 --- a/faq.php +++ b/faq.php @@ -26,6 +26,7 @@ $body = <<Do not post, request, or link to any content illegal in the United States of America. Do not create boards with the sole purpose of posting or spreading such content.

Other than that, you are free to institute whatever rules you want on your board.

+

More information about US obscenity laws and how they relate to 8chan boards

How do I add more volunteers?

You may do this in your board settings, click on "Edit board volunteers".

How do I manage my board?

From 5b5a0b9061bc15ab866b60ee11736ddd4df83e98 Mon Sep 17 00:00:00 2001 From: 8chan Date: Sat, 20 Dec 2014 07:01:25 -0800 Subject: [PATCH 81/86] Force subject OP --- inc/8chan-mod-pages.php | 2 ++ templates/mod/settings.html | 1 + 2 files changed, 3 insertions(+) diff --git a/inc/8chan-mod-pages.php b/inc/8chan-mod-pages.php index 33a6e752..0fe9255f 100644 --- a/inc/8chan-mod-pages.php +++ b/inc/8chan-mod-pages.php @@ -405,6 +405,7 @@ FLAGS; $katex = isset($_POST['katex']) ? '$config[\'katex\'] = true;$config[\'additional_javascript\'][] = \'js/katex/katex.min.js\'; $config[\'markup\'][] = array("/\[tex\](.+?)\[\/tex\]/ms", "\$1"); $config[\'additional_javascript\'][] = \'js/katex-enable.js\';' : ''; $user_flags = isset($_POST['user_flags']) ? "if (file_exists('$b/flags.php')) { include 'flags.php'; }\n" : ''; $captcha = isset($_POST['captcha']) ? 'true' : 'false'; + $force_subject_op = isset($_POST['force_subject_op']) ? 'true' : 'false'; $oekaki_js = <<
{% trans %}Forced anonymous{% endtrans %}
{% trans %}YouTube/Voocaroo embedding{% endtrans %}
{% trans %}Require image for OP{% endtrans %}
{% trans %}Require subject for OP{% endtrans %}
{% trans %}Disable images{% endtrans %}
{% trans %}Poster ID's{% endtrans %}
{% trans %}Display SAGE! after saged posts{% endtrans %}