CoolCMS API

CommentThread extends BaseComponent
in package
uses ModalAwareTrait, PrivilegeGuardTrait

Manages the comments of one thread ({@see \App\UI\Api\Base\Models\Entities\CommentThread}) in the admin.

Keyed by threadId, so it is host-agnostic: it renders in any admin edit form that owns a thread (book, article, gallery, discussion) via {control commentThread $threadId}.

The list is a tree (by parentId) rendered whole. Adding and editing run in the shared admin ModalWindow: the addComment! and editComment! signals render _modal.latte to a string, fill the modal with it and open it from PHP (showModal()). The form is submitted via ajax and the modal stays open after saving; showModal() only refills its content (shows success) and the commentList snippet is redrawn. The thread travels in the form as a hidden threadId, not as a persistent parameter, because link('this') of the form does not carry it. Deleting is soft: the deleted flag is toggled by the toggleDeleted! signal.

Table of Contents

Properties

$container  : Container
$routerDispatcher  : RouterDispatcher
$translator  : ITranslator
$appContext  : AppContext
$componentParameters  : array<string, mixed>
$currencyContext  : CurrencyContext|null
$deviceDetector  : DeviceDetector|null
$languageContext  : LanguageContext|null
$localizationContext  : LocalizationContext|null
$settingContext  : SettingContext
$themeContext  : ThemeContext|null
$translatorDomain  : string|null
$adding  : bool
A new comment is being added (modal with an empty form).
$addParentId  : int|null
Parent of the comment being added (reply in the thread); null for a root.
$commentManager  : CommentManager
$editId  : int|null
Edited comment (set by `handleEditComment()` or by the submit to re-render the modal).
$justSaved  : bool
The comment was just saved, so the modal shows a confirmation instead of the form (stays open).
$threadContextProvider  : callable(): mixed|null
IMPORTANT: the hidden `threadId` field is client-editable, so it is only an echo, not the thread. The source of truth is this server-side context, supplied by the host (presenter or edit component) when it creates the component: `fn() => $this->editedArticle?->getCommentThreadId()`. It is read at signal/submit time, when the host's `action*()` has already loaded the edited entity.
$threadId  : int
Thread whose comments the component manages. Not a persistent parameter, because Nette does not reliably carry it into signals and `link('this')`; it is passed explicitly as a parameter of every signal (`{link addComment! threadId => …}`) and into the form submit via the hidden `threadId` field.
$toggleFailed  : bool
The `deleted` toggle (`toggleDeleted!`) failed, so the list renders an error message.

Methods

