Issues (1963)

html/inc/forum.inc (27 issues)

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
define('FORUM_LH_PCT', '25%');      // width of LH column
28
29
$forum_error = "";
30
    // for functions that return null on error,
31
    // look here for an explanation
32
33
// sorting styles (for both threads and posts)
34
//
35
define('MODIFIED_NEW', 1);
36
define('MODIFIED_OLD', 2);
37
define('VIEWS_MOST', 3);
38
define('REPLIES_MOST', 4);
39
define('CREATE_TIME_NEW', 5);
40
define('CREATE_TIME_OLD', 6);
41
define('POST_SCORE', 7);
42
43
// names for the above
44
//
45
$thread_sort_styles[CREATE_TIME_OLD] = tra("Oldest first");
46
$thread_sort_styles[CREATE_TIME_NEW] = tra("Newest first");
47
$thread_sort_styles[POST_SCORE] = tra("Highest rated posts first");
48
49
$forum_sort_styles[MODIFIED_NEW] = tra("Newest post first");
50
$forum_sort_styles[VIEWS_MOST] = tra("Most views first");
51
$forum_sort_styles[REPLIES_MOST] = tra("Most posts first");
52
$forum_sort_styles[CREATE_TIME_NEW] = tra("Newest first");
53
54
// values for thread.status
55
define('THREAD_SOLVED', 1);
56
57
define('AVATAR_WIDTH', 100);
58
define('AVATAR_HEIGHT',100);
59
60
define('ST_NEW_TIME', 1209600); //3600*24*14 - 14 days
61
define('ST_NEW', 'New member');
62
63
if (!defined('MAXIMUM_EDIT_TIME')) {
64
    define('MAXIMUM_EDIT_TIME', 3600);
65
        // allow edits of forums posts up till one hour after posting.
66
}
67
68
define('MAX_FORUM_LOGGING_TIME', 2419200); //3600*24*28 - 28 days
69
define('NO_CONTROLS', 0);
70
define('FORUM_CONTROLS', 1);
71
define('HELPDESK_CONTROLS', 2);
72
define("EXCERPT_LENGTH", "120");
73
74
define('NEW_IMAGE', 'img/unread_post.png');
75
define('NEW_IMAGE_STICKY', 'img/unread_sticky.png');
76
define('NEW_IMAGE_LOCKED', 'img/unread_locked.png');
77
define('NEW_IMAGE_STICKY_LOCKED', 'img/unread_sticky_locked.png');
78
define('IMAGE_STICKY', 'img/sticky_post.png');
79
define('IMAGE_LOCKED', 'img/locked_post.png');
80
define('IMAGE_HIDDEN', 'img/hidden.png');
81
define('IMAGE_STICKY_LOCKED', 'img/sticky_locked_post.png');
82
define('IMAGE_POST', 'img/post.png');
83
define('NEW_IMAGE_HEIGHT','15');
84
define('EMPHASIZE_IMAGE', 'img/emphasized_post.png');
85
define('EMPHASIZE_IMAGE_HEIGHT','15');
86
define('FILTER_IMAGE', 'img/filtered_post.png');
87
define('FILTER_IMAGE_HEIGHT','15');
88
define('RATE_POSITIVE_IMAGE', 'img/rate_positive.png');
89
define('RATE_POSITIVE_IMAGE_HEIGHT','9');
90
define('RATE_NEGATIVE_IMAGE', 'img/rate_negative.png');
91
define('RATE_NEGATIVE_IMAGE_HEIGHT','9');
92
define('REPORT_POST_IMAGE', 'img/report_post.png');
93
define('REPORT_POST_IMAGE_HEIGHT','9');
94
95
define('SOLUTION', tra('This answered my question'));
96
define('SUFFERER', tra('I also have this question'));
97
define('OFF_TOPIC', tra('Off-topic'));
98
99
define ('DEFAULT_LOW_RATING_THRESHOLD', -25);
100
define ('DEFAULT_HIGH_RATING_THRESHOLD', 5);
101
102
// special user attributes
103
//
104
define('S_MODERATOR', 0);
105
define('S_ADMIN', 1);
106
define('S_DEV', 2);
107
define('S_TESTER', 3);
108
define('S_VOLUNTEER', 4);
109
define('S_VOLUNTEER_TESTER', 5);
110
define('S_SCIENTIST', 6);
111
define('S_HELP_DESK_EXPERT', 7);
112
define('S_NFLAGS', 8);
113
114
$special_user_bitfield[S_MODERATOR] = tra("Volunteer moderator");
115
$special_user_bitfield[S_ADMIN] = tra("Project administrator");
116
$special_user_bitfield[S_DEV] = tra("Project developer");
117
$special_user_bitfield[S_TESTER] = tra("Project tester");
118
$special_user_bitfield[S_VOLUNTEER] = tra("Volunteer developer");
119
$special_user_bitfield[S_VOLUNTEER_TESTER] = tra("Volunteer tester");
120
$special_user_bitfield[S_SCIENTIST] = tra("Project scientist");
121
$special_user_bitfield[S_HELP_DESK_EXPERT] = tra("Help desk expert");
122
123
function link_count($x) {
124
    $n = 0;
125
    while (1) {
126
        $x = strstr($x, "[url");
127
        if (!$x) break;
128
        $n++;
129
        $x = substr($x, 4);
130
    }
131
    return $n;
132
}
133
134
// show a banner with search form on left and PM info on right
135
//
136
function show_forum_header($user) {
137
    echo '<form action="forum_search_action.php" method="POST">
138
    ';
139
    start_table();
140
    echo '
141
        <tr>
142
    ';
143
144
    // Search
145
    echo '
146
        <td>
147
        <input type="hidden" name="search_max_time" value="0">
148
        <input type="hidden" name="search_forum" value="-1">
149
        <input type="hidden" name="search_sort" value="'.CREATE_TIME_NEW.'">
150
        <input type="text" class="" name="search_keywords">
151
    ';
152
    echo sprintf(
153
        '<input class="%s" %s title="%s" type="submit" value="%s"><br>',
154
        'btn btn-sm',
155
        button_style(),
156
        tra("Search for words in forum messages"),
157
        tra("Search forums")
158
    );
159
    echo '
160
        <small><a href="forum_search.php">'.tra("Advanced search").'</a></small>
161
        </td>
162
    ';
163
164
    if ($user) {
165
        echo "<td align=\"right\">\n";
166
        echo "<p>".tra("Private messages").": ", pm_notification($user);
167
        echo "</td>\n";
168
    }
169
    echo "</tr>
170
    ";
171
    end_table();
172
    echo "</form>
173
    ";
174
}
175
176
// return forum/thread title, with links.
177
//
178
function forum_title($category, $forum, $thread, $link_thread=false) {
179
    if ($category) {
180
        $is_helpdesk = $category->is_helpdesk;
181
    } else {
182
        $is_helpdesk = false;
183
    }
184
185
    $where = $is_helpdesk?tra("Questions and Answers"):tra("Message boards");
186
    $top_url = $is_helpdesk?"forum_help_desk.php":"forum_index.php";
187
188
    $x = '';
189
    if (!$forum && !$thread) {
190
        $x .= $where;
191
    } else if ($forum && !$thread) {
192
        $x .= sprintf('<a href="%s">%s</a> : %s',
193
            $top_url,
194
            $where,
195
            $forum->title
196
        );
197
    } else if ($forum && $thread) {
198
        $x .= sprintf('<a href="%s">%s</a> : ',
199
            $top_url,
200
            $where
201
        );
202
        $x .= sprintf('<a href="forum_forum.php?id=%d">%s</a> : ',
203
            $forum->id,
204
            $forum->title
205
        );
206
        if ($link_thread) {
207
            $x .= sprintf('<a href=forum_thread.php?id=%d>%s</a>',
208
                $thread->id,
209
                cleanup_title($thread->title)
210
            );
211
        } else {
212
            $x .= cleanup_title($thread->title);
213
        }
214
    } else {
215
        $x = "Invalid thread ID";
216
    }
217
    return $x;
218
}
219
220
function team_forum_title($forum, $thread=null, $link_thread=false) {
221
    $team = BoincTeam::lookup_id($forum->category);
222
    $x = sprintf('<a href="forum_index.php">%s</a> :',
223
        tra("Message boards")
224
    );
225
    if ($thread) {
226
        $x .= sprintf('<a href=team_forum.php?teamid=%d>%s</a>',
227
            $team->id,
228
            tra("%1 message board", $team->name)
229
        );
230
        if ($link_thread) {
231
            $x .= " : <a href=forum_thread.php?id=$thread->id>$thread->title</a>";
232
        } else {
233
            $x .= " : $thread->title";
234
        }
235
    } else {
236
        $x .= tra("%1 message board", $team->name);
237
    }
238
    return $x;
239
}
240
241
// start a table of forum posts
242
//
243
function start_forum_table($headings) {
244
    $a = array();
245
    foreach ($headings as $h) {
246
        $a[] = null;
247
    }
248
    $a[1] = 'style="width: 100%"';
249
    start_table('table-striped');
250
    row_heading_array($headings, $a);
251
}
252
253
function page_link($url, $page_num, $items_per_page, $text) {
254
    return " <a href=\"$url&amp;start=" . $page_num*$items_per_page . "\">$text</a> ";
255
}
256
257
// return a string for navigating pages
258
//
259
function page_links($url, $nitems, $items_per_page, $start){
260
    // How many pages to potentially show before and after this one:
261
    $preshow = 3;
262
    $postshow = 3;
263
264
    $x = "";
265
266
    if ($nitems <= $items_per_page) return "";
267
    $npages = ceil($nitems / $items_per_page);
268
    $curpage = ceil($start / $items_per_page);
269
270
    // If this is not the first page, display "previous"
271
    //
272
    if ($curpage > 0){
273
        $x .= page_link(
274
            $url, $curpage-1, $items_per_page,
275
            tra("Previous")." &middot; "
276
        );
277
    }
278
279
    if ($curpage - $preshow > 0) {
280
        $x .= page_link($url, 0, $items_per_page, "1");
281
        if ($curpage - $preshow > 1) {
282
            $x .= " . . . ";
283
        } else {
284
            $x .= " &middot; ";
285
        }
286
    }
287
    // Display a list of pages surrounding this one
288
    //
289
    for ($i=$curpage-$preshow; $i<=$curpage+$postshow; $i++){
290
        $page_str = (string)($i+1);
291
        if ($i < 0) continue;
292
        if ($i >= $npages) break;
293
294
        if ($i == $curpage) {
295
            $x .= "<b>$page_str</b>";
296
        } else {
297
            $x .= page_link($url, $i, $items_per_page, $page_str);
298
        }
299
        if ($i == $npages-1) break;
300
        if ($i == $curpage+$postshow) break;
301
        $x .= " &middot; ";
302
    }
303
304
    if ($curpage + $postshow < $npages-1) {
305
        $x .= " . . . ";
306
        $x .= page_link($url, $npages-1, $items_per_page, $npages);
307
    }
308
    // If there is a next page
309
    //
310
    if ($curpage < $npages-1){
311
        $x .= page_link(
312
            $url, $curpage+1, $items_per_page,
313
            " &middot; ".tra("Next")
314
        );
315
    }
316
    $x .= "\n";
317
    return $x;
318
}
319
320
function thread_is_unread($user, $thread) {
321
    if (!$user) return false;
322
    if ($thread->timestamp <= $user->prefs->mark_as_read_timestamp) return false;
323
    $log = BoincForumLogging::lookup($user->id, $thread->id);
324
    if ($log && ($thread->timestamp <= $log->timestamp)) return false;
325
    return true;
326
}
327
328
//  Process a user-supplied title to remove HTML stuff
329
//
330
function cleanup_title($title) {
331
    $x = sanitize_tags(bb2html($title));
332
    $x = trim($x);
333
    if (strlen($x)==0) return "(no title)";
334
    else return $x;
335
}
336
337
function can_reply($thread, $forum, $user) {
338
    if ($thread->locked) {
339
        if (!is_moderator($user, $forum)) return false;
340
    }
341
    return true;
342
}
343
344
// Show the posts in a thread for a user.
345
// If $start is null, enforce jump-to-first-unread
346
//
347
function show_posts(
348
    $thread, $forum, $start, $postid, $sort_style, $filter, $logged_in_user
0 ignored issues
show
Multi-line function declarations must define one parameter per line
Loading history...
349
) {
350
    $num_to_show = 20;
351
    if ($logged_in_user && $logged_in_user->prefs->display_wrap_postcount > 0) {
352
        $num_to_show = $logged_in_user->prefs->display_wrap_postcount;
353
    }
354
355
    // let moderators see all posts, including hidden ones
356
    //
357
    if (is_moderator($logged_in_user, $forum)) {
358
        $show_hidden = true;
359
    } else {
360
        $show_hidden = false;
361
    }
362
363
    $posts = get_thread_posts($thread->id, $sort_style, $show_hidden);
364
365
    $latest_viewed = 0;
366
    $forum_log = null;
367
    if ($logged_in_user) {
368
        $forum_log = BoincForumLogging::lookup($logged_in_user->id, $thread->id);
369
        if ($forum_log) {
370
            $latest_viewed = $forum_log->timestamp;
371
        }
372
    }
373
374
    if ($sort_style == CREATE_TIME_OLD) {
375
        // show the last page
376
        //
377
        $nposts = sizeof($posts);
378
        if ($nposts) $nposts -= 1;
0 ignored issues
show
Decrement operators should be used where possible; found "$nposts -= 1;" but expected "$nposts--"
Loading history...
379
        $page = (int)($nposts/$num_to_show);
380
        $default_start = $page*$num_to_show;
381
    } else {
382
        $default_start = 0;
383
    }
384
385
    // jump to a specific post if needed
386
    //
387
    $jump_to_post = null;
388
    if ($start === null) {
389
        if ($postid) {
390
            // jump to a specific post
391
            //
392
            $i = 0;
393
            foreach ($posts as $post) {
394
                if ($post->id == $postid) {
395
                    $start = $i - ($i % $num_to_show);
396
                    $jump_to_post = $post;
397
                    break;
398
                }
399
                $i++;
400
            }
401
            if ($start === null) {
402
                echo "Post $postid not found.";
403
                return;
404
            }
405
        } else if ($logged_in_user && $logged_in_user->prefs->jump_to_unread) {
406
            // jump to the first unread post
407
            //
408
            $i = 0;
409
            $ibest = 0;
410
            foreach ($posts as $post) {
411
                if ($post->timestamp > $latest_viewed) {
412
                    if (!$jump_to_post || ($post->timestamp < $jump_to_post->timestamp)) {
413
                        $jump_to_post = $post;
414
                        $ibest = $i;
415
                    }
416
                }
417
                $i++;
418
            }
419
            // if jump to post, figure out what page to show
420
            //
421
            if ($jump_to_post) {
422
                $start = $ibest - ($ibest % $num_to_show);
423
            } else {
424
                $start = $default_start;
425
            }
426
        } else {
427
            $start = $default_start;
428
        }
429
    }
430
431
    $page_nav = page_links(
432
        "forum_thread.php?id=$thread->id&sort_style=$sort_style",
433
        sizeof($posts),
434
        $num_to_show,
435
        $start
436
    );
437
438
    echo $page_nav;
439
440
    $num_shown = 0;
441
    $num_skipped = 0;
442
    $headings = array(tra("Author"), tra("Message"));
443
    start_forum_table($headings);
444
445
    $latest_shown_timestamp = 0;
446
    foreach ($posts as $post) {
447
        if ($num_skipped < $start) {
448
            $num_skipped++;
449
            continue;
450
        }
451
        if ($num_shown == $num_to_show) {
452
            break;
453
        }
454
        show_post(
455
            $post, $thread, $forum, $logged_in_user, $start, $latest_viewed,
456
            FORUM_CONTROLS, $filter
457
        );
458
459
        if ($post->timestamp > $latest_shown_timestamp) {
460
            $latest_shown_timestamp = $post->timestamp;
461
        }
462
        $num_shown++;
463
    }
464
    end_table();
465
    echo $page_nav;
466
467
    if ($jump_to_post) {
468
        echo "<script>function jumpToUnread(){location.href='#".$jump_to_post->id."';}</script>";
469
    } else {
470
        echo "<script>function jumpToUnread(){};</script>";
471
    }
472
473
    // if thread has no visible posts, user has seen them all
474
    //
475
    if ($num_shown == 0) {
476
        $latest_shown_timestamp = time();
477
    }
478
479
    if ($logged_in_user) {
480
        if (!$forum_log || $latest_shown_timestamp > $forum_log->timestamp) {
481
            BoincForumLogging::replace(
482
                $logged_in_user->id, $thread->id, $latest_shown_timestamp
483
            );
484
        }
485
    }
486
}
487
488
function get_ignored_list($user) {
489
    return explode("|", $user->prefs->ignorelist);
490
}
491
492
function add_ignored_user($user, $other_user) {
493
    $list = explode("|", $user->prefs->ignorelist);
494
    foreach ($list as $key=>$userid) {
0 ignored issues
show
Expected 1 space before "=>"; 0 found
Loading history...
Expected 1 space after "=>"; 0 found
Loading history...
495
        if ($userid == $other_user->id) {
496
            return true;
497
        }
498
    }
499
    $list[] = $other_user->id;
500
    $x = implode("|", array_values($list));
501
    return $user->prefs->update("ignorelist='$x'");
502
}
503
504
function remove_ignored_user($user, $other_user) {
505
    $list = explode("|", $user->prefs->ignorelist);
506
    foreach ($list as $key=>$userid) {
0 ignored issues
show
Expected 1 space before "=>"; 0 found
Loading history...
Expected 1 space after "=>"; 0 found
Loading history...
507
        if ($userid == $other_user->id) {
508
            unset($list[$key]);
509
        }
510
    }
511
    $x = implode("|", array_values($list));
512
    return $user->prefs->update("ignorelist='$x'");
513
}
514
515
function is_ignoring($user, $other_user) {
516
    $list = explode("|", $user->prefs->ignorelist);
517
    return in_array($other_user->id, $list);
518
}
519
520
// if avatar is from gravatar, make it HTTPS
521
//
522
function avatar_url($url) {
523
    return str_replace('http:', 'https:', $url);
524
}
525
526
// Display an individual post.
527
// Generates a table row with two cells: author and message
528
//
529
function show_post(
530
    $post, $thread, $forum, $logged_in_user, $start=0,
0 ignored issues
show
Multi-line function declarations must define one parameter per line
Loading history...
531
    $latest_viewed=0, $controls=FORUM_CONTROLS, $filter=true
0 ignored issues
show
Multi-line function declarations must define one parameter per line
Loading history...
532
) {
533
    global $country_to_iso3166_2;
534
535
    $user = BoincUser::lookup_id($post->user);
536
537
    // If the user no longer exists, skip the post
538
    //
539
    if (!$user){
540
        return;
541
    }
542
543
    $config = get_config();
544
    BoincForumPrefs::lookup($user);
545
    if (is_banished($user) && !is_moderator($logged_in_user, $forum)) {
546
        return;
547
    }
548
549
    $no_forum_rating = parse_bool($config, "no_forum_rating");
550
551
    $tokens = "";
552
    $options = get_output_options($logged_in_user);
553
    if (is_admin($user)) {
554
        $options->htmlitems = false;
555
    }
556
557
    // check whether the poster is on the list of people to ignore
558
    //
559
    $ignore_poster = false;
560
    if ($logged_in_user){
561
        $tokens = url_tokens($logged_in_user->authenticator);
562
        if (is_ignoring($logged_in_user, $user)){
563
            $ignore_poster = true;
564
        }
565
    }
566
567
    // The creator can edit the post, but only in a specified amount of time
568
    // (exception: a moderator can edit his/her posts at any time)
569
    //
570
    $can_edit = false;
571
    if ($logged_in_user) {
572
        if ($user->id == $logged_in_user->id) {
573
            if (is_moderator($logged_in_user, $forum)) {
574
                $can_edit = true;
575
            } else if (can_reply($thread, $forum, $logged_in_user)) {
576
                $time_limit = $post->timestamp+MAXIMUM_EDIT_TIME;
577
                $can_edit = time()<$time_limit;
578
            } else {
579
                $can_edit = false;
580
            }
581
        }
582
    }
583
584
    // Print the special user lines, if any
585
    //
586
    global $special_user_bitfield;
587
    $fstatus="";
588
    $keys = array_keys($special_user_bitfield);
589
    $is_posted_by_special = false;
590
    for ($i=0; $i<sizeof($special_user_bitfield);$i++) {
0 ignored issues
show
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...
591
        if ($user->prefs && $user->prefs->privilege($keys[$i])) {
592
            $fstatus .= "<nobr>".$special_user_bitfield[$keys[$i]]."<nobr><br>";
593
            $is_posted_by_special = true;
594
        }
595
    }
596
597
    if (is_moderator($logged_in_user, $forum) && is_banished($user)) {
598
        $fstatus .= '(banished)';
599
    }
600
601
    // Highlight special users if set in prefs;
602
    //
603
    if ($logged_in_user && $logged_in_user->prefs){
604
        $highlight = $logged_in_user->prefs->highlight_special && $is_posted_by_special;
605
    } else {
606
        $highlight = $is_posted_by_special;
607
    }
608
    $class = $highlight?' style="border-left: 5px solid LightGreen" ':'';
609
610
    // row and start of author col
611
    //
612
    echo "
613
        <tr>
614
        <td $class>
615
        <a name=\"$post->id\"></a>
616
    ";
617
618
    echo user_links($user, 0, 30);
619
    echo "<br>";
620
    if ($user->create_time > time()-ST_NEW_TIME) $fstatus.=ST_NEW."<br>";
621
    echo "<span class=\"small\">";
622
    if ($fstatus) echo "$fstatus";
623
624
    if (!$filter || !$ignore_poster){
625
        if ($user->prefs && $user->prefs->avatar!="" && (!$logged_in_user || ($logged_in_user->prefs->hide_avatars==false))) {
626
            echo "<img width=\"".AVATAR_WIDTH."\" height=\"".AVATAR_HEIGHT."\" src=\"".avatar_url($user->prefs->avatar)."\" alt=\"Avatar\"><br>";
627
        }
628
    }
629
    echo "<p> </p>";
630
631
    $url = "pm.php?action=new&amp;userid=".$user->id;
632
    $name = $user->name;
633
    show_button_small($url, tra("Send message"), tra("Send %1 a private message",$name));
634
    echo '<br>'.tra("Joined: %1", gmdate('j M y', $user->create_time)), "<br>";
635
636
    if (!isset($user->nposts)) {
637
        $user->nposts = BoincPost::count("user=$user->id");
638
    }
639
640
    if (function_exists('project_forum_user_info')){
641
        project_forum_user_info($user);
642
    } else {
643
        echo tra("Posts: %1", $user->nposts)."<br>";
644
        // circumvent various forms of identity spoofing
645
        // by displaying the  user id of the poster.
646
        //
647
        //echo "ID: ".$user->id."<br>";
648
        if (!NO_COMPUTING) {
649
            echo tra("Credit: %1", number_format($user->total_credit)) ."<br>";
650
            echo tra("RAC: %1",    number_format($user->expavg_credit))."<br>";
651
        }
652
653
        // to use this feature:
654
        // - get flags from http://www.famfamfam.com/lab/icons/flags/famfamfam_flag_icons.zip
655
        // - put the .png's in html/user/flags/
656
        // - put define("COUNTRY_FLAGS", 1); in your html/project/project.inc
657
        //
658
        if (USER_COUNTRY && defined("COUNTRY_FLAGS")) {
659
            if (array_key_exists($user->country, $country_to_iso3166_2)) {
660
                $code = $country_to_iso3166_2[$user->country];
661
                echo "<img class=flag alt=\"$user->country\" title=\"$user->country\" src=flags/$code.png>\n";
662
            }
663
        }
664
        echo badges_string(true, $user, BADGE_HEIGHT_SMALL);
665
    }
666
667
    // end of author col, start of message col
668
    //
669
    echo '</span>
670
        </td>
671
        <td height="1%">
672
        <div class="small">
673
    ';
674
675
    if ($controls == FORUM_CONTROLS) {
676
        echo "<form action=\"forum_rate.php?post=", $post->id, "\" method=\"post\">";
677
    }
678
679
    if ($logged_in_user && $post->timestamp > $latest_viewed){
680
        show_image(NEW_IMAGE, tra("You haven't read this message yet"), tra("Unread"), NEW_IMAGE_HEIGHT);
681
    }
682
683
    echo " <a href=\"forum_thread.php?id=".$thread->id."&amp;postid=$post->id\">".tra("Message %1", $post->id)."</a> - ";
684
    if ($post->hidden) echo "<font color=red>[".tra("hidden")."] </font>";
685
    echo tra("Posted: %1", pretty_time_str($post->timestamp)), " ";
686
687
    if ($post->parent_post) {
688
        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; ";
689
    }
690
    if ($can_edit && $controls != NO_CONTROLS) {
691
        show_button_small("forum_edit.php?id=".$post->id."$tokens", tra("Edit"), tra("Edit this message"));
692
    }
693
    if (is_moderator($logged_in_user, $forum)) {
694
        show_post_moderation_links($config, $logged_in_user, $post, $forum, $tokens);
695
    }
696
    if ($post->modified) {
697
        echo "<br>".tra("Last modified: %1", pretty_time_Str($post->modified));
698
    }
699
    if ($ignore_poster && $filter){
700
        echo "<br>" .tra(
701
            "This post is hidden because the sender is on your 'ignore' list.  Click %1 here %2 to view hidden posts",
702
            "<a href=\"?id=".$thread->id."&amp;filter=false&amp;start=$start#".$post->id."\">",
703
            "</a>"
704
        );
705
    }
706
    if ($controls == FORUM_CONTROLS) {
707
        echo "</form>\n";
708
    }
709
    echo "</div>
710
        <p>
711
    ";
712
713
    if (!$filter || !$ignore_poster){
714
        $posttext = $post->content;
715
716
        // If the creator of this post has a signature and
717
        // wants it to be shown for this post AND the logged in
718
        // user has signatures enabled: show it
719
        //
720
        $posttext = output_transform($posttext, $options);
721
        if ($post->signature && (!$logged_in_user || !$logged_in_user->prefs->hide_signatures)){
722
            $sig = output_transform($user->prefs->signature, $options);
723
            $posttext .= "<hr>$sig\n";
724
        }
725
726
        // show message in a panel
727
        //
728
        echo '<div class="panel panel-default" style="word-break: break-word;">
729
            <div class="panel-body">'
730
            .$posttext
731
            .'</div></div>
732
        ';
733
734
        echo '<div class="small"
735
            <span>ID: '. $post->id
736
        ;
0 ignored issues
show
Space found before semicolon; expected "id;" but found "id
;"
Loading history...
737
        if ($no_forum_rating) {
738
            echo " &middot; <a href=\"forum_report_post.php?post=".$post->id."\">";
739
            show_image(REPORT_POST_IMAGE, tra("Report this post as offensive"), tra("Report as offensive"), REPORT_POST_IMAGE_HEIGHT);
740
            echo "</a>";
741
        } else {
742
            $rating = $post->rating();
743
            echo " &middot; ".tra("Rating: %1", $rating)." &middot; ".tra("rate: ")."
744
                <a href=\"forum_rate.php?post=".$post->id."&amp;choice=p$tokens\">
745
            ";
746
            show_image(RATE_POSITIVE_IMAGE, tra("Click if you like this message"), tra("Rate +"), RATE_POSITIVE_IMAGE_HEIGHT);
747
            echo "</a> / <a href=\"forum_rate.php?post=".$post->id."&amp;choice=n$tokens\">";
748
            show_image(RATE_NEGATIVE_IMAGE, tra("Click if you don't like this message"), tra("Rate -"), RATE_NEGATIVE_IMAGE_HEIGHT);
749
            echo "</a> <a href=\"forum_report_post.php?post=".$post->id."\">";
750
            show_image(REPORT_POST_IMAGE, tra("Report this post as offensive"), tra("Report as offensive"), REPORT_POST_IMAGE_HEIGHT);
751
            echo "</a>";
752
        }
753
        if (($controls == FORUM_CONTROLS) && (can_reply($thread, $forum, $logged_in_user))) {
754
            echo "&nbsp;&nbsp;&nbsp;&nbsp;";
755
            $url = "forum_reply.php?thread=" . $thread->id . "&amp;post=" . $post->id . "&amp;no_quote=1#input";
756
            // "Reply" is used as a verb
757
            show_button($url, tra("Reply"), tra("Post a reply to this message"));
758
            $url = "forum_reply.php?thread=" . $thread->id . "&amp;post=" . $post->id . "#input";
759
            // "Quote" is used as a verb
760
            show_button($url, tra("Quote"), tra("Post a reply by quoting this message"));
761
        }
762
        echo "</span>";
763
    }
764
    // end of message col and row; add separator row
765
    //
766
    echo "</td></tr>
767
        <tr><td colspan=2></td></tr>
768
    ";
769
}
770
771
// Show a post and its context (e.g. for search results, user posts)
772
//
773
function show_post_and_context($post, $thread, $forum, $options, $n) {
774
    $thread = BoincThread::lookup_id($post->thread);
775
    $forum = BoincForum::lookup_id($thread->forum);
776
777
    $content = output_transform($post->content, $options);
778
    $when = time_diff_str($post->timestamp, time());
779
    $user = BoincUser::lookup_id($post->user);
780
    if (!$user){
781
        return;
782
    }
783
784
    $config = get_config();
785
    $title = cleanup_title($thread->title);
786
    if ($post->hidden) {
787
        $deleted = "<br><font color=red>[".tra("Hidden by a moderator")."]</font>";
788
    } else {
789
        $deleted = "";
790
    }
791
    switch ($forum->parent_type) {
792
    case 0:
793
        $category = BoincCategory::lookup_id($forum->category);
794
        $title= forum_title($category, $forum, $thread, true);
795
        break;
796
    case 1:
797
        $title= team_forum_title($forum);
798
        break;
799
    }
800
    row_array([
801
        sprintf(
802
            '%d) %s
803
                <br><a href="forum_thread.php?id=%d&amp;postid=%d">%s</a>
804
                <br>%s%s
805
            ',
806
            $n, $title,
807
            $thread->id, $post->id,
808
            tra("Message %1", $post->id),
809
            tra("Posted %1 by %2", $when, user_links($user)),
810
            $deleted
811
        ),
812
        $content
0 ignored issues
show
There should be a trailing comma after the last value of an array declaration.
Loading history...
813
    ]);
814
}
815
816
function is_banished($user) {
817
    BoincForumPrefs::lookup($user);
818
    return ($user->prefs->banished_until > time());
819
}
820
821
// $user is the logged-in user.
822
// They're trying to make a forum post or send a personal message.
823
// If they're banished, don't allow it
824
//
825
function check_banished($user) {
826
    if (is_banished($user)) {
827
        error_page(
828
            tra("You may not post or rate messages until %1", gmdate('M j, Y', $user->prefs->banished_until))
829
        );
830
    }
831
}
832
833
function post_rules() {
834
    if (defined('FORUM_RULES')) return FORUM_RULES;
835
    if (function_exists("project_forum_post_rules")) {
836
      $project_rules=project_forum_post_rules();
837
    } else {
838
      $project_rules="";
839
    }
840
    return sprintf("
841
        <ul>
842
        <li> %s
843
        <li> %s
844
        <li> %s
845
        <li> %s
846
        <li> %s
847
        <li> %s
848
        <li> %s
849
        <li> %s
850
        <li> %s
851
        %s
852
        </ul>
853
        ",
854
        tra("Posts must be 'kid friendly': they may not contain content that is obscene, hate-related, sexually explicit or suggestive."),
855
        tra("No commercial advertisements."),
856
        tra("No links to web sites involving sexual content, gambling, or intolerance of others."),
857
        tra("No messages intended to annoy or antagonize other people, or to hijack a thread."),
858
        tra("No messages that are deliberately hostile, threatening, or insulting."),
859
        tra("No abusive messages involving race, religion, nationality, gender, class or sexuality."),
860
        tra("Posts that violate these rules may be deleted."),
861
        tra("The posting privileges of violators may be suspended or revoked."),
862
        tra("If your account is suspended, don't create a new one."),
863
        $project_rules
864
    );
865
}
866
867
function post_warning($forum=null) {
868
    $x = '<p><div style="text-align:left">';
869
870
    // let projects add extra instructions in specific forums,
871
    // e.g. Questions and Problems
872
    //
873
    if (function_exists('project_forum_post_info')) {
874
        $x .= project_forum_post_info($forum);
875
    }
876
877
    $x .= "
878
        <small>
879
        ".post_rules()."
880
        </small>
881
        </div>
882
    ";
883
    return $x;
884
}
885
886
function notify_thread_subscriber($thread, $user) {
887
    BoincForumPrefs::lookup($user);
888
    if ($user->prefs->pm_notification == 1) {
889
        send_thread_notification_email($thread, $user);
890
    }
891
    $now = time();
892
    $type = NOTIFY_SUBSCRIBED_THREAD;
893
    BoincNotify::replace(
894
        "userid=$user->id, create_time=$now, type=$type, opaque=$thread->id"
895
    );
896
}
897
898
// notify subscribed users, except for the given user
899
//
900
function notify_thread_subscribers($thread, $user) {
901
    $subs = BoincSubscription::enum("threadid=$thread->id");
902
    foreach ($subs as $sub) {
903
        if ($user && ($user->id == $sub->userid)) continue;
904
        $user2 = BoincUser::lookup_id($sub->userid);
905
        if ($user2) {
906
            notify_thread_subscriber($thread, $user2);
907
        }
908
    }
909
}
910
911
function notify_forum_subscriber($forum, $user) {
912
    BoincForumPrefs::lookup($user);
913
    if ($user->prefs->pm_notification == 1) {
914
        send_forum_notification_email($forum, $user);
915
    }
916
    $now = time();
917
    $type = NOTIFY_SUBSCRIBED_FORUM;
918
    BoincNotify::replace(
919
        "userid=$user->id, create_time=$now, type=$type, opaque=$forum->id"
920
    );
921
}
922
923
// notify subscribed users, except for the given user
924
//
925
function notify_forum_subscribers($forum, $user) {
926
    $id = -$forum->id;
927
    $subs = BoincSubscription::enum("threadid=$id");
928
    foreach ($subs as $sub) {
929
        if ($user && ($user->id == $sub->userid)) continue;
930
        $user2 = BoincUser::lookup_id($sub->userid);
931
        if ($user2) {
932
            notify_forum_subscriber($forum, $user2);
933
        }
934
    }
935
}
936
937
// Various functions for adding/hiding/unhiding stuff.
938
// These take care of counts and timestamps.
939
// Don't do these things directly - use these functions
940
//
941
function create_post($content, $parent_id, $user, $forum, $thread, $signature) {
942
    global $forum_error;
943
    if (POST_MAX_LINKS
944
        && link_count($content) > POST_MAX_LINKS
945
        && !is_moderator($user, $forum)
946
    ) {
947
        $forum_error = "Too many links.";
948
        return null;
949
    }
950
    $content = substr($content, 0, 64000);
951
    $content = BoincDb::escape_string($content);
952
    $now = time();
953
    $sig = $signature?1:0;
954
    $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)");
