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
$container
public
Container
$container
Attributes
- #[Inject]
$routerDispatcher
public
RouterDispatcher
$routerDispatcher
$translator
public
ITranslator
$translator
Tags
$appContext
protected
AppContext
$appContext
$componentParameters
protected
array<string, mixed>
$componentParameters
= []
component parameters set by setComponentParameter()
$currencyContext
protected
CurrencyContext|null
$currencyContext
= NULL
$deviceDetector
protected
DeviceDetector|null
$deviceDetector
= NULL
$languageContext
protected
LanguageContext|null
$languageContext
= NULL
$localizationContext
protected
LocalizationContext|null
$localizationContext
= NULL
$settingContext
protected
SettingContext
$settingContext
$themeContext
protected
ThemeContext|null
$themeContext
= NULL
$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
$commentManager read-only
private
CommentManager
$commentManager
$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>
__construct()
public
__construct(CommentManager $commentManager) : mixed
Parameters
- $commentManager : CommentManager
createComponent()
public
createComponent(string $name) : IComponent|null
Parameters
- $name : string
Return values
IComponent|nullcreateComponentCommentForm()
public
createComponentCommentForm() : Form
Return values
FormgetAppContext()
public
getAppContext() : AppContext
Return values
AppContextgetComponentParameter()
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
ContainergetCurrencyContext()
public
getCurrencyContext() : CurrencyContext|null
Return values
CurrencyContext|nullgetLanguageContext()
public
getLanguageContext() : LanguageContext|null
Return values
LanguageContext|nullgetLocalizationContext()
public
getLocalizationContext() : LocalizationContext|null
Return values
LocalizationContext|nullgetRouterDispatcher()
public
getRouterDispatcher() : RouterDispatcher
Return values
RouterDispatchergetSettingContext()
public
getSettingContext() : SettingContext
Return values
SettingContextgetSettings()
public
getSettings() : array<int, array<string, mixed>>
Return values
array<int, array<string, mixed>>getThemeContext()
public
getThemeContext() : ThemeContext|null
Return values
ThemeContext|nullhandleAddComment()
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
Return values
boolinitializeAllContexts()
public
initializeAllContexts() : void
injectDeviceDetector()
public
injectDeviceDetector(DeviceDetector $detector) : void
Parameters
- $detector : DeviceDetector
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>
onCreate()
public
onCreate() : void
render()
public
render([int|null $threadId = null ]) : void
Parameters
- $threadId : int|null = null
setAppContext()
public
setAppContext(AppContext $appContext) : $this
Parameters
- $appContext : AppContext
Return values
$thissetComponentParameter()
Sets a component parameter.
public
setComponentParameter(string $name, mixed $value) : void
Parameters
- $name : string
- $value : mixed
setCurrencyContext()
public
setCurrencyContext(CurrencyContext|null $currencyContext) : $this
Parameters
- $currencyContext : CurrencyContext|null
Return values
$thissetLanguageContext()
public
setLanguageContext(LanguageContext|null $languageContext) : $this
Parameters
- $languageContext : LanguageContext|null
Return values
$thissetLocalizationContext()
public
setLocalizationContext(LocalizationContext|null $localizationContext) : $this
Parameters
- $localizationContext : LocalizationContext|null
Return values
$thissetSettingContext()
public
setSettingContext(SettingContext $settingContext) : $this
Parameters
- $settingContext : SettingContext
Return values
$thissetThemeContext()
public
setThemeContext(ThemeContext|null $themeContext) : $this
Parameters
- $themeContext : ThemeContext|null
Return values
$thissetThreadContext()
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
staticsetup()
public
setup(BasePresenter|BasePresenter $presenter) : void
Parameters
- $presenter : BasePresenter|BasePresenter
setupAppContext()
public
setupAppContext(AppContext $appContext) : void
Parameters
- $appContext : AppContext
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
Return values
TapplyCommentForm()
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:
- the thread is the host context; a forged hidden
threadIddoes not redirect the write; - the reply parent is looked up by
id + threadId, so a parent from another thread inserts nothing; - 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|nullfrontPresenter()
Returns the front presenter the component is attached to (null outside the front end or when detached).
protected
frontPresenter() : BasePresenter|null
Return values
BasePresenter|nullgetModalWindow()
protected
getModalWindow() : ModalWindow
Return values
ModalWindowgetTranslator()
protected
getTranslator() : ITranslator
Return values
ITranslatorisTargetAllowed()
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
boolrequirePrivilege()
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
-
AdminPageentity (hydrated, hence justobject)
Return values
string|nullsetTemplateFile()
protected
setTemplateFile(string $dir, string $filename) : void
Parameters
- $dir : string
- $filename : string
setTranslator()
protected
setTranslator(ITranslator $translator) : self
Parameters
- $translator : ITranslator
Return values
selfbuildNewComment()
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
CommentcontextThreadId()
Thread id from the host context; null when the host edits no thread (fail-closed).
private
contextThreadId() : int|null
Return values
int|nullcreateMultiplierComponent()
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|nullcreateStandardComponent()
Creates a standard component (not a multiplier).
private
createStandardComponent(string $name) : IComponent|null
Parameters
- $name : string
Return values
IComponent|nullfindCommentInThread()
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|nullloadCommentTree()
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
stringrequireContextThread()
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
intshowModal()
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
stringstrOrNull()
private
strOrNull(mixed $value) : string|null
Parameters
- $value : mixed