Passed
Pull Request — master (#5728)
by David
10:20 queued 27s
created

thread_list_item()   B

Complexity

Conditions 7
Paths 9

Size

Total Lines 34
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 29
nc 9
nop 3
dl 0
loc 34
rs 8.5226
c 0
b 0
f 0
1
<?php
2
// This file is part of BOINC.
3
// http://boinc.berkeley.edu
4
// Copyright (C) 2008 University of California
5
//
6
// BOINC is free software; you can redistribute it and/or modify it
7
// under the terms of the GNU Lesser General Public License
8
// as published by the Free Software Foundation,
9
// either version 3 of the License, or (at your option) any later version.
10
//
11
// BOINC is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
// See the GNU Lesser General Public License for more details.
15
//
16
// You should have received a copy of the GNU Lesser General Public License
17
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
18
19
require_once("../inc/forum_db.inc");
20
require_once("../inc/pm.inc");
21
require_once("../inc/team.inc");
22
require_once("../inc/user.inc");
23
require_once("../inc/news.inc");
24
require_once("../inc/text_transform.inc");
25
26
define('THREADS_PER_PAGE', 50);
27
28
$forum_error = "";
29
    // for functions that return null on error,
30
    // look here for an explanation
31
32
// sorting styles (for both threads and posts)
33
//
34
define('MODIFIED_NEW', 1);
35
define('MODIFIED_OLD', 2);
36
define('VIEWS_MOST', 3);
37
define('REPLIES_MOST', 4);
38
define('CREATE_TIME_NEW', 5);
39
define('CREATE_TIME_OLD', 6);
40
define('POST_SCORE', 7);
41
42
// names for the above
43
//
44
$thread_sort_styles[CREATE_TIME_OLD] = tra("Oldest first");
45
$thread_sort_styles[CREATE_TIME_NEW] = tra("Newest first");
46
$thread_sort_styles[POST_SCORE] = tra("Highest rated posts first");
47
48
$forum_sort_styles[MODIFIED_NEW] = tra("Newest post first");
49
$forum_sort_styles[VIEWS_MOST] = tra("Most views first");
50
$forum_sort_styles[REPLIES_MOST] = tra("Most posts first");
51
$forum_sort_styles[CREATE_TIME_NEW] = tra("Newest first");
52
53
// values for thread.status
54
define('THREAD_SOLVED', 1);
55
56
define('AVATAR_WIDTH', 100);
57
define('AVATAR_HEIGHT',100);
58
59
define('ST_NEW_TIME', 1209600); //3600*24*14 - 14 days
60
define('ST_NEW', 'New member');
61
62
define('MAXIMUM_EDIT_TIME',3600);
63
    // allow edits of forums posts up till one hour after posting.
64
65
define('MAX_FORUM_LOGGING_TIME', 2419200); //3600*24*28 - 28 days
66
define('NO_CONTROLS', 0);
67
define('FORUM_CONTROLS', 1);
68
define('HELPDESK_CONTROLS', 2);
69
define("EXCERPT_LENGTH", "120");
70
71
define('NEW_IMAGE', 'img/unread_post.png');
72
define('NEW_IMAGE_STICKY', 'img/unread_sticky.png');
73
define('NEW_IMAGE_LOCKED', 'img/unread_locked.png');
74
define('NEW_IMAGE_STICKY_LOCKED', 'img/unread_sticky_locked.png');
75
define('IMAGE_STICKY', 'img/sticky_post.png');
76
define('IMAGE_LOCKED', 'img/locked_post.png');
77
define('IMAGE_HIDDEN', 'img/hidden.png');
78
define('IMAGE_STICKY_LOCKED', 'img/sticky_locked_post.png');
79
define('IMAGE_POST', 'img/post.png');
80
define('NEW_IMAGE_HEIGHT','15');
81
define('EMPHASIZE_IMAGE', 'img/emphasized_post.png');
82
define('EMPHASIZE_IMAGE_HEIGHT','15');
83
define('FILTER_IMAGE', 'img/filtered_post.png');
84
define('FILTER_IMAGE_HEIGHT','15');
85
define('RATE_POSITIVE_IMAGE', 'img/rate_positive.png');
86
define('RATE_POSITIVE_IMAGE_HEIGHT','9');
87
define('RATE_NEGATIVE_IMAGE', 'img/rate_negative.png');
88
define('RATE_NEGATIVE_IMAGE_HEIGHT','9');
89
define('REPORT_POST_IMAGE', 'img/report_post.png');
90
define('REPORT_POST_IMAGE_HEIGHT','9');
91
92
define('SOLUTION', tra('This answered my question'));
93
define('SUFFERER', tra('I also have this question'));
94
define('OFF_TOPIC', tra('Off-topic'));
95
96
define ('DEFAULT_LOW_RATING_THRESHOLD', -25);
97
define ('DEFAULT_HIGH_RATING_THRESHOLD', 5);
98
99
// special user attributes
100
//
101
define('S_MODERATOR', 0);
102
define('S_ADMIN', 1);
103
define('S_DEV', 2);
104
define('S_TESTER', 3);
105
define('S_VOLUNTEER', 4);
106
define('S_VOLUNTEER_TESTER', 5);
107
define('S_SCIENTIST', 6);
108
define('S_HELP_DESK_EXPERT', 7);
109
define('S_NFLAGS', 8);
110
111
$special_user_bitfield[S_MODERATOR] = tra("Volunteer moderator");
112
$special_user_bitfield[S_ADMIN] = tra("Project administrator");
113
$special_user_bitfield[S_DEV] = tra("Project developer");
114
$special_user_bitfield[S_TESTER] = tra("Project tester");
115
$special_user_bitfield[S_VOLUNTEER] = tra("Volunteer developer");
116
$special_user_bitfield[S_VOLUNTEER_TESTER] = tra("Volunteer tester");
117
$special_user_bitfield[S_SCIENTIST] = tra("Project scientist");
118
$special_user_bitfield[S_HELP_DESK_EXPERT] = tra("Help desk expert");
119
120
function link_count($x) {
121
    $n = 0;
122
    while (1) {
123
        $x = strstr($x, "[url");
124
        if (!$x) break;
125
        $n++;
126
        $x = substr($x, 4);
127
    }
128
    return $n;
129
}
130
131
// show a banner with search form on left and PM info on right
132
//
133
function show_forum_header($user) {
134
    echo '<form action="forum_search_action.php" method="POST">
135
    ';
136
    start_table();
137
    echo '
138
        <tr>
139
    ';
140
141
    // Search
142
    echo '
143
        <td>
144
        <input type="hidden" name="search_max_time" value="0">
145
        <input type="hidden" name="search_forum" value="-1">
146
        <input type="hidden" name="search_sort" value="'.CREATE_TIME_NEW.'">
147
        <input type="text" class="" name="search_keywords">
148
    ';
149
    echo sprintf(
150
        '<input class="%s" %s title="%s" type="submit" value="%s"><br>',
151
        'btn btn-sm',
152
        button_style(),
153
        tra("Search for words in forum messages"),
154
        tra("Search forums")
155
    );
156
    echo '
157
        <small><a href="forum_search.php">'.tra("Advanced search").'</a></small>
158
        </td>
159
    ';
160
161
    if ($user) {
162
        echo "<td align=\"right\">\n";
163
        echo "<p>".tra("Private messages").": ", pm_notification($user);
164
        echo "</td>\n";
165
    }
166
    echo "</tr>
167
    ";
168
    end_table();
169
    echo "</form>
170
    ";
171
}
172
173
// return forum/thread title, with links.
174
//
175
function forum_title($category, $forum, $thread, $link_thread=false) {
176
    if ($category) {
177
        $is_helpdesk = $category->is_helpdesk;
178
    } else {
179
        $is_helpdesk = false;
180
    }
181
182
    $where = $is_helpdesk?tra("Questions and Answers"):tra("Message boards");
183
    $top_url = $is_helpdesk?"forum_help_desk.php":"forum_index.php";
184
185
    $x = '';
186
    if (!$forum && !$thread) {
187
        $x .= $where;
188
    } else if ($forum && !$thread) {
189
        $x .= sprintf('<a href="%s">%s</a> : %s',
190
            $top_url,
191
            $where,
192
            $forum->title
193
        );
194
    } else if ($forum && $thread) {
195
        $x .= sprintf('<a href="%s">%s</a> :',
196
            $top_url,
197
            $where
198
        );
199
        $x .= sprintf('<a href="forum_forum.php?id=%d">%s</a> :',
200
            $forum->id,
201
            $forum->title
202
        );
203
        if ($link_thread) {
204
            $x .= sprintf('<a href=forum_thread.php?id=%d>%s</a>',
205
                $thread->id,
206
                cleanup_title($thread->title)
207
            );
208
        } else {
209
            $x .= cleanup_title($thread->title);
210
        }
211
    } else {
212
        $x = "Invalid thread ID";
213
    }
214
    return $x;
215
}
216
217
function team_forum_title($forum, $thread=null, $link_thread=false) {
218
    $team = BoincTeam::lookup_id($forum->category);
219
    $x = sprintf('<a href="forum_index.php">%s</a> :',
220
        tra("Message boards")
221
    );
222
    if ($thread) {
223
        $x .= sprintf('<a href=team_forum.php?teamid=%d>%s</a>',
224
            $team->id,
225
            tra("%1 message board", $team->name)
226
        );
227
        if ($link_thread) {
228
            $x .= " : <a href=forum_thread.php?id=$thread->id>$thread->title</a>";
229
        } else {
230
            $x .= " : $thread->title";
231
        }
232
    } else {
233
        $x .= tra("%1 message board", $team->name);
234
    }
235
    return $x;
236
}
237
238
// start a table of forum posts
239
//
240
function start_forum_table($headings) {
241
    $a = array();
242
    foreach ($headings as $h) {
243
        $a[] = null;
244
    }
245
    $a[1] = 'style="width: 100%"';
246
    start_table('table-striped');
247
    row_heading_array($headings, $a);
248
}
249
250
function page_link($url, $page_num, $items_per_page, $text) {
251
    return " <a href=\"$url&amp;start=" . $page_num*$items_per_page . "\">$text</a> ";
252
}
253
254
// return a string for navigating pages
255
//
256
function page_links($url, $nitems, $items_per_page, $start){
257
    // How many pages to potentially show before and after this one:
258
    $preshow = 3;
259
    $postshow = 3;
260
261
    $x = "";
262
263
    if ($nitems <= $items_per_page) return "";
264
    $npages = ceil($nitems / $items_per_page);
265
    $curpage = ceil($start / $items_per_page);
266
267
    // If this is not the first page, display "previous"
268
    //
269
    if ($curpage > 0){
270
        $x .= page_link(
271
            $url, $curpage-1, $items_per_page,
272
            tra("Previous")." &middot; "
273
        );
274
    }
275
276
    if ($curpage - $preshow > 0) {
277
        $x .= page_link($url, 0, $items_per_page, "1");
278
        if ($curpage - $preshow > 1) {
279
            $x .= " . . . ";
280
        } else {
281
            $x .= " &middot; ";
282
        }
283
    }
284
    // Display a list of pages surrounding this one
285
    //
286
    for ($i=$curpage-$preshow; $i<=$curpage+$postshow; $i++){
287
        $page_str = (string)($i+1);
288
        if ($i < 0) continue;
289
        if ($i >= $npages) break;
290
291
        if ($i == $curpage) {
292
            $x .= "<b>$page_str</b>";
293
        } else {
294
            $x .= page_link($url, $i, $items_per_page, $page_str);
295
        }
296
        if ($i == $npages-1) break;
297
        if ($i == $curpage+$postshow) break;
298
        $x .= " &middot; ";
299
    }
300
301
    if ($curpage + $postshow < $npages-1) {
302
        $x .= " . . . ";
303
        $x .= page_link($url, $npages-1, $items_per_page, $npages);
304
    }
305
    // If there is a next page
306
    //
307
    if ($curpage < $npages-1){
308
        $x .= page_link(
309
            $url, $curpage+1, $items_per_page,
310
            " &middot; ".tra("Next")
311
        );
312
    }
313
    $x .= "\n";
314
    return $x;
315
}
316
317
function thread_is_unread($user, $thread) {
318
    if (!$user) return false;
319
    if ($thread->timestamp <= $user->prefs->mark_as_read_timestamp) return false;
320
    $log = BoincForumLogging::lookup($user->id, $thread->id);
321
    if ($log && ($thread->timestamp <= $log->timestamp)) return false;
322
    return true;
323
}
324
325
//  Process a user-supplied title to remove HTML stuff
326
//
327
function cleanup_title($title) {
328
    $x = sanitize_tags(bb2html($title));
329
    $x = trim($x);
330
    if (strlen($x)==0) return "(no title)";
331
    else return $x;
332
}
333
334
function can_reply($thread, $forum, $user) {
335
    if ($thread->locked) {
336
        if (!is_moderator($user, $forum)) return false;
337
    }
338
    return true;
339
}
340
341
// Show the posts in a thread for a user.
342
// If $start is null, enforce jump-to-first-unread
343
//
344
function show_posts(
345
    $thread, $forum, $start, $postid, $sort_style, $filter, $logged_in_user
0 ignored issues
show
Coding Style introduced by
Multi-line function declarations must define one parameter per line
Loading history...
346
) {
347
    $num_to_show = 20;
348
    if ($logged_in_user && $logged_in_user->prefs->display_wrap_postcount > 0) {
349
        $num_to_show = $logged_in_user->prefs->display_wrap_postcount;
350
    }
351
352
    // let moderators see all posts, including hidden ones
353
    //
354
    if (is_moderator($logged_in_user, $forum)) {
355
        $show_hidden = true;
356
    } else {
357
        $show_hidden = false;
358
    }
359
360
    $posts = get_thread_posts($thread->id, $sort_style, $show_hidden);
361
362
    $latest_viewed = 0;
363
    $forum_log = null;
364
    if ($logged_in_user) {
365
        $forum_log = BoincForumLogging::lookup($logged_in_user->id, $thread->id);
366
        if ($forum_log) {
367
            $latest_viewed = $forum_log->timestamp;
368
        }
369
    }
370
371
    if ($sort_style == CREATE_TIME_OLD) {
372
        // show the last page
373
        //
374
        $nposts = sizeof($posts);
375
        if ($nposts) $nposts -= 1;
0 ignored issues
show
Coding Style introduced by
Decrement operators should be used where possible; found "$nposts -= 1;" but expected "$nposts--"
Loading history...
376
        $page = (int)($nposts/$num_to_show);
377
        $default_start = $page*$num_to_show;
378
    } else {
379
        $default_start = 0;
380
    }
381
382
    // jump to a specific post if needed
383
    //
384
    $jump_to_post = null;
385
    if ($start === null) {
386
        if ($postid) {
387
            // jump to a specific post
388
            //
389
            $i = 0;
390
            foreach ($posts as $post) {
391
                if ($post->id == $postid) {
392
                    $start = $i - ($i % $num_to_show);
393
                    $jump_to_post = $post;
394
                    break;
395
                }
396
                $i++;
397
            }
398
            if ($start === null) {
399
                echo "Post $postid not found.";
400
                return;
401
            }
402
        } else if ($logged_in_user && $logged_in_user->prefs->jump_to_unread) {
403
            // jump to the first unread post
404
            //
405
            $i = 0;
406
            $ibest = 0;
407
            foreach ($posts as $post) {
408
                if ($post->timestamp > $latest_viewed) {
409
                    if (!$jump_to_post || ($post->timestamp < $jump_to_post->timestamp)) {
410
                        $jump_to_post = $post;
411
                        $ibest = $i;
412
                    }
413
                }
414
                $i++;
415
            }
416
            // if jump to post, figure out what page to show
417
            //
418
            if ($jump_to_post) {
419
                $start = $ibest - ($ibest % $num_to_show);
420
            } else {
421
                $start = $default_start;
422
            }
423
        } else {
424
            $start = $default_start;
425
        }
426
    }
427
428
    $page_nav = page_links(
429
        "forum_thread.php?id=$thread->id&sort_style=$sort_style",
430
        sizeof($posts),
431
        $num_to_show,
432
        $start
433
    );
434
435
    echo $page_nav;
436
437
    $num_shown = 0;
438
    $num_skipped = 0;
439
    $headings = array(tra("Author"), tra("Message"));
440
    start_forum_table($headings);
441
442
    $latest_shown_timestamp = 0;
443
    foreach ($posts as $post) {
444
        if ($num_skipped < $start) {
445
            $num_skipped++;
446
            continue;
447
        }
448
        if ($num_shown == $num_to_show) {
449
            break;
450
        }
451
        show_post(
452
            $post, $thread, $forum, $logged_in_user, $start, $latest_viewed,
453
            FORUM_CONTROLS, $filter
454
        );
455
456
        if ($post->timestamp > $latest_shown_timestamp) {
457
            $latest_shown_timestamp = $post->timestamp;
458
        }
459
        $num_shown++;
460
    }
461
    end_table();
462
    echo $page_nav;
463
464
    if ($jump_to_post) {
465
        echo "<script>function jumpToUnread(){location.href='#".$jump_to_post->id."';}</script>";
466
    } else {
467
        echo "<script>function jumpToUnread(){};</script>";
468
    }
469
470
    // if thread has no visible posts, user has seen them all
471
    //
472
    if ($num_shown == 0) {
473
        $latest_shown_timestamp = time();
474
    }
475
476
    if ($logged_in_user) {
477
        if (!$forum_log || $latest_shown_timestamp > $forum_log->timestamp) {
478
            BoincForumLogging::replace(
479
                $logged_in_user->id, $thread->id, $latest_shown_timestamp
480
            );
481
        }
482
    }
483
}
484
485
function get_ignored_list($user) {
486
    return explode("|", $user->prefs->ignorelist);
487
}
488
489
function add_ignored_user($user, $other_user) {
490
    $list = explode("|", $user->prefs->ignorelist);
491
    foreach ($list as $key=>$userid) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space before "=>"; 0 found
Loading history...
Coding Style introduced by
Expected 1 space after "=>"; 0 found
Loading history...
492
        if ($userid == $other_user->id) {
493
            return true;
494
        }
495
    }
496
    $list[] = $other_user->id;
497
    $x = implode("|", array_values($list));
498
    return $user->prefs->update("ignorelist='$x'");
499
}
500
501
function remove_ignored_user($user, $other_user) {
502
    $list = explode("|", $user->prefs->ignorelist);
503
    foreach ($list as $key=>$userid) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space before "=>"; 0 found
Loading history...
Coding Style introduced by
Expected 1 space after "=>"; 0 found
Loading history...
504
        if ($userid == $other_user->id) {
505
            unset($list[$key]);
506
        }
507
    }
