Skip to main content

Overview

ForkBB provides comprehensive moderation tools to help administrators and moderators maintain forum quality and enforce community guidelines. The system includes user bans, content reports, premoderation, and granular permission controls.

Moderation Roles

Administrators

Full system access with all permissions

Global Moderators

Can moderate all forums

Forum Moderators

Moderate specific forums only

Permission Checks

Administrator Status

app/Models/User/User.php
protected function getisAdmin(): bool
{
    return FORK_GROUP_ADMIN === $this->group_id;
}

protected function getisAdmMod(): bool
{
    return $this->isAdmin
        || 1 === $this->g_moderator;
}

Forum-Specific Moderation

app/Models/User/User.php
public function isModerator(Model $model): bool
{
    if (1 !== $this->g_moderator) {
        return false;
    }
    
    while (! $model instanceof Forum) {
        $model = $model->parent;
        
        if (! $model instanceof Model) {
            throw new RuntimeException('Moderator\'s rights can not be found');
        }
    }
    
    return isset($model->moderators[$this->id]);
}
Moderator permissions are checked hierarchically. Topics inherit permissions from their parent forum.

Ban System

The ban system is implemented in app/Models/BanList/BanList.php:17 and supports multiple ban types:

Ban Types

Ban specific usernames (case-insensitive):
app/Models/BanList/BanList.php
public function banFromName(?string $name): int
{
    $name = $this->trimToNull($name, true);
    
    if (null === $name) {
        throw new InvalidArgumentException('Expected username, not empty string');
    }
    
    return $this->userList[$name] ?? 0;
}

Ban Initialization

app/Models/BanList/BanList.php
const CACHE_KEY = 'banlist';

public function init(): BanList
{
    $list = $this->c->Cache->get(self::CACHE_KEY);
    
    if (! isset($list['banList'], $list['userList'], $list['emailList'], $list['ipList'], $list['firstExpire'])) {
        $list = $this->load();
        
        if (true !== $this->c->Cache->set(self::CACHE_KEY, $list)) {
            throw new RuntimeException('Unable to write value to cache - ' . self::CACHE_KEY);
        }
    }
    
    $this->banList     = $list['banList'];
    $this->userList    = $list['userList'];
    $this->emailList   = $list['emailList'];
    $this->ipList      = $list['ipList'];
    $this->firstExpire = $list['firstExpire'];
    
    return $this;
}
Ban lists are cached for performance. Clear the cache after modifying bans.

IP Address Handling

IP addresses are converted to hexadecimal for efficient storage and comparison:
app/Models/BanList/BanList.php
public function ip2hex(string $ip): string
{
    $bin = \inet_pton($ip);
    
    if (false === $bin) {
        if (\preg_match('%[:a-fA-F]|\d{4}%', $ip)) {
            $result = '';
            // 0000-ffff for IPv6
            foreach (\explode(':', $ip) as $cur) {
                $result .= \substr('0000' . \strtolower($cur), -4);
            }
        } else {
            $result = '-';
            // 00-ff for IPv4
            foreach (\explode('.', $ip) as $cur) {
                $result .= \sprintf('%02x', $cur);
            }
        }
        
        return $result;
    } else {
        // The hyphen is needed for joint sorting of IPv4 and IPv6
        return (isset($bin[4]) ? '' : '-') . \bin2hex($bin);
    }
}

Ban Checking

app/Models/User/User.php
protected function getisBanByName(): bool
{
    return ! $this->isAdmin
        && $this->c->bans->banFromName($this->username) > 0;
}
Administrators cannot be banned. Ban checks are bypassed for admin accounts.

Ban Cache Management

app/Models/BanList/BanList.php
public function reset(): BanList
{
    if (true !== $this->c->Cache->delete(self::CACHE_KEY)) {
        throw new RuntimeException('Unable to remove key from cache - ' . self::CACHE_KEY);
    }
    
    return $this;
}

Content Reports

Users can report inappropriate content for moderator review, implemented in app/Models/Report/Report.php:18.

Report Structure

app/Models/Report/Report.php
protected function setauthor(User $user): void
{
    if ($user->isGuest) {
        throw new RuntimeException('Bad author');
    }
    
    $this->reported_by = $user->id;
}

protected function getauthor(): User
{
    if (
        $this->reported_by < 1
        || ! ($user = $this->c->users->load($this->reported_by)) instanceof User
    ) {
        $user = $this->c->users->guest([
            'username' => '{User #' . $this->reported_by .'}',
        ]);
    }
    
    if (! $user instanceof User) {
        throw new RuntimeException('No author data');
    }
    
    return $user;
}

Report Post Association

app/Models/Report/Report.php
protected function setpost(Post $post): void
{
    if ($post->id < 1) {
        throw new RuntimeException('Bad post');
    }
    
    $this->post_id = $post->id;
}

protected function getpost(): ?Post
{
    if ($this->post_id < 1) {
        throw new RuntimeException('No post data');
    }
    
    return $this->c->posts->load($this->post_id);
}

Report Resolution

app/Models/Report/Report.php
protected function setmarker(User $user): void
{
    if (! empty($this->zapped_by)) {
        throw new RuntimeException('Report already has a marker');
    } elseif ($user->isGuest) {
        throw new RuntimeException('Bad marker');
    }
    
    $this->zapped_by = $user->id;
    $this->zapped    = \time();
}

