Search by

belisoful / prado-forum

belisoful

A full-featured, template-control driven forum extension for PRADO 4.4: categories, boards, threads, posts, Markdown, reactions, polls, tags, attachments, subscriptions, notifications, search, moderation, feeds, permissions and cron maintenance.

Package info

github.com/belisoful/prado-forum

Type:prado4-extension

pkg:composer/belisoful/prado-forum

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

dev-master 2026-11-04 00:22 UTC

This package is auto-updated.

Last update: 2026-11-04 01:36:26 UTC


README

A complete, template-control driven forum for PRADO 4.4 applications, installed through Composer as a prado4-extension.

The forum is built the PRADO way: one TDbPluginModule (BEForumModule) owns the configuration, the database and the domain managers; every piece of UI is a TTemplateControl you can drop on any page; pages are plain .page templates composed from those controls; authorization runs through PRADO's TPermissionsManager and TAuthorizationRules; and every decision point is a dy* dynamic event so behaviors can extend or replace forum logic without touching the extension.

Features

  • Categories, nested boards (locked, hidden, members-only), threads and posts with denormalised counters and last-post tracking
  • Thread types (discussion, question with accepted answer, announcement), pinning with expiry, locking, moving, soft deletion and restore, permanent purge
  • Markdown (safe mode) and plain text content, rendered through HTMLPurifier, with @mention links, quoting and preview
  • Edit history with revisions, edit windows, flood control, approval queue for new content
  • Reactions (configurable set), polls (single or multiple choice, closing time, revote), tags and tag cloud, attachments with type and size limits
  • Subscriptions to boards and threads, in-forum notifications (replies, mentions, quotes, reactions, accepted answers, moderation, badges, reports), bookmarks, read tracking with unread markers and "first unread post" links
  • Built-in search index over posts and thread titles (prefix matching, quoted phrases, relevance ranking; identical on SQLite, MySQL and PostgreSQL; replaceable through dySearchCriteria)
  • Thread moderation: lock, pin, move, approve, delete, restore, purge, merge into another thread and split replies off into a new thread
  • Moderation: reports, moderation log, warnings, temporary and permanent bans, board moderators, badges
  • Member profiles (display name, avatar/gravatar, signature, bio, timezone, notification preferences), member directory, statistics
  • RSS/Atom feeds (TFeedService), a read-only JSON API (TJsonService), prado-cli forum/* shell commands, cron maintenance tasks (TCronModule)
  • SQLite (zero configuration), MySQL/MariaDB and PostgreSQL through one driver-neutral schema with versioned migrations
  • Localizable through Prado::localize, themable through a CSS class prefix and an optional bundled stylesheet

Requirements

  • PHP 8.2 or higher with pdo, json, mbstring, intl, dom and one of pdo_sqlite, pdo_mysql, pdo_pgsql
  • PRADO 4.4 or higher (the Active Record package pradosoft/prado-activerecord is installed with the forum)

Installation

composer require belisoful/prado-forum

Configuration

Add the module to the application configuration (XML shown; PHP configuration works the same way). The module is the bootstrap class declared in composer.json, so the module id may also be the package name.

<modules>
  <module id="db" class="Prado\Data\TDataSourceConfig">
    <database ConnectionString="mysql:host=localhost;dbname=app;charset=utf8mb4" Username="app" Password="secret" />
  </module>
  <module id="forum" class="Belisoful\Forum\BEForumModule"
      ConnectionID="db"
      Title="Community"
      AdminUsers="admin"
      ModeratorRoles="Moderators" />
</modules>
<parameters>
  <parameter id="PluginContentId" value="Main" />
</parameters>

Without ConnectionID the forum creates beforum.db (SQLite) in the runtime directory. With AutoInstall (the default) the tables are created and upgraded on first use; php prado-cli.php forum/install does the same from the shell and forum/seed creates a first category and board.

The pages ship with <com:TContent ID=<%$ PluginContentId %>>, the PRADO plugin convention: the page service must set a MasterClass whose layout provides a TContentPlaceHolder with that id, a <com:THead /> and a <com:TForm> (without a master, PRADO throws templatecontrol_placeholder_inexistent).

Module properties

Property Default Purpose
ConnectionID TDataSourceConfig module id; empty uses SQLite in the runtime path
TablePrefix forum_ Table name prefix (set before initialization)
Title Forum Forum name used in titles and feeds
PagePathPrefix Forum Page path prefix of the shipped pages (Forum.Index, Forum.Thread, ...)
AdminUsers, AdminRoles Users/roles holding every forum permission
ModeratorUsers, ModeratorRoles Users/roles holding the moderation permission everywhere
ThreadsPerPage, PostsPerPage, ItemsPerPage 25, 20, 20 Page sizes
ContentFormat markdown Default content format (markdown or text)
EditWindow 0 Seconds an owner may edit a post, 0 for unlimited
FloodInterval 30 Minimum seconds between posts of a member, 0 disables
MaxTitleLength, MinPostLength, MaxPostLength, MaxTagsPerThread 255, 1, 65535, 5 Validation limits
EnablePolls, EnableAttachments, EnableReactions, EnableTags, EnableSubscriptions, EnableSignatures, EnableBookmarks, EnableBadges true Feature switches
RequireApproval false New threads and replies of non-moderators await approval
ReactionTypes like,love,laugh,wow,sad Available reactions
AttachmentPath, AttachmentMaxSize, AttachmentTypes runtime path, 2 MB, png,jpg,jpeg,gif,webp,pdf,txt,zip Attachment storage and limits
AutoCreateMembers true Create a member profile for every authenticated user on first contact
AutoInstall true Install and upgrade the schema automatically
CssClassPrefix, EnableDefaultStyles beforum, true CSS class prefix of the controls and the bundled stylesheet
DateFormat, Timezone, GuestName M j, Y H:i, UTC, Guest Display settings
SearchMinLength, EnableSearchIndex, NotificationRetentionDays 3, true, 90 Search (minimum query length, built-in index or LIKE fallback) and maintenance settings
RendererClass, UrlBuilderClass, ShellActionClass Replace the content renderer, URL builder or shell action with subclasses

Pages and controls

The extension ships template-only pages under src/Pages/Forum (Index, Board, Thread, NewThread, EditPost, Search, Tag, Member, Members, Notifications, Subscriptions, Bookmarks, Attachment, Admin.Structure, Admin.Moderation, Admin.Members). They are reachable as ?page=Forum.Thread&thread=12 (pages are &pg=2) and the like; with TUrlMapping the URLs become pretty without any forum change (see Pretty URLs).

PRADO only serves plugin pages that live under the application directory. If your vendor/ directory is outside it (the usual layout), either point the module at a copy of the pages inside your application (PluginPagesPath) or build your own pages from the controls, which is the intended way to integrate the forum into a site:

<com:TContent ID="Main">
  <com:BEForumToolbar />
  <com:BEForumBreadcrumbs BoardID=<%= $this->Request['board'] %> />
  <com:BEForumBoardHeader BoardID=<%= $this->Request['board'] %> />
  <com:BEForumThreadList BoardID=<%= $this->Request['board'] %> PageSize="15" />
</com:TContent>

Controls (namespace Belisoful\Forum\Web\UI): BEForumToolbar, BEForumBreadcrumbs, BEForumSearchBox, BEForumCategoryList, BEForumBoardHeader, BEForumThreadList, BEForumThreadView, BEForumPostList, BEForumPostView, BEForumPostEditor, BEForumThreadEditor, BEForumPoll, BEForumReactionBar, BEForumSubscribeButton, BEForumModerationTools, BEForumSearchResults, BEForumTagCloud, BEForumRecentPosts, BEForumStatistics, BEForumMemberCard, BEForumMemberProfile, BEForumProfileEditor, BEForumMemberList, BEForumNotificationList, BEForumSubscriptionList, BEForumBookmarkList, BEForumAttachmentDownload, BEForumAdminStructure, BEForumAdminModeration, BEForumAdminMembers, plus the BEForumPager and the item renderers BEForumBoardRow, BEForumThreadRow.

Templates name the controls by their short class name (<com:BEForumThreadList>): composer.json declares the class map config/classes.json under extra.prado.class-map, which PRADO 4.4 registers for every installed extension. Every control finds the forum module through PRADO's plugin lookup; set ModuleID when several forums exist. Templates only render pre-built, HTML escaped view models: all logic lives in the managers.

Permissions

Permission names live in Belisoful\Forum\Security\BEForumPermissions: forum_view, forum_search, forum_thread_create, forum_thread_edit, forum_thread_delete, forum_thread_merge, forum_thread_split, forum_post_create, forum_post_edit, forum_post_delete, forum_react, forum_poll_create, forum_poll_vote, forum_attach, forum_report, forum_subscribe, forum_bookmark, forum_profile_edit, forum_moderate, forum_admin and forum_shell. By preset, viewing and searching are open to everyone, creating content needs an authenticated user, editing and deleting are for owners and moderators, and merging and splitting threads are for moderators (board moderators, ModeratorUsers/ModeratorRoles, administrators).

Without a TPermissionsManager sensible preset rules apply: everyone reads, authenticated users write, owners edit their own content, board moderators and the configured ModeratorUsers/ModeratorRoles moderate, AdminUsers/AdminRoles administer.

With a TPermissionsManager the module registers the same permissions (with the preset rules) and the manager's roles and rules take over:

<module id="permissions" class="Prado\Security\Permissions\TPermissionsManager" DefaultRoles="Default">
  <role name="ForumAdministrator" children="forum_admin, forum_moderate" />
  <role name="ForumModerator" children="forum_moderate" />
  <role name="Default" children="forum_view, forum_thread_create, forum_post_create, forum_react, forum_poll_vote, forum_attach, forum_report, forum_subscribe, forum_bookmark, forum_profile_edit" />
  <permissionrule name="forum_thread_create" action="deny" users="?" />
</module>

Application users get a class behavior: $this->User->getForumMember(), getForumDisplayName() and forumCan('forum_moderate').

Feeds, JSON, shell and cron

<services>
  <service id="feed" class="Prado\Web\Services\TFeedService">
    <feed id="forum" class="Belisoful\Forum\Feeds\BEForumFeed" />
    <feed id="forum-posts" class="Belisoful\Forum\Feeds\BEForumFeed" Scope="posts" Format="atom" />
  </service>
  <service id="json" class="Prado\Web\Services\TJsonService">
    <json id="forum" class="Belisoful\Forum\Feeds\BEForumJsonResponse" />
  </service>
</services>

?feed=forum&board=3, ?feed=forum-posts&thread=7, ?json=forum&action=threads|posts|search|tags|stats.

Shell: php prado-cli.php forum/status|install|upgrade|seed|recount|rerender|reindex|maintenance|ddl [driver]. reindex rebuilds the search index (run it once after upgrading to schema version 2, or after disabling and re-enabling EnableSearchIndex).

Cron: the module advertises forum_maintenance (expired bans and pins, old notifications, unused tags) to TCronModule; schedule the recount separately as a forum->recountStatistics method task.

Pretty URLs

examples/forum-urls.xml maps every forum page to a path (/forum/thread/12/page/2/, /forum/board/3/, /forum/member/alice/, ...). Copy it into your application (for example protected/config/forum-urls.xml), register a TUrlMapping as the URL manager of the request and switch the request to path URLs:

<modules>
  <module id="request" class="THttpRequest" UrlFormat="Path" UrlManager="forum-urls" />
  <module id="forum-urls" class="Prado\Web\TUrlMapping" EnableCustomUrl="true" ConfigFile="Application.config.forum-urls" />
</modules>

The forum builds every link through THttpRequest::constructUrl(), so the patterns apply to the controls, feeds and JSON links without any forum change; parameters that a pattern does not name (search queries, filters) are appended as a query string. examples/htaccess.example holds the Apache rewrite that hands such paths to index.php. Patterns are matched in order: keep the variants with more parameters (the page ones) before the shorter ones.

Extending the forum

  • Events: the module raises onThreadCreated, onThreadMerged, onThreadSplit, onPostCreated, onPostUpdated, onPostDeleted, onReaction, onPollVoted, onReportCreated, onNotification, onMemberBanned, ... with a BEForumEventParameter (record, actor, data). Attach handlers in a behavior or module: $forum->onNotification[] = [$mailer, 'send'];
  • Dynamic events: attach behaviors to the module or its managers to filter decisions: dyAuthorize, dyCreateManager, dyCreateRenderer, dyBuildUrl, dyValidateThread, dyValidatePost, dyEditWindow, dyNotify, dySearchCriteria, dyAllowedAttachment, dyReactionReputation, dyRenderContent, dyRenderFormat (add a content format), dyCreateMember, dyValidateProfile, dyIsThreadVisible, dyIsPostVisible, dySearchTerms (filter the indexed terms), dyCronTaskInfos.
  • Managers: subclass a manager and register it with setManager() or the dyCreateManager event; all business rules live there (getThreads(), getPosts(), getMembers(), getBoards(), getReactions(), getPolls(), getTags(), getAttachments(), getSubscriptions(), getNotifications(), getSearch(), getModeration(), getReadTracker(), getBookmarks(), getStatistics()).
  • Records: Belisoful\Forum\Data\BEForum* Active Records raise PRADO's OnInsert/OnUpdate/OnDelete events and accept class behaviors.
  • Templates: copy the pages, recompose the controls, override CssClassPrefix, or disable the bundled stylesheet.

Development

composer install
vendor/bin/php-cs-fixer fix --dry-run
vendor/bin/phpstan analyse --memory-limit=512M
composer unittest

The unit tests run on SQLite by default. Set BEFORUM_TEST_DSN, BEFORUM_TEST_USER and BEFORUM_TEST_PASSWORD to run the same suite against MySQL or PostgreSQL (the schema is recreated for every test).

License

BSD-3-Clause, see LICENSE.

Author

Brad Anderson (belisoful@icloud.com)