__call()  : mixed
Dispatches `renderXyz()` to `render('Xyz', ...$args)`.
__construct()  : mixed
createComponent()  : IComponent|null
createComponentCommentForm()  : Form
getAppContext()  : AppContext
getComponentParameter()  : mixed
Returns a component parameter, or null when it is not set.
getContainer()  : Container
getCurrencyContext()  : CurrencyContext|null
getLanguageContext()  : LanguageContext|null
getLocalizationContext()  : LocalizationContext|null
getRouterDispatcher()  : RouterDispatcher
getSettingContext()  : SettingContext
getSettings()  : array<int, array<string, mixed>>
getThemeContext()  : ThemeContext|null
handleAddComment()  : void
Opens the shared modal for adding a comment (optionally as a reply to `$parentId`).
handleEditComment()  : void
Opens the shared modal for editing a comment.
handleToggleDeleted()  : void
Soft-deletes or restores a comment (toggles the `deleted` flag).
hasServiceByType()  : bool
Returns whether the container has a service of the given type.
initializeAllContexts()  : void
injectDeviceDetector()  : void
onCommentFormError()  : void
Handles failed validation (missing name/text, expired CSRF token...): redraws the modal content so the errors show. Restores the edit/add state from the submitted hidden fields.
onCommentFormSuccess()  : void
onCreate()  : void
render()  : void
setAppContext()  : $this
setComponentParameter()  : void
Sets a component parameter.
setCurrencyContext()  : $this
setLanguageContext()  : $this
setLocalizationContext()  : $this
setSettingContext()  : $this
setThemeContext()  : $this
setThreadContext()  : static
Sets the server-side thread context: the host supplies a callback returning the thread id of the entity being edited right now. It is called on every request, so a stale value cannot linger from an earlier request.
setup()  : void
setupAppContext()  : void
typedComponent()  : T
Returns a subcomponent with a verified type.
applyCommentForm()  : string|null
The only write path of the form: context gate and save.
frontPresenter()  : BasePresenter|null
Returns the front presenter the component is attached to (null outside the front end or when detached).
getModalWindow()  : ModalWindow
getTranslator()  : ITranslator
isTargetAllowed()  : bool
Returns whether the logged-in user may open the given admin target. For visibility (tiles, autocomplete, buttons); it does not throw.
requirePrivilege()  : void
Throws 403 when the logged-in user lacks the given admin privilege.
resolvePageTarget()  : string|null
Returns the RBAC key of the target page: `<presenter>:<action>`, e.g. `:Admin:System:User:show`.
setTemplateFile()  : void
setTranslator()  : self
buildNewComment()  : Comment
Builds a new comment attached to the thread (the remaining fields are filled by the success handler).
contextThreadId()  : int|null
Thread id from the host context; null when the host edits no thread (fail-closed).
createMultiplierComponent()  : Multiplier<string|int, IComponent>
Creates a multiplier for the component.
createNewComponentInstance()  : IComponent|null
Creates a new component instance without adding it to the presenter; used by the multiplier.
createStandardComponent()  : IComponent|null
Creates a standard component (not a multiplier).
findCommentInThread()  : Comment|null
Finds a comment always within the thread. No unscoped variant exists on purpose, so future code cannot use one by mistake.
loadCommentTree()  : array<int, array<int, Comment>>
Loads the thread's comments and groups them into a tree by `parentId` (`0` holds the roots, otherwise `parentId` => children).
renderModalContent()  : string
Renders the modal content (header and form or confirmation) to a string for `ModalWindow`.
requireContextThread()  : int
Signal gate: a `threadId` from a hand-built URL may be used only when it equals the host context; otherwise 403. A signal can be invoked by hand just like a form submit.
showModal()  : void
Fills and opens the shared admin {@see \App\UI\Admin\System\Components\ModalWindowComponent\ModalWindow} with the current state (form or confirmation). Idempotent: called again after the submit, it redraws only the modal content (the modal stays open, see `ModalWindow::open()`).
str()  : string
strOrNull()  : string|null

Properties

$componentParameters

protected array<string, mixed> $componentParameters = []

component parameters set by setComponentParameter()

$translatorDomain

protected string|null $translatorDomain = 'admin.base.components.commentThread'

$adding

A new comment is being added (modal with an empty form).

private bool $adding = false

$addParentId

Parent of the comment being added (reply in the thread); null for a root.

private int|null $addParentId = null

$editId

Edited comment (set by `handleEditComment()` or by the submit to re-render the modal).

private int|null $editId = null

$justSaved

The comment was just saved, so the modal shows a confirmation instead of the form (stays open).

private bool $justSaved = false

$threadContextProvider

IMPORTANT: the hidden `threadId` field is client-editable, so it is only an echo, not the thread. The source of truth is this server-side context, supplied by the host (presenter or edit component) when it creates the component: `fn() => $this->editedArticle?->getCommentThreadId()`. It is read at signal/submit time, when the host's `action*()` has already loaded the edited entity.

private callable(): mixed|null $threadContextProvider = null

Fail-closed: without a context the component saves nothing and opens no modal, so every new host must call setThreadContext().

$threadId

Thread whose comments the component manages. Not a persistent parameter, because Nette does not reliably carry it into signals and `link('this')`; it is passed explicitly as a parameter of every signal (`{link addComment! threadId => …}`) and into the form submit via the hidden `threadId` field.

private int $threadId = 0

$toggleFailed

The `deleted` toggle (`toggleDeleted!`) failed, so the list renders an error message.

private bool $toggleFailed = false