955
    if (!$id) {
956
        $forum_error = "Failed to add post to DB.";
957
        return null;
958
    }
959
960
    notify_thread_subscribers($thread, $user);
961
962
    $user->prefs->update("posts=posts+1");
963
    $thread->update("replies=replies+1, timestamp=$now");
964
    $forum->update("posts=posts+1, timestamp=$now");
965
    return $id;
966
}
967
968
// call this when hide or delete a post;
969
// it sets timestamp to time of last non-hidden post
970
//
971
function update_thread_timestamp($thread) {
972
    $posts = BoincPost::enum("thread=$thread->id and hidden=0 order by timestamp desc limit 1");
973
    if (count($posts)>0) {
974
        $post = $posts[0];
975
        $thread->update("timestamp=$post->timestamp");
976
    }
977
}
978
979
function update_forum_timestamp($forum) {
980
    $threads = BoincThread::enum("forum=$forum->id and hidden=0 order by timestamp desc limit 1");
981
    if (count($threads)>0) {
982
        $thread = $threads[0];
983
        $forum->update("timestamp=$thread->timestamp");
984
    }
985
}
986
987
function create_thread($title, $content, $user, $forum, $signature, $export) {
988
    global $forum_error;
989
    if (POST_MAX_LINKS
990
        && link_count($content) > POST_MAX_LINKS
991
        && !is_moderator($user, $forum)
992
    ) {
993
        $forum_error = "Too many links.";
994
        return null;
995
    }
996
    $title = trim($title);
997
    $title = sanitize_tags($title);
998
    $title = BoincDb::escape_string($title);
999
    $now = time();
1000
    $status = 0;
1001
    if (is_news_forum($forum) && !$export) {
1002
        $status = 1;
1003
    }
1004
    $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)");
