# WebsiteManager Manages websites in the current account: creation, publishing, and branch and app lifecycle. Also covers TLS certificate status, temporary IP whitelists and website level app settings. Reached from GraalJS as the platform's central entry point for website administration tasks that do not belong to a single app. Most methods resolve the current tenant account from the request context, and some are restricted to a website context or to account administrators. Package: `co.kademi.server.web` Group: Managers ## Properties | Property | Returns | Description | |---|---|---| | `centralLoginUrl` | `String` | Fully qualified URL of the central login page on the root organisation's admin domain, used to sign in once and be recognised across the account's websites. | | `currentOp` | [CurrentOp](CurrentOp.md) | The current operation, a per-request holder of attributes such as the cached CSP script nonce, shared across code handling the current request. | | `currentResource` | `CommonResource` | The current page being rendered, if there is a request in context with a "page" attribute or a rendering data model holding one. | | `currentSiteOnPrimaryDomain` | `boolean` | Whether the current request's host name is on the account's primary domain, ie the current root folder is a website and the request host ends with the primary domain. Returns false outside a website context, when there is no current request, or when the account has no primary domain configured. | | `currentWebsite` | [Website](Website.md) | The website the current request is being served against, if the current root folder is a website's root folder. | | `hostname` | `String` | The local host name of the server process handling the request, as reported by the operating system. | | `ipWhitelistMaxHours` | `int` | The longest window a whitelist may cover, in hours, for the admin page to enforce in the browser too. | | `rootFolder` | [RootFolder](RootFolder.md) | The root folder resolved for the current request, eg a website's root folder when the request is served against a website, or the admin root folder in an admin context. | | `websites` | List<[Website](Website.md)> | Every website belonging to the current account. | | `websiteSigninProviders` | [SigninProvidersList](SigninProvidersList.md) | The sign-in providers (eg social/SSO login options) available to the current website. | ## Methods ### `emailAddressForWebsite(Website website)` Returns: `String` Builds a "noreply@" sender address for the given website, using the website's domain name. | Parameter | Description | |---|---| | `website` | the website to build the address for | ### `findWebsitesWithApp(String appId)` Returns: List<[Website](Website.md)> Finds the websites in the current account which have the given app active. | Parameter | Description | |---|---| | `appId` | the instance id of the app to search for | ### `updateAll(Branch branch, boolean missingOnly)` Returns: `void` Updates every app in the current account to its latest available version, ignoring the given branch and missingOnly arguments and always updating unconditionally at the account level. | Parameter | Description | |---|---| | `branch` | not used by this overload | | `missingOnly` | not used by this overload | ### `getMissing(Branch websiteBranch, Narrative narrative)` Returns: List<[Dependency](Dependency.md)> Finds app dependencies that are required by an active app but are not themselves active, for the given website branch or, if no branch is given, at the account level. Progress notes are appended to the given narrative as the check runs. | Parameter | Description | |---|---| | `websiteBranch` | the website branch to check, or null to check at the account level | | `narrative` | collects progress notes as the check runs, may be null | ### `getUpdates(Branch websiteBranch, boolean missingOnly)` Returns: List<[Update](Update.md)> Finds apps in the given website branch, or at the account level if no branch is given, for which a newer version is available in the marketplace. | Parameter | Description | |---|---| | `websiteBranch` | the website branch to check, or null to check at the account level | | `missingOnly` | if true, only returns apps that are missing rather than every app with an update available | ### `getApps(Branch websiteBranch)` Returns: List<[AppControlBean](AppControlBean.md)> Finds every app available to the given website branch, or to the account if no branch is given, each wrapped with its active status and version information for display on an app management page. | Parameter | Description | |---|---| | `websiteBranch` | the website branch to list apps for, or null to list at the account level | ### `getCentralLoginUrl()` Returns: `String` Fully qualified URL of the central login page on the root organisation's admin domain, used to sign in once and be recognised across the account's websites. ### `isCurrentSiteOnPrimaryDomain()` Returns: `boolean` Whether the current request's host name is on the account's primary domain, ie the current root folder is a website and the request host ends with the primary domain. Returns false outside a website context, when there is no current request, or when the account has no primary domain configured. ### `getWebsiteSigninProviders()` Returns: [SigninProvidersList](SigninProvidersList.md) The sign-in providers (eg social/SSO login options) available to the current website. ### `generateCsrfToken()` Returns: `String` Generates a CSRF token for the current user, using the first CSRF key configured in the current root folder's IDP policy. Returns an empty string if there is no IDP policy or it defines no CSRF keys. ### `generateCsrfToken(Profile currentProfile, String key)` Returns: `String` Generates a CSRF token for the given profile and key, combining the profile's id and the current time. Returns null if the key is blank or token generation fails. | Parameter | Description | |---|---| | `currentProfile` | the profile the token is generated for, or null to generate an anonymous token with a user id of 0 | | `key` | the CSRF key to sign the token with | ### `getWebsiteDkimTxtRecord(Website website)` Returns: `String` Builds the DNS TXT record value that publishes the website's DKIM public key, derived from its stored DKIM private key. Returns null if the website is null or has no DKIM selector configured. | Parameter | Description | |---|---| | `website` | the website to build the DKIM record for | ### `copyContentByHash(String sourceHash, Branch websiteBranch, Object oTransformFn)` Returns: `void` Copies all resources (files and folders) identified by the source hash into the destination branch. Folders within the source are preserved. An optional transform function may replace or rename each file as it is copied. | Parameter | Description | |---|---| | `sourceHash` | the hash identifying the source content tree to copy from | | `websiteBranch` | the destination branch to copy resources into | | `oTransformFn` | optional callback, given the name and hash of each existing file, returning either a String or byte array or input stream to use as the new content, or null to keep the existing hash; may be a JavaScript function or a Java Function, or null to copy unchanged | ### `findWebsiteById(long id)` Returns: [Website](Website.md) Finds a website in the current account by its database id. | Parameter | Description | |---|---| | `id` | the website's id | ### `findWebsiteByName(String name)` Returns: [Website](Website.md) Finds a website in the current account by its name. | Parameter | Description | |---|---| | `name` | the website's name | ### `createAndPopulateWebsite(String newName, String websiteTitle, String themeName, String contentHash)` Returns: [Website](Website.md) Creates a new website in the current account, installs the default apps for the given theme (unless the theme name is blank), and optionally sets the live branch content to the given hash. Fires a WebsiteCreatedEvent once the website is saved. | Parameter | Description | |---|---| | `newName` | the new website's path-safe name, must be unique within the account | | `websiteTitle` | the new website's display title | | `themeName` | the theme to enable and initialise default apps for, or blank to skip app initialisation | | `contentHash` | the hash of a content tree to set as the live branch's head commit, or null to leave it empty | ### `saveWebsite(Website website)` Returns: `void` Saves changes made to a website record, such as its title, domain name or redirect. The website must be one obtained from this manager, so that it is already scoped to the current account. | Parameter | Description | |---|---| | `website` | the website to save | ### `addGroupToWebsite(Group group, Website website)` Returns: `void` Adds a group to a website, granting the group's members whatever access the website's group configuration gives them. | Parameter | Description | |---|---| | `group` | the group to add | | `website` | the website to add the group to | ### `getCertificatesForWebsite(Website website)` Returns: [CommonList](CommonList.md) Finds the secure (TLS) certificates that have been uploaded for a website. | Parameter | Description | |---|---| | `website` | the website to find certificates for | ### `getChainStatus(WebsiteSecureCertificateBean wscb)` Returns: `String` Validates a certificate's chain (its own certificate followed by any configured root certificates) and reports the outcome as a status string for display: "valid", "invalid" if the chain does not validate or cannot be validated, or "missing" if the chain cannot even be parsed. | Parameter | Description | |---|---| | `wscb` | the certificate bean to validate the chain for | ### `getCertificateStatus(WebsiteSecureCertificateBean wscb)` Returns: `String` Reports the validity status of a certificate against its website's domain and expiry date, as a status string for display: "none" if no certificate text is stored, "expired" if past its expiry date, "valid" or "selfsigned-valid" if it validates for the website's domain, otherwise "notvalid" or "selfsigned-notvalid". | Parameter | Description | |---|---| | `wscb` | the certificate bean to check | ### `getPrivateKeyStatus(WebsiteSecureCertificateBean wscb)` Returns: `String` Reports whether a certificate's stored private key matches its certificate, as a status string for display: "none" if no private key is stored, "no-certificate" if there is a key but no certificate to check it against, otherwise "valid" or "not-valid". | Parameter | Description | |---|---| | `wscb` | the certificate bean to check | ### `getCertificateIssuer(WebsiteSecureCertificateBean wscb)` Returns: `String` Builds a display string for a certificate's issuer, combining the issuer's organisation and common name from its distinguished name, eg "Example CA - Example Root". | Parameter | Description | |---|---| | `wscb` | the certificate bean to read the issuer from | ### `initDefaultApps(Branch websiteBranch, String themeName)` Returns: `void` Enables the given theme and every core app for the website's organisation, plus the KToolbar app, in the given branch. Used to set up a new website's default set of active apps. | Parameter | Description | |---|---| | `websiteBranch` | the branch to enable the theme and default apps in | | `themeName` | the theme to enable | ### `setAppStatuses(Branch websiteBranch, List appIds, boolean enabled)` Returns: `void` Turns a batch of apps on or off in a website branch. | Parameter | Description | |---|---| | `websiteBranch` | the branch to change app status in | | `appIds` | the instance ids of the apps to change | | `enabled` | true to enable the apps, false to disable them | ### `setAppStatus(Branch websiteBranch, String appId, boolean enabled)` Returns: `void` Turns an app on or off in a website branch. | Parameter | Description | |---|---| | `websiteBranch` | the branch to change app status in | | `appId` | the instance id of the app to change | | `enabled` | true to enable the app, false to disable it | ### `getWebsites()` Returns: List<[Website](Website.md)> Every website belonging to the current account. ### `getIpWhitelists(Website website)` Returns: `List` One website's temporary IP whitelists, expired ones included, newest first. | Parameter | Description | |---|---| | `website` | the website to list for | ### `createIpWhitelist(String websiteName, String ips, String cidrs, Date startDate, Date endDate, String reason)` Returns: `WebsiteIpWhitelist` Lets a set of addresses reach one of the account's websites for a fixed window. The global IDP policy decides what an allowed request then gets, see WebsiteIpWhitelistIDPExpression. | Parameter | Description | |---|---| | `websiteName` | the name of the website, resolved strictly within the current account | | `ips` | single IP addresses, one per line, may be blank if cidrs are given | | `cidrs` | CIDR ranges, one per line, may be blank if ips are given | | `startDate` | when the whitelist starts applying | | `endDate` | when it stops, no more than WebsiteIpWhitelist.MAX_DURATION_HOURS after the start | | `reason` | free text describing why it was asked for, may be blank | ### `revokeIpWhitelist(long id)` Returns: `void` Ends a whitelist immediately, whether or not it has started, looking it up by id. | Parameter | Description | |---|---| | `id` | the whitelist id | ### `revokeIpWhitelist(WebsiteIpWhitelist whitelist)` Returns: `void` Ends a whitelist immediately, whether or not it has started. | Parameter | Description | |---|---| | `whitelist` | the whitelist to end, which must belong to the current account | ### `getIpWhitelistMaxHours()` Returns: `int` The longest window a whitelist may cover, in hours, for the admin page to enforce in the browser too. ### `findChildWebsites(Long childOrgId)` Returns: `Map` Retrieves a map of child account websites and their titles for the current tenant organisation. | Parameter | Description | |---|---| | `childOrgId` | the id of the child organisation to retrieve websites for | ### `updateEditable(Website website, Branch branch, boolean isEditable)` Returns: `void` Sets whether a website branch may be edited: a non-editable branch is marked readonly, and an editable branch has its cached published data cleared. | Parameter | Description | |---|---| | `website` | the website the branch belongs to, used only for logging | | `branch` | the branch to update | | `isEditable` | true to make the branch editable, false to make it readonly | ### `publish(Branch branch)` Returns: `void` Publishes a branch as its repository's live branch, without triggering a search reindex. See publish(Branch, boolean) for the full behaviour. | Parameter | Description | |---|---| | `branch` | the branch to publish | ### `publish(Branch branch, boolean reindex)` Returns: `void` Publishes a branch as its repository's live branch. For a website, first compiles and saves its combined JS, LESS and CSS dependencies. The whole operation runs under a global lock on the repository. If reindex is true, a search reindex job is queued for every search index configured for the repository's account. | Parameter | Description | |---|---| | `branch` | the branch to publish | | `reindex` | if true, queues a search reindex job for each configured index after publishing | ### `isForceHttps(Website website)` Returns: `boolean` Whether the website's live branch is configured to force HTTPS. | Parameter | Description | |---|---| | `website` | the website to check | ### `getSecuritySettings(Branch branch)` Returns: `Map` Reads the security settings configured for a website branch: HTTPS enforcement, HSTS, and the security response headers the branch sends. These are branch scoped rather than website scoped, so a draft branch can carry different settings to the live one. A value that has never been set is returned as null, which is not the same as being switched off - a null xContentTypeOptions means the header is not sent at all, where an empty string means it was explicitly disabled. | Parameter | Description | |---|---| | `branch` | the website branch to read settings for | ### `findBestContentDeliveryService()` Returns: `String` Finds the name of the content delivery service used by the first website in the current account that has a CDN configured and is neither an alias nor a redirect. ### `findContentDeliveryService(Website website)` Returns: `ContentDeliveryService` The content delivery service configured for a website. | Parameter | Description | |---|---| | `website` | the website to look up | ### `getRootFolder()` Returns: [RootFolder](RootFolder.md) The root folder resolved for the current request, eg a website's root folder when the request is served against a website, or the admin root folder in an admin context. ### `websiteRootFolder(Website website)` Returns: [WebsiteRootFolder](WebsiteRootFolder.md) The root folder for a website's live branch. | Parameter | Description | |---|---| | `website` | the website to get the root folder for | ### `websiteRootFolder(Website website, Branch b)` Returns: [WebsiteRootFolder](WebsiteRootFolder.md) The root folder for a website's given branch. | Parameter | Description | |---|---| | `website` | the website to get the root folder for | | `b` | the branch to get the root folder for | ### `checkStylesheet(Website website, Branch branch)` Returns: `String` Compiles the website version's stylesheet exactly as the site builds it, and reports the first LESS error, or null when it compiles. Every LESS dependency is concatenated into one document before compiling, so a fault in any one of them - a stray brace in a site's own custom stylesheet, a variable that does not exist - fails all of it, and a caller checking only its own file would not see that.

