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