508
    $x = implode("|", array_values($list));
509
    return $user->prefs->update("ignorelist='$x'");
510
}
511
512
function is_ignoring($user, $other_user) {
513
    $list = explode("|", $user->prefs->ignorelist);
514
    return in_array($other_user->id, $list);
515
}
516
517
// if avatar is from gravatar, make it HTTPS
518
//
519
function avatar_url($url) {
520
    return str_replace('http:', 'https:', $url);
521
}
522
523
// Display an individual post.
524
// Generates a table row with two cells: author and message
525
//
526
function show_post(
527
    $post, $thread, $forum, $logged_in_user, $start=0,
0 ignored issues
show
Coding Style introduced by
Multi-line function declarations must define one parameter per line
Loading history...
528
    $latest_viewed=0, $controls=FORUM_CONTROLS, $filter=true
0 ignored issues
show
Coding Style introduced by
Multi-line function declarations must define one parameter per line
Loading history...
529
) {
530
    global $country_to_iso3166_2;
531
532
    $user = BoincUser::lookup_id($post->user);
533
534
    // If the user no longer exists, skip the post
535
    //
536
    if (!$user){
537
        return;
538
    }
539
540
    $config = get_config();
541
    BoincForumPrefs::lookup($user);
542
    if (is_banished($user) && !is_moderator($logged_in_user, $forum)) {
543
        return;
544
    }
545
546
    $no_forum_rating = parse_bool($config, "no_forum_rating");
547
548
    $tokens = "";
549
    $options = get_output_options($logged_in_user);
550
    if (is_admin($user)) {
551
        $options->htmlitems = false;
552
    }
553
554
    // check whether the poster is on the list of people to ignore
555
    //
556
    $ignore_poster = false;
557
    if ($logged_in_user){
558
        $tokens = url_tokens($logged_in_user->authenticator);
559
        if (is_ignoring($logged_in_user, $user)){
560
            $ignore_poster = true;
561
        }
562
    }
563
564
    // The creator can edit the post, but only in a specified amount of time
565
    // (exception: a moderator can edit his/her posts at any time)
566
    //
567
    $can_edit = false;
568
    if ($logged_in_user) {
569
        if ($user->id == $logged_in_user->id) {
570
            if (is_moderator($logged_in_user, $forum)) {
571
                $can_edit = true;
572
            } else if (can_reply($thread, $forum, $logged_in_user)) {
573
                $time_limit = $post->timestamp+MAXIMUM_EDIT_TIME;
574
                $can_edit = time()<$time_limit;
575
            } else {
576
                $can_edit = false;
577
            }
578
        }
579
    }
580
581
    // Print the special user lines, if any
582
    //
583
    global $special_user_bitfield;
584
    $fstatus="";
585
    $keys = array_keys($special_user_bitfield);
586
    $is_posted_by_special = false;
587
    for ($i=0; $i<sizeof($special_user_bitfield);$i++) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after second semicolon of FOR loop; 0 found
Loading history...
Coding Style Performance introduced by
The use of sizeof() inside a loop condition is not allowed; assign the return value to a variable and use the variable in the loop condition instead
Loading history...
Performance Best Practice introduced by
It seems like you are calling the size function sizeof() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
588
        if ($user->prefs && $user->prefs->privilege($keys[$i])) {
589
            $fstatus.="<nobr>".$special_user_bitfield[$keys[$i]]."<nobr><br>";
590
            $is_posted_by_special = true;
591
        }
592
    }
