CoolCMS API

AdvertPresenter extends BasePresenter
in package
uses AdvertListTrait, AdvertFavListTrait, AdvertDetailTrait, PermissionTrait, CategoryTrait, RegionTrait, ParameterTrait, AddAdvertFormTrait, EditAdvertFormTrait, AdvertAuthorizationFormTrait, AdvertPasswordResetFormTrait, EditStatisticsTrait, EditResponsesTrait, EditRenewTrait, EditExtendTrait, EditDeleteTrait, ArraysInitTrait, UserInitTrait, FavouriteHandlerTrait, ReportTrait, FollowTrait

FinalYes

Table of Contents

Constants

DEFAULT_URL_PARAMS  : mixed = [ 'page' => null, 'view' => null, 'order' => nu...
URL state reset for a link that starts a new flow (e.g. "Add advert" from a list).
DEFUALT_URL_PARAMS  : mixed = self::DEFAULT_URL_PARAMS
IDENTITY_LOCALIZED_KEYS  : mixed = ['site.name', 'site.tagline']
Text identity keys that have language variants `<key>.<alias>`.
SESSION_NAMESPACE  : mixed = 'front'
advertSessionExpiration  : mixed = 7200
extendWindowDays  : mixed = 2
Number of days before expiration when extending is offered.
FALLBACK_LAYOUT  : mixed = '@layout_one.latte'
Layout used when the theme lacks the requested one.
IDENTITY_ASSET_KEYS  : mixed = ['site.logo.assetId' => 'setLogo', 'site.logo.d...
Setting key to DTO setter; the order matches the form fields.
IDENTITY_MODULE  : mixed = 1
Identity settings live in the System module.
LIST_INCLUDES  : mixed = ['parameterRels.parameter.translations', 'asset...
Includes of the list; identical for the DB path and the search endpoint.
LOCALE_MAP  : mixed = ['cs' => 'cs_CZ', 'en' => 'en_US', 'sk' => 'sk_...
Maps the short language code to the full locale of the registered NEON sources.
manageLinkKeyPrefix  : string = 'front.bazaar.presenters.advert.manageLink.'
passwordResetKeyPrefix  : string = 'front.bazaar.presenters.advert.passwordReset.'
responseDetailModalTarget  : mixed = 'responseDetailModal'
Unique key of the response detail modal in `ModalRegistry`.
sessionSection  : mixed = 'front.bazaar.advert.edit'
SPECIFICATION_SKIP_KEYS  : array<string|int, string> = ['name', 'keywords', 'text', 'type', 'state', '...
tabConfirm  : mixed = 'confirm'
tabDelete  : mixed = 'delete'
tabExtend  : mixed = 'extend'
tabForm  : mixed = 'form'
Tab keys of the edit page; they match the files in `Templates/Advert/Sections/Edit/`.
tabRenew  : mixed = 'renew'
tabResponses  : mixed = 'responses'
tabStatistics  : mixed = 'statistics'

Properties

$accessTokenValidator  : AccessTokenValidator
Validates access tokens beyond signature and expiry.
$accountDeletionSessionManager  : AccountDeletionSessionManager
Account deletion — cancel ticket of a sign-in refused while the account awaits deletion.
$advertFollowerManager  : AdvertFollowerManager
$advertLinksConfig  : AdvertLinksConfig
$advertParameterListFactory  : AdvertParameterListFactory
$advertPasswordResetManager  : AdvertPasswordResetManager
$advertReportManager  : AdvertReportManager
$advertStatisticsManager  : AdvertStatisticsManager
$authManager  : AuthManager
$authStorageManager  : AuthStorageManager
$blacklistedWordManager  : BlacklistedWordManager
$capabilityTokenIssuer  : CapabilityTokenIssuer
Issuer of the capability token for the Api layer (see {@see self::checkAutorization()}).
$categoryId  : string|null
$categorySlugTranslator  : CategoryNameTranslator
Router translator: the same one that builds the slug from the id in links (see init below).
$catId  : array<string|int, int>|null
$consentGateFactory  : ConsentGateFactory
$container  : Container
$count  : int|null
$currencyContext  : CurrencyContext
$currencyManager  : CurrencyManager
$deviceDetector  : DeviceDetector
$displayPaymentRegionManager  : PaymentRegionManager
IMPORTANT: a custom name, not `$paymentRegionManager`. Several presenters (Discussion/Bazaar Payment) have the region manager in the constructor, and a same-named property would break the container ("Cannot redeclare").
$eventDispatcher  : EventDispatcher
$forgotPasswordFormFactory  : ForgotPasswordFormFactory
$frontFormLimiter  : FrontFormLimiter
$googleIntegrations  : GoogleIntegrations
Google services from the third-party connections, used in the page head (`analytics.latte` templates).
$id  : int
$identityAssetManager  : AssetManager
$jwtManager  : JwtManager
$languageContext  : LanguageContext
$languageManager  : LanguageManager
$linksPaymentItemManager  : PaymentItemManager
$localizationContext  : LocalizationContext
$localizationManager  : LocalizationManager
$loginDeviceCookie  : LoginDeviceCookie
"Known device" cookie for the password login throttle ({@see self::rememberLoginDevice()}).
$modalRegistry  : ModalRegistry
$modalWindowsFactory  : ModalWindowsFactory
$name  : string|null
$pageManager  : PageManager
$presenterContext  : PresenterContext
$reCaptchaRuntime  : ReCaptchaRuntime
Decides at runtime whether forms use reCAPTCHA (configuration plus the connection set up in the admin).
$recommendFormFactory  : RecommendFormFactory
$regionSlugTranslator  : RegionNameTranslator
Router translator: the same one that builds the slug from the id in links (see init below).
$responseFormFactory  : ResponseFormFactory
$responseManager  : ResponseManager
$routerDispatcher  : RouterDispatcher
$securityConfig  : SecurityConfig
reCAPTCHA switch for templates (`$reCaptchaEnabled` in the `scripts.latte` templates).
$settingContext  : SettingContext
$settingManager  : SettingManager
$siteBaseUrl  : SiteBaseUrl
Website address for links outside the request; public because components use it (`MessageInfoLine`).
$slug  : string|int|null
$socialsShareFactory  : SocialsShareFactory
$tab  : string|null
Active section (tab) of the edit page; see `Templates/Advert/Sections/Edit/`.
$tariffManager  : TariffManager
$themeContext  : ThemeContext
$themeManager  : ThemeManager
$themeResolver  : DefaultThemeResolver
$translator  : ITranslator
$translatorSessionResolver  : Session
$userManager  : UserManager
$debugMode  : bool
$metaData  : MetaDataDTO|null
$ogData  : OgDataDTO|null
$pageEntity  : Page|null
$productionMode  : bool
$siteIdentity  : SiteIdentityDto|null
$translatorDomain  : string
$advert  : Advert|null
$advertFormFactory  : AdvertFormFactory
$advertManager  : AdvertManager
$blacklistedWords  : array<string|int, string>
$categoryManager  : CategoryManager
$categoryNodesCache  : array<int, array<string, mixed>>|null
$currentCategory  : Category|null
$currentRegion  : Region|null
$esSearcher  : EsSearcher
$favouriteSessionSection  : string
$favouriteSnippets  : array<int, string>
$frontUserManager  : UserManager
$hasPaidAccess  : bool
$isLoggingIn  : bool
$parameterManager  : ParameterManager
$parameterValues  : array<string|int, object>
$regionManager  : RegionManager
$selectedCategoryList  : array<string|int, Category>
$theme  : Theme|null

Methods

__construct()  : mixed
actionAdd()  : void
actionDetail()  : void
actionEdit()  : void
actionFavouriteList()  : void
actionList()  : void
addFavouriteSnippets()  : void
authFormSucceeded()  : void
beforeRender()  : void
checkAccessPermission()  : bool
completeExternalLogin()  : bool
Completes an OAuth login (`LoginExternalComponent`) with the same storage as a password login.
createComponent()  : IComponent|null
createComponentAdvertParameterList()  : AdvertParameterList
createComponentAuthForm()  : Form
createComponentConsentGate()  : ConsentGate
createComponentForgotPasswordForm()  : ForgotPasswordForm
createComponentPasswordResetForm()  : Form
createComponentRecommendForm()  : RecommendForm
createComponentResponseForm()  : ResponseForm
createComponentSocialsShare()  : SocialsShare
formatLayoutTemplateFiles()  : array<string|int, mixed>
Returns candidate layout files; Nette takes the first existing one.
getAdvert()  : Advert|null
getContainer()  : Container
getCurrentCategory()  : Category|null
getCurrentRegion()  : Region|null
getDefaultUrlParams()  : array<string, null>
getFavouriteSnippets()  : array<int, string>
getPageEntity()  : Page|null
getRouterDispatcher()  : RouterDispatcher
getTranslator()  : ITranslator
handleAddToFavourites()  : void
handleChangeLocale()  : void
handleDeleteAdvert()  : void
Handles the advert deletion signal.
handleDelFromFavourites()  : void
handleExtendAdvert()  : void
Handles the advert extension signal before expiration.
handleFollow()  : void
handleLogout()  : void
handleRenewAdvert()  : void
Handles the renewal signal of an expired advert.
handleReport()  : void
Writes to the DB: AJAX from the page only; a bare GET (crawler, link prefetch) ends with 403.
handleShowPhone()  : void
Handles the "show phone" action of an advert.
handleShowResponseDetailModal()  : void
Handles the signal that shows the response detail in a modal window.
handleSignOut()  : void
Sign-out from an ordinary link (account menu), also without JavaScript.
handleUnfollow()  : void
hasServiceByType()  : bool
Returns whether the container has a service of the given type.
initCurrentCategory()  : void
initCurrentRegion()  : void
initFavourites()  : void
initializeTranslator()  : void
initParameterValues()  : self
initUser()  : void
loadModalWindowComponent()  : void
loginWithCredentials()  : void
Logs in by username and password; called only by the login form (after validation and CSRF).
passwordResetFormSucceeded()  : void
prepareFilters()  : array<int, Filter>
redirectToPendingAccountDeletion()  : void
Account deletion: a sign-in (password or OAuth) was refused because the account awaits deletion: keep the cancel ticket in the session and go to the "Account deletion" page, which offers "Cancel the deletion". Without a ticket (should not happen) the visitor gets the plain refusal flash instead.
refreshAdvertSession()  : void
Updates the session token after editing: rewrites the hash and extends the expiration.
renderAdd()  : void
renderAuthorizationForm()  : void
renderDetail()  : void
renderEdit()  : void
renderFavouriteList()  : void
renderList()  : void
renderPasswordReset()  : void
renderPermissionDenied()  : void
setAdvert()  : self
setConfigParameters()  : void
setFavouritesForTemplate()  : void
setFavouriteSnippets()  : void
setTranslator()  : $this
startup()  : void
typedComponent()  : T
Returns a subcomponent with a verified type.
buildSiteIdentity()  : SiteIdentityDto
checkAuthentication()  : void
createComponentAddAdvertForm()  : AdvertForm
createComponentCategorySelectForm()  : Form
createComponentEditAdvertForm()  : AdvertForm
createComponentFrontSystemModalWindows()  : ModalWindows
currentAbsoluteUrl()  : string
Returns `link('//this')`, or the request address without query when it cannot be built.
fallbackToMainLocalization()  : bool
Whether the presenter may fall back to the main localization for an unknown locale; a regular page may not (404).
findExpressPaymentTariffs()  : iterable<string|int, ExpressTariffInterface>
Express payment offers whatever the advert can still be boosted with, so the quick prices have to come with the tariff — see {@see ExpressPaymentTrait}.
getExpressPaymentModuleCode()  : string
getExpressPaymentReference()  : string|null
getFavouriteIds()  : array<int, int>
getFavouriteSessionStorage()  : Session
getSessionKey()  : string
Returns a session key string, e.g. 'front.auth.step1'.
getSessionSection()  : SessionSection
Returns a Nette\SessionSection instance using a dynamically built session key.
initFollower()  : void
initializeMetaData()  : void
initializeOgData()  : void
initializePresenterContext()  : void
initSiteIdentity()  : void
isAdvertExpired()  : bool
Returns whether the advert is expired, so the "Renew" tab is offered.
isAdvertNearExpiration()  : bool
Returns whether the advert is within `extendWindowDays` before expiration (and not yet expired), so the "Extend" tab is offered.
isSameSiteRequest()  : bool
Tells whether the request comes from the own site (Nette SameSite cookie); protects GET actions with a side effect.
loadPageEntity()  : Page|null
Returns the page of the request by `slug` (empty is the home page, a number is the ID, otherwise `href`).
logout()  : void
metaData()  : MetaDataDTO
Returns the page meta data; filled by startup, an empty DTO before that.
ogData()  : OgDataDTO
Returns the page OG data; filled by `initializeOgData()` in startup, an empty DTO before that.
prepareBlacklistedWords()  : void
prepareEditResponses()  : void
Prepares the data of the responses tab; `renderEdit()` calls it only when the tab is active.
prepareEditStatistics()  : void
Prepares the data of the statistics tab; `renderEdit()` calls it only when the tab is active.
prepareMetaData()  : MetaDataDTO
prepareOgData()  : OgDataDTO
prepareSpecifications()  : void
Builds the advert specifications array for rendering (name, value, type).
setTranslatorDomain()  : void
buildCategoryHierarchy()  : array<int, array<string, mixed>>
Builds the recursive category tree by `parentId`.
buildCategoryNodes()  : array<int, array<string, mixed>>
buildDefaultOgImage()  : OgImageDTO|null
Builds the default OG image from the site identity (admin, settings, site identity).
buildEditTabs()  : array<int, string>
Builds the list of available tabs. Renew and Extend appear by the expiration state.
buildRegionSubtreeFilters()  : array<string|int, BaseFilter>
Builds the "in region" filter from the suggest (`REGION_PICKER`, `p[<id>]=<regionId>`): adverts whose region lies in the subtree of the selected node, so a county also finds the adverts of its municipalities. Same shape as the list by the region in the path ({@see RegionTrait::prepareRegionFilter()}). A nonexistent region means no filter (an invalid id from the URL must not hide the whole list).
buildResponseDetailContent()  : string
Builds the response detail HTML (a partial rendered to a string) for the modal content.
buildResponseDetailError()  : string
categoryLabel()  : string
checkAutorization()  : bool
Checks whether the current user may edit the advert.
checkUserPaymentValidity()  : bool
collectAdvertVisibilityIssues()  : array<int, string>
Returns the reasons why the advert is not publicly visible; an empty array means it is fine.
collectLeafItems()  : array<int, string>
Builds the flat list `{leafId => label}` for `addCheckboxList`; only leaf categories (without children) get a checkbox.
createIdentity()  : SimpleIdentity|null
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).
expireIdleLogin()  : bool
Checks whether the login expired from inactivity.
extractBaseParameterIds()  : array<string|int, int>
Extracts the unique base `parameterId` from the URL keys (strips the suffixes `_from`, `_to`, `_d`, `_r`).
formatLayoutCandidates()  : array<int, string>
getCategoryNodes()  : array<int, array<string, mixed>>
getClientIp()  : string
getFavouriteSection()  : SessionSection
getPasswordResetKey()  : string
grantAdvertEditCapability()  : void
Passes the fact that this visitor just proved the advert password to the Api layer. The token is minted again on every request from the current session state (already verified against the DB by the caller) and is never stored.
grantAdvertEditCapabilityFromSession()  : void
Issues the capability from the session before the advert is loaded. The Api verifies it itself (password digest from the DB in `ownsByCapability()`), so a stale session record opens nothing. Without it `get` for editing would give an anonymous editor a masked e-mail and the form would fail validation. {@see self::checkAutorization()} then issues the capability again after the check against the fresh hash, which stays decisive for access.
grantAdvertSessionAfterReset()  : void
Same session token as `AdvertAuthorizationFormTrait::authFormSucceeded()` writes after the password form. The hash is read back fresh from the Api — `checkAutorization()` compares it with the DB value on every request.
hasPaidLinks()  : bool
True when the advert has an active, valid item of the "Show links" tariff (`AdvertLinksConfig`, default 4): the description then shows its web addresses as links.
hasPasswordResetKey()  : bool
Does the request carry the reset key from the e-mailed link (query) or the form (POST)?
identitySetting()  : string
Returns the module 1 key value as a string (empty when the key is missing or not a scalar).
identitySettingOrNull()  : string|null
Works like {@see identitySetting()}, but an empty value is null (the template omits the field).
initializeDisplayRegion()  : void
Initializes the currency and VAT display of prices.
initializeExternalFiles()  : void
initializeMetaTags()  : void
initializePage()  : void
isAdvertOwner()  : bool
isCategoryBranchActive()  : bool
Whether the category and all its ancestors are active (nested set: an ancestor encloses lft/rgt).
isCategoryStepCompleted()  : bool
isDevelopment()  : bool
isEntityInFavourites()  : bool
isRegionBranchActive()  : bool
Whether the region and all its ancestors are active (nested set: an ancestor encloses lft/rgt).
layoutFileExists()  : bool
loadCategoryTree()  : array<string|int, Category>
loadIdentityAssets()  : array<int, Asset>
Loads the identity assets by id, keyed by id.
loadLocalization()  : Localization|null
Returns the localization by `loc` from the URL, or `null` when there is no such locale.
loadSelectedCategories()  : array<string|int, Category>
localizationCurrencyKey()  : string|null
Front needs no currency key from the localization; the currency and the display region are filled by {@see CurrencyInitTrait::initializeDisplayRegion()}.
localizedIdentitySetting()  : string
Returns the language-dependent identity value: `<key>.<page language alias>`; an empty or missing value falls back to the base key (default language). An empty string means neither is set.
logoutNetteUser()  : void
parseStatDate()  : DateTimeImmutable|null
Parses a date in the Y-m-d format; returns null otherwise.
prepareAdvertSearchFilter()  : Filter
Builds the fulltext filter through ES (ids become `SearchIdFilter`; visibility and paging stay in DB phase 1). When ES is off, down or the query is short, it transparently falls back to MySQL `MATCH_AGAINST` over `parameterRels.value`.
prepareCategoryFilter()  : void
prepareOrderBy()  : array<string, bool>
prepareOtherAdverts()  : void
Loads the other adverts of the owner and the count of their published adverts for the seller card.
prepareParameterFilter()  : void
preparePriceFilter()  : void
Adds the upper price bound from the URL (`?priceTo=...`) sent by `SearchBox`.
prepareRegionFilter()  : void
prepareSearchFilters()  : array<int, Filter>
Builds the filters for the search endpoint: everything except fulltext (the Api side does it, ES or `MATCH_AGAINST`). That is visibility + category/region + parameter filters from `$_GET['p']`, which are DB phase 1.
prepareView()  : void
processFailedLogin()  : void
processInvalidToken()  : void
processLogin()  : void
processLoginException()  : void
processMissingToken()  : void
processSuccessfulLogin()  : void
processValidToken()  : void
readLoginDeviceToken()  : string|null
Returns the raw known-device cookie for the Api login (the Api verifies its signature).
redeemManageKeyFromLink()  : void
Handles the "advert management" link from the insertion e-mail (`edit?id=...&manageKey=...`), which replaces the readable password the e-mail carried before.
redirectAfterLoginAction()  : void
Ends a sign-in attempt. A classic (non-AJAX) successful sign-in lands on "My account" ({@see PageId::SYSTEM_USER_ACCOUNT}); a failed one on the home page. An AJAX sign-in stays on the current page and only redraws the flash messages.
refreshNetteIdentity()  : bool
Refreshes the token for the same logged-in user.
rememberLoginDevice()  : void
Issues or renews the "known device" cookie after a successful login (password or OAuth).
resolveLoginErrorMessage()  : string
Resolves the translated login error text.
resolveStatGranularity()  : string
Validates the granularity from the request against the allowed values.
resolveStatRange()  : DateTimeImmutable}
Returns the date range from the request. When a bound is missing or invalid, the default range of the granularity is used.
revokeTokensOnLogout()  : void
Calls the Api logout (revokes the refresh family and deny-lists the access `jti`) for the tokens of the current session.
searchViaApi()  : Advert>, 1: int, 2: bool, 3: array>}|null
Searches adverts through the Api endpoint `api/bazaar/advert/search` instead of building the filter in the presenter. Three states:
setDefaultAvatar()  : array{rel: string}
setNetteUser()  : void
showResponseDetailModal()  : void
Registers the response detail modal and tells the container to show it.
tryRefresh()  : bool
Tries an automatic refresh when the access token has expired.

Constants

DEFAULT_URL_PARAMS

URL state reset for a link that starts a new flow (e.g. "Add advert" from a list).

public mixed DEFAULT_URL_PARAMS = [ 'page' => null, 'view' => null, 'order' => null, 'count' => null, 'cat' => null, 'catId' => null, 'categoryId' => null, 'search' => null, 'slug' => null, 'id' => null, 'tab' => null, 'c' => null, 'filter' => null, 'pr' => null, // Private messages (`MessagePresenter`): without the reset the mailbox and the // unread filter would leak into links outside messages. 'box' => null, 'unread' => null, ]

Nette adds the #[Persistent] parameters of the current request to a link when the target presenter inherits them from the same class, so /inzeraty/automobily would produce /pridat-inzerat/?count=0&categoryId=automobily. An explicit null cancels that: ComponentReflection::saveState() skips a key already present in the parameters, so the global state is not filled in and null drops out of the URL. null is safe even for untyped or non-nullable parameters (public int $id) because the skipped value is never cast.

The list covers all own persistent parameters of front presenters. Parameters declared on this BasePresenter (slug, lang, loc) are carried on purpose and do not belong in the reset, except slug, which the caller always overrides with the page ID.

IMPORTANT: a new #[Persistent] parameter must be added here too; otherwise it leaks into links. Checked by tests/Unit/Core/DefaultUrlParamsCoverageTest.php.

DEFUALT_URL_PARAMS

misspelled name kept for existing callers, use DEFAULT_URL_PARAMS

public mixed DEFUALT_URL_PARAMS = self::DEFAULT_URL_PARAMS

IDENTITY_LOCALIZED_KEYS

Text identity keys that have language variants `<key>.<alias>`.

public mixed IDENTITY_LOCALIZED_KEYS = ['site.name', 'site.tagline']

SESSION_NAMESPACE

protected mixed SESSION_NAMESPACE = 'front'

extendWindowDays

Number of days before expiration when extending is offered.

private mixed extendWindowDays = 2

FALLBACK_LAYOUT

Layout used when the theme lacks the requested one.

private mixed FALLBACK_LAYOUT = '@layout_one.latte'

@layout_one.latte (one column, shared header and footer) exists in every theme and is the plainest page the system can render, so anything fits in it.

IMPORTANT: not @layout.latte; that is a bare Nette skeleton titled "Nette Web" without header, footer or styles, hardly better than a missing layout.

IDENTITY_ASSET_KEYS

Setting key to DTO setter; the order matches the form fields.

private mixed IDENTITY_ASSET_KEYS = ['site.logo.assetId' => 'setLogo', 'site.logo.dark.assetId' => 'setLogoDark', 'site.favicon.assetId' => 'setFavicon', 'site.og.assetId' => 'setOgImage']

IDENTITY_MODULE

Identity settings live in the System module.

private mixed IDENTITY_MODULE = 1

LIST_INCLUDES

Includes of the list; identical for the DB path and the search endpoint.

private mixed LIST_INCLUDES = ['parameterRels.parameter.translations', 'assetRels.asset.translations', 'categoryRels.category.translations', 'user']

LOCALE_MAP

Maps the short language code to the full locale of the registered NEON sources.

private mixed LOCALE_MAP = ['cs' => 'cs_CZ', 'en' => 'en_US', 'sk' => 'sk_SK']

The short code (cs, en, sk) comes from LanguageContext, derived from the URL parameter loc/lang; the full locales are cs_CZ, en_US, sk_SK. translator.neon configures one shared fallback chain [cs_CZ, cs, en_US, en, sk_SK, sk] for all languages, so a short code would always map to cs_CZ first, whatever language is active. A direct match on the full locale bypasses that.

The Contributte LocaleResolvers\Router (the only configured resolver) looks for the route parameter locale, but this application routes under loc, so it always returns null and the translator falls back to default: cs. An explicit setLocale() from here bypasses the resolver, which Translator::getLocale() asks only while no locale has been set explicitly.

responseDetailModalTarget

Unique key of the response detail modal in `ModalRegistry`.

private mixed responseDetailModalTarget = 'responseDetailModal'

SPECIFICATION_SKIP_KEYS

private array<string|int, string> SPECIFICATION_SKIP_KEYS = ['name', 'keywords', 'text', 'type', 'state', 'street', 'city', 'zip', 'country', 'regionId', 'latitude', 'longitude', 'currency', 'is_vehicle', 'priceType', 'price', 'allow_price_suggestion', 'email', 'phone', 'url', 'datetime', 'date', 'time']

parameter keys that are not rendered in the specifications section

tabForm

Tab keys of the edit page; they match the files in `Templates/Advert/Sections/Edit/`.

private mixed tabForm = 'form'

Properties

$accessTokenValidator

Validates access tokens beyond signature and expiry.

public AccessTokenValidator $accessTokenValidator

A revoked token (jti on the deny-list) that has not expired yet would otherwise keep the session logged in although the Api guard rejects the same token. Same contract as JwtAuthTrait::checkJwtAuth(), one implementation.

Attributes
#[Inject]

$categoryId

public string|null $categoryId = null
Attributes
#[Persistent]

$categorySlugTranslator

Router translator: the same one that builds the slug from the id in links (see init below).

public CategoryNameTranslator $categorySlugTranslator
Attributes
#[Inject]

$catId

public array<string|int, int>|null $catId = null

ids of the categories chosen in step 1 (null = nothing chosen yet)

Attributes
#[Persistent]

$displayPaymentRegionManager

IMPORTANT: a custom name, not `$paymentRegionManager`. Several presenters (Discussion/Bazaar Payment) have the region manager in the constructor, and a same-named property would break the container ("Cannot redeclare").

public PaymentRegionManager $displayPaymentRegionManager
Attributes
#[Inject]

$googleIntegrations

Google services from the third-party connections, used in the page head (`analytics.latte` templates).

public GoogleIntegrations $googleIntegrations
Attributes
#[Inject]

$loginDeviceCookie

"Known device" cookie for the password login throttle ({@see self::rememberLoginDevice()}).

public LoginDeviceCookie $loginDeviceCookie
Attributes
#[Inject]

$reCaptchaRuntime

Decides at runtime whether forms use reCAPTCHA (configuration plus the connection set up in the admin).

public ReCaptchaRuntime $reCaptchaRuntime
Attributes
#[Inject]

$regionSlugTranslator

Router translator: the same one that builds the slug from the id in links (see init below).

public RegionNameTranslator $regionSlugTranslator
Attributes
#[Inject]

$securityConfig

reCAPTCHA switch for templates (`$reCaptchaEnabled` in the `scripts.latte` templates).

public SecurityConfig $securityConfig
Attributes
#[Inject]

$siteBaseUrl

Website address for links outside the request; public because components use it (`MessageInfoLine`).

public SiteBaseUrl $siteBaseUrl
Attributes
#[Inject]

$slug

public string|int|null $slug = NULL
Attributes
#[Persistent]

$tab

Active section (tab) of the edit page; see `Templates/Advert/Sections/Edit/`.

public string|null $tab = null
Attributes
#[Persistent]

$blacklistedWords

private array<string|int, string> $blacklistedWords = []

forbidden words in the target language, for the render-time replacement

$categoryNodesCache

private array<int, array<string, mixed>>|null $categoryNodesCache = null

lazily cached tree for the select step

$favouriteSnippets

private array<int, string> $favouriteSnippets = ['favouriteLinksSnippet', 'flashMessagesSnippet']

$parameterValues

private array<string|int, object> $parameterValues = []

parameter options by id

$selectedCategoryList

private array<string|int, Category> $selectedCategoryList = []

categories loaded in step 2 by $catId

Methods

__construct()

public __construct(AdvertManager $advertManager, CategoryManager $categoryManager, RegionManager $regionManager, ParameterManager $parameterManager, AdvertFormFactory $advertFormFactory, EsSearcher $esSearcher, UserManager $frontUserManager) : mixed
Parameters
$advertManager : AdvertManager
$categoryManager : CategoryManager
$regionManager : RegionManager
$parameterManager : ParameterManager
$advertFormFactory : AdvertFormFactory
$esSearcher : EsSearcher
$frontUserManager : UserManager

actionDetail()

public actionDetail(string|int|null $slug, int $id, string $loc) : void
Parameters
$slug : string|int|null
$id : int
$loc : string

actionList()

public actionList(int|null $page, string|int|null $category, string|int|null $loc, int|null $order) : void
Parameters
$page : int|null
$category : string|int|null
$loc : string|int|null
$order : int|null

addFavouriteSnippets()

public addFavouriteSnippets(array<int, string> $snippets) : void
Parameters
$snippets : array<int, string>

checkAccessPermission()

public checkAccessPermission(array<string|int, mixed> $categoryList) : bool
Parameters
$categoryList : array<string|int, mixed>
Return values
bool

completeExternalLogin()

Completes an OAuth login (`LoginExternalComponent`) with the same storage as a password login.

public completeExternalLogin(array<string, mixed> $resultData) : bool

IMPORTANT: the whole session section is cleared first and the refresh token from the Api must be stored. Otherwise the user drops out after 15 minutes, and the refresh token of the previous account stays in the section, so tryRefresh() would log account A back in after expiry (shared computer).

Parameters
$resultData : array<string, mixed>

data from the Api loginExternal (token, refreshToken, payload)

Return values
bool

createComponent()

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

formatLayoutTemplateFiles()

Returns candidate layout files; Nette takes the first existing one.

public formatLayoutTemplateFiles() : array<string|int, mixed>

IMPORTANT: when the theme lacks the layout, a fallback is added. The layout name is stored in cms_system_pages.layout, one column shared by all themes, so a page set to @text_layout_two.latte wants that file even in a theme that never got it. Without a fallback Nette finds nothing and renders the presenter without a layout: the page has no header, footer or styles but returns HTTP 200.

The fallback is logged, because a silent fallback would hide the problem entirely; when the page asks for the fallback layout itself and the theme lacks it, there is nowhere to fall back to, so it is logged as an error and the page renders without a layout.

Return values
array<string|int, mixed>

getContainer()

public getContainer() : Container
Return values
Container

getDefaultUrlParams()

public getDefaultUrlParams() : array<string, null>
Return values
array<string, null>

getFavouriteSnippets()

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

handleAddToFavourites()

public handleAddToFavourites(int $entityId[, string $style = 'Style_1' ]) : void
Parameters
$entityId : int
$style : string = 'Style_1'

handleChangeLocale()

public handleChangeLocale(string $locale) : void
Parameters
$locale : string

handleDeleteAdvert()

Handles the advert deletion signal.

public handleDeleteAdvert() : void

Soft delete: sets the column deleted = 1 (same as the cron deleteLongExpired). Editing cannot continue after deletion, so it redirects to the homepage.

handleDelFromFavourites()

public handleDelFromFavourites(int $entityId[, string $style = 'Style_1' ]) : void
Parameters
$entityId : int
$style : string = 'Style_1'

handleExtendAdvert()

Handles the advert extension signal before expiration.

public handleExtendAdvert() : void

The Api layer computes the new expiration from the intervals of the advert categories.

handleFollow()

public handleFollow(int $advertId) : void
Parameters
$advertId : int

handleRenewAdvert()

Handles the renewal signal of an expired advert.

public handleRenewAdvert() : void

Shares the computation with extending: extendExpiration() counts the new expiration from today for an expired advert (see the Api AdvertExpirationTrait).

handleReport()

Writes to the DB: AJAX from the page only; a bare GET (crawler, link prefetch) ends with 403.

public handleReport(int $advertId) : void
Parameters
$advertId : int
Attributes
#[Requires]
$ajax: true

handleShowPhone()

Handles the "show phone" action of an advert.

public handleShowPhone() : void

IMPORTANT: the signal goes through three protections, otherwise the template would print a number that is already in the data without any check:

  1. #[Requires(sameOrigin: true, ajax: true)]: a foreign page cannot trigger it and a non-AJAX call ends with 403 instead of a silent no-op,
  2. the guard :Api:System:User:showPhone on the Api layer,
  3. a rate limit of 20 per hour per IP (ratelimit.showPhone); without it the numbers could be harvested by walking through the adverts.

UserManager::revealPhone() provides the guard and the limit. An empty answer (denied, limit exhausted, seller without a number) does not switch showPhone; the template keeps the masked value without a visible error.

An advert without an account keeps the phone on the advert ($advert->get('phone')), not on a user, so revealPhone() cannot serve it; the per-IP limit for that branch is held by FrontFormLimiter::isAdvertPhoneRevealAllowed().

Attributes
#[Requires]
$sameOrigin: true
$ajax: true

handleShowResponseDetailModal()

Handles the signal that shows the response detail in a modal window.

public handleShowResponseDetailModal(int $responseId) : void

Loads the response, verifies that it belongs to the edited advert, marks it as read and sends the rendered detail to ModalWindow.

Parameters
$responseId : int

handleSignOut()

Sign-out from an ordinary link (account menu), also without JavaScript.

public handleSignOut() : void

Nette refuses every signal whose request is not same-origin (Sec-Fetch-Site) and redirects to the same URL without do, so a typed or bookmarked ?do=signOut (Sec-Fetch-Site: none) never ran. A user-initiated navigation cannot be forged by another site, so it is allowed here as well; a cross-site request still ends in detectedCsrf().

Attributes
#[Requires]
$sameOrigin: false

handleUnfollow()

public handleUnfollow(int $advertId) : void
Parameters
$advertId : int

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

initCurrentRegion()

public initCurrentRegion() : void

initFavourites()

public initFavourites(string $sessionSection) : void
Parameters
$sessionSection : string

initializeTranslator()

public initializeTranslator(string|null $alias) : void
Parameters
$alias : string|null

initParameterValues()

public initParameterValues() : self
Return values
self

loadModalWindowComponent()

public loadModalWindowComponent() : void

loginWithCredentials()

Logs in by username and password; called only by the login form (after validation and CSRF).

public loginWithCredentials(string $username, string $password[, bool $remember = false ]) : void

IMPORTANT: this must not be a handle* method. Nette finds signals by that prefix, so a public handleLogin() would be a signal on every presenter (?do=login&username=…&password=…): the password could end up in the URL and the server log, and the form token would be bypassed.

Parameters
$username : string
$password : string
$remember : bool = false

prepareFilters()

public prepareFilters([bool $useParameterFilters = true ]) : array<int, Filter>
Parameters
$useParameterFilters : bool = true
Return values
array<int, Filter>

redirectToPendingAccountDeletion()

Account deletion: a sign-in (password or OAuth) was refused because the account awaits deletion: keep the cancel ticket in the session and go to the "Account deletion" page, which offers "Cancel the deletion". Without a ticket (should not happen) the visitor gets the plain refusal flash instead.

public redirectToPendingAccountDeletion(mixed $resultData) : void
Parameters
$resultData : mixed

refreshAdvertSession()

Updates the session token after editing: rewrites the hash and extends the expiration.

public refreshAdvertSession(Advert $advert) : void

Call it after a successful save, when $advert->getPassword() holds the new hash.

Parameters
$advert : Advert

renderDetail()

public renderDetail(string|int|null $slug, int $id, string $loc) : void
Parameters
$slug : string|int|null
$id : int
$loc : string

renderList()

public renderList(int $page, string|int|null $category, string|int $loc, int $order[, int $count = 20 ]) : void
Parameters
$page : int
$category : string|int|null
$loc : string|int
$order : int
$count : int = 20

setConfigParameters()

public setConfigParameters([bool $debugMode = false ][, bool $productionMode = false ]) : void
Parameters
$debugMode : bool = false
$productionMode : bool = false

setFavouritesForTemplate()

public setFavouritesForTemplate(int $id) : void
Parameters
$id : int

setFavouriteSnippets()

public setFavouriteSnippets(array<int, string> $snippets) : void
Parameters
$snippets : array<int, string>

setTranslator()

public setTranslator(ITranslator $translator) : $this
Parameters
$translator : ITranslator
Return values
$this

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

currentAbsoluteUrl()

Returns `link('//this')`, or the request address without query when it cannot be built.

protected currentAbsoluteUrl() : string

IMPORTANT: it is called from startup() of every front page (MetaDataInitTrait, OgDataInitTrait), before Nette verifies the required action parameters. A request without them (/editace-firmy without id for actionEdit(int $id)) should end as a 404, but link('//this') reports an invalid link there; in debug mode that is an E_USER_WARNING which Tracy (strict mode) turns into a 500 that catch cannot catch. So InvalidLinkException is forced for the duration of the call and the mode is restored in finally (same principle as SafeLinkExtension::generateSafeLink(), which cannot handle the //this target because it verifies that the target presenter exists).

Return values
string

fallbackToMainLocalization()

Whether the presenter may fall back to the main localization for an unknown locale; a regular page may not (404).

protected fallbackToMainLocalization() : bool
Return values
bool

findExpressPaymentTariffs()

Express payment offers whatever the advert can still be boosted with, so the quick prices have to come with the tariff — see {@see ExpressPaymentTrait}.

protected findExpressPaymentTariffs() : iterable<string|int, ExpressTariffInterface>
Return values
iterable<string|int, ExpressTariffInterface>

getExpressPaymentModuleCode()

protected getExpressPaymentModuleCode() : string
Return values
string

getExpressPaymentReference()

protected getExpressPaymentReference() : string|null
Return values
string|null

getFavouriteIds()

protected getFavouriteIds() : array<int, int>
Return values
array<int, int>

getFavouriteSessionStorage()

protected getFavouriteSessionStorage() : Session
Return values
Session

getSessionKey()

Returns a session key string, e.g. 'front.auth.step1'.

protected getSessionKey([string|null $namespace = null ][, string|null $subsection = null ], string ...$extra) : string
Parameters
$namespace : string|null = null

Base namespace (module prefix). Uses SESSION_NAMESPACE if null.

$subsection : string|null = null

Subsection under the namespace. Uses SESSION_SUBSECTION constant if null.

$extra : string

Additional parts to append (e.g. 'step1', 'wizard', etc.).

Return values
string

getSessionSection()

Returns a Nette\SessionSection instance using a dynamically built session key.

protected getSessionSection([string|null $namespace = null ][, string|null $subsection = null ], string ...$extra) : SessionSection
Parameters
$namespace : string|null = null
$subsection : string|null = null
$extra : string
Tags
see
getSessionKey()
Return values
SessionSection

isAdvertExpired()

Returns whether the advert is expired, so the "Renew" tab is offered.

protected isAdvertExpired() : bool
Return values
bool

isAdvertNearExpiration()

Returns whether the advert is within `extendWindowDays` before expiration (and not yet expired), so the "Extend" tab is offered.

protected isAdvertNearExpiration() : bool
Return values
bool

isSameSiteRequest()

Tells whether the request comes from the own site (Nette SameSite cookie); protects GET actions with a side effect.

protected isSameSiteRequest() : bool

getHttpRequest() returns the IRequest interface, while isSameSite() exists only on Nette\Http\Request.

Return values
bool

loadPageEntity()

Returns the page of the request by `slug` (empty is the home page, a number is the ID, otherwise `href`).

protected loadPageEntity() : Page|null

Returns null when the page does not exist; the 404 is decided by BasePresenter::startup(). The error page (ErrorPresenter) overrides the method because it has no page in the DB and the layout needs an entity.

Return values
Page|null

ogData()

Returns the page OG data; filled by `initializeOgData()` in startup, an empty DTO before that.

protected ogData() : OgDataDTO
Return values
OgDataDTO

prepareBlacklistedWords()

protected prepareBlacklistedWords() : void

prepareEditResponses()

Prepares the data of the responses tab; `renderEdit()` calls it only when the tab is active.

protected prepareEditResponses() : void

prepareEditStatistics()

Prepares the data of the statistics tab; `renderEdit()` calls it only when the tab is active.

protected prepareEditStatistics() : void

prepareSpecifications()

Builds the advert specifications array for rendering (name, value, type).

protected prepareSpecifications(Advert $advert) : void

Skips the skipped keys, child option parameters and empty values, so an empty array directly means "nothing to show".

Parameters
$advert : Advert

buildCategoryHierarchy()

Builds the recursive category tree by `parentId`.

private buildCategoryHierarchy(array<string|int, Category> $flat) : array<int, array<string, mixed>>

Each node: ['id', 'category', 'depth', 'isLeaf', 'label', 'priceLabel', 'children'].

Parameters
$flat : array<string|int, Category>
Return values
array<int, array<string, mixed>>

buildCategoryNodes()

private buildCategoryNodes(array<string|int, Category> $categories, array<int, array<string|int, Category>> $childrenByParent, int $depth, int|null $langId) : array<int, array<string, mixed>>
Parameters
$categories : array<string|int, Category>
$childrenByParent : array<int, array<string|int, Category>>
$depth : int
$langId : int|null
Return values
array<int, array<string, mixed>>

buildDefaultOgImage()

Builds the default OG image from the site identity (admin, settings, site identity).

private buildDefaultOgImage(string $alt) : OgImageDTO|null

Default only: detail pages override the image with their own (OgDataTrait::prepareOgDataForDetail()), after this call. Without a configured image og:image is not rendered at all.

Parameters
$alt : string
Return values
OgImageDTO|null

buildEditTabs()

Builds the list of available tabs. Renew and Extend appear by the expiration state.

private buildEditTabs() : array<int, string>
Return values
array<int, string>

buildRegionSubtreeFilters()

Builds the "in region" filter from the suggest (`REGION_PICKER`, `p[<id>]=<regionId>`): adverts whose region lies in the subtree of the selected node, so a county also finds the adverts of its municipalities. Same shape as the list by the region in the path ({@see RegionTrait::prepareRegionFilter()}). A nonexistent region means no filter (an invalid id from the URL must not hide the whole list).

private buildRegionSubtreeFilters(int|null $parameterId, array<int|string, mixed> $urlParams) : array<string|int, BaseFilter>
Parameters
$parameterId : int|null
$urlParams : array<int|string, mixed>
Return values
array<string|int, BaseFilter>

buildResponseDetailContent()

Builds the response detail HTML (a partial rendered to a string) for the modal content.

private buildResponseDetailContent(Response $response, object|null $currency) : string
Parameters
$response : Response
$currency : object|null
Return values
string

buildResponseDetailError()

private buildResponseDetailError() : string
Return values
string

checkAutorization()

Checks whether the current user may edit the advert.

private checkAutorization() : bool

A logged-in owner passes without a session. A guest needs a valid session token (id + hash + expiration) stored after typing the password.

When the session check passes, a capability token for the Api layer is issued as well (self::grantAdvertEditCapability()). Without it an anonymous editor is a plain guest for the Api guard, which rejects save, extend and delete in internalMode: enforce.

IMPORTANT: the order is binding. The capability is issued only after the comparison $hash !== $advertHash, that is, after the check against the fresh hash from the DB. Issued earlier, it would lose the property this mechanism exists for: an owner password change must immediately invalidate every session that is being edited.

Return values
bool

checkUserPaymentValidity()

private checkUserPaymentValidity() : bool
Return values
bool

collectAdvertVisibilityIssues()

Returns the reasons why the advert is not publicly visible; an empty array means it is fine.

private collectAdvertVisibilityIssues(Advert $advert) : array<int, string>
Parameters
$advert : Advert
Return values
array<int, string>

collectLeafItems()

Builds the flat list `{leafId => label}` for `addCheckboxList`; only leaf categories (without children) get a checkbox.

private collectLeafItems(array<string|int, mixed> $nodes) : array<int, string>
Parameters
$nodes : array<string|int, mixed>

nodes {id, label, isLeaf, children} (children have the same shape)

Return values
array<int, string>

createIdentity()

private createIdentity(array<string, mixed> $payload, string $token) : SimpleIdentity|null
Parameters
$payload : array<string, mixed>
$token : string
Return values
SimpleIdentity|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

expireIdleLogin()

Checks whether the login expired from inactivity.

private expireIdleLogin(AuthSessionStorage $authStorage) : bool

If so, it revokes the tokens (like a logout), clears the auth section, logs out the Nette identity and returns true; otherwise it moves the activity stamp. The limit is 30 minutes without "remember me", 31 days with it (AuthSessionStorage::isIdleExpired()).

Parameters
$authStorage : AuthSessionStorage
Return values
bool

extractBaseParameterIds()

Extracts the unique base `parameterId` from the URL keys (strips the suffixes `_from`, `_to`, `_d`, `_r`).

private extractBaseParameterIds(array<string|int, mixed> $urlParams) : array<string|int, int>
Parameters
$urlParams : array<string|int, mixed>
Return values
array<string|int, int>

formatLayoutCandidates()

private formatLayoutCandidates(Theme $theme, string $layout) : array<int, string>
Parameters
$theme : Theme
$layout : string
Return values
array<int, string>

getCategoryNodes()

private getCategoryNodes() : array<int, array<string, mixed>>
Return values
array<int, array<string, mixed>>

getFavouriteSection()

private getFavouriteSection() : SessionSection
Return values
SessionSection

grantAdvertEditCapability()

Passes the fact that this visitor just proved the advert password to the Api layer. The token is minted again on every request from the current session state (already verified against the DB by the caller) and is never stored.

private grantAdvertEditCapability(int $advertId, string $passwordHash, int $expiration) : void

NOTE: userId in the token stays empty for a guest and unchanged for a logged-in user; the capability is not an identity. See CapabilityTokenIssuer.

Parameters
$advertId : int
$passwordHash : string
$expiration : int

grantAdvertEditCapabilityFromSession()

Issues the capability from the session before the advert is loaded. The Api verifies it itself (password digest from the DB in `ownsByCapability()`), so a stale session record opens nothing. Without it `get` for editing would give an anonymous editor a masked e-mail and the form would fail validation. {@see self::checkAutorization()} then issues the capability again after the check against the fresh hash, which stays decisive for access.

private grantAdvertEditCapabilityFromSession(int $advertId) : void
Parameters
$advertId : int

grantAdvertSessionAfterReset()

Same session token as `AdvertAuthorizationFormTrait::authFormSucceeded()` writes after the password form. The hash is read back fresh from the Api — `checkAutorization()` compares it with the DB value on every request.

private grantAdvertSessionAfterReset(int $advertId) : void
Parameters
$advertId : int

True when the advert has an active, valid item of the "Show links" tariff (`AdvertLinksConfig`, default 4): the description then shows its web addresses as links.

private hasPaidLinks(Advert $advert) : bool
Parameters
$advert : Advert
Return values
bool

hasPasswordResetKey()

Does the request carry the reset key from the e-mailed link (query) or the form (POST)?

private hasPasswordResetKey() : bool
Return values
bool

identitySetting()

Returns the module 1 key value as a string (empty when the key is missing or not a scalar).

private identitySetting(string $key) : string
Parameters
$key : string
Return values
string

identitySettingOrNull()

Works like {@see identitySetting()}, but an empty value is null (the template omits the field).

private identitySettingOrNull(string $key) : string|null
Parameters
$key : string
Return values
string|null

initializeDisplayRegion()

Initializes the currency and VAT display of prices.

private initializeDisplayRegion() : void

IMPORTANT: the display region is the payment region of the localization. The currency column of the localization is not read and ?curr= from the URL is ignored on the front end; the only source is DisplayRegionResolver. The cart and checkout may switch the region by the customer's choice (CheckoutRegionTrait), the catalog always shows the localization region.

Call it after initializeCurrencyList() (the resolver needs the currencies).

initializeExternalFiles()

private initializeExternalFiles(Page|null $pageEntity) : void
Parameters
$pageEntity : Page|null

initializeMetaTags()

private initializeMetaTags(Page|null $pageEntity) : void
Parameters
$pageEntity : Page|null

isCategoryBranchActive()

Whether the category and all its ancestors are active (nested set: an ancestor encloses lft/rgt).

private isCategoryBranchActive(Category $category) : bool
Parameters
$category : Category
Return values
bool

isCategoryStepCompleted()

private isCategoryStepCompleted() : bool
Return values
bool

isEntityInFavourites()

private isEntityInFavourites(int $entityId) : bool
Parameters
$entityId : int
Return values
bool

isRegionBranchActive()

Whether the region and all its ancestors are active (nested set: an ancestor encloses lft/rgt).

private isRegionBranchActive(Region $region) : bool
Parameters
$region : Region
Return values
bool

layoutFileExists()

private layoutFileExists(array<int, string> $files) : bool
Parameters
$files : array<int, string>
Return values
bool

loadIdentityAssets()

Loads the identity assets by id, keyed by id.

private loadIdentityAssets(array<int, int> $ids) : array<int, Asset>

Fail-soft: a deleted asset (its id remains in settings) is simply not loaded and the template falls back; a missing logo must not take down the whole front end.

Parameters
$ids : array<int, int>
Return values
array<int, Asset>

loadLocalization()

Returns the localization by `loc` from the URL, or `null` when there is no such locale.

private loadLocalization() : Localization|null

IMPORTANT: an unknown locale must return null (a non-null return type would turn a forged prefix such as /xx/ into a TypeError, HTTP 500). BasePresenter::startup() turns null into a 404; the error page takes the main website localization via fallbackToMainLocalization().

Return values
Localization|null

loadSelectedCategories()

private loadSelectedCategories(array<string|int, int> $ids) : array<string|int, Category>
Parameters
$ids : array<string|int, int>
Return values
array<string|int, Category>

localizationCurrencyKey()

Front needs no currency key from the localization; the currency and the display region are filled by {@see CurrencyInitTrait::initializeDisplayRegion()}.

private localizationCurrencyKey(object $localization) : string|null
Parameters
$localization : object
Return values
string|null

localizedIdentitySetting()

Returns the language-dependent identity value: `<key>.<page language alias>`; an empty or missing value falls back to the base key (default language). An empty string means neither is set.

private localizedIdentitySetting(string $baseKey) : string
Parameters
$baseKey : string
Return values
string

parseStatDate()

Parses a date in the Y-m-d format; returns null otherwise.

private parseStatDate(string $value) : DateTimeImmutable|null
Parameters
$value : string
Return values
DateTimeImmutable|null

prepareAdvertSearchFilter()

Builds the fulltext filter through ES (ids become `SearchIdFilter`; visibility and paging stay in DB phase 1). When ES is off, down or the query is short, it transparently falls back to MySQL `MATCH_AGAINST` over `parameterRels.value`.

private prepareAdvertSearchFilter(string $search, object $language) : Filter
Parameters
$search : string
$language : object

LanguageContext returns a real entity (never a proxy)

Return values
Filter

prepareCategoryFilter()

private prepareCategoryFilter(array<int, Filter> &$filters) : void
Parameters
$filters : array<int, Filter>

prepareOrderBy()

private prepareOrderBy() : array<string, bool>
Return values
array<string, bool>

prepareOtherAdverts()

Loads the other adverts of the owner and the count of their published adverts for the seller card.

private prepareOtherAdverts(Advert $advert) : void

IMPORTANT: the owner is looked up by insertUserId, not by e-mail. The Api masks the e-mail on output (PersonalDataPolicy), so a filter by the masked e-mail never matches and the list would always be empty. An anonymous advert (no owner account) has nothing to search by: the list stays empty and the count card is not shown.

One query loads all published adverts of the owner: the count is their number (what the visitor really sees) and the list is the same without the one being displayed.

Parameters
$advert : Advert

prepareParameterFilter()

private prepareParameterFilter(array<int, Filter> &$filters[, bool $withSearch = true ]) : void
Parameters
$filters : array<int, Filter>
$withSearch : bool = true

false = do not add the fulltext filter, only the parameter filters ($_GET['p']). The search endpoint uses it: the Api side does the search there (ES relevance, MATCH_AGAINST fallback), so a second DB fulltext would be a pointless extra query and would narrow the result differently than ES.

preparePriceFilter()

Adds the upper price bound from the URL (`?priceTo=...`) sent by `SearchBox`.

private preparePriceFilter(array<int, Filter> &$filters) : void

It filters the denormalized indexed column advert.price (decimal), not the parameter price: a parameter value is text and can be neither compared nor indexed. The column exists for sorting but fits a range just as well.

IMPORTANT: adverts without a price (price IS NULL, typically "by agreement") are not shown when a bound is chosen. This is intended: the user set a ceiling and an offer without a price cannot meet it.

Without the parameter in the URL nothing is added.

Parameters
$filters : array<int, Filter>

prepareRegionFilter()

private prepareRegionFilter(array<int, Filter> &$filters) : void
Parameters
$filters : array<int, Filter>

prepareSearchFilters()

Builds the filters for the search endpoint: everything except fulltext (the Api side does it, ES or `MATCH_AGAINST`). That is visibility + category/region + parameter filters from `$_GET['p']`, which are DB phase 1.

private prepareSearchFilters() : array<int, Filter>
Return values
array<int, Filter>

processLogin()

private processLogin(string $username, string $password[, bool $remember = false ]) : void
Parameters
$username : string
$password : string
$remember : bool = false

processLoginException()

private processLoginException(Exception $e) : void
Parameters
$e : Exception

readLoginDeviceToken()

Returns the raw known-device cookie for the Api login (the Api verifies its signature).

private readLoginDeviceToken() : string|null
Return values
string|null

Handles the "advert management" link from the insertion e-mail (`edit?id=...&manageKey=...`), which replaces the readable password the e-mail carried before.

private redeemManageKeyFromLink(int $advertId) : void

The Api verifies the key (:Api:Bazaar:Advert:redeemManageKey: digest, 7 days, bound to the current password). A valid key creates the same session as typing the password (self::grantAdvertSessionAfterReset()); an invalid or expired one gives a message and the password form. Both cases redirect to the edit page without the key, so it stays out of the browser history and the Referer header. The key is not consumed; it is valid until it expires.

Parameters
$advertId : int

redirectAfterLoginAction()

Ends a sign-in attempt. A classic (non-AJAX) successful sign-in lands on "My account" ({@see PageId::SYSTEM_USER_ACCOUNT}); a failed one on the home page. An AJAX sign-in stays on the current page and only redraws the flash messages.

private redirectAfterLoginAction([bool $succeeded = false ]) : void
Parameters
$succeeded : bool = false

refreshNetteIdentity()

Refreshes the token for the same logged-in user.

private refreshNetteIdentity(array<string, mixed> $payload, string $token) : bool

The identity data (token, name, email) are overwritten in the stored object without login(), so neither the session ID nor the form CSRF tokens change. The identity lives in the session section as an object and the change is written at the end of the request.

Parameters
$payload : array<string, mixed>
$token : string
Return values
bool —

false when not possible (nobody logged in or another user), the caller then uses login

rememberLoginDevice()

Issues or renews the "known device" cookie after a successful login (password or OAuth).

private rememberLoginDevice(mixed $payload) : void

The cookie (LoginDeviceCookie) is a signed proof that this browser has already logged in to the account. The next login sends it to the Api (self::processLogin()), so above the account limit the password from this browser is still verified and foreign attempts do not lock the owner out. HttpOnly, SameSite=Lax, Secure on HTTPS; lifetime ratelimit.loginDevice.cookieLifetime.

Parameters
$payload : mixed

resolveLoginErrorMessage()

Resolves the translated login error text.

private resolveLoginErrorMessage(Result $result) : string

IMPORTANT: a throttle by the brute-force gate must not be shown as "wrong password"; the limit would then look like an inexplicably failing form. The generic results.rateLimitExceeded carries no seconds and does not belong to login, so the login flow has its own key login.rateLimit.message (same family as front.general.login.error.message), with the seconds passed as %count% (Contributte: numeric second argument).

The Api sends retryAfter in data (AuthPresenter::rateLimitedResult()); the internal bridge call does not see the Retry-After header.

Parameters
$result : Result
Return values
string

resolveStatGranularity()

Validates the granularity from the request against the allowed values.

private resolveStatGranularity() : string
Return values
string

resolveStatRange()

Returns the date range from the request. When a bound is missing or invalid, the default range of the granularity is used.

private resolveStatRange(string $granularity) : DateTimeImmutable}
Parameters
$granularity : string
Return values
DateTimeImmutable} —

[from, to]

revokeTokensOnLogout()

Calls the Api logout (revokes the refresh family and deny-lists the access `jti`) for the tokens of the current session.

private revokeTokensOnLogout(AuthSessionStorage $authStorage, mixed $userId) : void

Deliberately fail-open: any failure is only logged, because the local logout must not stay unfinished.

Parameters
$authStorage : AuthSessionStorage
$userId : mixed

searchViaApi()

Searches adverts through the Api endpoint `api/bazaar/advert/search` instead of building the filter in the presenter. Three states:

private searchViaApi(string $search, bool $orderChosen, Paginator|null $paginator) : Advert>, 1: int, 2: bool, 3: array>}|null

(a) search without a manual sort choice: orderBys = null, ES sorts and pages by score (fromEs: true); visibility and parameter filters stay in the DB, (b) search with a manual choice (/o-N in the URL): orderBys from prepareOrderBy(), the DB sorts, pages and counts over the ES id set and relevance is not used, (c) without a search this path is not taken at all and nothing changes.

NOTE: in state (a) the precedence of topped adverts does not apply. prepareOrderBy() normally puts topedUntil > NOW() first, but on the relevance path ES sorts by score and the DB does not take part in the order. Without a search, and with a manual sort choice, topping works unchanged.

All filters are sent, including the $_GET['p'] parameter filters: they are DB phase 1 just like visibility, and the ES ids are only candidates.

null = use the DB path: the endpoint returned no list (deny or bridge outage), or ES is not running and without a manual choice the DB branch of the endpoint would return unsorted rows.

Parameters
$search : string
$orderChosen : bool
$paginator : Paginator|null
Return values
Advert>, 1: int, 2: bool, 3: array>}|null —

[entities, totalCount, fromEs, highlights]

setDefaultAvatar()

private setDefaultAvatar() : array{rel: string}
Return values
array{rel: string}

setNetteUser()

private setNetteUser(array<string, mixed>|null $payload, string $token) : void
Parameters
$payload : array<string, mixed>|null
$token : string

showResponseDetailModal()

Registers the response detail modal and tells the container to show it.

private showResponseDetailModal(string $content[, int|null $responseId = null ]) : void
Parameters
$content : string
$responseId : int|null = null

tryRefresh()

Tries an automatic refresh when the access token has expired.

private tryRefresh(AuthSessionStorage $authStorage) : bool

If the session holds a refresh token and the Api accepts it, the new pair is stored in the session and true is returned; otherwise false, and the caller drops the session.

Parameters
$authStorage : AuthSessionStorage
Return values
bool
On this page

Search results