Per request (the component is rebuilt on every request), so the message does not survive another redraw. Flash cannot be used: the signal runs via ajax and the layout's flashMessagesSnippet is not redrawn in a component response.

Methods

__call()

Dispatches `renderXyz()` to `render('Xyz', ...$args)`.

public __call(string $name, array<string|int, mixed> $args) : mixed

Called only when the component has no renderXyz() method of its own.

Parameters
$name : string
$args : array<string|int, mixed>

createComponent()

public createComponent(string $name) : IComponent|null
Parameters
$name : string
Return values
IComponent|null

getComponentParameter()

Returns a component parameter, or null when it is not set.

public getComponentParameter(string $name) : mixed
Parameters
$name : string

getContainer()

public getContainer() : Container
Return values
Container

getSettings()

public getSettings() : array<int, array<string, mixed>>
Return values
array<int, array<string, mixed>>

handleAddComment()

Opens the shared modal for adding a comment (optionally as a reply to `$parentId`).

public handleAddComment(int $threadId[, int|null $parentId = null ]) : void
Parameters
$threadId : int
$parentId : int|null = null

handleEditComment()

Opens the shared modal for editing a comment.

public handleEditComment(int $threadId, int $id) : void
Parameters
$threadId : int
$id : int

handleToggleDeleted()

Soft-deletes or restores a comment (toggles the `deleted` flag).

public handleToggleDeleted(int $threadId, int $id) : void
Parameters
$threadId : int
$id : int
Attributes
#[Requires]
$methods: 'POST'

hasServiceByType()

Returns whether the container has a service of the given type.

public hasServiceByType(string $className) : bool

The name is composed dynamically (module plus component), so class_exists() runs first, and a true result guarantees a valid type.

Parameters
$className : string
Tags
phpstan-assert-if-true

class-string $className

Return values
bool

onCommentFormError()

Handles failed validation (missing name/text, expired CSRF token...): redraws the modal content so the errors show. Restores the edit/add state from the submitted hidden fields.

public onCommentFormError(Form $form) : void
Parameters
$form : Form

onCommentFormSuccess()

public onCommentFormSuccess(Form $form, ArrayHash<string|int, mixed> $values) : void
Parameters
$form : Form
$values : ArrayHash<string|int, mixed>

render()

public render([int|null $threadId = null ]) : void
Parameters
$threadId : int|null = null

setComponentParameter()

Sets a component parameter.

public setComponentParameter(string $name, mixed $value) : void
Parameters
$name : string
$value : mixed

setThreadContext()

Sets the server-side thread context: the host supplies a callback returning the thread id of the entity being edited right now. It is called on every request, so a stale value cannot linger from an earlier request.

public setThreadContext(callable(): mixed $provider) : static
Parameters
$provider : callable(): mixed
Return values
static

typedComponent()

Returns a subcomponent with a verified type.

public typedComponent(string $name, T> $type) : T

Use it instead of an inline @var above $this['x'] (which does not narrow the type) and instead of the generic IComponent that $this['x'] returns. See Form::typedComponent().

Parameters
$name : string
$type : T>
Tags
template
Return values
T

applyCommentForm()

The only write path of the form: context gate and save.

protected applyCommentForm(int $commentId, int|null $postedThreadId, int|null $parentId, array<string, mixed> $values) : string|null

Returns a relative error message key (component domain), or null on success.

Invariants enforced by the server, not by the form:

  1. the thread is the host context; a forged hidden threadId does not redirect the write;
  2. the reply parent is looked up by id + threadId, so a parent from another thread inserts nothing;
  3. the edited comment is looked up by id + threadId, so a foreign comment cannot be saved.

Deliberately free of presenter, template and form (manager and entity only), so it can be tested in isolation without DB and DI (CommentThreadContextGuardTest).

Parameters
$commentId : int
$postedThreadId : int|null
$parentId : int|null
$values : array<string, mixed>
Return values
string|null

frontPresenter()

Returns the front presenter the component is attached to (null outside the front end or when detached).

protected frontPresenter() : BasePresenter|null
Return values
BasePresenter|null

