Issues (380)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  Header Injection
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

class/TopicRenderer.php (1 issue)

1
<?php
2
3
namespace XoopsModules\Newbb;
4
5
/**
6
 * NewBB 5.0x,  the forum module for XOOPS project
7
 *
8
 * @copyright      XOOPS Project (https://xoops.org)
9
 * @license        GNU GPL 2 or later (https://www.gnu.org/licenses/gpl-2.0.html)
10
 * @author         Taiwen Jiang (phppp or D.J.) <[email protected]>
11
 * @since          4.00
12
 * @package        module::newbb
13
 */
14
15
use Xmf\Request;
16
use XoopsModules\Newbb;
17
18
19
20
/**
21
 * Topic Renderer
22
 *
23
 * @author    D.J. (phppp)
24
 * @copyright copyright &copy; Xoops Project
25
 * @package   module::newbb
26
 */
27
class TopicRenderer
28
{
29
    public $vars = [];
30
31
    /**
32
     * reference to moduleConfig
33
     */
34
    public $config;
35
36
    /**
37
     * Current user has no access to current page
38
     */
39
    private $noperm = false;
40
41
    /**
42
     * For multiple forums
43
     */
44
    public $is_multiple = false;
45
46
    /**
47
     * force to parse vars (run against static vars) irmtfan
48
     */
49
    public $force = false;
50
51
    /**
52
     * Vistitor's level: 0 - anonymous; 1 - user; 2 - moderator or admin
53
     */
54
    public $userlevel = 0;
55
56
    public $query = [];
57
58
    /**
59
     * reference to an object handler
60
     */
61
    private $handler;
62
63
    /**
64
     * Requested page
65
     */
66
    private $page = 'list.topic.php';
67
68
    /**
69
     * query variables
70
     */
71
    private $args = ['forum', 'uid', 'lastposter', 'type', 'status', 'mode', 'sort', 'order', 'start', 'since'];
72
73
    /**
74
     * Constructor
75
     */
76
    //    public function TopicRenderer()
77
    public function __construct()
78
    {
79
        $this->handler = \XoopsModules\Newbb\Helper::getInstance()->getHandler('Topic');
80
    }
81
82
    /**
83
     * Access the only instance of this class
84
     * @return TopicRenderer
85
     */
86
    public static function getInstance()
87
    {
88
        static $instance;
89
        if (null === $instance) {
90
            $instance = new static();
91
        }
92
93
        return $instance;
94
    }
95
96
    public function init()
97
    {
98
        $this->noperm = false;
99
        $this->query  = [];
100
    }
101
102
    /**
103
     * @param $var
104
     * @param $val
105
     * @return array|int|string
106
     */
107
    public function setVar($var, $val)
108
    {
109
        switch ($var) {
110
            case 'forum':
111
                if (\is_numeric($val)) {
112
                    $val = (int)$val;
113
                    // START irmtfan - if the forum is array
114
                } elseif (\is_array($val)) {
115
                    $val = \implode('|', $val);
116
                    //} elseif (!empty($val)) {
117
                    //    $val = implode("|", array_map("intval", explode(", ", $val)));
118
                }
119
                // END irmtfan - if the forum is array
120
                break;
121
            case 'type':
122
            case 'mode':
123
            case 'order':
124
            case 'start':
125
            case 'since':
126
                $val = (int)$val;
127
                break;
128
            case 'uid': // irmtfan add multi topic poster
129
            case 'lastposter': // irmtfan add multi lastposter
130
                break;
131
            case 'status':
132
                // START irmtfan to accept multiple status
133
                $val = \is_array($val) ? $val : [$val];
134
                $val = \implode(',', $val);
135
                //$val = (in_array($val, array_keys($this->getStatus( $this->userlevel ))) ) ? $val : "all"; //irmtfan no need to check if status is empty or not
136
                //if ($val === "all" && !$this->is_multiple) $val = ""; irmtfan commented because it is done in sort
137
                // END irmtfan to accept multiple status
138
                break;
139
            default:
140
                break;
141
        }
142
143
        return $val;
144
    }
145
146
    /**
147
     * @param array $vars
148
     */
149
    public function setVars(array $vars = [])
150
    {
151
        $this->init();
152
153
        foreach ($vars as $var => $val) {
154
            if (!\in_array($var, $this->args)) {
155
                continue;
156
            }
157
        }
158
        $this->parseVars();
159
    }
160
161
    /**
162
     * @param string $hash request hash, i.e. get, post
163
     */
164
    public function setVarsFromRequest($hash = 'get')
165
    {
166
        $this->init();
167
168
        foreach ($this->args as $var) {
169
            if (Request::hasVar($var, $hash)) {
170
                continue;
171
            }
172
            switch ($var) {
173
                case 'forum':
174
                case 'status':
175
                    $this->setVar($var, Request::getArray($var, [], $hash));
176
                    break;
177
                default:
178
                    $this->setVar($var, Request::getString($var, '', $hash));
179
                    break;
180
            }
181
        }
182
        $this->parseVars();
183
    }
184
185
    /**
186
     * @param null $status
187
     */
188
    public function myParseStatus($status = null)
189
    {
190
        switch ($status) {
191
            case 'digest':
192
                $this->query['where'][] = 't.topic_digest = 1';
193
                break;
194
            case 'undigest':
195
                $this->query['where'][] = 't.topic_digest = 0';
196
                break;
197
            case 'sticky':
198
                $this->query['where'][] = 't.topic_sticky = 1';
199
                break;
200
            case 'unsticky':
201
                $this->query['where'][] = 't.topic_sticky = 0';
202
                break;
203
            case 'lock':
204
                $this->query['where'][] = 't.topic_status = 1';
205
                break;
206
            case 'unlock':
207
                $this->query['where'][] = 't.topic_status = 0';
208
                break;
209
            case 'poll':
210
                $this->query['where'][] = 't.topic_haspoll = 1';
211
                break;
212
            case 'unpoll':
213
                $this->query['where'][] = 't.topic_haspoll = 0';
214
                break;
215
            case 'voted':
216
                $this->query['where'][] = 't.votes > 0';
217
                break;
218
            case 'unvoted':
219
                $this->query['where'][] = 't.votes < 1';
220
                break;
221
            case 'replied':
222
                $this->query['where'][] = 't.topic_replies > 0';
223
                break;
224
            case 'unreplied':
225
                $this->query['where'][] = 't.topic_replies < 1';
226
                break;
227
            case 'viewed':
228
                $this->query['where'][] = 't.topic_views > 0';
229
                break;
230
            case 'unviewed':
231
                $this->query['where'][] = 't.topic_views < 1';
232
                break;
233
            case 'read':
234
                // Skip
235
                if (empty($this->config['read_mode'])) {
236
                    // Use database
237
                } elseif (2 == $this->config['read_mode']) {
238
                    // START irmtfan use read_uid to find the unread posts when the user is logged in
239
                    $read_uid = \is_object($GLOBALS['xoopsUser']) ? $GLOBALS['xoopsUser']->getVar('uid') : 0;
240
                    if (!empty($read_uid)) {
241
                        $this->query['join'][]  = 'LEFT JOIN ' . $this->handler->db->prefix('newbb_reads_topic') . ' AS r ON r.read_item = t.topic_id AND r.uid = ' . $read_uid . ' ';
242
                        $this->query['where'][] = 'r.post_id = t.topic_last_post_id';
243
                    }
244
245
                    // END irmtfan change criteria to get from uid p.uid = last post submit user id
246
                    // User cookie
247
                } elseif (1 == $this->config['read_mode']) {
248
                    // START irmtfan fix read_mode = 1 bugs - for all users (member and anon)
249
                    $startdate = !empty($this->vars['since']) ? (\time() - \newbbGetSinceTime($this->vars['since'])) : 0;
250
                    $lastvisit = \max($GLOBALS['last_visit'], $startdate);
251
                    if ($lastvisit) {
252
                        $readmode1query = '';
253
                        if ($lastvisit > $startdate) {
254
                            $readmode1query = 'p.post_time < ' . $lastvisit;
255
                        }
256
                        $topics         = [];
257
                        $topic_lastread = \newbbGetCookie('LT', true);
258
                        if (\count($topic_lastread) > 0) {
259
                            foreach ($topic_lastread as $id => $time) {
260
                                if ($time > $lastvisit) {
261
                                    $topics[] = $id;
262
                                }
263
                            }
264
                        }
265
                        if (\count($topics) > 0) {
266
                            $topicquery = ' t.topic_id IN (' . \implode(',', $topics) . ')';
267
                            // because it should be OR
268
                            $readmode1query = !empty($readmode1query) ? '(' . $readmode1query . ' OR ' . $topicquery . ')' : $topicquery;
269
                        }
270
                        $this->query['where'][] = $readmode1query;
271
                    }
272
                    // END irmtfan fix read_mode = 1 bugs - for all users (member and anon)
273
                }
274
                break;
275
            case 'unread':
276
                // Skip
277
                if (empty($this->config['read_mode'])) {
278
                    // Use database
279
                } elseif (2 == $this->config['read_mode']) {
280
                    // START irmtfan use read_uid to find the unread posts when the user is logged in
281
                    $read_uid = \is_object($GLOBALS['xoopsUser']) ? $GLOBALS['xoopsUser']->getVar('uid') : 0;
282
                    if (!empty($read_uid)) {
283
                        $this->query['join'][]  = 'LEFT JOIN ' . $this->handler->db->prefix('newbb_reads_topic') . ' AS r ON r.read_item = t.topic_id AND r.uid = ' . $read_uid . ' ';
284
                        $this->query['where'][] = '(r.read_id IS NULL OR r.post_id < t.topic_last_post_id)';
285
                    }
286
287
                    // END irmtfan change criteria to get from uid p.uid = last post submit user id
288
                    // User cookie
289
                } elseif (1 == $this->config['read_mode']) {
290
                    // START irmtfan fix read_mode = 1 bugs - for all users (member and anon)
291
                    $startdate = !empty($this->vars['since']) ? (\time() - \newbbGetSinceTime($this->vars['since'])) : 0;
292
                    $lastvisit = \max($GLOBALS['last_visit'], $startdate);
293
                    if ($lastvisit) {
294
                        if ($lastvisit > $startdate) {
295
                            $this->query['where'][] = 'p.post_time > ' . $lastvisit;
296
                        }
297
                        $topics         = [];
298
                        $topic_lastread = \newbbGetCookie('LT', true);
299
                        if (\count($topic_lastread) > 0) {
300
                            foreach ($topic_lastread as $id => $time) {
301
                                if ($time > $lastvisit) {
302
                                    $topics[] = $id;
303
                                }
304
                            }
305
                        }
306
                        if (\count($topics) > 0) {
307
                            $this->query['where'][] = ' t.topic_id NOT IN (' . \implode(',', $topics) . ')';
308
                        }
309
                    }
310
                    // END irmtfan fix read_mode = 1 bugs - for all users (member and anon)
311
                }
312
                break;
313
            case 'pending':
314
                if ($this->userlevel < 2) {
315
                    $this->noperm = true;
316
                } else {
317
                    $this->query['where'][] = 't.approved = 0';
318
                }
319
                break;
320
            case 'deleted':
321
                if ($this->userlevel < 2) {
322
                    $this->noperm = true;
323
                } else {
324
                    $this->query['where'][] = 't.approved = -1';
325
                }
326
                break;
327
            case 'all': // For viewall.php; do not display sticky topics at first
328
            case 'active': // same as 'all'
329
                $this->query['where'][] = 't.approved = 1';
330
                break;
331
            default: // irmtfan do nothing
332
                break;
333
        }
334
    }
335
336
    /**
337
     * @param $var
338
     * @param $val
339
     */
340
    public function parseVar($var, $val)
341
    {
342
        switch ($var) {
343
            case 'forum':
344
                /** @var Newbb\ForumHandler $forumHandler */ $forumHandler = \XoopsModules\Newbb\Helper::getInstance()->getHandler('Forum');
345
                // START irmtfan - get forum Ids by values. parse positive values to forum IDs and negative values to category IDs. value=0 => all valid forums
346
                // Get accessible forums
347
                $accessForums = $forumHandler->getIdsByValues(\array_map('\intval', @\explode('|', $val)));
348
                // Filter specified forums if any
349
                //if (!empty($val) && $_forums = @explode('|', $val)) {
350
                //$accessForums = array_intersect($accessForums, array_map('intval', $_forums));
351
                //}
352
                $this->vars['forum'] = $this->setVar('forum', $accessForums);
353
                // END irmtfan - get forum Ids by values. parse positive values to forum IDs and negative values to category IDs. value=0 => all valid forums
354
355
                if (empty($accessForums)) {
356
                    $this->noperm = true;
357
                    // irmtfan - it just return return the forum_id only when the forum_id is the first allowed forum - no need for this code implode is enough removed.
358
                    //} elseif (count($accessForums) === 1) {
359
                    //$this->query["where"][] = "t.forum_id = " . $accessForums[0];
360
                } else {
361
                    $this->query['where'][] = 't.forum_id IN ( ' . \implode(', ', $accessForums) . ' )';
362
                }
363
                break;
364
            case 'uid': // irmtfan add multi topic poster
365
                if (-1 !== $val) {
366
                    $val                    = \implode(',', \array_map('\intval', \explode(',', $val)));
367
                    $this->query['where'][] = 't.topic_poster IN ( ' . $val . ' )';
368
                }
369
                break;
370
            case 'lastposter': // irmtfan add multi lastposter
371
                if (-1 !== $val) {
372
                    $val                    = \implode(',', \array_map('\intval', \explode(',', $val)));
373
                    $this->query['where'][] = 'p.uid IN ( ' . $val . ' )';
374
                }
375
                break;
376
            case 'since':
377
                if (!empty($val)) {
378
                    // START irmtfan if unread && read_mode = 1 and last_visit > startdate do not add where query | to accept multiple status
379
                    $startdate = \time() - \newbbGetSinceTime($val);
380
                    if (\in_array('unread', \explode(',', $this->vars['status'])) && 1 == $this->config['read_mode']
381
                        && $GLOBALS['last_visit'] > $startdate) {
382
                        break;
383
                    }
384
                    // irmtfan digest_time | to accept multiple status
385
                    if (\in_array('digest', \explode(',', $this->vars['status']))) {
386
                        $this->query['where'][] = 't.digest_time > ' . $startdate;
387
                    }
388
                    // irmtfan - should be >= instead of =
389
                    $this->query['where'][] = 'p.post_time >= ' . $startdate;
390
                    // END irmtfan if unread && read_mode = 1 and last_visit > startdate do not add where query
391
                }
392
                break;
393
            case 'type':
394
                if (!empty($val)) {
395
                    $this->query['where'][] = 't.type_id = ' . $val;
396
                }
397
                break;
398
            case 'status':
399
                // START irmtfan to accept multiple status
400
                $val = \explode(',', $val);
401
                // irmtfan - add 'all' to always parse t.approved = 1
402
                if (0 === \count(\array_intersect($val, ['all', 'active', 'pending', 'deleted']))) {
403
                    $val[] = 'all';
404
                }
405
                foreach ($val as $key => $status) {
406
                    $this->myParseStatus($status);
407
                }
408
                // END irmtfan to accept multiple status
409
                break;
410
            case 'sort':
411
                $sort = $this->getSort($val, 'sort');
412
                if ($sort) {
413
                    $this->query['sort'][] = $sort . (empty($this->vars['order']) ? ' DESC' : ' ASC');
414
                } else { // irmtfan if sort is not in the list
415
                    $this->query['sort'][] = 't.topic_last_post_id' . (empty($this->vars['order']) ? ' DESC' : ' ASC');
416
                }
417
                break;
418
            default:
419
                break;
420
        }
421
    }
422
423
    /**
424
     * @return bool
425
     */
426
    public function parseVars()
427
    {
428
        static $parsed;
429
        // irmtfan - force to parse vars (run against static vars)
430
        if (null !== $parsed && !$this->force) {
431
            return true;
432
        }
433
434
        if (!isset($this->vars['forum'])) {
435
            $this->vars['forum'] = null;
436
        }
437
        //irmtfan parse status for rendering topic correctly - if empty($_GET(status)) it will show all topics include deleted and pendings. 'all' instead of all
438
        if (!isset($this->vars['status'])) {
439
            $this->vars['status'] = 'all';
440
        }
441
        // irmtfan if sort is not set or is empty get a default sort- if empty($_GET(sort)) | if sort=null eg: /list.topic.php?sort=
442
        if (empty($this->vars['sort'])) {
443
            $this->vars['sort'] = 'lastpost';
444
        } // use lastpost instead of sticky
445
446
        foreach ($this->vars as $var => $val) {
447
            $this->parseVar($var, $val);
448
            if (empty($val)) {
449
                unset($this->vars[$var]);
450
            }
451
        }
452
        $parsed = true;
453
454
        return true;
455
    }
456
457
    /**
458
     * @param null $header
459
     * @param null $var
460
     * @return array|null
461
     */
462
    public function getSort($header = null, $var = null)
463
    {
464
        $headers = [
465
            'topic'           => [
466
                'title' => \_MD_NEWBB_TOPICS,
467
                'sort'  => 't.topic_title',
468
            ],
469
            'forum'           => [
470
                'title' => \_MD_NEWBB_FORUM,
471
                'sort'  => 't.forum_id',
472
            ],
473
            'poster'          => [
474
                'title' => \_MD_NEWBB_TOPICPOSTER, /*irmtfan _MD_NEWBB_POSTER to _MD_NEWBB_TOPICPOSTER*/
475
                'sort'  => 't.topic_poster',
476
            ],
477
            'replies'         => [
478
                'title' => \_MD_NEWBB_REPLIES,
479
                'sort'  => 't.topic_replies',
480
            ],
481
            'views'           => [
482
                'title' => \_MD_NEWBB_VIEWS,
483
                'sort'  => 't.topic_views',
484
            ],
485
            'lastpost'        => [ // irmtfan show topic_page_jump_icon smarty
486
                                   'title' => \_MD_NEWBB_LASTPOST,
487
                                   /*irmtfan _MD_NEWBB_DATE to _MD_NEWBB_LASTPOSTTIME again change to _MD_LASTPOST*/
488
                                   'sort'  => 't.topic_last_post_id',
489
            ],
490
            // START irmtfan add more sorts
491
            'lastposttime'    => [ // irmtfan same as lastpost
492
                                   'title' => \_MD_NEWBB_LASTPOSTTIME,
493
                                   'sort'  => 't.topic_last_post_id',
494
            ],
495
            'lastposter'      => [ // irmtfan
496
                                   'title' => \_MD_NEWBB_POSTER,
497
                                   'sort'  => 'p.uid', // poster uid
498
            ],
499
            'lastpostmsgicon' => [ // irmtfan
500
                                   'title' => \_MD_NEWBB_MESSAGEICON,
501
                                   'sort'  => 'p.icon', // post message icon
502
            ],
503
            'ratings'         => [
504
                'title' => \_MD_NEWBB_RATINGS,
505
                'sort'  => 't.rating', // irmtfan t.topic_rating to t.rating
506
            ],
507
            'votes'           => [
508
                'title' => \_MD_NEWBB_VOTES,
509
                'sort'  => 't.votes',
510
            ],
511
            'publish'         => [
512
                'title' => \_MD_NEWBB_TOPICTIME,
513
                'sort'  => 't.topic_id',
514
            ],
515
            'digest'          => [
516
                'title' => \_MD_NEWBB_DIGEST,
517
                'sort'  => 't.digest_time',
518
            ],
519
            'sticky'          => [
520
                'title' => \_MD_NEWBB_STICKY,
521
                'sort'  => 't.topic_sticky',
522
            ],
523
            'lock'            => [
524
                'title' => \_MD_NEWBB_LOCK,
525
                'sort'  => 't.topic_status',
526
            ],
527
            'poll'            => [
528
                'title' => \_MD_NEWBB_POLL_POLL,
529
                'sort'  => 't.poll_id',
530
            ],
531
        ];
532
        $types   = $this->getTypes();
533
        if (!empty($types)) {
534
            $headers['type'] = [
535
                'title' => _MD_NEWBB_TYPE,
536
                'sort'  => 't.type_id',
537
            ];
538
        }
539
        if (2 == $this->userlevel) {
540
            $headers['approve'] = [
541
                'title' => \_MD_NEWBB_APPROVE,
542
                'sort'  => 't.approved',
543
            ];
544
        }
545
        // END irmtfan add more sorts
546
        if (empty($header) && empty($var)) {
547
            return $headers;
548
        }
549
        if (!empty($var) && !empty($header)) {
550
            return @$headers[$header][$var];
551
        }
552
        if (empty($var)) {
553
            return @$headers[$header];
554
        }
555
        $ret = null;
556
        foreach (\array_keys($headers) as $key) {
557
            $ret[$key] = @$headers[$key][$var];
558
        }
559
560
        return $ret;
561
    }
562
563
    // START irmtfan add Display topic headers function
564
565
    /**
566
     * @param null $header
567
     * @return array
568
     */
569
    public function getHeader($header = null)
570
    {
571
        $headersSort = $this->getSort('', 'title');
572
        // additional headers - important: those cannot be in sort anyway
573
        $headers = \array_merge(
574
            $headersSort,
575
            [
576
                'attachment' => \_MD_NEWBB_TOPICSHASATT, // show attachment smarty
577
                'read'       => \_MD_NEWBB_MARK_UNREAD . '|' . \_MD_NEWBB_MARK_READ, // read/unread show topic_folder smarty
578
                'pagenav'    => \_MD_NEWBB_PAGENAV_DISPLAY, // show topic_page_jump smarty - sort by topic_replies?
579
            ]
580
        );
581
582
        return $this->getFromKeys($headers, $header);
583
    }
584
585
    // END irmtfan add Display topic headers function
586
587
    /**
588
     * @param null $type
589
     * @param null $status
590
     * @return array
591
     */
592
    public function getStatus($type = null, $status = null)
593
    {
594
        $links       = [
595
            //""            => "", /* irmtfan remove empty array */
596
            'all'       => _ALL,
597
            'digest'    => \_MD_NEWBB_DIGEST,
598
            'undigest'  => \_MD_NEWBB_UNDIGEST, // irmtfan add
599
            'sticky'    => \_MD_NEWBB_STICKY, // irmtfan add
600
            'unsticky'  => \_MD_NEWBB_UNSTICKY, // irmtfan add
601
            'lock'      => \_MD_NEWBB_LOCK, // irmtfan add
602
            'unlock'    => \_MD_NEWBB_UNLOCK, // irmtfan add
603
            'poll'      => \_MD_NEWBB_TOPICHASPOLL, // irmtfan add
604
            'unpoll'    => \_MD_NEWBB_TOPICHASNOTPOLL, // irmtfan add
605
            'voted'     => \_MD_NEWBB_VOTED, // irmtfan add
606
            'unvoted'   => \_MD_NEWBB_UNVOTED, // irmtfan add
607
            'viewed'    => \_MD_NEWBB_VIEWED, // irmtfan add
608
            'unviewed'  => \_MD_NEWBB_UNVIEWED, // irmtfan add
609
            'replied'   => \_MD_NEWBB_REPLIED, // irmtfan add
610
            'unreplied' => \_MD_NEWBB_UNREPLIED,
611
            'read'      => \_MD_NEWBB_READ, // irmtfan add
612
            'unread'    => \_MD_NEWBB_UNREAD,
613
        ];
614
        $links_admin = [
615
            'active'  => \_MD_NEWBB_TYPE_ADMIN,
616
            'pending' => \_MD_NEWBB_TYPE_PENDING,
617
            'deleted' => \_MD_NEWBB_TYPE_DELETED,
618
        ];
619
620
        // all status, for admin
621
        if ($type > 1) {
622
            $links = \array_merge($links, $links_admin); // irmtfan to accept multiple status
623
        }
624
625
        return $this->getFromKeys($links, $status); // irmtfan to accept multiple status
626
    }
627
628
    /**
629
     * @param \Smarty $xoopsTpl
630
     * @throws \RuntimeException
631
     */
632
    public function buildSelection(\Smarty $xoopsTpl)
633
    {
634
        $selection         = ['action' => $this->page];
635
        $selection['vars'] = $this->vars;
636
        require_once \dirname(__DIR__) . '/include/functions.forum.php';
637
        $forum_selected     = empty($this->vars['forum']) ? null : \explode('|', @$this->vars['forum']);
638
        $selection['forum'] = '<select name="forum[]" multiple="multiple">';
639
        $selection['forum'] .= '<option value="0">' . \_MD_NEWBB_ALL . '</option>';
640
        $selection['forum'] .= \newbbForumSelectBox($forum_selected);
641
        $selection['forum'] .= '</select>';
642
643
        $sort_selected     = $this->vars['sort'];
644
        $sorts             = $this->getSort('', 'title');
645
        $selection['sort'] = "<select name='sort'>";
646
        if (!\is_array($sorts)) {
647
            throw new \RuntimeException('$sorts must be an array.');
648
        }
649
        foreach ($sorts as $sort => $title) {
650
            $selection['sort'] .= "<option value='" . $sort . "' " . (($sort == $sort_selected) ? " selected='selected'" : '') . '>' . $title . '</option>';
651
        }
652
        $selection['sort'] .= '</select>';
653
654
        $selection['order'] = "<select name='order'>";
655
        $selection['order'] .= "<option value='0' " . (empty($this->vars['order']) ? " selected='selected'" : '') . '>' . _DESCENDING . '</option>';
656
        $selection['order'] .= "<option value='1' " . (!empty($this->vars['order']) ? " selected='selected'" : '') . '>' . _ASCENDING . '</option>';
657
        $selection['order'] .= '</select>';
658
659
        $since              = isset($this->vars['since']) ? $this->vars['since'] : $this->config['since_default'];
660
        $selection['since'] = \newbbSinceSelectBox($since);
661
662
        $xoopsTpl->assign_by_ref('selection', $selection);
663
    }
664
665
    /**
666
     * @param \Smarty $xoopsTpl
667
     */
668
    public function buildSearch(\Smarty $xoopsTpl)
669
    {
670
        $search             = [];
671
        $search['forum']    = @$this->vars['forum'];
672
        $search['since']    = @$this->vars['since'];
673
        $search['searchin'] = 'both';
674
675
        $xoopsTpl->assign_by_ref('search', $search);
676
    }
677
678
    /**
679
     * @param \Smarty $xoopsTpl
680
     * @throws \RuntimeException
681
     */
682
    public function buildHeaders(\Smarty $xoopsTpl)
683
    {
684
        $args = [];
685
        foreach ($this->vars as $var => $val) {
686
            if ('sort' === $var || 'order' === $var) {
687
                continue;
688
            }
689
            $args[] = "{$var}={$val}";
690
        }
691
692
        $headers = $this->getSort('', 'title');
693
        if (!\is_array($headers)) {
694
            throw new \RuntimeException('$headers must be an array.');
695
        }
696
        foreach ($headers as $header => $title) {
697
            $_args = ["sort={$header}"];
698
            if (@$this->vars['sort'] == $header) {
699
                $_args[] = 'order=' . ((@$this->vars['order'] + 1) % 2);
700
            }
701
            $headers_data[$header]['title'] = $title;
702
            $headers_data[$header]['link']  = $this->page . '?' . \implode('&amp;', \array_merge($args, $_args));
703
        }
704
        $xoopsTpl->assign_by_ref('headers', $headers_data);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $headers_data seems to be defined by a foreach iteration on line 696. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
705
    }
706
707
    /**
708
     * @param \Smarty $xoopsTpl
709
     */
710
    public function buildFilters(\Smarty $xoopsTpl)
711
    {
712
        $args = [];
713
        foreach ($this->vars as $var => $val) {
714
            if ('status' === $var) {
715
                continue;
716
            }
717
            $args[] = "{$var}={$val}";
718
        }
719
720
        $links = $this->getStatus($this->userlevel);
721
722
        $status = [];
723
        foreach ($links as $link => $title) {
724
            $_args                  = ["status={$link}"];
725
            $status[$link]['title'] = $title;
726
            $status[$link]['link']  = $this->page . '?' . \implode('&amp;', \array_merge($args, $_args));
727
        }
728
        $xoopsTpl->assign_by_ref('filters', $status);
729
    }
730
731
    /**
732
     * @param null $type_id
733
     * @return mixed
734
     */
735
    public function getTypes($type_id = null)
736
    {
737
        static $types;
738
        if (!isset($types)) {
739
            /** @var Newbb\TypeHandler $typeHandler */
740
            $typeHandler = \XoopsModules\Newbb\Helper::getInstance()->getHandler('Type');
741
742
            $types = $typeHandler->getByForum(\explode('|', @$this->vars['forum']));
743
        }
744
745
        if (empty($type_id)) {
746
            return $types;
747
        }
748
749
        return @$types[$type_id];
750
    }
751
752
    /**
753
     * @param \Smarty $xoopsTpl
754
     * @return bool
755
     */
756
    public function buildTypes(\Smarty $xoopsTpl)
757
    {
758
        $status = [];
759
        if (!$types = $this->getTypes()) {
760
            return true;
761
        }
762
763
        $args = [];
764
        foreach ($this->vars as $var => $val) {
765
            if ('type' === $var) {
766
                continue;
767
            }
768
            $args[] = "{$var}={$val}";
769
        }
770
771
        foreach ($types as $id => $type) {
772
            $_args                = ["type={$id}"];
773
            $status[$id]['title'] = $type['type_name'];
774
            $status[$id]['link']  = $this->page . '?' . \implode('&amp;', \array_merge($args, $_args));
775
        }
776
        $xoopsTpl->assign_by_ref('types', $status);
777
    }
778
779
    /**
780
     * @param \Smarty $xoopsTpl
781
     * @return bool
782
     */
783
    public function buildCurrent(\Smarty $xoopsTpl)
784
    {
785
        if (empty($this->vars['status']) && !$this->is_multiple) {
786
            return true;
787
        }
788
789
        $args = [];
790
        foreach ($this->vars as $var => $val) {
791
            $args[] = "{$var}={$val}";
792
        }
793
794
        $status          = [];
795
        $status['title'] = \implode(',', $this->getStatus($this->userlevel, $this->vars['status'])); // irmtfan to accept multiple status
796
        //$status['link'] = $this->page.(empty($this->vars['status']) ? '' : '?status='.$this->vars['status']);
797
        $status['link'] = $this->page . (empty($args) ? '' : '?' . \implode('&amp;', $args));
798
799
        $xoopsTpl->assign_by_ref('current', $status);
800
    }
801
802
    /**
803
     * @param \Smarty $xoopsTpl
804
     */
805
    public function buildPagenav(\Smarty $xoopsTpl)
806
    {
807
        $count_topic = $this->getCount();
808
        if ($count_topic > $this->config['topics_per_page']) {
809
            $args = [];
810
            foreach ($this->vars as $var => $val) {
811
                if ('start' === $var) {
812
                    continue;
813
                }
814
                $args[] = "{$var}={$val}";
815
            }
816
            require_once $GLOBALS['xoops']->path('class/pagenav.php');
817
            $nav = new \XoopsPageNav($count_topic, $this->config['topics_per_page'], @$this->vars['start'], 'start', \implode('&amp;', $args));
818
            if (isset($GLOBALS['xoopsModuleConfig']['do_rewrite'])) {
819
                $nav->url = \formatURL(Request::getString('SERVER_NAME', '', 'SERVER')) . ' /' . $nav->url;
820
            }
821
            if ('select' === $this->config['pagenav_display']) {
822
                $navi = $nav->renderSelect();
823
            } elseif ('image' === $this->config['pagenav_display']) {
824
                $navi = $nav->renderImageNav(4);
825
            } else {
826
                $navi = $nav->renderNav(4);
827
            }
828
            $xoopsTpl->assign('pagenav', $navi);
829
        } else {
830
            $xoopsTpl->assign('pagenav', '');
831
        }
832
    }
833
834
    /**
835
     * @return int
836
     */
837
    public function getCount()
838
    {
839
        if ($this->noperm) {
840
            return 0;
841
        }
842
843
        $selects = [];
844
        $froms   = [];
845
        $joins   = [];
846
        $wheres  = [];
847
848
        // topic fields
849
        $selects[] = 'COUNT(*)';
850
851
        $froms[]  = $this->handler->db->prefix('newbb_topics') . ' AS t ';
852
        $joins[]  = 'LEFT JOIN ' . $this->handler->db->prefix('newbb_posts') . ' AS p ON p.post_id = t.topic_last_post_id';
853
        $wheres[] = '1 = 1';
854
855
        $sql = '    SELECT ' . \implode(', ', $selects) . '     FROM ' . \implode(', ', $froms) . '        ' . \implode(' ', $joins) . (!empty($this->query['join']) ? '        ' . \implode(' ', $this->query['join']) : '') . // irmtfan bug fix: Undefined index: join when post_excerpt = 0
856
               '     WHERE ' . \implode(' AND ', $wheres) . '        AND ' . @\implode(' AND ', @$this->query['where']);
857
858
        if (!$result = $this->handler->db->query($sql)) {
859
            return 0;
860
        }
861
        [$count] = $this->handler->db->fetchRow($result);
862
863
        return $count;
864
    }
865
866
    /**
867
     * @param \Smarty $xoopsTpl
868
     * @return array|void
869
     */
870
    public function renderTopics(\Smarty $xoopsTpl = null)
871
    {
872
        $myts = \MyTextSanitizer::getInstance(); // irmtfan Instanciate
873
874
        $ret = [];
875
        //$this->parseVars();
876
877
        if ($this->noperm) {
878
            if (\is_object($xoopsTpl)) {
879
                $xoopsTpl->assign_by_ref('topics', $ret);
880
881
                return;
882
            }
883
884
            return $ret;
885
        }
886
887
        $selects = [];
888
        $froms   = [];
889
        $joins   = [];
890
        $wheres  = [];
891
892
        // topic fields
893
        $selects[] = 't.*';
894
        // post fields
895
        $selects[] = 'p.post_time as last_post_time, p.poster_name as last_poster_name, p.icon, p.post_id, p.uid';
896
897
        $froms[]  = $this->handler->db->prefix('newbb_topics') . ' AS t ';
898
        $joins[]  = 'LEFT JOIN ' . $this->handler->db->prefix('newbb_posts') . ' AS p ON p.post_id = t.topic_last_post_id';
899
        $wheres[] = '1 = 1';
900
901
        if (!empty($this->config['post_excerpt'])) {
902
            $selects[]             = 'p.post_karma, p.require_reply, pt.post_text';
903
            $this->query['join'][] = 'LEFT JOIN ' . $this->handler->db->prefix('newbb_posts_text') . ' AS pt ON pt.post_id = t.topic_last_post_id';
904
        }
905
        //if (empty($this->query["sort"])) $this->query["sort"][] = 't.topic_last_post_id DESC'; // irmtfan commented no need
906
907
        $sql = '    SELECT ' . \implode(', ', $selects) . '     FROM ' . \implode(', ', $froms) . '        ' . \implode(' ', $joins) . (!empty($this->query['join']) ? '        ' . \implode(' ', $this->query['join']) : '') . // irmtfan bug fix: Undefined index join when post_excerpt = 0
908
               '     WHERE ' . \implode(' AND ', $wheres) . '        AND ' . @\implode(' AND ', @$this->query['where']) . '     ORDER BY ' . \implode(', ', $this->query['sort']);
909
910
        if (!$result = $this->handler->db->query($sql, $this->config['topics_per_page'], @$this->vars['start'])) {
911
            if (\is_object($xoopsTpl)) {
912
                $xoopsTpl->assign_by_ref('topics', $ret);
913
914
                return;
915
            }
916
917
            return $ret;
918
        }
919
920
        require_once \dirname(__DIR__) . '/include/functions.render.php';
921
        require_once \dirname(__DIR__) . '/include/functions.session.php';
922
        require_once \dirname(__DIR__) . '/include/functions.time.php';
923
        require_once \dirname(__DIR__) . '/include/functions.read.php';
924
        require_once \dirname(__DIR__) . '/include/functions.topic.php';
925
926
        $sticky    = 0;
927
        $topics    = [];
928
        $posters   = [];
929
        $reads     = [];
930
        $types     = [];
931
        $forums    = [];
932
        $anonymous = $myts->htmlSpecialChars($GLOBALS['xoopsConfig']['anonymous']);
933
934
        while (false !== ($myrow = $this->handler->db->fetchArray($result))) {
935
            if ($myrow['topic_sticky']) {
936
                ++$sticky;
937
            }
938
939
            // ------------------------------------------------------
940
            // START irmtfan remove topic_icon hardcode smarty
941
            // topic_icon: just regular topic_icon
942
            if (!empty($myrow['icon'])) {
943
                $topic_icon = '<img align="middle" src="' . XOOPS_URL . '/images/subject/' . \htmlspecialchars($myrow['icon'], \ENT_QUOTES | \ENT_HTML5) . '" alt="" >';
944
            } else {
945
                $topic_icon = '<img align="middle" src="' . XOOPS_URL . '/images/icons/no_posticon.gif" alt="" >';
946
            }
947
            // END irmtfan remove topic_icon hardcode smarty
948
949
            // ------------------------------------------------------
950
            // rating_img
951
            $rating = \number_format($myrow['rating'] / 2, 0);
952
            // irmtfan - add alt key for rating
953
            if ($rating < 1) {
954
                $rating_img = \newbbDisplayImage('blank');
955
            } else {
956
                $rating_img = \newbbDisplayImage('rate' . $rating, \constant('_MD_NEWBB_RATE' . $rating));
957
            }
958
959
            // ------------------------------------------------------
960
            // topic_page_jump
961
            $topic_page_jump      = '';
962
            $topic_page_jump_icon = '';
963
            $totalpages           = \ceil(($myrow['topic_replies'] + 1) / $this->config['posts_per_page']);
964
            if ($totalpages > 1) {
965
                $topic_page_jump .= '&nbsp;&nbsp;';
966
                $append          = false;
967
                for ($i = 1; $i <= $totalpages; ++$i) {
968
                    if ($i > 3 && $i < $totalpages) {
969
                        if (!$append) {
970
                            $topic_page_jump .= '...';
971
                            $append          = true;
972
                        }
973
                    } else {
974
                        $topic_page_jump .= '[<a href="' . XOOPS_URL . '/modules/newbb/viewtopic.php?topic_id=' . $myrow['topic_id'] . '&amp;start=' . (($i - 1) * $this->config['posts_per_page']) . '">' . $i . '</a>]';
975
                        // irmtfan remove here and move
976
                        //$topic_page_jump_icon = "<a href='" . XOOPS_URL . "/modules/newbb/viewtopic.php?topic_id=" . $myrow['topic_id'] . "&amp;start=" . (($i - 1) * $this->config['posts_per_page']) . "" . "'>" . newbbDisplayImage('document',_MD_NEWBB_GOTOLASTPOST) . '</a>';
977
                    }
978
                }
979
            }
980
            // irmtfan - move here for both topics with and without pages - change topic_id to post_id
981
            $topic_page_jump_icon = "<a href='" . XOOPS_URL . '/modules/newbb/viewtopic.php?post_id=' . $myrow['topic_last_post_id'] . '' . "'>" . \newbbDisplayImage('lastposticon', _MD_NEWBB_GOTOLASTPOST) . '</a>';
982
983
            // ------------------------------------------------------
984
            // => topic array
985
986
            $topic_title = $myts->htmlSpecialChars($myrow['topic_title']);
987
            // irmtfan use topic_title_excerpt for block topic title length
988
            $topic_title_excerpt = $topic_title;
989
            if (!empty($this->config['topic_title_excerpt'])) {
990
                $topic_title_excerpt = \xoops_substr($topic_title, 0, $this->config['topic_title_excerpt']);
991
            }
992
            // irmtfan hardcode class commented
993
            //if ($myrow['topic_digest']) {
994
            //   $topic_title = "<span class='digest'>" . $topic_title . "</span>";
995
            //}
996
997
            if (empty($this->config['post_excerpt'])) {
998
                $topic_excerpt = '';
999
            } elseif (($myrow['post_karma'] > 0 || $myrow['require_reply'] > 0) && !\newbbIsAdmin($myrow['forum_id'])) {
1000
                $topic_excerpt = '';
1001
            } else {
1002
                $topic_excerpt = \xoops_substr(\newbbHtml2text($myts->displayTarea($myrow['post_text'])), 0, $this->config['post_excerpt']);
1003
                $topic_excerpt = \str_replace('[', '&#91;', $myts->htmlSpecialChars($topic_excerpt));
1004
            }
1005
1006
            $topics[$myrow['topic_id']] = [
1007
                'topic_id'               => $myrow['topic_id'],
1008
                'topic_icon'             => $topic_icon,
1009
                'type_id'                => $myrow['type_id'],
1010
                'topic_title_excerpt'    => $topic_title_excerpt,
1011
                //irmtfan use topic_title_excerpt
1012
                //'topic_link'    => XOOPS_URL . '/modules/newbb/viewtopic.php?topic_id=' . $myrow['topic_id'], // . '&amp;forum=' . $myrow['forum_id'], // irmtfan comment
1013
                'topic_link'             => 'viewtopic.php?topic_id=' . $myrow['topic_id'],
1014
                // irmtfan remove hardcode link
1015
                'rating_img'             => $rating_img,
1016
                'votes'                  => $myrow['votes'],
1017
                //irmtfan added
1018
                'topic_page_jump'        => $topic_page_jump,
1019
                'topic_page_jump_icon'   => $topic_page_jump_icon,
1020
                'topic_replies'          => $myrow['topic_replies'],
1021
                'topic_poster_uid'       => $myrow['topic_poster'],
1022
                'topic_poster_name'      => !empty($myrow['poster_name']) ? $myts->htmlSpecialChars($myrow['poster_name']) : $anonymous,
1023
                'topic_views'            => $myrow['topic_views'],
1024
                'topic_time'             => \newbbFormatTimestamp($myrow['topic_time']),
1025
                'topic_last_post_id'     => $myrow['topic_last_post_id'],
1026
                //irmtfan added
1027
                'topic_last_posttime'    => \newbbFormatTimestamp($myrow['last_post_time']),
1028
                'topic_last_poster_uid'  => $myrow['uid'],
1029
                'topic_last_poster_name' => !empty($myrow['last_poster_name']) ? $myts->htmlSpecialChars($myrow['last_poster_name']) : $anonymous,
1030
                'topic_forum'            => $myrow['forum_id'],
1031
                'topic_excerpt'          => $topic_excerpt,
1032
                'sticky'                 => $myrow['topic_sticky'] ? \newbbDisplayImage('topic_sticky', \_MD_NEWBB_TOPICSTICKY) : '',
1033
                // irmtfan bug fixed
1034
                'lock'                   => $myrow['topic_status'] ? \newbbDisplayImage('topic_locked', \_MD_NEWBB_TOPICLOCK) : '',
1035
                //irmtfan added
1036
                'digest'                 => $myrow['topic_digest'] ? \newbbDisplayImage('topic_digest', \_MD_NEWBB_TOPICDIGEST) : '',
1037
                //irmtfan added
1038
                'poll'                   => $myrow['topic_haspoll'] ? \newbbDisplayImage('poll', \_MD_NEWBB_TOPICHASPOLL) : '',
1039
                //irmtfan added
1040
                'approve'                => $myrow['approved'],
1041
                //irmtfan added
1042
            ];
1043
1044
            /* users */
1045
            $posters[$myrow['topic_poster']] = 1;
1046
            $posters[$myrow['uid']]          = 1;
1047
            // reads
1048
            if (!empty($this->config['read_mode'])) {
1049
                $reads[$myrow['topic_id']] = (1 == $this->config['read_mode']) ? $myrow['last_post_time'] : $myrow['topic_last_post_id'];
1050
            }
1051
            // types
1052
            if (!empty($myrow['type_id'])) {
1053
                //$types[$myrow['type_id']] = 1;
1054
            }
1055
            // forums
1056
            $forums[$myrow['forum_id']] = 1;
1057
        }
1058
        $posters_name = \newbbGetUnameFromIds(\array_keys($posters), $this->config['show_realname'], true);
1059
        $topic_isRead = \newbbIsRead('topic', $reads);
1060
        /*
1061
        $type_list = [];
1062
        if (count($types) > 0) {
1063
            $typeHandler =  Newbb\Helper::getInstance()->getHandler('Type');
1064
            $type_list = $typeHandler->getAll(new \Criteria("type_id", "(".implode(", ", array_keys($types)).")", "IN"), null, false);
1065
        }
1066
        */
1067
        $type_list = $this->getTypes();
1068
        /** @var Newbb\ForumHandler $forumHandler */
1069
        $forumHandler = \XoopsModules\Newbb\Helper::getInstance()->getHandler('Forum');
1070
1071
        if (\count($forums) > 0) {
1072
            $forum_list = $forumHandler->getAll(new \Criteria('forum_id', '(' . \implode(', ', \array_keys($forums)) . ')', 'IN'), ['forum_name', 'hot_threshold'], false);
1073
        } else {
1074
            $forum_list = $forumHandler->getAll();
1075
        }
1076
1077
        foreach (\array_keys($topics) as $id) {
1078
            $topics[$id]['topic_read']       = empty($topic_isRead[$id]) ? 0 : 1; // add topic-read/topic-new smarty variable
1079
            $topics[$id]['topic_forum_link'] = '<a href="' . XOOPS_URL . '/modules/newbb/viewforum.php?forum=' . $topics[$id]['topic_forum'] . '">' . $forum_list[$topics[$id]['topic_forum']]['forum_name'] . '</a>';
1080
1081
            //irmtfan use topic_title_excerpt -- add else
1082
            if (!empty($topics[$id]['type_id']) && isset($type_list[$topics[$id]['type_id']])) {
1083
                $topics[$id]['topic_title'] = \getTopicTitle($topics[$id]['topic_title_excerpt'], $type_list[$topics[$id]['type_id']]['type_name'], $type_list[$topics[$id]['type_id']]['type_color']);
1084
            } else {
1085
                $topics[$id]['topic_title'] = $topics[$id]['topic_title_excerpt'];
1086
            }
1087
            $topics[$id]['topic_poster']      = !empty($posters_name[$topics[$id]['topic_poster_uid']]) ? $posters_name[$topics[$id]['topic_poster_uid']] : $topics[$id]['topic_poster_name'];
1088
            $topics[$id]['topic_last_poster'] = !empty($posters_name[$topics[$id]['topic_last_poster_uid']]) ? $posters_name[$topics[$id]['topic_last_poster_uid']] : $topics[$id]['topic_last_poster_name'];
1089
            // ------------------------------------------------------
1090
            // START irmtfan remove hardcodes from topic_folder smarty
1091
            // topic_folder: priority: newhot -> hot/new -> regular
1092
            //list($topic_status, $topic_digest, $topic_replies) = $topics[$id]["stats"]; irmtfan
1093
            // START irmtfan - add topic_folder_text for alt
1094
            //if ($topics[$id]["lock"] === 1) {
1095
            //    $topic_folder = 'topic_locked';
1096
            //    $topic_folder_text = _MD_NEWBB_TOPICLOCKED;
1097
            //} else {
1098
            //if ($topic_digest) {
1099
            //    $topic_folder = 'topic_digest';
1100
            //    $topic_folder_text = _MD_NEWBB_TOPICDIGEST;
1101
            if ($topics[$id]['topic_replies'] >= $forum_list[$topics[$id]['topic_forum']]['hot_threshold']) {
1102
                $topic_folder      = empty($topic_isRead[$id]) ? 'topic_hot_new' : 'topic_hot';
1103
                $topic_folder_text = empty($topic_isRead[$id]) ? \_MD_NEWBB_MORETHAN : \_MD_NEWBB_MORETHAN2;
1104
            } else {
1105
                $topic_folder      = empty($topic_isRead[$id]) ? 'topic_new' : 'topic';
1106
                $topic_folder_text = empty($topic_isRead[$id]) ? \_MD_NEWBB_NEWPOSTS : \_MD_NEWBB_NONEWPOSTS;
1107
            }
1108
            //}
1109
            // END irmtfan remove hardcodes from topic_folder smarty
1110
            $topics[$id]['topic_folder'] = \newbbDisplayImage($topic_folder, $topic_folder_text);
1111
            // END irmtfan - add topic_folder_text for alt
1112
1113
            unset($topics[$id]['topic_poster_name'], $topics[$id]['topic_last_poster_name']); // irmtfan remove $topics[$id]["stats"] because it is not exist now
1114
        }
1115
1116
        if (\count($topics) > 0) {
1117
            $sql    = ' SELECT DISTINCT topic_id FROM ' . $this->handler->db->prefix('newbb_posts') . " WHERE attachment != ''" . ' AND topic_id IN (' . \implode(',', \array_keys($topics)) . ')';
1118
            $result = $this->handler->db->query($sql);
1119
            if ($result) {
1120
                while (list($topic_id) = $this->handler->db->fetchRow($result)) {
1121
                    $topics[$topic_id]['attachment'] = '&nbsp;' . \newbbDisplayImage('attachment', \_MD_NEWBB_TOPICSHASATT);
1122
                }
1123
            }
1124
        }
1125
1126
        if (\is_object($xoopsTpl)) {
1127
            $xoopsTpl->assign_by_ref('sticky', $sticky);
1128
            $xoopsTpl->assign_by_ref('topics', $topics);
1129
1130
            return;
1131
        }
1132
1133
        return [$topics, $sticky];
1134
    }
1135
1136
    // START irmtfan to create an array from selected keys of an array
1137
1138
    /**
1139
     * @param        $array
1140
     * @param null   $keys
1141
     * @return array
1142
     */
1143
    public function getFromKeys($array, $keys = null)
1144
    {
1145
        if (empty($keys)) {
1146
            return $array;
1147
        } // all keys
1148
        $keyarr = \is_string($keys) ? \explode(',', $keys) : $keys;
1149
        $keyarr = \array_intersect(\array_keys($array), $keyarr); // keys should be in array
1150
        $ret    = [];
1151
        foreach ($keyarr as $key) {
1152
            $ret[$key] = $array[$key];
1153
        }
1154
1155
        return $ret;
1156
    }
1157
1158
    // END irmtfan to create an array from selected keys of an array
1159
}
1160