593
594
    // Highlight special users if set in prefs;
595
    //
596
    if ($logged_in_user && $logged_in_user->prefs){
597
        $highlight = $logged_in_user->prefs->highlight_special && $is_posted_by_special;
598
    } else {
599
        $highlight = $is_posted_by_special;
600
    }
601
    $class = $highlight?' style="border-left: 5px solid LightGreen" ':'';
602
603
    // row and start of author col
604
    //
605
    echo "
606
        <tr>
607
        <td $class>
608
        <a name=\"$post->id\"></a>
609
    ";
610
611
    echo user_links($user, 0, 30);
612
    echo "<br>";
613
    if ($user->create_time > time()-ST_NEW_TIME) $fstatus.=ST_NEW."<br>";
614
    echo "<span class=\"small\">";
615
    if ($fstatus) echo "$fstatus";
616
617
    if (!$filter || !$ignore_poster){
618
        if ($user->prefs && $user->prefs->avatar!="" && (!$logged_in_user || ($logged_in_user->prefs->hide_avatars==false))) {
619
            echo "<img width=\"".AVATAR_WIDTH."\" height=\"".AVATAR_HEIGHT."\" src=\"".avatar_url($user->prefs->avatar)."\" alt=\"Avatar\"><br>";
620
        }
621
    }