protected function getmarker(): User
{
    if (
        $this->zapped_by < 1
        || ! ($user = $this->c->users->load($this->zapped_by)) instanceof User
    ) {
        $user = $this->c->users->guest([
            'username' => '{User #' . $this->zapped_by .'}',
        ]);
    }
    
    if (! $user instanceof User) {
        throw new RuntimeException('No marker data');
    }
    
    return $user;
}

public function getlinkZap(): string
{
    if (empty($this->zapped)) {
        return $this->c->Router->link(
            'AdminReportsZap',
            [
                'id' => $this->id,
            ]
        );
    } else {
        return '';
    }
}
Reports track both the reporting user and the moderator who resolved it.

User Promotion

Moderators can promote users based on activity:
app/Models/User/User.php
public function linkPromote(Post $post): ?string
{
    if (
        (
            $this->isAdmin
            || (
                $this->isAdmMod
                && 1 === $this->g_mod_promote_users
            )
        )
        && $this->id !== $post->user->id
        && 0 < $post->user->g_promote_min_posts * $post->user->g_promote_next_group
        && ! $post->user->isBanByName
    ) {
        return $this->c->Router->link(
            'AdminUserPromote',
            [
                'uid' => $post->user->id,
                'pid' => $post->id,
            ]
        );
    } else {
        return null;
    }
}

Promotion Requirements

1

Permission Check

User must be admin or have g_mod_promote_users permission
2

Not Self

Cannot promote yourself
3

Promotion Configured

Target user must have promotion settings configured
4

Not Banned

Target user must not be banned

Moderator Assignment

Forums can have assigned moderators:
app/Models/Forum/Forum.php
public function modAdd(User ...$users): void
{
    $attr = $this->getModelAttr('moderators');
    
    if (
        empty($attr)
        || ! \is_array($attr)
    ) {
        $attr = [];
    }
    
    foreach ($users as $user) {
        if (! $user instanceof User) {
            throw new InvalidArgumentException('Expected User');
        }
        
        $attr[$user->id] = $user->username;
    }
    
    $this->moderators = $attr;
}

public function modDelete(User ...$users): void
{
    $attr = $this->getModelAttr('moderators');
    
    if (
        empty($attr)
        || ! \is_array($attr)
    ) {
        return;
    }
    
    foreach ($users as $user) {
        if (! $user instanceof User) {
            throw new InvalidArgumentException('Expected User');
        }
        
        unset($attr[$user->id]);
    }
    
    $this->moderators = $attr;
}

Content Control

Topic Management

Lock/Unlock

Prevent or allow new replies

Sticky/Unsticky

Pin topics to the top of forums

Move Topics

Relocate topics between forums

Merge Topics

Combine related discussions

Post Management

  • Edit Posts: Moderators can edit any post
  • Delete Posts: Soft or hard delete inappropriate content
  • Merge Posts: Combine consecutive posts from same user
  • Split Topics: Extract posts into new topic

Premoderation

New posts can require approval before appearing:
Premoderation is typically applied to new users or users with low post counts to prevent spam.

Censorship

Automatic word filtering:
// app/Models/Censorship/Censorship.php
// Filters inappropriate words
// Applies to posts, signatures, and usernames
Censored words can be:
  • Replaced with asterisks
  • Replaced with custom text
  • Used to trigger moderation flags

Admin Lists

Track and display admin/moderator activity:
// app/Models/AdminList/AdminList.php
// Shows recent admin actions
// Audit trail for accountability

Moderation Best Practices

Establish and communicate clear community guidelines. Document what is and isn’t acceptable behavior.
Apply rules consistently across all users. Document ban reasons and durations.
Assign moderators to specific forums they’re knowledgeable about. Distribute workload.
Start with warnings, then temporary bans, before permanent bans. Give users chances to improve.
Review reports promptly. Thank users who submit valid reports to encourage community policing.
Document ban reasons, evidence, and duration. This helps with appeals and consistency.

Moderation Workflow

1

Report Submitted

User reports inappropriate content via report button
2

Moderator Review

Moderator reviews reported content and context
3

Action Taken

Edit, delete, or leave content; warn or ban user if needed
4

Report Resolved

Mark report as handled with resolution notes
5

User Notification

Optionally notify user of action taken

Permission Hierarchy

Administrators (Full Access)

├─ Global Moderators (All Forums)
│  ├─ Edit/Delete any post
│  ├─ Lock/Move/Merge topics
│  ├─ Ban users (if configured)
│  └─ View reports

└─ Forum Moderators (Specific Forums)
   ├─ Edit/Delete posts in assigned forums
   ├─ Lock/Move topics within permission
   └─ View reports for their forums

IP Tracking

Track user IP addresses for ban enforcement and investigation:
  • Login IP tracking
  • Post IP tracking
  • IP-based duplicate account detection
  • Proxy/VPN detection (if configured)
IP tracking raises privacy concerns. Ensure compliance with local privacy laws and disclose tracking in your privacy policy.

Bulk Moderation

Perform actions on multiple items:
  • Delete multiple posts at once
  • Move multiple topics
  • Ban multiple users (spam cleanup)
  • Prune old topics

Moderation Statistics

Track moderation activity:
  • Reports handled per moderator
  • Average report resolution time
  • Ban statistics
  • Deleted content counts

User Management

User accounts, groups, and permissions

Forums & Topics

Content structure and organization

Security Considerations

Enforce strong passwords for moderators. Enable 2FA if available. Regularly audit moderator actions.
Combine username, email, and IP bans. Monitor for patterns of ban evasion. Consider device fingerprinting.
Track users who submit false reports. Take action against abuse of the report system.
Log all moderation actions. Provide appeal process. Remove moderators who abuse privileges.