Passed
Push — master ( aa9c38...5241a6 )
by Vitalii
01:23 queued 21s
created

notify_forum_subscriber()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

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

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

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

    return false;
}

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

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

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

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

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

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