622
    echo "<p> </p>";
623
624
    $url = "pm.php?action=new&amp;userid=".$user->id;
625
    $name = $user->name;
626
    show_button_small($url, tra("Send message"), tra("Send %1 a private message",$name));
627
    echo '<br>'.tra("Joined: %1", gmdate('j M y', $user->create_time)), "<br>";
628
629
    if (!isset($user->nposts)) {
630
        $user->nposts = BoincPost::count("user=$user->id");
631
    }
632
633
    if (function_exists('project_forum_user_info')){
634
        project_forum_user_info($user);
635
    } else {
636
        echo tra("Posts: %1", $user->nposts)."<br>";
637
        // circumvent various forms of identity spoofing
638
        // by displaying the  user id of the poster.
639
        //
640
        //echo "ID: ".$user->id."<br>";
641
        if (!NO_COMPUTING) {
642
            echo tra("Credit: %1", number_format($user->total_credit)) ."<br>";
643
            echo tra("RAC: %1",    number_format($user->expavg_credit))."<br>";
644
        }
645
646
        // to use this feature:
647
        // - get flags from http://www.famfamfam.com/lab/icons/flags/famfamfam_flag_icons.zip
648
        // - put the .png's in html/user/flags/
649
        // - put define("COUNTRY_FLAGS", 1); in your html/project/project.inc
650
        //
651
        if (USER_COUNTRY && defined("COUNTRY_FLAGS")) {
652
            if (array_key_exists($user->country, $country_to_iso3166_2)) {
653
                $code = $country_to_iso3166_2[$user->country];
654
                echo "<img class=flag alt=\"$user->country\" title=\"$user->country\" src=flags/$code.png>\n";
655
            }
656
        }
657
        echo badges_string(true, $user, BADGE_HEIGHT_SMALL);
658
    }
659
660
    // end of author col, start of message col
661
    //
662
    echo '</span>
663
        </td>
664
        <td height="1%">
665
        <div class="small">
666
    ';
667
668
    if ($controls == FORUM_CONTROLS) {
669
        echo "<form action=\"forum_rate.php?post=", $post->id, "\" method=\"post\">";
670
    }
671
672
    if ($logged_in_user && $post->timestamp > $latest_viewed){
673
        show_image(NEW_IMAGE, tra("You haven't read this message yet"), tra("Unread"), NEW_IMAGE_HEIGHT);
674
    }
675
676
    echo " <a href=\"forum_thread.php?id=".$thread->id."&amp;postid=$post->id\">".tra("Message %1", $post->id)."</a> - ";
677
    if ($post->hidden) echo "<font color=red>[".tra("hidden")."] </font>";
678
    echo tra("Posted: %1", pretty_time_str($post->timestamp)), " ";
679
680
    if ($post->parent_post) {
681
        echo tra(" - in response to ")."<a href=\"forum_thread.php?id=".$thread->id."&amp;postid=".$post->parent_post."\">".tra("Message %1", $post->parent_post)."</a>. &nbsp; ";
682
    }
683
    if ($can_edit && $controls != NO_CONTROLS) {
684
        show_button_small("forum_edit.php?id=".$post->id."$tokens", tra("Edit"), tra("Edit this message"));
685
    }
686
    if (is_moderator($logged_in_user, $forum)) {
687
        show_post_moderation_links($config, $logged_in_user, $post, $forum, $tokens);
688
    }
689
    if ($post->modified) {
690
        echo "<br>".tra("Last modified: %1", pretty_time_Str($post->modified));
691
    }
692
    if ($ignore_poster && $filter){
693
        echo "<br>" .tra(
694
            "This post is hidden because the sender is on your 'ignore' list.  Click %1 here %2 to view hidden posts",
695
            "<a href=\"?id=".$thread->id."&amp;filter=false&amp;start=$start#".$post->id."\">",
696
            "</a>"
697
        );
698
    }
699
    if ($controls == FORUM_CONTROLS) {
700
        echo "</form>\n";
701
    }
702
    echo "</div>
703
        <p>
704
    ";
705
706
    if (!$filter || !$ignore_poster){
707
        $posttext = $post->content;
708
709
        // If the creator of this post has a signature and
710
        // wants it to be shown for this post AND the logged in
711
        // user has signatures enabled: show it
712
        //
713
        $posttext = output_transform($posttext, $options);
714
        if ($post->signature && (!$logged_in_user || !$logged_in_user->prefs->hide_signatures)){
715
            $sig = output_transform($user->prefs->signature, $options);
716
            $posttext .= "<hr>$sig\n";
717
        }
718
719
        // show message in a panel
720
        //
721
        echo '<div class="panel panel-default" style="word-break: break-word;">
722
            <div class="panel-body">'
723
            .$posttext
724
            .'</div></div>
725
        ';
726
727
        echo '<div class="small"
728
            <span>ID: '. $post->id
729
        ;
0 ignored issues
show
Coding Style introduced by
Space found before semicolon; expected "id;" but found "id
;"
Loading history...
730
        if ($no_forum_rating) {
731
            echo " &middot; <a href=\"forum_report_post.php?post=".$post->id."\">";
732
            show_image(REPORT_POST_IMAGE, tra("Report this post as offensive"), tra("Report as offensive"), REPORT_POST_IMAGE_HEIGHT);
733
            echo "</a>";
734
        } else {
735
            $rating = $post->rating();
736
            echo " &middot; ".tra("Rating: %1", $rating)." &middot; ".tra("rate: ")."
737
                <a href=\"forum_rate.php?post=".$post->id."&amp;choice=p$tokens\">
738
            ";
739
            show_image(RATE_POSITIVE_IMAGE, tra("Click if you like this message"), tra("Rate +"), RATE_POSITIVE_IMAGE_HEIGHT);
740
            echo "</a> / <a href=\"forum_rate.php?post=".$post->id."&amp;choice=n$tokens\">";
741
            show_image(RATE_NEGATIVE_IMAGE, tra("Click if you don't like this message"), tra("Rate -"), RATE_NEGATIVE_IMAGE_HEIGHT);
742
            echo "</a> <a href=\"forum_report_post.php?post=".$post->id."\">";
743
            show_image(REPORT_POST_IMAGE, tra("Report this post as offensive"), tra("Report as offensive"), REPORT_POST_IMAGE_HEIGHT);
744
            echo "</a>";
745
        }
746
        if (($controls == FORUM_CONTROLS) && (can_reply($thread, $forum, $logged_in_user))) {
747
            echo "&nbsp;&nbsp;&nbsp;&nbsp;";
748
            $url = "forum_reply.php?thread=" . $thread->id . "&amp;post=" . $post->id . "&amp;no_quote=1#input";
749
            // "Reply" is used as a verb
750
            show_button($url, tra("Reply"), tra("Post a reply to this message"));
751
            $url = "forum_reply.php?thread=" . $thread->id . "&amp;post=" . $post->id . "#input";
752
            // "Quote" is used as a verb
753
            show_button($url, tra("Quote"), tra("Post a reply by quoting this message"));
754
        }
755
        echo "</span>";
756
    }
757
    // end of message col and row; add separator row
758
    //
759
    echo "</td></tr>
760
        <tr><td colspan=2></td></tr>
761
    ";
