/ catalog-test.php
catalog-test.php
  1  <?php
  2  // haha i named this file catalog.php now everyone but me will find it
  3  // awkward to type
  4  // nvm moot is a nerd
  5  function generate_catalog()
  6  {
  7    global $log;
  8  
  9    if( !STATIC_REBUILD ) log_cache();
 10    $catalogjson = array();
 11    
 12    $i = 0;
 13    
 14    foreach( $log['THREADS'] as $thread ) {
 15      catalog_thread($log[$thread], $catalogjson, $i);
 16      ++$i;
 17    }
 18    
 19    $catalogjson = array(
 20      'threads'  => $catalogjson,
 21      
 22      'count'    => count( $log['THREADS'] ),
 23      'slug'     => BOARD_DIR,
 24      'anon'     => S_ANONAME,
 25      'mtime'    => time(),
 26      'pagesize' => DEF_PAGES
 27    );
 28    
 29    if (!REPLIES_SHOWN && IS_REBUILDD) {
 30      $catalogjson['no_lr'] = true;
 31    }
 32    
 33    if (SHOW_COUNTRY_FLAGS) {
 34      $catalogjson['flags'] = true;
 35    }
 36    
 37    if( SPOILERS ) $catalogjson['custom_spoiler'] = (int)SPOILER_NUM;
 38  
 39    $catalogjson = json_encode( $catalogjson );
 40  
 41    $catalog = catalog( $catalogjson );
 42  
 43    print_page( INDEX_DIR . 'catalog.html', $catalog );
 44  
 45    return true;
 46  }
 47  
 48  function catalog_thread($res, &$json, $pos)
 49  {
 50    global $log;
 51  
 52    $reps = $res['replycount'];
 53    $sub  = $res['sub'];
 54  
 55    $imgs = $res['imgreplycount'];
 56    $last_reply_id = null;
 57    $capcodelist = array();
 58    
 59    if (TEXT_ONLY) {
 60      $time_prop = 'now';
 61    }
 62    else {
 63      $time_prop = 'time';
 64    }
 65    
 66    foreach( $res['children'] as $reply => $unused ) {
 67      $last_reply_id = $reply;
 68      
 69      if( META_BOARD && $log[$reply]['capcode'] != 'none' ) {
 70        $tCapcode = $log[$reply]['capcode'];
 71        if( $tCapcode == 'admin_highlight' ) $tCapcode = 'admin';
 72  
 73        if( $tCapcode != 'none' ) {
 74          $capcodelist[$tCapcode] = 1;
 75        }
 76      }
 77    }
 78    
 79    if ($last_reply_id === null) {
 80      $last_reply = array( 'id' => $res['no'] );
 81    }
 82    else {
 83      $lr_data = $log[$last_reply_id];
 84      
 85      $last_reply = array(
 86        'id'    => $last_reply_id,
 87        'date'  => $lr_data['time']
 88      );
 89      
 90      if( $lr_data['capcode'] != 'none' ) $last_reply['capcode'] = $lr_data['capcode'];
 91      
 92      $force_anon = ( ( FORCED_ANON || META_BOARD ) && $lr_data['capcode'] != 'admin' && $lr_data['capcode'] != 'admin_highlight' );
 93      
 94      if( !$force_anon ) {
 95        if( strpos( $lr_data['name'], '</span> <span class="postertrip">' ) !== false ) {
 96          list( $last_reply['author'], $last_reply['trip'] ) = explode( '</span> <span class="postertrip">', $lr_data['name'] );
 97        } else {
 98          $last_reply['author'] = $lr_data['name'];
 99        }
100      } else {
101        $last_reply['author'] = S_ANONAME;
102      }
103    }
104  
105    $json[$res['no']] = array(
106      'date' => $res[$time_prop],
107      'file' => mb_convert_encoding($res['filename'], 'UTF-8', 'UTF-8') . $res['ext'],
108      'r'    => $reps,
109      'i'    => $imgs,
110      'lr'   => $last_reply,
111      'b'    => $pos
112    );
113    /*
114    if( META_BOARD && $capcodelist ) {
115      $json[$res['no']]['capcodereps'] = implode(',', array_keys($capcodelist));
116    }
117    */
118    if ($res['capcode'] == 'none') {
119      if (SHOW_COUNTRY_FLAGS && (!ENABLE_BOARD_FLAGS || $res['board_flag'] == '')) {
120        $json[$res['no']]['country'] = $res['country'];
121      }
122    }
123    
124    $com = $res['com'];
125  
126    if( strpos( $com, 'class="abbr"' ) !== false ) {
127      $com = preg_replace( '#(<br>)+<span class="abbr">(.+)$#s', '', $com );
128    }
129    
130    if (!TEXT_ONLY) {
131      $com = preg_replace( '#(<br>)+#', ' ', $com );
132    }
133    else {
134      $com = preg_replace( '#(<br>)+#', "\n", $com );
135    }
136    
137    if (BOARD_DIR === 'b') { // fixme, hardcoded for now
138      $com = truncate_comment($com, 300, true);
139    }
140    else {
141      if (SJIS_TAGS) {
142        $com = preg_replace('/<span class="sjis".+?<\/span>/', '[SJIS]', $com);
143      }
144      $com = strip_tags($com, '<s>');
145    }
146    
147    $has_spoilers = (bool)SPOILERS;
148  
149    if (!$res['permaage'] && !$res['sticky']) {
150      if( $reps >= MAX_RES ) $json[$res['no']]['bumplimit'] = 1;
151      if( $imgs >= MAX_IMGRES ) $json[$res['no']]['imagelimit'] = 1;
152    }
153  
154    if( $res['sticky'] ) $json[$res['no']]['sticky'] = 1;
155    if( $res['closed'] ) $json[$res['no']]['closed'] = 1;
156  
157    if( $res['capcode'] != 'none' ) $json[$res['no']]['capcode'] = $res['capcode'];
158    
159    $force_anon = ( ( FORCED_ANON || META_BOARD ) && $res['capcode'] != 'admin' && $res['capcode'] != 'admin_highlight' );
160    
161    if( !$force_anon ) {
162      if( strpos( $res['name'], '</span> <span class="postertrip">' ) !== false ) {
163        list( $json[$res['no']]['author'], $json[$res['no']]['trip'] ) = explode( '</span> <span class="postertrip">', $res['name'] );
164      } else {
165        $json[$res['no']]['author'] = $res['name'];
166      }
167    } else {
168      $json[$res['no']]['author'] = S_ANONAME;
169    }
170  
171    if( $res['fsize'] != 0 && $res['filedeleted'] != 1 ) {
172      $json[$res['no']]['imgurl'] = $res['tim'];
173      $json[$res['no']]['tn_w'] = $res['tn_w'];
174      $json[$res['no']]['tn_h'] = $res['tn_h'];
175    }
176  
177    if( $res['filedeleted'] == 1 ) $json[$res['no']]['imgdel'] = true;
178  
179    if( strpos( $res['sub'], 'SPOILER<>' ) !== false ) {
180      $json[$res['no']]['imgspoiler'] = true;
181      $sub                            = substr( $res['sub'], 9 );
182    }
183  
184    $json[$res['no']]['sub'] = $sub;
185    $json[$res['no']]['teaser'] = $com;
186  }
187  
188  function catalog($catjson) {
189    $nav  = file_get_contents_cached( NAV_TXT );
190    $foot = file_get_contents_cached( NAV2_TXT );
191    
192    $nav  = preg_replace( '/href="(\/\/boards.(?:4chan|4channel).org)?\/([a-z0-9]+)\/"/', 'href="$1/$2/catalog"', $nav );
193    $nav  = preg_replace( '/href="(\/\/boards.(?:4chan|4channel).org)?\/f\/catalog"/', 'href="$1/f/"', $nav );
194    
195    $title = strip_tags( TITLE );
196    
197    $js = '';
198  
199    // danbo ads start
200    if (defined('ADS_DANBO') && ADS_DANBO) {
201      $js .= '<script>';
202      
203      if (DEFAULT_BURICHAN) {
204        $js .= "var danbo_rating = '__SFW__';";
205      }
206      else {
207        $js .= "var danbo_rating = '__NSFW__';";
208      }
209      
210      $_danbo_fallbacks = [];
211      
212      if (defined('AD_BIDGEAR_TOP') && AD_BIDGEAR_TOP)  {
213        $_danbo_fallbacks['t_bg'] = AD_BIDGEAR_TOP;
214      }
215      else {
216        if (defined('AD_ABC_TOP_DESKTOP') && AD_ABC_TOP_DESKTOP)  {
217          $_danbo_fallbacks['t_abc_d'] = AD_ABC_TOP_DESKTOP;
218        }
219        
220        if (defined('AD_ABC_TOP_MOBILE') && AD_ABC_TOP_MOBILE)  {
221          $_danbo_fallbacks['t_abc_m'] = AD_ABC_TOP_MOBILE;
222        }
223      }
224      
225      if (defined('AD_BIDGEAR_BOTTOM') && AD_BIDGEAR_BOTTOM)  {
226        $_danbo_fallbacks['b_bg'] = AD_BIDGEAR_BOTTOM;
227      }
228      else if (defined('AD_ABC_BOTTOM_MOBILE') && AD_ABC_BOTTOM_MOBILE)  {
229        $_danbo_fallbacks['b_abc_m'] = AD_ABC_BOTTOM_MOBILE;
230      }
231      
232      if (!$_danbo_fallbacks) {
233        $_danbo_fallbacks = 'null';
234      }
235      else {
236        $_danbo_fallbacks = json_encode($_danbo_fallbacks);
237      }
238      
239      $js .= 'var danbo_fb = ' . $_danbo_fallbacks . ';';
240      
241      $js .= '</script>';
242      
243      $js .= '<script src="https://static.danbo.org/publisher/q2g345hq2g534-4chan/js/preload.4chan.js" defer></script>';
244    }
245    // danbo ads end
246    
247    // PubFuture
248    if (DEFAULT_BURICHAN) {
249      $js .= '<script async data-cfasync="false" src="https://cdn.pubfuture-ad.com/v2/unit/pt.js"></script>';
250    }
251    
252    if (TEST_BOARD) {
253      // Main catalog JS
254      $js .= '<script type="text/javascript" src="' . STATIC_SERVER . 'js/test/catalog-8psvqAqszI.' . JS_VERSION_TEST . '.js"></script>';
255      
256      // Painter JS + CSS
257      if (ENABLE_PAINTERJS) {
258        $js .= '<script type="text/javascript" src="' . STATIC_SERVER . 'js/test/tegaki-8psvqAqszI.' . JS_VERSION_TEST . '.js"></script>'
259          . '<link rel="stylesheet" href="' . STATIC_SERVER . 'css/tegaki-8psvqAqszI.' . CSS_VERSION_TEST . '.css">';
260      }
261      
262      // Core JS
263      $js .= '<script type="text/javascript" src="' . STATIC_SERVER . 'js/test/core-8psvqAqszI.' . JS_VERSION_TEST . '.js"></script>';
264    }
265    else {
266      // Main catalog JS
267      $js .= '<script type="text/javascript" src="' . STATIC_SERVER . 'js/catalog.min.' . JS_VERSION_CATALOG . '.js"></script>';
268      
269      // Painter JS + CSS
270      if (ENABLE_PAINTERJS) {
271        $js .= '<script type="text/javascript" src="' . STATIC_SERVER . 'js/tegaki.min.' . JS_VERSION_PAINTER . '.js"></script>'
272          . '<link rel="stylesheet" href="' . STATIC_SERVER . 'css/tegaki.' . CSS_VERSION_PAINTER . '.css">';
273      }
274      
275      // Core JS
276      $js .= '<script type="text/javascript" src="' . STATIC_SERVER . 'js/core.min.' . JS_VERSION_CORE . '.js"></script>';
277    }
278    
279    $css         = STATIC_SERVER . 'css';
280    $cssv        = TEST_BOARD ? CSS_VERSION_TEST : CSS_VERSION_CATALOG;
281    $style_group = style_group();
282  
283    $flags = SHOW_COUNTRY_FLAGS ? '<link rel="stylesheet" type="text/css" href="' . $css . '/flags.' . CSS_VERSION_FLAGS . '.css">' : '';
284  
285    $titlepart = $subtitle = '';
286    
287    if( TITLE_IMAGE_TYPE == 1 ) {
288      $titleimg = rand_from_flatfile( YOTSUBA_DIR, 'title_banners.txt' );
289      $titlepart .= '<div id="bannerCnt" class="title desktop" data-src="' . $titleimg . '"></div>';
290    } elseif( TITLE_IMAGE_TYPE == 2 ) {
291      $titlepart .= '<img class="title" src="' . TITLEIMG . '" onclick="this.src = this.src;">';
292    }
293  
294    if( defined( 'SUBTITLE' ) ) {
295      $subtitle = '<div class="boardSubtitle">' . SUBTITLE . '</div>';
296    }
297    
298    /**
299     * ADS
300     */
301    $topad = '';
302    
303    $bottomad = '';
304    
305    if (defined('AD_CUSTOM_BOTTOM') && AD_CUSTOM_BOTTOM) {
306      $bottomad .= '<div>' . AD_CUSTOM_BOTTOM . '<hr></div>';
307    }/*
308    else if (defined('AD_ABC_BOTTOM_MOBILE') && AD_ABC_BOTTOM_MOBILE) {
309      $bottomad .= '<div class="adg-rects mobile"><div class="adg-m adp-250" data-abc="' . AD_ABC_BOTTOM_MOBILE . '"></div><hr></div>';
310    }
311    else if (defined('AD_BIDGEAR_BOTTOM') && AD_BIDGEAR_BOTTOM)  {
312      $bottomad .= '<div class="adc-resp-bg" data-ad-bg="' . AD_BIDGEAR_BOTTOM . '"></div>';
313    }*/
314    else if (defined('ADS_DANBO') && ADS_DANBO)  {
315      $bottomad .= '<div id="danbo-s-b" class="danbo-slot"></div><div class="adl">[<a target="_blank" href="https://www.4channel.org/advertise">Advertise on 4chan</a>]</div><hr>';
316    }
317  
318    $favicon          = FAVICON;
319    $meta_robots      = META_ROBOTS;
320    $meta_description = META_DESCRIPTION;
321    $meta_keywords    = META_KEYWORDS;
322  
323    $body_class = explode('_', $style_group);
324    $body_class = $body_class[0];
325    $body_class .= ' is_catalog board_' . BOARD_DIR;
326  
327    $canonical = '<link rel="canonical" href="https://boards.4chan.org/'.BOARD_DIR.'/catalog">';
328    
329    $embedearly = EMBEDEARLY;
330    $embedlate = EMBEDLATE;
331    $adembedearly = AD_EMBEDEARLY;
332    
333    $start_thread = S_FORM_THREAD;
334    
335    $jsVersion = TEST_BOARD ? JS_VERSION_TEST : JS_VERSION;
336    $comlen = MAX_COM_CHARS;
337    $maxfs = MAX_KB * 1024;
338    $jsCooldowns = json_encode(array(
339      'thread' => RENZOKU3,
340      'reply' => RENZOKU,
341      'image' => RENZOKU2
342    ));
343    
344    if (defined('CSS_EVENT_NAME') && CSS_EVENT_NAME) {
345      $event_css_html = '<option value="_special">Special</option>';
346      
347      // Christmas 2021
348      if (CSS_EVENT_NAME === 'tomorrow') {
349        $js .= <<<JJS
350  <script src='//s.4cdn.org/js/snow.js'></script>
351  <script>
352    function fc_tomorrow_init() {
353      if (window.matchMedia && window.matchMedia('(min-width: 481px)').matches) {
354        fc_spawn_snow(Math.floor(Math.random() * 50) + 50);
355      }
356    }
357    function fc_tomorrow_cleanup() {
358      fc_remove_snow();
359    }
360  </script>
361  <style>
362  .boardBanner, #delform, .navLinksBot.desktop {
363    border-image-slice: 50 0 50 0;
364    border-image-width: 40px 0px 0px 0px;
365    border-image-outset: 0px 0px 0px 0px;
366    border-image-repeat: repeat repeat;
367    border-image-source: url('https://s.4cdn.org/image/temp/garland.png');
368    border-style: solid;
369    padding-top: 50px;
370  }
371  </style>
372  JJS;
373      }
374      
375      $js .= '<script>var css_event = "' . CSS_EVENT_NAME . '";';
376      
377      if (defined('CSS_EVENT_VERSION')) {
378        $_css_event_version = (int)CSS_EVENT_VERSION;
379      }
380      else {
381        $_css_event_version = 1;
382      }
383      
384      $js .= 'var css_event_v = ' . $_css_event_version . ';';
385      
386      $js .= '</script>';
387    }
388    else {
389      $event_css_html = '';
390    }
391    
392    if (PARTY) {
393      $partyHats = 'var partyHats = "' . PARTY_IMAGE . '";';
394    }
395    else {
396      $partyHats = '';
397    }
398    
399    if (ENABLE_ARCHIVE) {
400      $archive_link =  ' <span class="btn-wrap"><a href="./archive" class="button">' . S_ARCHIVE . '</a></span>';
401    }
402    else {
403      $archive_link = '';
404    }
405    
406    if (TEXT_ONLY) {
407      $text_only = 'var text_only = true;';
408      $body_text_css = ' text_only';
409      $ctrl_css = ' hidden';
410    }
411    else {
412      $text_only = $body_text_css = '';
413      $ctrl_css = '';
414    }
415    
416    $adg_js = 'var _adg = 1;';
417    
418    $postform = '';
419    form( $postform, 0, '', false, true );
420  
421    $cat = <<<HTML
422  <!DOCTYPE html>
423  <html>
424  <head>
425    <meta charset="utf-8">
426    <title>$title - Catalog - 4chan</title>
427    <meta name="robots" content="$meta_robots">
428    <meta name="description" content="$meta_description">
429    <meta name="keywords" content="$meta_keywords">
430    <meta name="viewport" content="width=device-width, initial-scale=1.0">
431    $canonical
432    <link id="mobile-css" rel="stylesheet" href="$css/catalog_mobile.$cssv.css" />
433    $js
434    $flags
435    <script type="text/javascript">$partyHats
436  $text_only
437  $adg_js
438  var jsVersion = $jsVersion;
439  var comlen = $comlen;
440  var maxFilesize = $maxfs;
441  var cooldowns = $jsCooldowns;
442  var catalog = $catjson;
443  var style_group = "$style_group";
444  var check_for_block = true;
445  var fourcat = new FC();
446  fourcat.applyCSS(null, "$style_group", $cssv);
447  </script>
448    <link rel="shortcut icon" href="$favicon" type="image/x-icon">
449  $embedearly 
450  $adembedearly
451  </head>
452  <body class="$body_class$body_text_css">
453  <div id="topnav" class="boardnav">$nav</div>
454  <div class="boardBanner">
455    $titlepart
456    <div class="boardTitle">$title</div>
457    $subtitle
458  </div>
459  <hr class="abovePostForm">
460  $topad
461  <div id="togglePostForm" class="mobilebtn mobile"><span class="btn-wrap"><span id="togglePostFormLinkMobile" class="button">$start_thread</span></span></div>
462  $postform
463  <hr>
464  <div id="content">
465  <div id="ctrl">
466    <div id="info">
467      <span class="navLinks mobilebtn"><span class="btn-wrap"><a href="./" class="button">Return</a></span>$archive_link <span id="tobottom" class="btn-wrap"><span class="button">Bottom</span></span> <span class="btn-wrap"><a id="refresh-btn" href="./catalog" class="button">Refresh</a></span></span><span id="filtered-label"> &mdash; Filtered threads: <span id="filtered-count"></span></span><span id="hidden-label"> &mdash; Hidden threads: <span id="hidden-count"></span> <span class="btn-wrap"><a id="filters-clear-hidden" href="">Show</a></span></span><span id="search-label"> &mdash; Search results for: <span id="search-term"></span></span>
468    </div>
469    <hr class="mobile">
470    <div id="settings" class="mobilebtn">
471      <span class="ctrl-wrap">Sort By:
472      <select id="order-ctrl" size="1">
473        <option value="alt">Bump order</option>
474        <option value="absdate">Last reply</option>
475        <option value="date">Creation date</option>
476        <option value="r">Reply count</option>
477      </select></span>
478      <span class="ctrl-wrap$ctrl_css">Image Size:
479      <select id="size-ctrl" size="1">
480        <option value="small">Small</option>
481        <option value="large">Large</option>
482      </select></span>
483      <span class="ctrl-wrap$ctrl_css">Show OP Comment:
484      <select id="teaser-ctrl" size="1">
485        <option value="off">Off</option>
486        <option value="on">On</option>
487      </select></span>
488      <span class="btn-wrap"><span id="filters-ctrl" class="button">Filters</span></span>
489      <span class="btn-wrap"><span id="qf-ctrl" class="button">Search</span></span>
490      <span style="display:none" id="qf-cnt">
491        <input type="text" id="qf-box" name="qf-box"><span id="qf-clear" class="button">&#x2716;</span>
492      </span>
493    </div>
494    <div class="clear"></div>
495  </div><hr>
496  <div id="threads"></div>
497  <hr>
498  <span class="navLinks navLinksBottom mobilebtn"><span class="btn-wrap"><a href="./" class="button">Return</a></span>$archive_link <span id="totop" class="btn-wrap"><span class="button">Top</span></span> <span class="btn-wrap"><a href="./catalog" class="button">Refresh</a></span></span><span id="filtered-label-bottom"> &mdash; Filtered threads: <span id="filtered-count-bottom"></span></span><span id="hidden-label-bottom"> &mdash; Hidden threads: <span id="hidden-count-bottom"></span> <span class="btn-wrap"><a id="filters-clear-hidden-bottom" href="">Show</a></span></span><span id="search-label-bottom"> &mdash; Search results for: <span id="search-term-bottom"></span></span>
499  <hr>
500  $bottomad
501  <div id="styleSwitcher">Style: <select id="styleSelector" size="1">
502    <option value="Yotsuba New">Yotsuba</option>
503    <option value="Yotsuba B New">Yotsuba‌ B</option>
504    <option value="Futaba New">Futaba</option>
505    <option value="Burichan New">Burichan</option>
506    <option value="Tomorrow">Tomorrow</option>
507    <option value="Photon">Photon</option>$event_css_html
508  </select></div>
509  </div>
510  $foot
511  <div id="backdrop" class="hidden"></div>
512  <noscript>
513    <style scoped type="text/css">
514      #nojs {
515        background-color: #000;
516        text-align: center;
517        position: fixed;
518        top: 0px;
519        left: 0px;
520        width: 100%;
521        height: 100%;
522      }
523      #nojs > span {
524        color: #000;
525        background-color: #8C92AC;
526        padding: 5px;
527        position: relative;
528        top: 35%;
529        font-size: 22px;
530      }
531    </style>
532    <div id="nojs">
533      <span>Your web browser must have JavaScript enabled in order for this site to display correctly.</span>
534    </div>
535  </noscript>
536  <menu type="context" id="ctxmenu-main"></menu>
537  <menu type="context" id="ctxmenu-thread"></menu>
538  <script type="text/javascript">
539    document.addEventListener('DOMContentLoaded', function() {
540      initAnalytics();
541      fourcat.init();
542      fourcat.loadCatalog(catalog);
543      $.on($.id('tobottom'), 'click', function() { window.scrollTo(0, document.documentElement.scrollHeight); });
544      $.on($.id('totop'), 'click', function() { window.scrollTo(0, 0); });
545    }, false);
546  </script>
547  $embedlate</body>
548  </html>
549  HTML;
550  
551    return $cat;
552  
553  }