1005
    if (!$id) {
1006
        $forum_error = "Failed to add thread to DB.";
1007
        return null;
1008
    }
1009
    $thread = BoincThread::lookup_id($id);
1010
    create_post($content, 0, $user, $forum, $thread, $signature);
1011
    $forum->update("threads=threads+1");
1012
    notify_forum_subscribers($forum, $user);
1013
    return $thread;
1014
}
1015
1016
function hide_post($post, $thread, $forum) {
1017
    $ret = $post->update("hidden=1");
1018
    if (!$ret) return $ret;
1019
    $thread->update("replies=if(replies>0, replies-1, 0)");
1020
    $forum->update("posts=if(posts>0, posts-1, 0)");
1021
    update_thread_timestamp($thread);
1022
    update_forum_timestamp($forum);
1023
    return true;
1024
}
1025
1026
function unhide_post($post, $thread, $forum) {
1027
    $ret = $post->update("hidden=0");
1028
    if (!$ret) return $ret;
1029
    $thread->update("replies=replies+1");
1030
    $forum->update("posts=posts+1");
1031
    update_thread_timestamp($thread);
1032
    update_forum_timestamp($forum);
1033
    return true;
1034
}
1035
1036
function delete_post($post, $thread, $forum) {
1037
    $post->delete();
1038
    if (!$post->hidden) {
1039
        $thread->update("replies=if(replies>0, replies-1, 0)");
1040
        $forum->update("posts=if(posts>0, posts-1, 0)");
1041
    }
1042
    $count = BoincPost::count("thread=$thread->id");
1043
    if ($count == 0) {
1044
        if (!$thread->hidden) {
1045
            $forum->update("threads=if(threads>0, threads-1, 0)");
1046
        }
1047
        $thread->delete();
1048
    } else {
1049
        update_thread_timestamp($thread);
1050
    }
1051
    return true;
1052
}
1053
1054
function delete_thread($thread, $forum) {
1055
    $nposts = BoincPost::count("thread=$thread->id and hidden=0");
1056
    $forum->update("posts=posts-$nposts");
1057
    BoincPost::delete_aux("thread=$thread->id");
1058
    if (!$thread->hidden) {
1059
        $forum->update("threads=if(threads>0, threads-1, 0)");
1060
    }
1061
    $thread->delete();
1062
}
1063
1064
// delete all forum records related to user
1065
//
1066
function forum_delete_user($user) {
1067
    $pp = BoincPost::enum("user=$user->id");
1068
    foreach ($pp as $p) {
1069
        $t = BoincThread::lookup_id($p->thread);
1070
        $f = BoincForum::lookup_id($t->forum);
1071
        if ($t && $f) {
1072
            delete_post($p, $t, $f);
1073
        }
1074
    }
1075
    $ss = BoincSubscription::enum("userid=$user->id");
1076
    foreach ($ss as $s) {
1077
        BoincSubscription::delete($s->userid, $s->threadid);
1078
    }
1079
    $p = BoincForumPrefs::lookup_userid($user->id);
1080
    if ($p) $p->delete();
1081
    BoincForumLogging::delete_aux("userid=$user->id");
1082
}
1083
1084
function move_post($post, $old_thread, $old_forum, $new_thread, $new_forum) {
1085
    global $g_logged_in_user;
1086
    $post->update("thread=$new_thread->id");
1087
    $old_thread->update("replies=if(replies>0, replies-1, 0)");
1088
    $new_thread->update("replies=replies+1");
1089
    $old_forum->update("posts=if(posts>0, posts-1, 0)");
1090
    $new_forum->update("posts=posts+1");
1091
    update_thread_timestamp($old_thread);
1092
    update_thread_timestamp($new_thread);
1093
    update_forum_timestamp($old_forum);
1094
    update_forum_timestamp($new_forum);
1095
    notify_thread_subscribers($new_thread, $g_logged_in_user);
1096
    return true;
1097
}
1098
1099
function hide_thread($thread, $forum) {
1100
    $ret = $thread->update("hidden=1");
1101
    if (!$ret) return $ret;
1102
    $forum->update("threads=if(threads>0, threads-1, 0)");
1103
    $forum->update("posts=posts-$thread->replies-1");
1104
    update_forum_timestamp($forum);
1105
    return true;
1106
}
1107
1108
function unhide_thread($thread, $forum) {
1109
    $ret = $thread->update("hidden=0");
1110
    if (!$ret) return $ret;
1111
    $forum->update("threads=threads+1, posts=posts+$thread->replies+1");
1112
    update_forum_timestamp($forum);
1113
    return true;
1114
}
1115
1116
function move_thread($thread, $old_forum, $new_forum) {
1117
    $now = time();
1118
    $old_forum->update("threads=if(threads>0, threads-1, 0), posts=posts-$thread->replies-1");
1119
    $new_forum->update("threads=threads+1, posts=posts+$thread->replies+1, timestamp=$now");
1120
    return $thread->update("forum=$new_forum->id");
1121
}
1122
1123
// $show_hidden: 1 if it is a moderator reading
1124
// Error page if this function returns NULL.
1125
// $forumID - int
1126
// $min - int
1127
// $nRec - int
1128
// $sort_style - string (checked by switch statement)
1129
// $show_hidden - bool (not directly passed to SQL)
1130
// $sticky - bool (not directly passed to SQL)
1131
//
1132
function get_forum_threads(
1133
    $forumID, $start=-1, $nRec=-1, $sort_style=MODIFIED_NEW,
0 ignored issues
show
Multi-line function declarations must define one parameter per line
Loading history...
1134
    $show_hidden = 0, $sticky = 1
0 ignored issues
show
Multi-line function declarations must define one parameter per line
Loading history...
1135
) {
1136
    //if (! (is_numeric($forumID) && is_numeric($min) && is_numeric($nRec))) {
1137
    //    return NULL;  // Something is wrong here.
1138
    //}
1139
1140
    $sql = 'forum = ' . $forumID ;
0 ignored issues
show
Space found before semicolon; expected "$forumID;" but found "$forumID ;"
Loading history...
1141
    $stickysql = "";
1142
    if ($sticky){
1143
        $stickysql = "sticky DESC, ";
1144
    }
1145
    if (!$show_hidden) {
1146
        $sql .= ' AND hidden = 0';
1147
    }
1148
    switch($sort_style) {
1149
    case MODIFIED_NEW:
1150
        $sql .= ' ORDER BY '.$stickysql.'timestamp DESC';
1151
        break;
1152
    case MODIFIED_OLD:
1153
        $sql .= ' ORDER BY '.$stickysql.'timestamp ASC';
1154
        break;
1155
    case VIEWS_MOST:
1156
        $sql .= ' ORDER BY '.$stickysql.'views DESC';
1157
        break;
1158
    case REPLIES_MOST:
1159
        $sql .= ' ORDER BY '.$stickysql.'replies DESC';
1160
        break;
1161
    case CREATE_TIME_NEW:
1162
        $sql .= ' ORDER by '.$stickysql.'create_time desc';
1163
        break;
1164
    case CREATE_TIME_OLD:
1165
        $sql .= ' ORDER by '.$stickysql.'create_time asc';
1166
        break;
1167
    case 'sufferers':
1168
        $sql .= ' ORDER by '.$stickysql.'sufferers desc';
1169
        break;
1170
    case 'activity':
1171
        $sql .= ' ORDER by '.$stickysql.'activity desc';
1172
        break;
1173
    case 'score':
1174
        $sql .= ' ORDER by '.$stickysql.'score desc';
1175
        break;
1176
    default:
0 ignored issues
show
DEFAULT keyword must be indented 4 spaces from SWITCH keyword
Loading history...
1177
        $sql .= ' ORDER BY '.$stickysql.'timestamp DESC';
1178
        break;
1179
    }
1180
    if ($start > -1) {
1181
        $sql .= ' LIMIT '.$start;
1182
        if ($nRec > -1) {
1183
            $sql .= ', '.$nRec;
1184
        }
1185
    } else if ($nRec > -1) {
1186
        $sql .= ' LIMIT '.$nRec;
1187
    }
1188
    return BoincThread::enum($sql);
1189
}
1190
1191
// $show_hidden = true when it is a moderator reading
1192
// error_page if this function returns NULL.
1193
// $sort_style - string (checked by switch statement)
1194
// $show_hidden - bool (not directly passed to SQL)
1195
//
1196
function get_thread_posts($threadid, $sort_style, $show_hidden) {
1197
    $sql = "thread=$threadid";
1198
    if (!$show_hidden) {
1199
        $sql .= ' AND hidden = 0';
1200
    }
1201
    switch($sort_style) {
1202
    case CREATE_TIME_NEW:
1203
        $sql .= ' ORDER BY timestamp desc';
1204
        break;
1205
    case CREATE_TIME_OLD:
1206
        $sql .= ' ORDER BY timestamp asc';
1207
        break;
1208
    case POST_SCORE:
1209
        $sql .= ' ORDER BY score DESC';
1210
        break;
1211
    default:
0 ignored issues
show
DEFAULT keyword must be indented 4 spaces from SWITCH keyword
Loading history...
1212
        $sql .= ' ORDER BY timestamp asc';
1213
        break;
1214
    }
1215
    return BoincPost::enum($sql);
1216
}
1217
1218
// Show links for post moderation actions;
1219
// logged in user has moderation rights.
1220
//
1221
function show_post_moderation_links(
1222
    $config, $logged_in_user, $post, $forum, $tokens
0 ignored issues
show
Multi-line function declarations must define one parameter per line
Loading history...
1223
){
0 ignored issues
show
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...
1224
    $moderators_allowed_to_ban = parse_bool($config, "moderators_allowed_to_ban");
1225
    $moderators_vote_to_ban = parse_bool($config, "moderators_vote_to_ban");
1226
1227
    if ($post->hidden) {
1228
        show_button_small("forum_moderate_post_action.php?action=unhide&amp;id=".$post->id."$tokens", tra("Unhide"), tra("Unhide this post"));
1229
    } else {
1230
        show_button_small("forum_moderate_post.php?action=hide&amp;id=".$post->id."$tokens", tra("Hide"), tra("Hide this post"));
1231
    }
1232
1233
    show_button_small(
1234
        "forum_moderate_post.php?action=move&amp;id=".$post->id."$tokens",
1235
        tra("Move"), tra("Move post to a different thread")
1236
    );
1237
1238
    if ($forum->parent_type == 0) {
1239
        if (is_admin($logged_in_user) || $moderators_allowed_to_ban) {
1240
            show_button_small("forum_moderate_post.php?action=banish_user&amp;id=".$post->id."&amp;userid=".$post->user."$tokens", tra("Banish author"));
1241
        }
1242
        if ($moderators_vote_to_ban) {
1243
            require_once("../inc/forum_banishment_vote.inc");
1244
            if (vote_is_in_progress($post->user)) {
1245
                show_button_small(
1246
                    "forum_banishment_vote.php?action=yes&amp;userid=".$post->user,
1247
                    tra("Vote to banish author")
1248
                );
1249
                show_button_small(
1250
                    "forum_banishment_vote.php?action=no&amp;userid=".$post->user,
1251
                    tra("Vote not to banish author")
1252
                );
1253
            } else {
1254
                show_button_small(
1255
                    "forum_banishment_vote.php?action=start&amp;userid=".$post->user,
1256
                    tra("Start vote to banish author")
1257
                );
1258
            }
1259
        }
1260
        if (is_admin($logged_in_user)) {
1261
            show_button_small("forum_moderate_post.php?action=delete&amp;id=".$post->id."$tokens", tra("Delete"), tra("Delete this post"));
1262
        }
1263
    }
1264
}
1265
1266
// is the given user allowed to
1267
// - add threads to News forum
1268
// - use HTML in posts
1269
// - delete threads and posts
1270
//
1271
function is_admin($user) {
1272
    if (!$user) return false;
1273
    if ($user->prefs->privilege(S_SCIENTIST)) return true;
1274
    if ($user->prefs->privilege(S_DEV)) return true;
1275
    if ($user->prefs->privilege(S_ADMIN)) return true;
1276
    return false;
1277
}
1278
1279
// return
1280
// 'yes' if logged in and can post (show New thread button)
1281
// 'login' if could post if logged in (show login to post msg)
1282
// 'no' if can't post (don't show anythin)
1283
//
1284
function user_can_create_thread($user, $forum) {
1285
    if ($forum->is_dev_blog) {
1286
        return is_admin($user)?'yes':'no';
1287
    }
1288
    return $user ?'yes':'login';
1289
}
1290
1291
function check_post_access($user, $forum) {
1292
    if (is_admin($user)) return;
1293
1294
    switch ($forum->parent_type) {
1295
    case 0:
1296
        if ($user->prefs->privilege(S_MODERATOR)) return;
1297
        break;
1298
    case 1:
1299
        $team = BoincTeam::lookup_id($forum->category);
1300
        if (is_team_admin($user, $team)) return;
1301
1302
        // non-team-members can't post
1303
        //
1304
        if ($user->teamid != $team->id) {
1305
            error_page(tra("Only team members can post to the team message board"));
1306
        }
1307
        break;
1308
    }
1309
1310
    // If user haven't got enough credit (according to forum regulations)
1311
    // We do not tell the (ab)user how much this is -
1312
    // no need to make it easy for them to break the system.
1313
    //
1314
    if ($user->total_credit<$forum->post_min_total_credit || $user->expavg_credit<$forum->post_min_expavg_credit) {
1315
        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));