getTranslator()

protected getTranslator() : ITranslator
Return values
ITranslator

isTargetAllowed()

Returns whether the logged-in user may open the given admin target. For visibility (tiles, autocomplete, buttons); it does not throw.

protected isTargetAllowed(string|null $key) : bool

IMPORTANT: fail-closed like requirePrivilege(): an empty key and a presenter without isAllowed() both mean no. A component that skips this question lists the target to everyone.

Parameters
$key : string|null
Return values
bool

requirePrivilege()

Throws 403 when the logged-in user lacks the given admin privilege.

protected requirePrivilege(string $key) : void

Call it as the first statement of a mutating signal, before loading data, so the check cannot be bypassed by something being written first.

Parameters
$key : string

resolvePageTarget()

Returns the RBAC key of the target page: `<presenter>:<action>`, e.g. `:Admin:System:User:show`.

protected static resolvePageTarget(object|null $page) : string|null

Same interpretation as Menu::filterByPrivileges(), BasePresenter::authorizeListing() and PrivilegeCheckTrait::checkCurrentPrivilege(). The key is derived from the page target only, never from a file or class name.

Returns null when the page is missing or has no presenter, so there is nothing to allow.

Parameters
$page : object|null

AdminPage entity (hydrated, hence just object)

Return values
string|null

setTemplateFile()

protected setTemplateFile(string $dir, string $filename) : void
Parameters
$dir : string
$filename : string

setTranslator()

protected setTranslator(ITranslator $translator) : self
Parameters
$translator : ITranslator
Return values
self

buildNewComment()

Builds a new comment attached to the thread (the remaining fields are filled by the success handler).

private buildNewComment(int $threadId, int|null $parentId) : Comment
Parameters
$threadId : int
$parentId : int|null
Return values
Comment

contextThreadId()

Thread id from the host context; null when the host edits no thread (fail-closed).

private contextThreadId() : int|null
Return values
int|null

createMultiplierComponent()

Creates a multiplier for the component.

private createMultiplierComponent(string $name) : Multiplier<string|int, IComponent>
Parameters
$name : string
Return values
Multiplier<string|int, IComponent>

createNewComponentInstance()

Creates a new component instance without adding it to the presenter; used by the multiplier.

private createNewComponentInstance(string $baseName[, int|string|null $uniqueKey = null ]) : IComponent|null
Parameters
$baseName : string
$uniqueKey : int|string|null = null
Return values
IComponent|null

createStandardComponent()

Creates a standard component (not a multiplier).

private createStandardComponent(string $name) : IComponent|null
Parameters
$name : string
Return values
IComponent|null

findCommentInThread()

Finds a comment always within the thread. No unscoped variant exists on purpose, so future code cannot use one by mistake.

private findCommentInThread(int $id, int $threadId) : Comment|null
Parameters
$id : int
$threadId : int
Return values
Comment|null

loadCommentTree()

Loads the thread's comments and groups them into a tree by `parentId` (`0` holds the roots, otherwise `parentId` => children).

private loadCommentTree() : array<int, array<int, Comment>>
Return values
array<int, array<int, Comment>>

renderModalContent()

Renders the modal content (header and form or confirmation) to a string for `ModalWindow`.

private renderModalContent() : string
Return values
string

requireContextThread()

Signal gate: a `threadId` from a hand-built URL may be used only when it equals the host context; otherwise 403. A signal can be invoked by hand just like a form submit.

private requireContextThread(int $threadId) : int
Parameters
$threadId : int
Return values
int

showModal()

Fills and opens the shared admin {@see \App\UI\Admin\System\Components\ModalWindowComponent\ModalWindow} with the current state (form or confirmation). Idempotent: called again after the submit, it redraws only the modal content (the modal stays open, see `ModalWindow::open()`).

private showModal() : void

str()

private str(mixed $value) : string
Parameters
$value : mixed
Return values
string

strOrNull()

private strOrNull(mixed $value) : string|null
Parameters
$value : mixed
Return values
string|null
On this page

Search results