762
}
763
764
// Show a post and its context (e.g. for search results, user posts)
765
//
766
function show_post_and_context($post, $thread, $forum, $options, $n) {
767
    $thread = BoincThread::lookup_id($post->thread);
768
    $forum = BoincForum::lookup_id($thread->forum);
769
770
    $content = output_transform($post->content, $options);
771
    $when = time_diff_str($post->timestamp, time());
772
    $user = BoincUser::lookup_id($post->user);
773
    if (!$user){
774
        return;
775
    }
776
777
    $config = get_config();
778
    $title = cleanup_title($thread->title);
779
    if ($post->hidden) {
780
        $deleted = "<br><font color=red>[".tra("Hidden by a moderator")."]</font>";
781
    } else {
782
        $deleted = "";
783
    }
784
    echo "
785
        <tr>
786
        <td>
787
            $n)
788
    ";
789
    switch ($forum->parent_type) {
790
    case 0:
791
        $category = BoincCategory::lookup_id($forum->category);
792
        echo forum_title($category, $forum, $thread, true);
793
        break;
794
    case 1:
795
        echo team_forum_title($forum);
796
        break;
797
    }
798
    echo "
799
        (<a href=\"forum_thread.php?id=".$thread->id."&amp;postid=".$post->id."\">".tra("Message %1", $post->id)."</a>)
800
        <br>
801
        ".tra("Posted %1 by %2", $when, user_links($user))." $deleted
802
        <br>
803
        Post:
804
        <hr>
805
        $content
806
        </td></tr>
807
    ";
808
}
809
810
function is_banished($user) {
811
    if (isset($user->prefs)) {
812
        return ($user->prefs->banished_until > time());
813
    } else {
814
        return false;
815
    }
816
}
817
818
function check_banished($user) {
819
    if (is_banished($user)) {
820
        error_page(
821
            tra("You may not post or rate messages until %1", gmdate('M j, Y', $user->prefs->banished_until))
822
        );
823
    }
824
}
825
826
function post_rules() {
827
    if (defined('FORUM_RULES')) return FORUM_RULES;
828
    if (function_exists("project_forum_post_rules")) {
829
      $project_rules=project_forum_post_rules();
830
    } else {
831
      $project_rules="";
832
    }
833
    return sprintf("
834
        <ul>
835
        <li> %s
836
        <li> %s
837
        <li> %s
838
        <li> %s
839
        <li> %s
840
        <li> %s
841
        <li> %s
842
        <li> %s
843
        <li> %s
844
        %s
845
        </ul>
846
        ",
847
        tra("Posts must be 'kid friendly': they may not contain content that is obscene, hate-related, sexually explicit or suggestive."),
848
        tra("No commercial advertisements."),
849
        tra("No links to web sites involving sexual content, gambling, or intolerance of others."),
850
        tra("No messages intended to annoy or antagonize other people, or to hijack a thread."),
851
        tra("No messages that are deliberately hostile, threatening, or insulting."),
852
        tra("No abusive messages involving race, religion, nationality, gender, class or sexuality."),
853
        tra("Posts that violate these rules may be deleted."),
854
        tra("The posting privileges of violators may be suspended or revoked."),
855
        tra("If your account is suspended, don't create a new one."),
856
        $project_rules
857
    );
858
}
859
860
function post_warning($forum=null) {
861
    $x = "<br><br>
862
        <table><tr><td align=left>
863
    ";
864
865
    // let projects add extra instructions in specific forums,
866
    // e.g. Questions and Problems
867
    //
868
    if (function_exists('project_forum_post_info')) {
869
        $x .= project_forum_post_info($forum);
870
    }
871
872
    $x .= "
873
        <small>
874
        ".post_rules()."
875
        </small>
876
        </td></tr></table>
877
    ";
878
    return $x;
879
}
880
881
function notify_thread_subscriber($thread, $user) {
882
    BoincForumPrefs::lookup($user);
883
    if ($user->prefs->pm_notification == 1) {
884
        send_thread_notification_email($thread, $user);
885
    }
886
    $now = time();
887
    $type = NOTIFY_SUBSCRIBED_THREAD;
888
    BoincNotify::replace(
889
        "userid=$user->id, create_time=$now, type=$type, opaque=$thread->id"
890
    );
891
}
892
893
// notify subscribed users, except for the given user
894
//
895
function notify_thread_subscribers($thread, $user) {
896
    $subs = BoincSubscription::enum("threadid=$thread->id");
897
    foreach ($subs as $sub) {
898
        if ($user && ($user->id == $sub->userid)) continue;
899
        $user2 = BoincUser::lookup_id($sub->userid);
900
        if ($user2) {
901
            notify_thread_subscriber($thread, $user2);
902
        }
903
    }
904
}
905
906
function notify_forum_subscriber($forum, $user) {
907
    BoincForumPrefs::lookup($user);
908
    if ($user->prefs->pm_notification == 1) {
909
        send_forum_notification_email($forum, $user);
910
    }
911
    $now = time();
912
    $type = NOTIFY_SUBSCRIBED_FORUM;
913
    BoincNotify::replace(
914
        "userid=$user->id, create_time=$now, type=$type, opaque=$forum->id"
915
    );
916
}
917
918
// notify subscribed users, except for the given user
919
//
920
function notify_forum_subscribers($forum, $user) {
921
    $id = -$forum->id;
922
    $subs = BoincSubscription::enum("threadid=$id");
923
    foreach ($subs as $sub) {
924
        if ($user && ($user->id == $sub->userid)) continue;
925
        $user2 = BoincUser::lookup_id($sub->userid);
926
        if ($user2) {
927
            notify_forum_subscriber($forum, $user2);
928
        }
929
    }
930
}
931
932
// Various functions for adding/hiding/unhiding stuff.
933
// These take care of counts and timestamps.
934
// Don't do these things directly - use these functions
935
//
936
function create_post($content, $parent_id, $user, $forum, $thread, $signature) {
937
    global $forum_error;
938
    if (POST_MAX_LINKS
939
        && link_count($content) > POST_MAX_LINKS
940
        && !is_moderator($user, $forum)
941
    ) {
942
        $forum_error = "Too many links.";
943
        return null;
944
    }
945
    $content = substr($content, 0, 64000);
946
    $content = BoincDb::escape_string($content);
947
    $now = time();
948
    $sig = $signature?1:0;
949
    $id = BoincPost::insert("(thread, user, timestamp, content, modified, parent_post, score, votes, signature, hidden) values ($thread->id, $user->id, $now, '$content', 0, $parent_id, 0, 0, $sig, 0)");
950
    if (!$id) {
951
        $forum_error = "Failed to add post to DB.";
952
        return null;
953
    }
954
955
    notify_thread_subscribers($thread, $user);
956
957
    $user->prefs->update("posts=posts+1");
958
    $thread->update("replies=replies+1, timestamp=$now");
959
    $forum->update("posts=posts+1, timestamp=$now");
960
    return $id;
961
}
962
963
// call this when hide or delete a post;
964
// it sets timestamp to time of last non-hidden post
965
//
966
function update_thread_timestamp($thread) {
967
    $posts = BoincPost::enum("thread=$thread->id and hidden=0 order by timestamp desc limit 1");
968
    if (count($posts)>0) {
969
        $post = $posts[0];
970
        $thread->update("timestamp=$post->timestamp");
971
    }
972
}
973
974
function update_forum_timestamp($forum) {
975
    $threads = BoincThread::enum("forum=$forum->id and hidden=0 order by timestamp desc limit 1");
976
    if (count($threads)>0) {
977
        $thread = $threads[0];
978
        $forum->update("timestamp=$thread->timestamp");
979
    }
980
}
981
982
function create_thread($title, $content, $user, $forum, $signature, $export) {
983
    global $forum_error;
984
    if (POST_MAX_LINKS
985
        && link_count($content) > POST_MAX_LINKS
986
        && !is_moderator($user, $forum)
987
    ) {
988
        $forum_error = "Too many links.";
989
        return null;
990
    }
991
    $title = trim($title);
992
    $title = sanitize_tags($title);
993
    $title = BoincDb::escape_string($title);
994
    $now = time();
995
    $status = 0;
996
    if (is_news_forum($forum) && !$export) {
997
        $status = 1;
998
    }
999
    $id  = BoincThread::insert("(forum, owner, status, title, timestamp, views, replies, activity, sufferers, score, votes, create_time, hidden, sticky, locked) values ($forum->id, $user->id, $status, '$title', $now, 0, -1, 0, 0, 0, 0, $now, 0, 0, 0)");
1000
    if (!$id) {
1001
        $forum_error = "Failed to add thread to DB.";
1002
        return null;
1003
    }
1004
    $thread = BoincThread::lookup_id($id);
1005
    create_post($content, 0, $user, $forum, $thread, $signature);
1006
    $forum->update("threads=threads+1");
1007
    notify_forum_subscribers($forum, $user);
1008
    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1009
    return $thread;
0 ignored issues
show
Unused Code introduced by
return $thread is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1010
}
1011
1012
function hide_post($post, $thread, $forum) {
1013
    $ret = $post->update("hidden=1");
1014
    if (!$ret) return $ret;
1015
    $thread->update("replies=if(replies>0, replies-1, 0)");
1016
    $forum->update("posts=if(posts>0, posts-1, 0)");
1017
    update_thread_timestamp($thread);
1018
    update_forum_timestamp($forum);
1019
    return true;
1020
}
1021
1022
function unhide_post($post, $thread, $forum) {
1023
    $ret = $post->update("hidden=0");
1024
    if (!$ret) return $ret;
1025
    $thread->update("replies=replies+1");
1026
    $forum->update("posts=posts+1");
1027
    update_thread_timestamp($thread);
1028
    update_forum_timestamp($forum);
1029
    return true;
1030
}
1031
1032
function delete_post($post, $thread, $forum) {
1033
    $post->delete();
1034
    if (!$post->hidden) {
1035
        $thread->update("replies=if(replies>0, replies-1, 0)");
1036
        $forum->update("posts=if(posts>0, posts-1, 0)");
1037
    }
1038
    $count = BoincPost::count("thread=$thread->id");
1039
    if ($count == 0) {
1040
        if (!$thread->hidden) {
1041
            $forum->update("threads=if(threads>0, threads-1, 0)");
1042
        }
1043
        $thread->delete();
1044
    } else {
1045
        update_thread_timestamp($thread);
1046
    }
1047
    return true;
1048
}
1049
1050
function delete_thread($thread, $forum) {
1051
    $nposts = BoincPost::count("thread=$thread->id and hidden=0");
1052
    $forum->update("posts=posts-$nposts");
1053
    BoincPost::delete_aux("thread=$thread->id");
1054
    if (!$thread->hidden) {
1055
        $forum->update("threads=if(threads>0, threads-1, 0)");
1056
    }
1057
    $thread->delete();
1058
}
1059
1060
// delete all forum records related to user
1061
//
1062
function forum_delete_user($user) {
1063
    $pp = BoincPost::enum("user=$user->id");
1064
    foreach ($pp as $p) {
1065
        $t = BoincThread::lookup_id($p->thread);
1066
        $f = BoincForum::lookup_id($t->forum);
1067
        if ($t && $f) {
1068
            delete_post($p, $t, $f);
1069
        }
1070
    }
1071
    $ss = BoincSubscription::enum("userid=$user->id");
1072
    foreach ($ss as $s) {
1073
        BoincSubscription::delete($s->userid, $s->threadid);
1074
    }
1075
    $p = BoincForumPrefs::lookup_userid($user->id);
1076
    if ($p) $p->delete();
1077
    BoincForumLogging::delete_aux("userid=$user->id");
1078
}
1079
1080
function move_post($post, $old_thread, $old_forum, $new_thread, $new_forum) {
1081
    global $g_logged_in_user;
1082
    $post->update("thread=$new_thread->id");
1083
    $old_thread->update("replies=if(replies>0, replies-1, 0)");
1084
    $new_thread->update("replies=replies+1");
1085
    $old_forum->update("posts=if(posts>0, posts-1, 0)");
1086
    $new_forum->update("posts=posts+1");
1087
    update_thread_timestamp($old_thread);
1088
    update_thread_timestamp($new_thread);
1089
    update_forum_timestamp($old_forum);
1090
    update_forum_timestamp($new_forum);
1091
    notify_thread_subscribers($new_thread, $g_logged_in_user);
1092
    return true;
1093
}
1094
1095
function hide_thread($thread, $forum) {
1096
    $ret = $thread->update("hidden=1");
1097
    if (!$ret) return $ret;
1098
    $forum->update("threads=if(threads>0, threads-1, 0)");
1099
    $forum->update("posts=posts-$thread->replies-1");
1100
    update_forum_timestamp($forum);
1101
    return true;
1102
}
1103
1104
function unhide_thread($thread, $forum) {
1105
    $ret = $thread->update("hidden=0");
1106
    if (!$ret) return $ret;
1107
    $forum->update("threads=threads+1, posts=posts+$thread->replies+1");
1108
    update_forum_timestamp($forum);
1109
    return true;
1110
}
1111
1112
function move_thread($thread, $old_forum, $new_forum) {
1113
    $now = time();
1114
    $old_forum->update("threads=if(threads>0, threads-1, 0), posts=posts-$thread->replies-1");
1115
    $new_forum->update("threads=threads+1, posts=posts+$thread->replies+1, timestamp=$now");
1116
    return $thread->update("forum=$new_forum->id");
1117
}
1118
1119
// $show_hidden: 1 if it is a moderator reading
1120
// Error page if this function returns NULL.
1121
// $forumID - int
1122
// $min - int
1123
// $nRec - int
1124
// $sort_style - string (checked by switch statement)
1125
// $show_hidden - bool (not directly passed to SQL)
1126
// $sticky - bool (not directly passed to SQL)
1127
//
1128
function get_forum_threads(
1129
    $forumID, $start=-1, $nRec=-1, $sort_style=MODIFIED_NEW,
0 ignored issues
show
Coding Style introduced by
Multi-line function declarations must define one parameter per line
Loading history...
1130
    $show_hidden = 0, $sticky = 1
0 ignored issues
show
Coding Style introduced by
Multi-line function declarations must define one parameter per line
Loading history...
1131
) {
1132
    //if (! (is_numeric($forumID) && is_numeric($min) && is_numeric($nRec))) {
1133
    //    return NULL;  // Something is wrong here.
1134
    //}
1135
1136
    $sql = 'forum = ' . $forumID ;
0 ignored issues
show
Coding Style introduced by
Space found before semicolon; expected "$forumID;" but found "$forumID ;"
Loading history...
1137
    $stickysql = "";
1138
    if ($sticky){
1139
        $stickysql = "sticky DESC, ";
1140
    }
1141
    if (!$show_hidden) {
1142
        $sql .= ' AND hidden = 0';
1143
    }
1144
    switch($sort_style) {
1145
    case MODIFIED_NEW:
1146
        $sql .= ' ORDER BY '.$stickysql.'timestamp DESC';
1147
        break;
1148
    case MODIFIED_OLD:
1149
        $sql .= ' ORDER BY '.$stickysql.'timestamp ASC';
1150
        break;
1151
    case VIEWS_MOST:
1152
        $sql .= ' ORDER BY '.$stickysql.'views DESC';
1153
        break;
1154
    case REPLIES_MOST:
1155
        $sql .= ' ORDER BY '.$stickysql.'replies DESC';
1156
        break;
1157
    case CREATE_TIME_NEW:
1158
        $sql .= ' ORDER by '.$stickysql.'create_time desc';
1159
        break;
1160
    case CREATE_TIME_OLD:
1161
        $sql .= ' ORDER by '.$stickysql.'create_time asc';
1162
        break;
1163
    case 'sufferers':
1164
        $sql .= ' ORDER by '.$stickysql.'sufferers desc';
1165
        break;
1166
    case 'activity':
1167
        $sql .= ' ORDER by '.$stickysql.'activity desc';
1168
        break;
1169
    case 'score':
1170
        $sql .= ' ORDER by '.$stickysql.'score desc';
1171
        break;
1172
    default:
0 ignored issues
show
Coding Style introduced by
DEFAULT keyword must be indented 4 spaces from SWITCH keyword
Loading history...
1173
        $sql .= ' ORDER BY '.$stickysql.'timestamp DESC';
1174
        break;
1175
    }
1176
    if ($start > -1) {
1177
        $sql .= ' LIMIT '.$start;
1178
        if ($nRec > -1) {
1179
            $sql .= ', '.$nRec;
1180
        }
1181
    } else if ($nRec > -1) {
1182
        $sql .= ' LIMIT '.$nRec;
1183
    }
1184
    return BoincThread::enum($sql);
1185
}
1186
1187
// $show_hidden = true when it is a moderator reading
1188
// error_page if this function returns NULL.
1189
// $sort_style - string (checked by switch statement)
1190
// $show_hidden - bool (not directly passed to SQL)
1191
//
1192
function get_thread_posts($threadid, $sort_style, $show_hidden) {
1193
    $sql = "thread=$threadid";
1194
    if (!$show_hidden) {
1195
        $sql .= ' AND hidden = 0';
1196
    }
1197
    switch($sort_style) {
1198
    case CREATE_TIME_NEW:
1199
        $sql .= ' ORDER BY timestamp desc';
1200
        break;
1201
    case CREATE_TIME_OLD:
1202
        $sql .= ' ORDER BY timestamp asc';
1203
        break;
1204
    case POST_SCORE:
1205
        $sql .= ' ORDER BY score DESC';
1206
        break;
1207
    default:
0 ignored issues
show
Coding Style introduced by
DEFAULT keyword must be indented 4 spaces from SWITCH keyword
Loading history...
1208
        $sql .= ' ORDER BY timestamp asc';
1209
        break;
1210
    }
1211
    return BoincPost::enum($sql);
1212
}
1213
1214
// Show links for post moderation actions;
1215
// logged in user has moderation rights.
1216
//
1217
function show_post_moderation_links(
1218
    $config, $logged_in_user, $post, $forum, $tokens
0 ignored issues
show
Coding Style introduced by
Multi-line function declarations must define one parameter per line
Loading history...
1219
){
0 ignored issues
show
Coding Style introduced by
There must be a single space between the closing parenthesis and the opening brace of a multi-line function declaration; found 0 spaces
Loading history...
1220
    $moderators_allowed_to_ban = parse_bool($config, "moderators_allowed_to_ban");
1221
    $moderators_vote_to_ban = parse_bool($config, "moderators_vote_to_ban");
1222
1223
    if ($post->hidden) {
1224
        show_button_small("forum_moderate_post_action.php?action=unhide&amp;id=".$post->id."$tokens", tra("Unhide"), tra("Unhide this post"));
1225
    } else {
1226
        show_button_small("forum_moderate_post.php?action=hide&amp;id=".$post->id."$tokens", tra("Hide"), tra("Hide this post"));
1227
    }
1228
1229
    show_button_small(
1230
        "forum_moderate_post.php?action=move&amp;id=".$post->id."$tokens",
1231
        tra("Move"), tra("Move post to a different thread")
1232
    );
1233
1234
    if ($forum->parent_type == 0) {
1235
        if (is_admin($logged_in_user) || $moderators_allowed_to_ban) {
1236
            show_button_small("forum_moderate_post.php?action=banish_user&amp;id=".$post->id."&amp;userid=".$post->user."$tokens", tra("Banish author"));
1237
        }
1238
        if ($moderators_vote_to_ban) {
1239
            require_once("../inc/forum_banishment_vote.inc");
1240
            if (vote_is_in_progress($post->user)) {
1241
                show_button_small(
1242
                    "forum_banishment_vote.php?action=yes&amp;userid=".$post->user,
1243
                    tra("Vote to banish author")
1244
                );
1245
                show_button_small(
1246
                    "forum_banishment_vote.php?action=no&amp;userid=".$post->user,
1247
                    tra("Vote not to banish author")
1248
                );
1249
            } else {
1250
                show_button_small(
1251
                    "forum_banishment_vote.php?action=start&amp;userid=".$post->user,
1252
                    tra("Start vote to banish author")
1253
                );
1254
            }
1255
        }
1256
        if (is_admin($logged_in_user)) {
1257
            show_button_small("forum_moderate_post.php?action=delete&amp;id=".$post->id."$tokens", tra("Delete"), tra("Delete this post"));
1258
        }
1259
    }
1260
}
1261
1262
// is the given user allowed to
1263
// - add threads to News forum
1264
// - use HTML in posts
1265
// - delete threads and posts
1266
//
1267
function is_admin($user) {
1268
    if (!$user) return false;
1269
    if ($user->prefs->privilege(S_SCIENTIST)) return true;
1270
    if ($user->prefs->privilege(S_DEV)) return true;
1271
    if ($user->prefs->privilege(S_ADMIN)) return true;
1272
    return false;
1273
}
1274
1275
function user_can_create_thread($user, $forum) {
1276
    if (!$user) return false;
1277
    if ($forum->is_dev_blog && !is_admin($user)) {
1278
        return false;
1279
    }
1280
    return true;
1281
}
1282
1283
function check_post_access($user, $forum) {
1284
    if (is_admin($user)) return;
1285
1286
    switch ($forum->parent_type) {
1287
    case 0:
1288
        if ($user->prefs->privilege(S_MODERATOR)) return;
1289
        break;
1290
    case 1:
1291
        $team = BoincTeam::lookup_id($forum->category);
1292
        if (is_team_admin($user, $team)) return;
1293
1294
        // non-team-members can't post
1295
        //
1296
        if ($user->teamid != $team->id) {
1297
            error_page(tra("Only team members can post to the team message board"));
1298
        }
1299
        break;
1300
    }
1301
1302
    // If user haven't got enough credit (according to forum regulations)
1303
    // We do not tell the (ab)user how much this is -
1304
    // no need to make it easy for them to break the system.
1305
    //
1306
    if ($user->total_credit<$forum->post_min_total_credit || $user->expavg_credit<$forum->post_min_expavg_credit) {
1307
        error_page(tra("To create a new thread in %1 you must have a certain level of average credit. This is to protect against abuse of the system.", $forum->title));
1308
    }
1309
1310
    // If the user is posting faster than forum regulations allow
1311
    // Tell the user to wait a while before creating any more posts
1312
    //
1313
    if (time()-$user->prefs->last_post <$forum->post_min_interval) {
1314
        error_page(tra("You cannot create threads right now. Please wait before trying again. This is to protect against abuse of the system."));
1315
    }
1316
}
1317
1318
function check_reply_access($user, $forum, $thread) {
1319
    if ($thread->locked && !is_moderator($user, $forum)) {
1320
        error_page(
1321
            tra("This thread is locked. Only forum moderators and administrators are allowed to post there.")
1322
        );
1323
    }
1324
    if ($thread->hidden) {
1325
        error_page(
1326
           tra("Can't post to a hidden thread.")
1327
        );
1328
    }
1329
1330
    check_post_access($user, $forum);
1331
}
1332
1333
// is the given user allowed to moderate the given forum? this includes
1334
// - post to locked threads
1335
// - see hidden threads and posts
1336
// - edit their posts at any time
1337
// - hide/unhide/move threads and posts
1338
1339
function is_moderator($user, $forum=null) {
1340
    if (!$user) return false;
1341
    BoincForumPrefs::lookup($user);
1342
    $type = $forum?$forum->parent_type:0;
1343
    switch ($type) {
1344
    case 0:
1345
        if ($user->prefs->privilege(S_MODERATOR)) return true;
1346
        if ($user->prefs->privilege(S_ADMIN)) return true;
1347
        if ($user->prefs->privilege(S_DEV)) return true;
1348
        if ($user->prefs->privilege(S_SCIENTIST)) return true;
1349
        break;
1350
    case 1:
1351
        if ($user->prefs->privilege(S_ADMIN)) return true;
1352
        $team = BoincTeam::lookup_id($forum->category);
1353
        return is_team_admin($user, $team);
1354
        break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1355
    }
1356
    return false;
1357
}
1358
1359
function thread_list_header($subscriptions=false) {
1360
    if ($subscriptions) {
1361
        $x = [
1362
            tra("Thread"),
1363
            tra("Author"),
1364
            "<nobr>".tra("Last post")."</nobr>",
1365
            'Unsubscribe'
0 ignored issues
show
Coding Style introduced by
There should be a trailing comma after the last value of an array declaration.
Loading history...
1366
        ];
1367
    } else {
1368
        $x = [
1369
            tra("Thread"),
1370
            tra("Posts"),
1371
            tra("Author"),
1372
            tra("Views"),
1373
            "<nobr>".tra("Last post")."</nobr>"
0 ignored issues
show
Coding Style introduced by
There should be a trailing comma after the last value of an array declaration.
Loading history...
1374
        ];
1375
    }
1376
    row_heading_array($x);
1377
}
1378
1379
// show a 1-line summary of thread and its forum.
1380
// Used for search results and subscription list
1381
//
1382
function thread_list_item($thread, $user, $subscriptions=false) {
1383
    $thread_forum = BoincForum::lookup_id($thread->forum);
1384
    if (!$thread_forum) return;
1385
    if (!is_forum_visible_to_user($thread_forum, $user)) return;
1386
    $owner = BoincUser::lookup_id($thread->owner);
1387
    if (!$owner) return;
1388
    switch($thread_forum->parent_type) {
1389
    case 0:
1390
        $category = BoincCategory::lookup_id($thread_forum->category);
1391
        $title = forum_title($category, $thread_forum, $thread, true);
1392
        break;
1393
    case 1:
1394
        $title = team_forum_title($thread_forum, $thread);
1395
        break;
1396
    default:
0 ignored issues
show
Coding Style introduced by
DEFAULT keyword must be indented 4 spaces from SWITCH keyword
Loading history...
Coding Style introduced by
DEFAULT case must have a breaking statement
Loading history...
1397
        $title = '???';
1398
    }
1399
    if ($subscriptions) {
1400
        $x = [
1401
            $title,
1402
            user_links($owner),
1403
            time_diff_str($thread->timestamp, time()),
1404
            "<input type=checkbox name=unsub_thread_$thread->id>"
0 ignored issues
show
Coding Style introduced by
There should be a trailing comma after the last value of an array declaration.
Loading history...
1405
        ];
1406
    } else {
1407
        $x = [
1408
            $title,
1409
            $thread->replies+1,
1410
            user_links($owner),
1411
            $thread->views,
1412
            time_diff_str($thread->timestamp, time())
0 ignored issues
show
Coding Style introduced by
There should be a trailing comma after the last value of an array declaration.
Loading history...
1413
        ];
1414
    }
1415
    row_array($x);
1416
}
1417
1418
// see if ID is in subscription list
1419
//
1420
function is_subscribed($id, $subs) {
1421
    foreach ($subs as $sub) {
1422
        if ($sub->threadid == $id) return true;
1423
    }
1424
    return false;
1425
}
1426
1427
function is_forum_visible_to_user($forum, $user) {
1428
    if ($forum->parent_type == 1) {
1429
        if (parse_config(get_config(), "<team_forums_members_only>")) {
1430
            if (!$user) return false;
1431
            if ($user->teamid != $forum->category) return false;
1432
        }
1433
    }
1434
    return true;
1435
}
1436
1437
function subscribed_thread_email_line($notify) {
1438
    $thread = BoincThread::lookup_id($notify->opaque);
1439
    return "There are new posts in the thread '$thread->title'";
1440
}
1441
1442
function subscribed_thread_web_line($notify) {
1443
    $thread = BoincThread::lookup_id($notify->opaque);
1444
    return tra("New posts in the thread %1","<a href=forum_thread.php?id=$thread->id>$thread->title</a>");
1445
}
1446
1447
function subscribed_thread_rss($notify) {
1448
    $thread = BoincThread::lookup_id($notify->opaque);
1449
    $title = tra("New posts in subscribed thread");
1450
    $msg = tra("There are new posts in the thread '%1'",$thread->title);
1451
    $url = secure_url_base()."forum_thread.php?id=$thread->id";
1452
    return [$title, $msg, $url];
1453
}
1454
1455
function subscribed_forum_email_line($notify) {
1456
    $forum = BoincForum::lookup_id($notify->opaque);
1457
    return "There are new threads in the forum '$forum->title'";
1458
}
1459
1460
function subscribed_forum_web_line($notify) {
1461
    $forum = BoincForum::lookup_id($notify->opaque);
1462
    return tra("New threads in the forum %1","<a href=forum_forum.php?id=$forum->id>$forum->title</a>");
1463
}
1464
1465
function subscribed_forum_rss($notify) {
1466
    $forum = BoincForum::lookup_id($notify->opaque);
1467
    $title = tra("New posts in subscribed forum");
1468
    $msg = tra("There are new threads in the forum '%1'",$forum->title);
1469
    $url = secure_url_base()."forum_forum.php?id=$forum->id";
1470
    return [$title, $msg, $url];
1471
}
1472
1473
function show_mark_as_read_button($user) {
1474
    if ($user) {
1475
        $return = urlencode(current_url());
1476
        $tokens = url_tokens($user->authenticator);
1477
        $url = "forum_index.php?read=1$tokens&amp;return=$return";
1478
        show_button($url,
1479
            tra("Mark all threads as read"),
1480
            tra("Mark all threads in all message boards as read.")
1481
        );
1482
    }
1483
}
1484
1485
function remove_subscriptions_forum($userid, $forumid) {
1486
    $subs = BoincSubscription::enum("userid=$userid and threadid>0");
1487
    foreach ($subs as $sub) {
1488
        $thread = BoincThread::lookup_id($sub->threadid);
1489
        if ($thread && $thread->forum == $forumid) {
1490
            BoincSubscription::delete($userid, $thread->id);
1491
        }
1492
    }
1493
    $notices = BoincNotify::enum("userid=$userid and type=".NOTIFY_SUBSCRIBED_THREAD);
1494
    foreach ($notices as $n) {
1495
        $thread = BoincThread::lookup_id($n->opaque);
1496
        if ($thread && $thread->forum == $forumid) {
1497
            $n->delete();
1498
        }
1499
    }
1500
}
1501
1502
function remove_subscriptions_thread($userid, $threadid) {
1503
    BoincSubscription::delete($userid, $threadid);
1504
    BoincNotify::delete_aux("userid=$userid and type=".NOTIFY_SUBSCRIBED_THREAD." and opaque=$threadid");
1505
}
1506
1507
function parse_forum_cookie() {
1508
    $x = array("", "");
1509
    if (isset($_COOKIE['sorting'])) {
1510
        $a = explode("|", $_COOKIE['sorting']);
1511
        if (array_key_exists(0, $a)) {
1512
            $x[0] = $a[0];
1513
        }
1514
        if (array_key_exists(1, $a)) {
1515
            $x[1] = $a[1];
1516
        }
1517
    }
1518
    return $x;
1519
}
1520
?>
1521