Worth calling before leaving a change in place, because the failure is otherwise close to invisible: the error is served with a CSS content type and a 200, cacheable for a week, so the site renders unstyled and nothing says why.

The sources are read and compiled directly rather than by fetching the combined .compile.less resource, because that URL only resolves within a request for the site itself - and this is called from an admin or agent context, about a different site. | Parameter | Description | |---|---| | `website` | the website to compile | | `branch` | the version of that website | ### `findThemeParams(Website website, Branch branch)` Returns: `Map` Every LESS parameter in force for one website version, merged in the order the stylesheet resolves them: Bootstrap's own defaults, Kademi's additions, the installed theme's variables, and finally the site's own overrides in /theme/theme-params.less, which win.

Given a website rather than read from the current context because the answer genuinely differs per site - the set of parameters depends on which theme is installed - and because resolving them walks the theme overlay for whichever host is current. Without arranging that first, a caller asking about one site is quietly told about another's theme. | Parameter | Description | |---|---| | `website` | the website whose parameters are wanted | | `branch` | the version of that website | ### `websiteDomainName(Website website)` Returns: `String` The domain name a website's live branch is served on. | Parameter | Description | |---|---| | `website` | the website to get the domain name for | ### `websiteDomainName(Website website, Branch branch)` Returns: `String` The domain name a website's given branch is served on. | Parameter | Description | |---|---| | `website` | the website to get the domain name for | | `branch` | the branch to get the domain name for, optional, if not provided will use the live branch | ### `websiteUrl(Website website)` Returns: `String` Builds a fully qualified URL for the live version of the given website, using https if the website forces https and http otherwise. | Parameter | Description | |---|---| | `website` | the website to build the URL for | ### `websiteUrl(Website website, Branch branch)` Returns: `String` Builds a fully qualified URL for the given branch of the given website, using https if the website forces https and http otherwise. | Parameter | Description | |---|---| | `website` | the website to build the URL for | | `branch` | the branch whose domain name is used to build the URL | ### `getCurrentWebsite()` Returns: [Website](Website.md) The website the current request is being served against, if the current root folder is a website's root folder. ### `getCurrentResource()` Returns: `CommonResource` The current page being rendered, if there is a request in context with a "page" attribute or a rendering data model holding one. ### `findAvailableCdns()` Returns: `List` Every content delivery service configured on this server instance. ### `findApp(Website w, String id)` Returns: `Application` Finds an active app on a website's live branch by its instance id. | Parameter | Description | |---|---| | `w` | the website to search | | `id` | the instance id of the app to find | ### `findApp(Website website, Branch b, String id)` Returns: `Application` Finds an active app on a website's given branch by its instance id, falling back to the live branch if no branch is given. | Parameter | Description | |---|---| | `website` | the website to search | | `b` | the branch to search, or null to search the live branch | | `id` | the instance id of the app to find | ### `updatePageContent(Branch branch, Path path, Map properties)` Returns: `void` Creates or updates an HTML page resource directly via the VfsSession (DataSession) API, bypassing the Milton resource/controller layer entirely, ie without a RenderFileResource. Intended for callers such as background jobs which need to write page content but have no HTTP request or controller to hand. If a resource already exists at the path its current content is parsed first, so properties not present in the given map are left unchanged. Missing parent directories are created as needed. | Parameter | Description | |---|---| | `branch` | the branch to update | | `path` | the path of the HTML resource within the branch | | `properties` | uses the same keys as RenderFileResource.processForm: template, title, body, itemType, category, tags. Only keys present in the map are applied. | ### `listDirectory(Branch branch, String dirPath)` Returns: `List>` Lists the immediate children of a directory within a branch's virtual filesystem, for discovery/traversal of pages and other resources. Pass a null or empty path to list the branch root. | Parameter | Description | |---|---| | `branch` | the branch (version) of the website to list | | `dirPath` | path of the directory to list, relative to the branch root; null or empty for the root | ### `findAvailableComponents(Branch branch, String pagePath)` Returns: `List` Finds the KEditor components available to be added to pages within the given branch, optionally filtered to those relevant to a specific page. Aggregates components registered by every app active on the branch; does not include website-local custom components. | Parameter | Description | |---|---| | `branch` | the branch (version) of the website to find components for | | `pagePath` | optional path, relative to the branch root, of a page to filter relevant components for; null to return the full catalogue | ### `findComponentApps(Branch branch, String pagePath)` Returns: `Map` Maps each available component id to the title of the app that provides it, for the same components and the same filtering as findAvailableComponents.