1316
    }
1317
1318
    // If the user is posting faster than forum regulations allow
1319
    // Tell the user to wait a while before creating any more posts
1320
    //
1321
    if (time()-$user->prefs->last_post <$forum->post_min_interval) {
1322
        error_page(tra("You cannot create threads right now. Please wait before trying again. This is to protect against abuse of the system."));
1323
    }
1324
}
1325
1326
function check_reply_access($user, $forum, $thread) {
1327
    if ($thread->locked && !is_moderator($user, $forum)) {
1328
        error_page(
1329
            tra("This thread is locked. Only forum moderators and administrators are allowed to post there.")
1330
        );
1331
    }
1332
    if ($thread->hidden) {
1333
        error_page(
1334
           tra("Can't post to a hidden thread.")
1335
        );
1336
    }
1337
1338
    check_post_access($user, $forum);
1339
}
1340
1341
// is the given user allowed to moderate the given forum? this includes
1342
// - post to locked threads
1343
// - see hidden threads and posts
1344
// - edit their posts at any time
1345
// - hide/unhide/move threads and posts
1346
1347
function is_moderator($user, $forum=null) {
1348
    if (!$user) return false;
1349
    BoincForumPrefs::lookup($user);
1350
    $type = $forum?$forum->parent_type:0;
1351
    switch ($type) {
1352
    case 0:
1353
        if ($user->prefs->privilege(S_MODERATOR)) return true;
1354
        if ($user->prefs->privilege(S_ADMIN)) return true;
1355
        if ($user->prefs->privilege(S_DEV)) return true;
1356
        if ($user->prefs->privilege(S_SCIENTIST)) return true;
1357
        break;
1358
    case 1:
1359
        if ($user->prefs->privilege(S_ADMIN)) return true;
1360
        $team = BoincTeam::lookup_id($forum->category);
1361
        return is_team_admin($user, $team);
1362
        break;
0 ignored issues
show
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...
1363
    }
1364
    return false;
1365
}
1366
1367
function thread_list_header($subscriptions=false) {
1368
    if ($subscriptions) {
1369
        $x = [
1370
            tra("Thread"),
1371
            tra("Author"),
1372
            "<nobr>".tra("Last post")."</nobr>",
1373
            'Unsubscribe'
0 ignored issues
show
There should be a trailing comma after the last value of an array declaration.
Loading history...
1374
        ];
1375
    } else {
1376
        $x = [
1377
            tra("Thread"),
1378
            tra("Posts"),
1379
            tra("Author"),
1380
            tra("Views"),
1381
            "<nobr>".tra("Last post")."</nobr>"
0 ignored issues
show
There should be a trailing comma after the last value of an array declaration.
Loading history...
1382
        ];
1383
    }
1384
    row_heading_array($x);
1385
}
1386
1387
// show a 1-line summary of thread and its forum.
1388
// Used for search results and subscription list
1389
//
1390
function thread_list_item($thread, $user, $subscriptions=false) {
1391
    $thread_forum = BoincForum::lookup_id($thread->forum);
1392
    if (!$thread_forum) return;
1393
    if (!is_forum_visible_to_user($thread_forum, $user)) return;
1394
    $owner = BoincUser::lookup_id($thread->owner);
1395
    if (!$owner) return;
1396
    switch($thread_forum->parent_type) {
1397
    case 0:
1398
        $category = BoincCategory::lookup_id($thread_forum->category);
1399
        $title = forum_title($category, $thread_forum, $thread, true);
1400
        break;
1401
    case 1:
1402
        $title = team_forum_title($thread_forum, $thread);
1403
        break;
1404
    default:
0 ignored issues
show
DEFAULT keyword must be indented 4 spaces from SWITCH keyword
Loading history...
DEFAULT case must have a breaking statement
Loading history...
1405
        $title = '???';
1406
    }
1407
    if ($subscriptions) {
1408
        $x = [
1409
            $title,
1410
            user_links($owner),
1411
            time_diff_str($thread->timestamp, time()),
1412
            "<input type=checkbox name=unsub_thread_$thread->id>"
0 ignored issues
show
There should be a trailing comma after the last value of an array declaration.
Loading history...
1413
        ];
1414
    } else {
1415
        $x = [
1416
            $title,
1417
            $thread->replies+1,
1418
            user_links($owner),
1419
            $thread->views,
1420
            time_diff_str($thread->timestamp, time())
0 ignored issues
show
There should be a trailing comma after the last value of an array declaration.
Loading history...
1421
        ];
1422
    }
1423
    row_array($x);
1424
}
1425
1426
// see if ID is in subscription list
1427
//
1428
function is_subscribed($id, $subs) {
1429
    foreach ($subs as $sub) {
1430
        if ($sub->threadid == $id) return true;
1431
    }
1432
    return false;
1433
}
1434
1435
// If it's a team forum, user must be member of team to view
1436
//
1437
function is_forum_visible_to_user($forum, $user) {
1438
    if ($forum->parent_type == 1) {
1439
        if (parse_config(get_config(), "<team_forums_members_only>")) {
1440
            if (!$user) return false;
1441
            if ($user->teamid != $forum->category) return false;
1442
        }
1443
    }
1444
    return true;
1445
}
1446
1447
function subscribed_thread_email_line($notify) {
1448
    $thread = BoincThread::lookup_id($notify->opaque);
1449
    return "There are new posts in the thread '$thread->title'";
1450
}
1451
1452
function subscribed_thread_web_line($notify) {
1453
    $thread = BoincThread::lookup_id($notify->opaque);
1454
    return tra("New posts in the thread %1","<a href=forum_thread.php?id=$thread->id>$thread->title</a>");
1455
}
1456
1457
function subscribed_thread_rss($notify) {
1458
    $thread = BoincThread::lookup_id($notify->opaque);
1459
    $title = tra("New posts in subscribed thread");
1460
    $msg = tra("There are new posts in the thread '%1'",$thread->title);
1461
    $url = secure_url_base()."forum_thread.php?id=$thread->id";
1462
    return [$title, $msg, $url];
1463
}
1464
1465
function subscribed_forum_email_line($notify) {
1466
    $forum = BoincForum::lookup_id($notify->opaque);
1467
    return "There are new threads in the forum '$forum->title'";
1468
}
1469
1470
function subscribed_forum_web_line($notify) {
1471
    $forum = BoincForum::lookup_id($notify->opaque);
1472
    return tra("New threads in the forum %1","<a href=forum_forum.php?id=$forum->id>$forum->title</a>");
1473
}
1474
1475
function subscribed_forum_rss($notify) {
1476
    $forum = BoincForum::lookup_id($notify->opaque);
1477
    $title = tra("New posts in subscribed forum");
1478
    $msg = tra("There are new threads in the forum '%1'",$forum->title);
1479
    $url = secure_url_base()."forum_forum.php?id=$forum->id";
1480
    return [$title, $msg, $url];
1481
}
1482
1483
function show_mark_as_read_button($user) {
1484
    if ($user) {
1485
        $return = urlencode(current_url());
1486
        $tokens = url_tokens($user->authenticator);
1487
        $url = "forum_index.php?read=1$tokens&amp;return=$return";
1488
        show_button($url,
1489
            tra("Mark all threads as read"),
1490
            tra("Mark all threads in all message boards as read.")
1491
        );
1492
    }
1493
}
1494
1495
function remove_subscriptions_forum($userid, $forumid) {
1496
    $subs = BoincSubscription::enum("userid=$userid and threadid>0");
1497
    foreach ($subs as $sub) {
1498
        $thread = BoincThread::lookup_id($sub->threadid);
1499
        if ($thread && $thread->forum == $forumid) {
1500
            BoincSubscription::delete($userid, $thread->id);
1501
        }
1502
    }
1503
    $notices = BoincNotify::enum("userid=$userid and type=".NOTIFY_SUBSCRIBED_THREAD);
1504
    foreach ($notices as $n) {
1505
        $thread = BoincThread::lookup_id($n->opaque);
1506
        if ($thread && $thread->forum == $forumid) {
1507
            $n->delete();
1508
        }
1509
    }
1510
}
1511
1512
function remove_subscriptions_thread($userid, $threadid) {
1513
    BoincSubscription::delete($userid, $threadid);
1514
    BoincNotify::delete_aux("userid=$userid and type=".NOTIFY_SUBSCRIBED_THREAD." and opaque=$threadid");
1515
}
1516
1517
function parse_forum_cookie() {
1518
    $x = array("", "");
1519
    if (isset($_COOKIE['sorting'])) {
1520
        $a = explode("|", $_COOKIE['sorting']);
1521
        if (array_key_exists(0, $a)) {
1522
            $x[0] = $a[0];
1523
        }
1524
        if (array_key_exists(1, $a)) {
1525
            $x[1] = $a[1];
1526
        }
1527
    }
1528
    return $x;
1529
}
1530
?>
1531