Skip to content
V
For developers
Architecture, conventions, the core and security
Modules / Discussion

Discussion#

The smallest module in the system — 13 entities, no dynamic parameters, no saved-search alerts, no expiry. The code lives in app/UI/Api/Discussion/, the front end in app/UI/Front/Discussion/.

What the module is made of#

Group Entities
content Message (a nested set), MessageAssetRel
taxonomy Category, CategoryText
interaction MessageVote, MessageReport
payment Tariff, TariffPrice, TariffQuickPrice, PaymentItem
moderation BlacklistedWord, BlacklistedWordText

A thread and a post are one entity#

Message is a nested set (lft, rgt, level). A thread is a root node, a reply is its descendant — there is no type difference between "topic" and "post".

Moving a post is not constrained in any way

An administrator can drag any node anywhere and the system does not check whether it makes sense. Dragging a reply under a different topic makes the discussion incoherent, and there is no undo button.

Deleting a node deletes the whole subtree

Deleting the opening post removes the entire thread including every reply. In the listing it looks like deleting a single row.

What the module does not have#

More useful than a feature list is knowing what is not here — it saves you hunting for a setting that does not exist:

Missing How it is worked around
locking a thread turn off "allow comments", or deactivate the post
disabling a category a category has no active flag — only deletion
paid access to a category only highlighting a post is paid for
per-category permissions only the global "signed-in users only" switches

The "free" field on a category is dead

The for_free column is in the listing and in the form, but nobody in the discussion module evaluates it — unlike in classifieds and the company catalogue, where it drives the listing price. It must therefore not be presented to the user as a paid category.

Thumb voting#

Votes are MessageVote rows with the value 1 = thumbs up, 0 = thumbs down. The counters on the post (votesUp, votesDown) are not written as an absolute value but as an increment — registerEvent() performs a server-side UPDATE … SET votesUp = votesUp + 1.

An increment instead of saving the whole entity is deliberate

Saving the whole entity would require write permission on the post, which a voter does not hold on someone else's post — and it would send an absolute value from a stale read, so two concurrent votes would lose one of themselves.

Old votes were not carried into the new counter

Older posts show zero. It is not a display bug and it cannot be recomputed retroactively — the historical data does not exist in the new shape.

Payments#

The only paid service is highlighting a post (toped_until). The payment item has no link to a category — unlike the company catalogue, where the payment buys a listing in a specific category.

Deleting an active payment item shortens the highlight

An extension from a payment only moves upwards (it is monotonic); a manual change in the administration can go both ways — and deleting the item behaves as a manual shortening.

Settings#

The module has index 8 in the settings table. The form today writes:

Key Type
messagesPerPage, commentsPerPage, maxImageSize number
automaticApproval, messageOnlyForLoggedIn, commentOnlyForLoggedIn, voteOnlyForLoggedIn switch

Automatic approval can be saved, but the new code does not read it

The switch is in the administration and the value is stored — and it affects no approval. Until somebody wires behaviour onto it, it is just a stored number.

Image dimensions and the watermark are not in the settings — do not bring them back

The variants are driven by assetVariants.neon (discussion-messages), the watermark by the global watermarkPath. The keys were deleted on 2026-08-13 (docs/sql/2026-08-13-settings-drop-unread-keys.sql); if somebody put them back into the form, the first save would recreate them, because saveSettingForm() writes every listed key.

Images on a post#

Saving replaces the whole list of images

If the upload field does not come up, the attachments disappear — the same mechanism as with any other collection. In detail: Forms, the section on file uploads.

The author's password#

Message carries a password column — an unauthenticated author can lock their post with it for later editing.

The password cannot be reset from the administration

The entity has it, the administration form does not edit it. An author who forgets it has to ask for a database intervention.

Scheduled tasks#

Endpoint What it does Recommended interval
/cron/discussion/elastic/sync incremental index synchronisation every 10 min
/cron/discussion/elastic/reindex a full reindex nightly
/cron/discussion/sitemap/{message,category} sitemaps nightly

Where to look#

I want Where
the post and tree logic app/UI/Api/Discussion/Models/Managers/MessageManager.php
voting app/UI/Front/Discussion/Components/CommentListRowComponent/Traits/VoteTrait.php
the incremental counter write app/Core/Traits/Api/Models/Managers/RegisterEventTrait.php
the module settings app/UI/Admin/Discussion/Presenters/SettingPresenter.php
the cron tasks app/UI/Cron/Discussion/Presenters/

Follow-up chapters: Blog · Company catalogue · Elasticsearch · Forms