Kademi has no components except the ones an app contributes, so "there is no such component" and "this site does not have that app" are entirely different answers to a caller, and only the second one is actionable. The aggregation in findAvailableComponents already knows which app each component came from and discards it; this keeps it, so a caller can say which app to install rather than only that something is absent. | Parameter | Description | |---|---| | `branch` | the branch (version) of the website to find components for | | `pagePath` | optional path, relative to the branch root, of a page to filter relevant components for; null to return the full catalogue | ### `setSetting(String appId, String name, String value)` Returns: `void` Creates or updates a named app setting at the account level. | Parameter | Description | |---|---| | `appId` | the instance id of the app the setting belongs to | | `name` | the setting's name | | `value` | the value to store | ### `setSetting(String appId, String name, Branch websiteBranch, String value)` Returns: `void` Creates or updates a named app setting in the given website branch. | Parameter | Description | |---|---| | `appId` | the instance id of the app the setting belongs to | | `name` | the setting's name | | `websiteBranch` | the branch to store the setting in | | `value` | the value to store | ### `getSetting(String appId, String name)` Returns: `String` Retrieves a named app setting at the account level. | Parameter | Description | |---|---| | `appId` | the instance id of the app the setting belongs to | | `name` | the setting's name | ### `getRawSetting(String appId, String name)` Returns: `String` Retrieves a named app setting at the account level, without environment variable substitution applied to the stored value. | Parameter | Description | |---|---| | `appId` | the instance id of the app the setting belongs to | | `name` | the setting's name | ### `getRawSetting(String appId, String name, Branch branch)` Returns: `String` Retrieves a named app setting in the given branch, without environment variable substitution applied to the stored value. | Parameter | Description | |---|---| | `appId` | the instance id of the app the setting belongs to | | `name` | the setting's name | | `branch` | the branch to read the setting from | ### `getSetting(String appId, String name, Branch websiteBranch)` Returns: `String` Retrieves a named app setting for the given website branch. If the branch level value is blank this also looks up the account level setting, though the value returned is always the branch level lookup. | Parameter | Description | |---|---| | `appId` | the instance id of the app the setting belongs to | | `name` | the setting's name | | `websiteBranch` | the branch to read the setting from | ### `updateAllWebsiteDeps(Branch branch)` Returns: `void` Installs or updates every dependency of every active app for the given branch, or at the account level if no branch is given. | Parameter | Description | |---|---| | `branch` | the branch to update dependencies for, or null to update at the account level | ### `updateAllWebsiteDeps(Branch branch, boolean missingOnly)` Returns: `void` Installs or updates every dependency of every active app for the given branch, or at the account level if no branch is given. | Parameter | Description | |---|---| | `branch` | the branch to update dependencies for, or null to update at the account level | | `missingOnly` | if true, only installs missing dependencies rather than updating every dependency | ### `updateAllWebsiteDeps(Branch branch, boolean missingOnly, Consumer progress)` Returns: `void` As updateAllWebsiteDeps(Branch, boolean), but reports which app is being updated as it goes. Intended for callers running this as a background task, so they can show the user the app currently being updated instead of an unqualified spinner. | Parameter | Description | |---|---| | `branch` | the branch to update dependencies for, or null to update at the account level | | `missingOnly` | if true, only installs missing dependencies rather than updating every dependency | | `progress` | called with a status message as each app is updated, may be null | ### `findMappingsForApp(Branch branch)` Returns: [ControllerMappingList](ControllerMappingList.md) Finds the controller URL mappings registered by every active app in the given branch. | Parameter | Description | |---|---| | `branch` | the branch to find app controller mappings for | ### `getCurrentOp()` Returns: [CurrentOp](CurrentOp.md) The current operation, a per-request holder of attributes such as the cached CSP script nonce, shared across code handling the current request. ### `findCspOptions(WebsiteRootFolder webroot)` Returns: `String` The configured Content-Security-Policy header options for a website root folder's branch. | Parameter | Description | |---|---| | `webroot` | the website root folder to look up CSP options for | ### `findXContentTypeOptions(WebsiteRootFolder webroot)` Returns: `String` The configured X-Content-Type-Options header value for a website root folder's branch. | Parameter | Description | |---|---| | `webroot` | the website root folder to look up the header value for | ### `generateScriptNonce()` Returns: `String` Generates a random base64 nonce for use in a Content-Security-Policy script-src directive, caching it on the current operation so repeated calls within the same request return the same value. ### `externalUrl(Website website, String path)` Returns: `String` Builds a fully qualified URL for an optional path on the given website. | Parameter | Description | |---|---| | `website` | the website to build the URL for | | `path` | the path to append, may be null | ### `getHostname()` Returns: `String` The local host name of the server process handling the request, as reported by the operating system. ### `removeWebsiteGroup(Website website, Group group)` Returns: `void` Removes a group's membership of a website. Only applies when both the website and the group belong to the current account; otherwise the request is logged and ignored. | Parameter | Description | |---|---| | `website` | the website to remove the group from | | `group` | the group to remove | ### `getWebsiteGroups(Website website)` Returns: List<[Group](Group.md)> Every group that has been added to a website. | Parameter | Description | |---|---| | `website` | the website to list groups for |