Compare commits

...

19 Commits

Author SHA1 Message Date
Teffen Ellis
07aa1f99f6 web: Normalize usage of private instances over compile-time private keyword. 2025-08-04 22:06:07 +02:00
Teffen Ellis
91f4777aa4 web: Add linter warning. 2025-08-04 22:06:07 +02:00
Teffen Ellis
1226e60c73 Add member accessibility to stories. 2025-08-04 22:06:07 +02:00
Teffen Ellis
5fa3dd95be web: Add member accessibility to remaining methods. 2025-08-04 22:06:06 +02:00
Teffen Ellis
9c588565df web: Add protected member accessibility to remaining properties. 2025-08-04 22:06:06 +02:00
Teffen Ellis
fa43b4b421 web: Misc add member accessibility. 2025-08-04 22:06:06 +02:00
Teffen Ellis
69fbd4f425 web: Add member accessibility to abstract methods. 2025-08-04 22:06:06 +02:00
Teffen Ellis
d44f33068f web: Use explicit override. 2025-08-04 22:06:05 +02:00
Teffen Ellis
26de7a21c7 web: Use private properties. 2025-08-04 22:06:05 +02:00
Teffen Ellis
234530a8c2 web: Add member accessibility to constructor. 2025-08-04 22:06:05 +02:00
Teffen Ellis
1f01c722ba web: Add member accessibility to static properties. 2025-08-04 22:06:05 +02:00
Teffen Ellis
36e03443f6 web: Add member accessibility to event listeners. 2025-08-04 22:06:04 +02:00
Teffen Ellis
3d734f1433 web: Add member accessibility to lifecycle methods. 2025-08-04 22:06:04 +02:00
Teffen Ellis
5dbee033dc web: Add member accessibility to remaining decorated properties. 2025-08-04 22:06:04 +02:00
Teffen Ellis
d3abff1f5a web: Add member accessibility to properties and states. 2025-08-04 22:06:04 +02:00
Teffen Ellis
823a3b312b web: Add member accessibility to render methods. 2025-08-04 22:06:04 +02:00
Teffen Ellis
fae89e1f74 web: Add public to style properties. 2025-08-04 22:06:03 +02:00
Teffen Ellis
6a218d818a web: Use private method over decorator. 2025-08-04 22:06:03 +02:00
Teffen Ellis
a821ea6074 web: Flesh out codemod.
Flesh out codemod.
2025-08-04 22:06:03 +02:00
478 changed files with 4081 additions and 3655 deletions

View File

@@ -0,0 +1,338 @@
/* eslint-disable max-depth */
/**
* @file TypeScript codemod to add explicit member accessibility modifiers to class methods and properties.
*
* Given a class like this:
*
* ```ts
* class Foo {
* static styles = [
* css`
* :host {
* display: block;
* }
* `,
* ];
*
* @property()
* name?: string;
*
* @state()
* isLoading = false;
*
* renderHeader() {
* return html`<h1>Hello world</h1>`;
* }
*
* render() {
* return html`
* ${this.renderHeader()}
* <div>Content</div>
* `;
* }
* }
* ```
*
* This codemod will transform it to this:
*
* ```ts
* class Foo {
* public static styles = [
* css`
* :host {
* display: block;
* }
* `,
* ];
*
* @property()
* public name?: string;
*
* @state()
* protected isLoading = false;
*
* protected renderHeader() {
* return html`<h1>Hello world</h1>`;
* }
*
* public render() {
* return html`
* ${this.renderHeader()}
* <div>Content</div>
* `;
* }
* }
* ```
*
* Rules:
* - Any method which is `render` is given an explicit `public` accessibility modifier.
* - Any method which begins with `render` is given an explicit `protected` accessibility modifier.
* - Any property which is static is given an explicit `public` accessibility modifier.
* - Any property with `@property` decorator is given an explicit `public` accessibility modifier.
* - Any property with `@state` decorator is given an explicit `protected` accessibility modifier.
* - Methods and properties which already have an accessibility modifier are left alone.
* - Private fields (starting with #) are skipped.
*/
import { readdirSync, readFileSync, writeFileSync } from "node:fs";
import path from "node:path";
import ts from "typescript";
/**
* @param {ts.NodeArray<ts.ModifierLike>} modifiers
*/
function hasAccessibilityModifier(modifiers) {
if (!modifiers) return false;
return modifiers.some(
(modifier) =>
modifier.kind === ts.SyntaxKind.PublicKeyword ||
modifier.kind === ts.SyntaxKind.PrivateKeyword ||
modifier.kind === ts.SyntaxKind.ProtectedKeyword,
);
}
/**
*
* @param {ts.NodeArray<ts.ModifierLike>} [modifiers]
* @returns
*/
function hasStaticModifier(modifiers) {
if (!modifiers) return false;
return modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword);
}
/**
* Check if a property has a decorator.
* @param {ts.PropertyDeclaration} member
* @returns {boolean}
*/
function isDecorated(member) {
if (!member.modifiers) return false;
return member.modifiers.some((modifier) => {
if (!ts.isDecorator(modifier)) return false;
// Handle simple decorator like @property
if (ts.isIdentifier(modifier.expression)) {
return true;
}
// Handle decorator with call like @property() or @property({...})
return (
ts.isCallExpression(modifier.expression) &&
ts.isIdentifier(modifier.expression.expression)
);
});
}
/**
* Check if a property has a specific decorator
* @param {ts.PropertyDeclaration} member
* @param {string} decoratorName
* @returns {boolean}
*/
function hasDecorator(member, decoratorName) {
if (!member.modifiers) return false;
return member.modifiers.some((modifier) => {
if (!ts.isDecorator(modifier)) return false;
// Handle simple decorator like @property
if (ts.isIdentifier(modifier.expression)) {
return modifier.expression.text === decoratorName;
}
// Handle decorator with call like @property() or @property({...})
if (
ts.isCallExpression(modifier.expression) &&
ts.isIdentifier(modifier.expression.expression)
) {
return modifier.expression.expression.text === decoratorName;
}
return false;
});
}
const PublicMethodNames = new Set([
"render",
"connectedCallback",
"disconnectedCallback",
"firstUpdated",
"updated",
"requestUpdate",
"willUpdate",
"performUpdate",
"adoptedCallback",
"attributeChangedCallback",
]);
/**
*
* @param {string} filePath
* @returns {boolean}
*/
function processFile(filePath) {
const source = readFileSync(filePath, "utf-8");
const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true);
/**
* @type {{ pos: number; text: string; }[]}
*/
const changes = [];
/**
* @param {ts.Node} node
*/
function collectChanges(node) {
if (ts.isClassDeclaration(node)) {
for (const member of node.members) {
if (ts.isMethodDeclaration(member) || ts.isPropertyDeclaration(member)) {
if (member.modifiers && hasAccessibilityModifier(member.modifiers)) {
continue; // Already has accessibility modifier
}
let accessibilityKeyword = null;
if (
ts.isMethodDeclaration(member) &&
member.name &&
ts.isIdentifier(member.name)
) {
const methodName = member.name.text;
if (PublicMethodNames.has(methodName)) {
accessibilityKeyword = "public ";
} else if (
methodName.startsWith("render") ||
methodName.startsWith("on") ||
methodName.startsWith("handle") ||
methodName.includes("Handler")
) {
accessibilityKeyword = "protected ";
}
} else if (ts.isPropertyDeclaration(member)) {
// Skip private fields (starting with #)
if (member.name.getText().includes("#")) {
continue;
}
// Static properties get public modifier
if (hasStaticModifier(member.modifiers)) {
accessibilityKeyword = "public ";
}
// Properties with @property decorator get public modifier
else if (hasDecorator(member, "property")) {
accessibilityKeyword = "public ";
}
// Properties with @state decorator get protected modifier
else if (hasDecorator(member, "state")) {
accessibilityKeyword = "protected ";
} else if (isDecorated(member)) {
accessibilityKeyword = "protected ";
}
}
if (accessibilityKeyword) {
// Find the position where we should insert the accessibility modifier
// For properties with decorators, we need to insert after the decorators
let insertPos = member.getStart(sourceFile);
// If the member has decorators, find the position after the last decorator
if (
member.modifiers &&
member.modifiers.some((modifier) => ts.isDecorator(modifier))
) {
const decorators = member.modifiers.filter((modifier) =>
ts.isDecorator(modifier),
);
const lastDecorator = decorators[decorators.length - 1];
insertPos = lastDecorator.getEnd();
// Find the next non-whitespace character position
const sourceText = sourceFile.getFullText();
while (
insertPos < sourceText.length &&
/\s/.test(sourceText[insertPos])
) {
insertPos++;
}
}
changes.push({
pos: insertPos,
text: accessibilityKeyword,
});
}
}
}
}
ts.forEachChild(node, collectChanges);
}
collectChanges(sourceFile);
if (changes.length === 0) {
return false;
}
// Sort changes by position in reverse order so we can apply them without affecting positions
changes.sort((a, b) => b.pos - a.pos);
let result = source;
for (const change of changes) {
result = result.slice(0, change.pos) + change.text + result.slice(change.pos);
}
writeFileSync(filePath, result, "utf-8");
// eslint-disable-next-line no-console
console.log(`Updated: ${filePath}`);
return true;
}
/**
*
* @param {string} dir
* @param {string[]} files
* @returns {string[]}
*/
function findTypeScriptFiles(dir = "src", files = []) {
const entries = readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
findTypeScriptFiles(fullPath, files);
} else if (
entry.name.endsWith(".ts") &&
!entry.name.endsWith(".d.ts") &&
!entry.name.endsWith(".test.ts") &&
!entry.name.endsWith(".spec.ts") &&
!entry.name.endsWith(".stories.ts")
) {
files.push(fullPath);
}
}
return files;
}
function main() {
const files = findTypeScriptFiles();
let changedFiles = 0;
for (const file of files) {
if (processFile(file)) {
changedFiles++;
}
}
// eslint-disable-next-line no-console
console.log(`Processed ${files.length} files, updated ${changedFiles} files`);
}
main();

View File

@@ -29,6 +29,12 @@ export default tseslint.config(
},
files: ["packages/**/*"],
},
{
rules: {
"@typescript-eslint/explicit-member-accessibility": "warn",
},
files: ["src/**/*.{ts,tsx,mts,cts}"],
},
{
rules: {
"no-void": "off",

View File

@@ -170,7 +170,7 @@ class Stage<T extends FlowInfoChallenge> {
const IS_INVALID = "is-invalid";
class IdentificationStage extends Stage<IdentificationChallenge> {
render() {
override render() {
this.html(`
<form id="ident-form">
<img class="mb-4 brand-icon" src="${ak().brand.branding_logo}" alt="">
@@ -206,7 +206,7 @@ class IdentificationStage extends Stage<IdentificationChallenge> {
}
class PasswordStage extends Stage<PasswordChallenge> {
render() {
override render() {
this.html(`
<form id="password-form">
<img class="mb-4 brand-icon" src="${ak().brand.branding_logo}" alt="">
@@ -230,13 +230,13 @@ class PasswordStage extends Stage<PasswordChallenge> {
}
class RedirectStage extends Stage<RedirectChallenge> {
render() {
override render() {
window.location.assign(this.challenge.to);
}
}
class AutosubmitStage extends Stage<AutosubmitChallenge> {
render() {
override render() {
this.html(`
<form id="autosubmit-form" action="${this.challenge.url}" method="POST">
<img class="mb-4 brand-icon" src="${ak().brand.branding_logo}" alt="">
@@ -403,7 +403,7 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
};
}
render() {
override render() {
if (this.challenge.deviceChallenges.length === 1) {
this.deviceChallenge = this.challenge.deviceChallenges[0];
}

View File

@@ -19,7 +19,7 @@ import PFAbout from "@patternfly/patternfly/components/AboutModalBox/about-modal
@customElement("ak-about-modal")
export class AboutModal extends WithLicenseSummary(WithBrandConfig(ModalButton)) {
static styles = [
public static styles = [
...ModalButton.styles,
PFAbout,
css`
@@ -29,7 +29,7 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(ModalButton))
`,
];
async getAboutEntries(): Promise<[string, string | TemplateResult][]> {
protected async getAboutEntries(): Promise<[string, string | TemplateResult][]> {
const status = await new AdminApi(DEFAULT_CONFIG).adminSystemRetrieve();
const version = await new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve();
let build: string | TemplateResult = msg("Release");

View File

@@ -79,7 +79,7 @@ export class AdminInterface extends WithCapabilitiesConfig(AuthenticatedInterfac
//#region Styles
static styles: CSSResult[] = [
public static styles: CSSResult[] = [
PFBase,
PFPage,
PFButton,
@@ -126,7 +126,7 @@ export class AdminInterface extends WithCapabilitiesConfig(AuthenticatedInterfac
//#region Lifecycle
constructor() {
public constructor() {
configureSentry(true);
super();
@@ -137,7 +137,7 @@ export class AdminInterface extends WithCapabilitiesConfig(AuthenticatedInterfac
this.sidebarOpen = this.#sidebarMatcher.matches;
}
public connectedCallback() {
public override connectedCallback() {
super.connectedCallback();
window.addEventListener(EVENT_NOTIFICATION_DRAWER_TOGGLE, () => {
@@ -159,14 +159,14 @@ export class AdminInterface extends WithCapabilitiesConfig(AuthenticatedInterfac
});
}
public disconnectedCallback(): void {
public override disconnectedCallback(): void {
super.disconnectedCallback();
this.#sidebarMatcher.removeEventListener("change", this.#sidebarMediaQueryListener);
WebsocketClient.close();
}
async firstUpdated(): Promise<void> {
public override async firstUpdated(): Promise<void> {
me().then((session) => {
this.user = session;
@@ -181,7 +181,7 @@ export class AdminInterface extends WithCapabilitiesConfig(AuthenticatedInterfac
});
}
render(): TemplateResult {
public override render(): TemplateResult {
const sidebarClasses = {
"pf-c-page__sidebar": true,
"pf-m-light": this.activeTheme === UiThemeEnum.Light,

View File

@@ -22,9 +22,9 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
@customElement("ak-admin-debug-page")
export class DebugPage extends AKElement {
static styles: CSSResult[] = [PFBase, PFCard, PFPage, PFGrid, PFButton];
public static override styles: CSSResult[] = [PFBase, PFCard, PFPage, PFGrid, PFButton];
render(): TemplateResult {
public override render(): TemplateResult {
return html`
<ak-page-header icon="pf-icon pf-icon-user" header="Debug"> </ak-page-header>
<section class="pf-c-page__main-section">

View File

@@ -37,7 +37,7 @@ const AdminOverviewBase = WithLicenseSummary(AKElement);
@customElement("ak-admin-overview")
export class AdminOverviewPage extends AdminOverviewBase {
static styles: CSSResult[] = [
public static styles: CSSResult[] = [
PFBase,
PFGrid,
PFPage,
@@ -63,7 +63,7 @@ export class AdminOverviewPage extends AdminOverviewBase {
`,
];
quickActions: QuickAction[] = [
protected quickActions: QuickAction[] = [
[msg("Create a new application"), paramURL("/core/applications", { createWizard: true })],
[msg("Check the logs"), paramURL("/events/log")],
[msg("Explore integrations"), "https://goauthentik.io/integrations/", true],
@@ -76,13 +76,13 @@ export class AdminOverviewPage extends AdminOverviewBase {
];
@state()
user?: SessionUser;
protected user?: SessionUser;
async firstUpdated(): Promise<void> {
public override async firstUpdated(): Promise<void> {
this.user = await me();
}
render(): TemplateResult {
public override render(): TemplateResult {
const username = this.user?.user.name || this.user?.user.username;
return html`<ak-page-header
@@ -154,7 +154,7 @@ export class AdminOverviewPage extends AdminOverviewBase {
</section>`;
}
renderCards() {
protected renderCards() {
const isEnterprise = this.hasEnterpriseLicense;
const classes = {
"card-container": true,

View File

@@ -18,7 +18,7 @@ import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
@customElement("ak-admin-dashboard-users")
export class DashboardUserPage extends AKElement {
static styles: CSSResult[] = [
public static override styles: CSSResult[] = [
PFGrid,
PFPage,
PFContent,
@@ -34,7 +34,7 @@ export class DashboardUserPage extends AKElement {
`,
];
render(): TemplateResult {
public override render(): TemplateResult {
return html`<ak-page-header icon="pf-icon pf-icon-user" header=${msg("User Statistics")}>
</ak-page-header>
<section class="pf-c-page__main-section">

View File

@@ -25,21 +25,19 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
@customElement("ak-system-tasks")
export class SystemTasksPage extends AKElement {
static get styles(): CSSResult[] {
return [
PFBase,
PFList,
PFBanner,
PFPage,
PFContent,
PFButton,
PFDescriptionList,
PFGrid,
PFCard,
];
}
public static override styles: CSSResult[] = [
PFBase,
PFList,
PFBanner,
PFPage,
PFContent,
PFButton,
PFDescriptionList,
PFGrid,
PFCard,
];
render(): TemplateResult {
public override render(): TemplateResult {
return html`<ak-page-header
icon="pf-icon pf-icon-automation"
header=${msg("System Tasks")}

View File

@@ -15,11 +15,11 @@ import PFTable from "@patternfly/patternfly/components/Table/table.css";
@customElement("ak-top-applications-table")
export class TopApplicationsTable extends AKElement {
@property({ attribute: false })
topN?: EventTopPerUser[];
public topN?: EventTopPerUser[];
static styles: CSSResult[] = [PFTable];
public static override styles: CSSResult[] = [PFTable];
firstUpdated(): void {
public override firstUpdated(): void {
new EventsApi(DEFAULT_CONFIG)
.eventsEventsTopPerUserList({
action: "authorize_application",
@@ -30,7 +30,7 @@ export class TopApplicationsTable extends AKElement {
});
}
renderRow(event: EventTopPerUser): TemplateResult {
protected renderRow(event: EventTopPerUser): TemplateResult {
return html`<tr role="row">
<td role="cell">${event.application.name}</td>
<td role="cell">${event.countedEvents}</td>
@@ -43,7 +43,7 @@ export class TopApplicationsTable extends AKElement {
</tr>`;
}
render(): TemplateResult {
public override render(): TemplateResult {
return html`<table class="pf-c-table pf-m-compact" role="grid">
<thead>
<tr role="row">

View File

@@ -22,7 +22,7 @@ export interface AdminStatus {
export abstract class AdminStatusCard<T> extends AggregateCard {
// Current data value state
@state()
value?: T;
public value?: T;
// Current status state derived from value
@state()
@@ -33,28 +33,27 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
protected error?: APIError;
// Abstract methods to be implemented by subclasses
abstract getPrimaryValue(): Promise<T>;
abstract getStatus(value: T): Promise<AdminStatus>;
protected abstract getPrimaryValue(): Promise<T>;
constructor() {
protected abstract getStatus(value: T): Promise<AdminStatus>;
public constructor() {
super();
// Proper binding for event handler
this.fetchData = this.fetchData.bind(this);
// Register refresh event listener
this.addEventListener(EVENT_REFRESH, this.fetchData);
this.addEventListener(EVENT_REFRESH, this.#fetchData);
}
// Lifecycle method: Called when component is added to DOM
connectedCallback(): void {
public override connectedCallback(): void {
super.connectedCallback();
// Initial data fetch
this.fetchData();
this.#fetchData();
}
/**
* Fetch primary data and handle errors
*/
private fetchData() {
#fetchData = () => {
this.getPrimaryValue()
.then((value: T) => {
this.value = value; // Triggers shouldUpdate
@@ -64,7 +63,7 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
this.status = undefined;
this.error = await parseAPIResponseError(error);
});
}
};
/**
* Lit lifecycle method: Determine if component should update
@@ -72,7 +71,7 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
* @param changed - Map of changed properties
* @returns boolean indicating if update should proceed
*/
shouldUpdate(changed: PropertyValues<this>) {
public override shouldUpdate(changed: PropertyValues<this>) {
if (changed.has("value") && this.value !== undefined) {
// When value changes, fetch new status
this.getStatus(this.value)
@@ -106,7 +105,7 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
* @param status - AdminStatus object containing icon and message
* @returns TemplateResult for status display
*/
private renderStatus(status: AdminStatus): SlottedTemplateResult {
protected renderStatus(status: AdminStatus): SlottedTemplateResult {
return html`
<p><i class="${status.icon}"></i>&nbsp;${this.renderValue()}</p>
${status.message ? html`<p class="subtext">${status.message}</p>` : nothing}
@@ -119,7 +118,7 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
* @param error - Error message to display
* @returns TemplateResult for error display
*/
private renderError(error: string): SlottedTemplateResult {
protected renderError(error: string): SlottedTemplateResult {
return html`
<p><i aria-hidden="true" class="fa fa-times"></i>&nbsp;${msg("Failed to fetch")}</p>
<p class="subtext">${error}</p>
@@ -131,7 +130,7 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
*
* @returns TemplateResult for loading spinner
*/
private renderLoading(): SlottedTemplateResult {
protected renderLoading(): SlottedTemplateResult {
return html`<ak-spinner size="${PFSize.Large}"></ak-spinner>`;
}
@@ -140,7 +139,7 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
*
* @returns TemplateResult for current component state
*/
renderInner(): SlottedTemplateResult {
protected override renderInner(): SlottedTemplateResult {
return html`
<p class="center-value">
${

View File

@@ -12,21 +12,21 @@ type StatusContent = { icon: string; message: TemplateResult };
@customElement("ak-admin-fips-status-system")
export class FipsStatusCard extends AdminStatusCard<SystemInfo> {
icon = "pf-icon pf-icon-server";
public override icon = "pf-icon pf-icon-server";
@state()
statusSummary?: string;
protected statusSummary?: string;
async getPrimaryValue(): Promise<SystemInfo> {
protected async getPrimaryValue(): Promise<SystemInfo> {
return await new AdminApi(DEFAULT_CONFIG).adminSystemRetrieve();
}
setStatus(summary: string, content: StatusContent): Promise<AdminStatus> {
protected setStatus(summary: string, content: StatusContent): Promise<AdminStatus> {
this.statusSummary = summary;
return Promise.resolve<AdminStatus>(content);
}
getStatus(value: SystemInfo): Promise<AdminStatus> {
protected getStatus(value: SystemInfo): Promise<AdminStatus> {
return value.runtime.opensslFipsEnabled
? this.setStatus(msg("OK"), {
icon: "fa fa-check-circle pf-m-success",
@@ -38,11 +38,11 @@ export class FipsStatusCard extends AdminStatusCard<SystemInfo> {
});
}
renderHeader(): TemplateResult {
protected override renderHeader(): TemplateResult {
return html`${msg("FIPS Status")}`;
}
renderValue(): TemplateResult {
protected override renderValue(): TemplateResult {
return html`${this.statusSummary}`;
}
}

View File

@@ -25,19 +25,19 @@ import PFCard from "@patternfly/patternfly/components/Card/card.css";
@customElement("ak-recent-events")
export class RecentEventsCard extends Table<Event> {
@property()
order = "-created";
public override order = "-created";
@property({ type: Number })
pageSize = 10;
public pageSize = 10;
async apiEndpoint(): Promise<PaginatedResponse<Event>> {
protected async apiEndpoint(): Promise<PaginatedResponse<Event>> {
return new EventsApi(DEFAULT_CONFIG).eventsEventsList({
...(await this.defaultEndpointConfig()),
pageSize: this.pageSize,
});
}
static styles: CSSResult[] = [
public static override styles: CSSResult[] = [
...super.styles,
PFCard,
css`
@@ -52,7 +52,7 @@ export class RecentEventsCard extends Table<Event> {
`,
];
columns(): TableColumn[] {
protected columns(): TableColumn[] {
return [
new TableColumn(msg("Action"), "action"),
new TableColumn(msg("User"), "user"),
@@ -62,13 +62,13 @@ export class RecentEventsCard extends Table<Event> {
];
}
renderToolbar(): TemplateResult {
protected override renderToolbar(): TemplateResult {
return html`<div class="pf-c-card__title">
<i class="pf-icon pf-icon-catalog"></i>&nbsp;${msg("Recent events")}
</div>`;
}
row(item: EventWithContext): SlottedTemplateResult[] {
protected row(item: EventWithContext): SlottedTemplateResult[] {
return [
html`<div><a href="${`#/events/log/${item.pk}`}">${actionToLabel(item.action)}</a></div>
<small>${item.app}</small>`,
@@ -81,7 +81,7 @@ export class RecentEventsCard extends Table<Event> {
];
}
renderEmpty(inner?: SlottedTemplateResult): TemplateResult {
protected override renderEmpty(inner?: SlottedTemplateResult): TemplateResult {
if (this.error) {
return super.renderEmpty(inner);
}

View File

@@ -12,14 +12,14 @@ import { customElement, state } from "lit/decorators.js";
@customElement("ak-admin-status-system")
export class SystemStatusCard extends AdminStatusCard<SystemInfo> {
now?: Date;
protected now?: Date;
icon = "pf-icon pf-icon-server";
public override icon = "pf-icon pf-icon-server";
@state()
statusSummary?: string;
protected statusSummary?: string;
async getPrimaryValue(): Promise<SystemInfo> {
protected async getPrimaryValue(): Promise<SystemInfo> {
this.now = new Date();
let status = await new AdminApi(DEFAULT_CONFIG).adminSystemRetrieve();
if (
@@ -38,7 +38,7 @@ export class SystemStatusCard extends AdminStatusCard<SystemInfo> {
// Called on fresh installations and whenever the embedded outpost is deleted
// automatically send the login URL when the user first visits the admin dashboard.
async setOutpostHost(): Promise<void> {
protected async setOutpostHost(): Promise<void> {
const outposts = await new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesList({
managedIexact: "goauthentik.io/outposts/embedded",
});
@@ -53,7 +53,7 @@ export class SystemStatusCard extends AdminStatusCard<SystemInfo> {
});
}
getStatus(value: SystemInfo): Promise<AdminStatus> {
protected getStatus(value: SystemInfo): Promise<AdminStatus> {
if (!value.embeddedOutpostDisabled && value.embeddedOutpostHost === "") {
this.statusSummary = msg("Warning");
return Promise.resolve<AdminStatus>({
@@ -84,11 +84,11 @@ export class SystemStatusCard extends AdminStatusCard<SystemInfo> {
});
}
renderHeader(): SlottedTemplateResult {
protected override renderHeader(): SlottedTemplateResult {
return msg("System status");
}
renderValue(): SlottedTemplateResult {
protected override renderValue(): SlottedTemplateResult {
return this.statusSummary ? html`${this.statusSummary}` : nothing;
}
}

View File

@@ -10,13 +10,13 @@ import { customElement } from "lit/decorators.js";
@customElement("ak-admin-status-version")
export class VersionStatusCard extends AdminStatusCard<Version> {
icon = "pf-icon pf-icon-bundle";
public override icon = "pf-icon pf-icon-bundle";
getPrimaryValue(): Promise<Version> {
protected getPrimaryValue(): Promise<Version> {
return new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve();
}
getStatus(value: Version): Promise<AdminStatus> {
protected getStatus(value: Version): Promise<AdminStatus> {
if (value.buildHash) {
return Promise.resolve<AdminStatus>({
icon: "fa fa-check-circle pf-m-success",
@@ -48,11 +48,11 @@ export class VersionStatusCard extends AdminStatusCard<Version> {
});
}
renderHeader(): TemplateResult {
protected override renderHeader(): TemplateResult {
return html`${msg("Version")}`;
}
renderValue(): TemplateResult {
protected override renderValue(): TemplateResult {
let text = this.value?.versionCurrent;
const versionFamily = this.value?.versionCurrent.split(".");
versionFamily?.pop();

View File

@@ -10,17 +10,17 @@ import { customElement } from "lit/decorators.js";
@customElement("ak-admin-status-card-workers")
export class WorkersStatusCard extends AdminStatusCard<Worker[]> {
icon = "pf-icon pf-icon-server";
public override icon = "pf-icon pf-icon-server";
getPrimaryValue(): Promise<Worker[]> {
protected getPrimaryValue(): Promise<Worker[]> {
return new TasksApi(DEFAULT_CONFIG).tasksWorkersList();
}
renderHeader(): TemplateResult {
protected override renderHeader(): TemplateResult {
return html`${msg("Workers")}`;
}
getStatus(value: Worker[]): Promise<AdminStatus> {
protected getStatus(value: Worker[]): Promise<AdminStatus> {
if (value.length < 1) {
return Promise.resolve<AdminStatus>({
icon: "fa fa-times-circle pf-m-danger",
@@ -37,7 +37,7 @@ export class WorkersStatusCard extends AdminStatusCard<Worker[]> {
});
}
renderValue() {
protected override renderValue() {
return html`${this.value?.length}`;
}
}

View File

@@ -11,7 +11,7 @@ import { customElement } from "lit/decorators.js";
@customElement("ak-charts-admin-login-authorization")
export class AdminLoginAuthorizeChart extends EventChart {
async apiRequest(): Promise<EventVolume[]> {
protected async apiRequest(): Promise<EventVolume[]> {
return new EventsApi(DEFAULT_CONFIG).eventsEventsVolumeList({
actions: [
EventActions.AuthorizeApplication,
@@ -21,7 +21,7 @@ export class AdminLoginAuthorizeChart extends EventChart {
});
}
getChartData(data: EventVolume[]): ChartData {
protected getChartData(data: EventVolume[]): ChartData {
const optsMap = new Map<EventActions, Partial<ChartDataset>>();
optsMap.set(EventActions.AuthorizeApplication, {
label: msg("Authorizations"),

View File

@@ -17,15 +17,15 @@ import { customElement, property } from "lit/decorators.js";
@customElement("ak-charts-admin-model-per-day")
export class AdminModelPerDay extends EventChart {
@property()
action: EventActions = EventActions.ModelCreated;
public action: EventActions = EventActions.ModelCreated;
@property()
label?: string;
public label?: string;
@property({ attribute: false })
query?: EventsEventsVolumeListRequest;
public query?: EventsEventsVolumeListRequest;
async apiRequest(): Promise<EventVolume[]> {
protected async apiRequest(): Promise<EventVolume[]> {
return new EventsApi(DEFAULT_CONFIG).eventsEventsVolumeList({
action: this.action,
historyDays: 30,
@@ -33,7 +33,7 @@ export class AdminModelPerDay extends EventChart {
});
}
getChartData(data: EventVolume[]): ChartData {
protected getChartData(data: EventVolume[]): ChartData {
return this.eventVolume(data, {
optsMap: new Map([
[

View File

@@ -16,11 +16,11 @@ import { customElement } from "lit/decorators.js";
@customElement("ak-admin-status-chart-outpost")
export class OutpostStatusChart extends AKChart<SummarizedSyncStatus[]> {
getChartType(): string {
public override getChartType(): string {
return "doughnut";
}
getOptions(): ChartOptions {
public override getOptions(): ChartOptions {
return {
plugins: {
legend: {
@@ -31,7 +31,7 @@ export class OutpostStatusChart extends AKChart<SummarizedSyncStatus[]> {
};
}
async apiRequest(): Promise<SummarizedSyncStatus[]> {
protected async apiRequest(): Promise<SummarizedSyncStatus[]> {
const api = new OutpostsApi(DEFAULT_CONFIG);
const outposts = await api.outpostsInstancesList({});
const outpostStats: SummarizedSyncStatus[] = [];
@@ -65,7 +65,7 @@ export class OutpostStatusChart extends AKChart<SummarizedSyncStatus[]> {
return outpostStats;
}
getChartData(data: SummarizedSyncStatus[]): ChartData {
protected getChartData(data: SummarizedSyncStatus[]): ChartData {
return {
labels: [msg("Healthy outposts"), msg("Outdated outposts"), msg("Unhealthy outposts")],
datasets: data.map((d) => {

View File

@@ -29,11 +29,11 @@ export interface SummarizedSyncStatus {
@customElement("ak-admin-status-chart-sync")
export class SyncStatusChart extends AKChart<SummarizedSyncStatus[]> {
getChartType(): string {
public override getChartType(): string {
return "doughnut";
}
getOptions(): ChartOptions {
public override getOptions(): ChartOptions {
return {
plugins: {
legend: {
@@ -44,7 +44,7 @@ export class SyncStatusChart extends AKChart<SummarizedSyncStatus[]> {
};
}
async fetchStatus<T>(
protected async fetchStatus<T>(
listObjects: () => Promise<PaginatedResponse<T>>,
fetchSyncStatus: (element: T) => Promise<SyncStatus>,
label: string,
@@ -92,7 +92,7 @@ export class SyncStatusChart extends AKChart<SummarizedSyncStatus[]> {
};
}
async apiRequest(): Promise<SummarizedSyncStatus[]> {
protected async apiRequest(): Promise<SummarizedSyncStatus[]> {
const statuses = [
await this.fetchStatus(
() => {
@@ -158,7 +158,7 @@ export class SyncStatusChart extends AKChart<SummarizedSyncStatus[]> {
return statuses;
}
getChartData(data: SummarizedSyncStatus[]): ChartData {
protected getChartData(data: SummarizedSyncStatus[]): ChartData {
return {
labels: [msg("Healthy"), msg("Failed"), msg("Unsynced / N/A")],
datasets: data.map((d) => {

View File

@@ -24,7 +24,7 @@ const hasLegalScheme = (url: string) =>
@customElement("ak-admin-settings-footer-link")
export class FooterLinkInput extends AkControlElement<FooterLink> {
static styles = [
public static override styles = [
PFBase,
PFInputGroup,
PFFormControl,
@@ -37,26 +37,26 @@ export class FooterLinkInput extends AkControlElement<FooterLink> {
];
@property({ type: Object, attribute: false })
footerLink: FooterLink = {
public footerLink: FooterLink = {
name: "",
href: "",
};
@queryAll(".ak-form-control")
controls?: HTMLInputElement[];
protected controls?: HTMLInputElement[];
json() {
public override json() {
return Object.fromEntries(
Array.from(this.controls ?? []).map((control) => [control.name, control.value]),
) as unknown as FooterLink;
}
get isValid() {
public override get isValid() {
const href = this.json()?.href ?? "";
return hasLegalScheme(href) && URL.canParse(href);
}
render() {
public override render() {
const onChange = () => {
this.dispatchEvent(new Event("change", { composed: true, bubbles: true }));
};

View File

@@ -33,19 +33,19 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
// Custom property accessors in Lit 2 require a manual call to requestUpdate(). See:
// https://lit.dev/docs/v2/components/properties/#accessors-custom
//
set settings(value: Settings | undefined) {
this._settings = value;
public set settings(value: Settings | undefined) {
this.#settings = value;
this.requestUpdate();
}
@property({ type: Object })
get settings() {
return this._settings;
public get settings() {
return this.#settings;
}
private _settings?: Settings;
#settings?: Settings;
static styles: CSSResult[] = [
public static override styles: CSSResult[] = [
...super.styles,
PFList,
css`
@@ -55,11 +55,11 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
`,
];
getSuccessMessage(): string {
public override getSuccessMessage(): string {
return msg("Successfully updated settings.");
}
async send(data: SettingsRequest): Promise<Settings> {
protected async send(data: SettingsRequest): Promise<Settings> {
const result = await new AdminApi(DEFAULT_CONFIG).adminSettingsUpdate({
settingsRequest: data,
});
@@ -67,12 +67,12 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
return result;
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
return html`
<ak-text-input
name="avatars"
label=${msg("Avatars")}
value="${ifDefined(this._settings?.avatars)}"
value="${ifDefined(this.#settings?.avatars)}"
input-hint="code"
.bighelp=${html`
<p class="pf-c-form__helper-text">
@@ -139,21 +139,21 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
<ak-switch-input
name="defaultUserChangeName"
label=${msg("Allow users to change name")}
?checked="${this._settings?.defaultUserChangeName}"
?checked="${this.#settings?.defaultUserChangeName}"
help=${msg("Enable the ability for users to change their name.")}
>
</ak-switch-input>
<ak-switch-input
name="defaultUserChangeEmail"
label=${msg("Allow users to change email")}
?checked="${this._settings?.defaultUserChangeEmail}"
?checked="${this.#settings?.defaultUserChangeEmail}"
help=${msg("Enable the ability for users to change their email.")}
>
</ak-switch-input>
<ak-switch-input
name="defaultUserChangeUsername"
label=${msg("Allow users to change username")}
?checked="${this._settings?.defaultUserChangeUsername}"
?checked="${this.#settings?.defaultUserChangeUsername}"
help=${msg("Enable the ability for users to change their username.")}
>
</ak-switch-input>
@@ -162,14 +162,14 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
label=${msg("Event retention")}
input-hint="code"
required
value="${ifDefined(this._settings?.eventRetention)}"
value="${ifDefined(this.#settings?.eventRetention)}"
.bighelp=${html`<p class="pf-c-form__helper-text">
${msg("Duration after which events will be deleted from the database.")}
</p>
<p class="pf-c-form__helper-text">
${msg(
html`When using an external logging solution for archiving, this can be
set to <code>minutes=5</code>.`,
public set to <code>minutes=5</code>.`,
)}
</p>
<p class="pf-c-form__helper-text">
@@ -184,19 +184,19 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
label=${msg("Reputation: lower limit")}
required
name="reputationLowerLimit"
value="${this._settings?.reputationLowerLimit ?? DEFAULT_REPUTATION_LOWER_LIMIT}"
value="${this.#settings?.reputationLowerLimit ?? DEFAULT_REPUTATION_LOWER_LIMIT}"
help=${msg("Reputation cannot decrease lower than this value. Zero or negative.")}
></ak-number-input>
<ak-number-input
label=${msg("Reputation: upper limit")}
required
name="reputationUpperLimit"
value="${this._settings?.reputationUpperLimit ?? DEFAULT_REPUTATION_UPPER_LIMIT}"
value="${this.#settings?.reputationUpperLimit ?? DEFAULT_REPUTATION_UPPER_LIMIT}"
help=${msg("Reputation cannot increase higher than this value. Zero or positive.")}
></ak-number-input>
<ak-form-element-horizontal label=${msg("Footer links")} name="footerLinks">
<ak-array-input
.items=${this._settings?.footerLinks ?? []}
.items=${this.#settings?.footerLinks ?? []}
.newItem=${() => ({ name: "", href: "" })}
.row=${(f?: FooterLink) =>
akFooterLinkInput({
@@ -215,7 +215,7 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
<ak-switch-input
name="gdprCompliance"
label=${msg("GDPR compliance")}
?checked="${this._settings?.gdprCompliance}"
?checked="${this.#settings?.gdprCompliance}"
help=${msg(
"When enabled, all the events caused by a user will be deleted upon the user's deletion.",
)}
@@ -224,14 +224,14 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
<ak-switch-input
name="impersonation"
label=${msg("Impersonation")}
?checked="${this._settings?.impersonation}"
?checked="${this.#settings?.impersonation}"
help=${msg("Globally enable/disable impersonation.")}
>
</ak-switch-input>
<ak-switch-input
name="impersonationRequireReason"
label=${msg("Require reason for impersonation")}
?checked="${this._settings?.impersonationRequireReason}"
?checked="${this.#settings?.impersonationRequireReason}"
help=${msg("Require administrators to provide a reason for impersonating a user.")}
>
</ak-switch-input>
@@ -240,7 +240,7 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
label=${msg("Default token duration")}
input-hint="code"
required
value="${ifDefined(this._settings?.defaultTokenDuration)}"
value="${ifDefined(this.#settings?.defaultTokenDuration)}"
.bighelp=${html`<p class="pf-c-form__helper-text">
${msg("Default duration for generated tokens")}
</p>
@@ -251,7 +251,7 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
label=${msg("Default token length")}
required
name="defaultTokenLength"
value="${this._settings?.defaultTokenLength ?? 60}"
value="${this.#settings?.defaultTokenLength ?? 60}"
help=${msg("Default length of generated tokens")}
></ak-number-input>
`;

View File

@@ -33,7 +33,7 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
@customElement("ak-admin-settings")
export class AdminSettingsPage extends AKElement {
static styles = [
public static override styles = [
PFBase,
PFButton,
PFPage,
@@ -52,7 +52,7 @@ export class AdminSettingsPage extends AKElement {
@state()
protected settings?: Settings;
constructor() {
public constructor() {
super();
this.#refresh();
@@ -74,7 +74,7 @@ export class AdminSettingsPage extends AKElement {
return this.form?.reset();
};
render() {
public override render() {
if (!this.settings) return nothing;
return html`

View File

@@ -12,16 +12,16 @@ import { customElement, property } from "lit/decorators.js";
@customElement("ak-charts-application-authorize")
export class ApplicationAuthorizeChart extends EventChart {
@property({ attribute: "application-id" })
applicationId!: string;
public applicationId!: string;
async apiRequest(): Promise<EventVolume[]> {
protected async apiRequest(): Promise<EventVolume[]> {
return new EventsApi(DEFAULT_CONFIG).eventsEventsVolumeList({
action: EventActions.AuthorizeApplication,
contextAuthorizedApp: this.applicationId.replaceAll("-", ""),
});
}
getChartData(data: EventVolume[]): ChartData {
protected getChartData(data: EventVolume[]): ChartData {
return this.eventVolume(data, {
optsMap: new Map([
[

View File

@@ -24,19 +24,19 @@ import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList
@customElement("ak-application-check-access-form")
export class ApplicationCheckAccessForm extends Form<{ forUser: number }> {
@property({ attribute: false })
application!: Application;
public application!: Application;
@property({ attribute: false })
result: PolicyTestResult | null = null;
public result: PolicyTestResult | null = null;
@property({ attribute: false })
request?: number;
public request?: number;
getSuccessMessage(): string {
public override getSuccessMessage(): string {
return msg("Successfully sent test-request.");
}
async send(data: { forUser: number }): Promise<PolicyTestResult> {
protected async send(data: { forUser: number }): Promise<PolicyTestResult> {
this.request = data.forUser;
const result = await new CoreApi(DEFAULT_CONFIG).coreApplicationsCheckAccessRetrieve({
slug: this.application?.slug,
@@ -45,14 +45,14 @@ export class ApplicationCheckAccessForm extends Form<{ forUser: number }> {
return (this.result = result);
}
reset(): void {
public override reset(): void {
super.reset();
this.result = null;
}
static styles: CSSResult[] = [...super.styles, PFDescriptionList];
public static override styles: CSSResult[] = [...super.styles, PFDescriptionList];
renderResult(): TemplateResult {
protected renderResult(): TemplateResult {
return html`
<ak-form-element-horizontal label=${msg("Passing")}>
<div class="pf-c-form__group-label">
@@ -92,7 +92,7 @@ export class ApplicationCheckAccessForm extends Form<{ forUser: number }> {
`;
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
return html`<ak-form-element-horizontal label=${msg("User")} required name="forUser">
<ak-search-select
.fetchObjects=${async (query?: string): Promise<User[]> => {

View File

@@ -33,14 +33,7 @@ import { ifDefined } from "lit/directives/if-defined.js";
@customElement("ak-application-form")
export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Application, string>) {
constructor() {
super();
this.handleConfirmBackchannelProviders = this.handleConfirmBackchannelProviders.bind(this);
this.makeRemoveBackchannelProviderHandler =
this.makeRemoveBackchannelProviderHandler.bind(this);
}
async loadInstance(pk: string): Promise<Application> {
protected async loadInstance(pk: string): Promise<Application> {
const app = await new CoreApi(DEFAULT_CONFIG).coreApplicationsRetrieve({
slug: pk,
});
@@ -50,21 +43,21 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
}
@property({ attribute: false })
provider?: number;
public provider?: number;
@state()
backchannelProviders: Provider[] = [];
protected backchannelProviders: Provider[] = [];
@property({ type: Boolean })
clearIcon = false;
public clearIcon = false;
getSuccessMessage(): string {
public override getSuccessMessage(): string {
return this.instance
? msg("Successfully updated application.")
: msg("Successfully created application.");
}
async send(data: Application): Promise<Application | void> {
protected async send(data: Application): Promise<Application | void> {
let app: Application;
data.backchannelProviders = this.backchannelProviders.map((p) => p.pk);
if (this.instance) {
@@ -97,21 +90,21 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
return app;
}
handleConfirmBackchannelProviders(items: Provider[]) {
#confirmBackchannelProviders = (items: Provider[]) => {
this.backchannelProviders = items;
this.requestUpdate();
return Promise.resolve();
}
};
makeRemoveBackchannelProviderHandler(provider: Provider) {
#removeBackchannelProvider = (provider: Provider) => {
return () => {
const idx = this.backchannelProviders.indexOf(provider);
this.backchannelProviders.splice(idx, 1);
this.requestUpdate();
};
}
};
handleClearIcon(ev: Event) {
protected handleClearIcon(ev: Event) {
ev.stopPropagation();
if (!(ev instanceof InputEvent) || !ev.target) {
return;
@@ -119,7 +112,7 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
this.clearIcon = !!(ev.target as HTMLInputElement).checked;
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
const alertMsg = msg(
"Using this form will only create an Application. In order to authenticate with the application, you will have to manually pair it with a Provider.",
);
@@ -164,8 +157,8 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
"Select backchannel providers which augment the functionality of the main provider.",
)}
.providers=${this.backchannelProviders}
.confirm=${this.handleConfirmBackchannelProviders}
.remover=${this.makeRemoveBackchannelProviderHandler}
.confirm=${this.#confirmBackchannelProviders}
.remover=${this.#removeBackchannelProvider}
.tooltip=${html`<pf-tooltip
position="top"
content=${msg("Add provider")}

View File

@@ -43,37 +43,40 @@ export const applicationListStyle = css`
@customElement("ak-application-list")
export class ApplicationListPage extends WithBrandConfig(TablePage<Application>) {
searchEnabled(): boolean {
protected override searchEnabled(): boolean {
return true;
}
pageTitle(): string {
protected pageTitle(): string {
return msg("Applications");
}
pageDescription(): string {
protected pageDescription(): string {
return msg(
str`External applications that use ${this.brandingTitle} as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.`,
);
}
pageIcon(): string {
protected pageIcon(): string {
return "pf-icon pf-icon-applications";
}
checkbox = true;
clearOnRefresh = true;
public override checkbox = true;
public override clearOnRefresh = true;
@property()
order = "name";
public override order = "name";
async apiEndpoint(): Promise<PaginatedResponse<Application>> {
protected async apiEndpoint(): Promise<PaginatedResponse<Application>> {
return new CoreApi(DEFAULT_CONFIG).coreApplicationsList({
...(await this.defaultEndpointConfig()),
superuserFullList: true,
});
}
static styles: CSSResult[] = [...TablePage.styles, PFCard, applicationListStyle];
public static styles: CSSResult[] = [...TablePage.styles, PFCard, applicationListStyle];
columns(): TableColumn[] {
protected columns(): TableColumn[] {
return [
new TableColumn(""),
new TableColumn(msg("Name"), "name"),
@@ -84,7 +87,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
];
}
protected renderSidebarAfter(): TemplateResult {
protected override renderSidebarAfter(): TemplateResult {
return html`<div class="pf-c-sidebar__panel pf-m-width-25">
<div class="pf-c-card">
<div class="pf-c-card__body">
@@ -94,7 +97,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
</div>`;
}
renderToolbarSelected(): TemplateResult {
protected override renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk
objectLabel=${msg("Application(s)")}
@@ -116,7 +119,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
</ak-forms-delete-bulk>`;
}
row(item: Application): TemplateResult[] {
protected row(item: Application): TemplateResult[] {
return [
html`<ak-app-icon
name=${item.name}
@@ -154,7 +157,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
];
}
renderObjectCreate(): TemplateResult {
protected override renderObjectCreate(): TemplateResult {
return html` <ak-application-wizard .open=${getURLParam("createWizard", false)}>
<button
slot="trigger"
@@ -172,7 +175,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
</ak-forms-modal>`;
}
renderToolbar(): TemplateResult {
protected override renderToolbar(): TemplateResult {
return html` ${super.renderToolbar()}
<ak-forms-confirm
successMessage=${msg("Successfully cleared application cache")}

View File

@@ -41,15 +41,15 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
@customElement("ak-application-view")
export class ApplicationViewPage extends AKElement {
@property({ type: String })
applicationSlug?: string;
public applicationSlug?: string;
@state()
application?: Application;
protected application?: Application;
@state()
missingOutpost = false;
protected missingOutpost = false;
static styles: CSSResult[] = [
public static override styles: CSSResult[] = [
PFBase,
PFList,
PFBanner,
@@ -61,7 +61,7 @@ export class ApplicationViewPage extends AKElement {
PFCard,
];
fetchIsMissingOutpost(providersByPk: Array<number>) {
protected fetchIsMissingOutpost(providersByPk: Array<number>) {
new OutpostsApi(DEFAULT_CONFIG)
.outpostsInstancesList({
providersByPk,
@@ -74,7 +74,7 @@ export class ApplicationViewPage extends AKElement {
});
}
fetchApplication(slug: string) {
protected fetchApplication(slug: string) {
new CoreApi(DEFAULT_CONFIG).coreApplicationsRetrieve({ slug }).then((app) => {
this.application = app;
if (
@@ -89,13 +89,13 @@ export class ApplicationViewPage extends AKElement {
});
}
willUpdate(changedProperties: PropertyValues<this>) {
public override willUpdate(changedProperties: PropertyValues<this>) {
if (changedProperties.has("applicationSlug") && this.applicationSlug) {
this.fetchApplication(this.applicationSlug);
}
}
render(): TemplateResult {
public override render(): TemplateResult {
return html`<ak-page-header
header=${this.application?.name || msg("Loading")}
description=${ifDefined(this.application?.metaPublisher)}
@@ -110,7 +110,7 @@ export class ApplicationViewPage extends AKElement {
${this.renderApp()}`;
}
renderApp(): TemplateResult {
protected renderApp(): TemplateResult {
if (!this.application) {
return html`<ak-empty-state default-label></ak-empty-state>`;
}

View File

@@ -35,7 +35,7 @@ const closeButtonIcon = html`<svg
@customElement("ak-application-wizard-hint")
export class AkApplicationWizardHint extends AKElement implements ShowHintControllerHost {
static styles = [
public static override styles = [
PFBase,
PFButton,
PFPage,
@@ -51,14 +51,14 @@ export class AkApplicationWizardHint extends AKElement implements ShowHintContro
];
@property({ type: Boolean, attribute: "show-hint" })
forceHint: boolean = false;
public forceHint: boolean = false;
@state()
showHint: boolean = true;
public showHint: boolean = true;
showHintController: ShowHintController;
public showHintController: ShowHintController;
constructor() {
public constructor() {
super();
this.showHintController = new ShowHintController(
this,
@@ -66,7 +66,7 @@ export class AkApplicationWizardHint extends AKElement implements ShowHintContro
);
}
renderReminder() {
protected renderReminder() {
const sectionStyles = {
paddingBottom: "0",
marginBottom: "-0.5rem",
@@ -98,7 +98,7 @@ export class AkApplicationWizardHint extends AKElement implements ShowHintContro
</section>`;
}
renderHint() {
protected renderHint() {
return html` <section class="pf-c-page__main-section pf-m-no-padding-mobile">
<ak-hint>
<ak-hint-body>
@@ -122,7 +122,7 @@ export class AkApplicationWizardHint extends AKElement implements ShowHintContro
</section>`;
}
render() {
public override render() {
return this.showHint || this.forceHint ? this.renderHint() : this.renderReminder();
}
}

View File

@@ -13,33 +13,33 @@ import { customElement, property } from "lit/decorators.js";
@customElement("ak-provider-select-table")
export class ProviderSelectModal extends TableModal<Provider> {
checkbox = true;
checkboxChip = true;
public override checkbox = true;
public override checkboxChip = true;
searchEnabled(): boolean {
protected override searchEnabled(): boolean {
return true;
}
@property({ type: Boolean })
backchannel = false;
public backchannel = false;
@property()
confirm!: (selectedItems: Provider[]) => Promise<unknown>;
public confirm!: (selectedItems: Provider[]) => Promise<unknown>;
order = "name";
public override order = "name";
async apiEndpoint(): Promise<PaginatedResponse<Provider>> {
protected async apiEndpoint(): Promise<PaginatedResponse<Provider>> {
return new ProvidersApi(DEFAULT_CONFIG).providersAllList({
...(await this.defaultEndpointConfig()),
backchannel: this.backchannel,
});
}
columns(): TableColumn[] {
protected columns(): TableColumn[] {
return [new TableColumn(msg("Name"), "username"), new TableColumn(msg("Type"))];
}
row(item: Provider): TemplateResult[] {
protected row(item: Provider): TemplateResult[] {
return [
html`<div>
<div>${item.name}</div>
@@ -48,11 +48,11 @@ export class ProviderSelectModal extends TableModal<Provider> {
];
}
renderSelectedChip(item: Provider): TemplateResult {
protected override renderSelectedChip(item: Provider): TemplateResult {
return html`${item.name}`;
}
renderModalInner(): TemplateResult {
protected override renderModalInner(): TemplateResult {
return html`<section class="pf-c-modal-box__header pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1 class="pf-c-title pf-m-2xl">

View File

@@ -24,38 +24,38 @@ export class AkBackchannelProvidersInput extends AKElement {
// visual clutter and legibility issues of ak-form-elemental-horizontal and patternfly in
// general.
protected createRenderRoot() {
protected override createRenderRoot() {
return this as HTMLElement;
}
@property({ type: String })
name!: string;
public name!: string;
@property({ type: String })
label = "";
public label = "";
@property({ type: Array })
providers: Provider[] = [];
public providers: Provider[] = [];
@property({ type: Object })
tooltip?: TemplateResult;
public tooltip?: TemplateResult;
@property({ attribute: false, type: Object })
confirm!: ({ items }: { items: Provider[] }) => Promise<void>;
public confirm!: ({ items }: { items: Provider[] }) => Promise<void>;
@property({ attribute: false, type: Object })
remover!: (provider: Provider) => () => void;
public remover!: (provider: Provider) => () => void;
@property({ type: String })
value = "";
public value = "";
@property({ type: Boolean })
required = false;
public required = false;
@property({ type: String })
help = "";
public help = "";
render() {
public override render() {
const renderOneChip = (provider: Provider) =>
html`<ak-chip
removable

View File

@@ -34,38 +34,38 @@ export class AkProviderInput extends AKElement {
// TODO: This abstraction is wrong; it's putting *more* layers in as a way of managing the
// visual clutter and legibility issues of ak-form-elemental-horizontal and patternfly in
// general.
protected createRenderRoot() {
protected override createRenderRoot() {
return this;
}
@property({ type: String })
name!: string;
public name!: string;
@property({ type: String })
label = "";
public label = "";
@property({ type: Number })
value?: number;
public value?: number;
@property({ type: Boolean })
required = false;
public required = false;
@property({ type: Boolean })
blankable = false;
public blankable = false;
@property({ type: String })
help = "";
public help = "";
constructor() {
public constructor() {
super();
this.selected = this.selected.bind(this);
}
selected(item: Provider) {
protected selected(item: Provider) {
return this.value !== undefined && this.value === item.pk;
}
render() {
public override render() {
return html` <ak-form-element-horizontal label=${this.label} name=${this.name}>
<ak-search-select
.selected=${this.selected}

View File

@@ -20,25 +20,25 @@ import PFContent from "@patternfly/patternfly/components/Content/content.css";
@customElement("ak-application-entitlement-form")
export class ApplicationEntitlementForm extends ModelForm<ApplicationEntitlement, string> {
async loadInstance(pk: string): Promise<ApplicationEntitlement> {
protected async loadInstance(pk: string): Promise<ApplicationEntitlement> {
return new CoreApi(DEFAULT_CONFIG).coreApplicationEntitlementsRetrieve({
pbmUuid: pk,
});
}
@property()
targetPk?: string;
public targetPk?: string;
getSuccessMessage(): string {
public override getSuccessMessage(): string {
if (this.instance?.pbmUuid) {
return msg("Successfully updated entitlement.");
}
return msg("Successfully created entitlement.");
}
static styles: CSSResult[] = [...super.styles, PFContent];
public static override styles: CSSResult[] = [...super.styles, PFContent];
send(data: ApplicationEntitlement): Promise<unknown> {
protected send(data: ApplicationEntitlement): Promise<unknown> {
if (this.targetPk) {
data.app = this.targetPk;
}
@@ -53,7 +53,7 @@ export class ApplicationEntitlementForm extends ModelForm<ApplicationEntitlement
});
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
<input
type="text"

View File

@@ -28,26 +28,26 @@ import { ifDefined } from "lit/directives/if-defined.js";
@customElement("ak-application-entitlements-list")
export class ApplicationEntitlementsPage extends Table<ApplicationEntitlement> {
@property()
app?: string;
public app?: string;
checkbox = true;
clearOnRefresh = true;
expandable = true;
public override checkbox = true;
public override clearOnRefresh = true;
public override expandable = true;
order = "order";
public override order = "order";
async apiEndpoint(): Promise<PaginatedResponse<ApplicationEntitlement>> {
protected async apiEndpoint(): Promise<PaginatedResponse<ApplicationEntitlement>> {
return new CoreApi(DEFAULT_CONFIG).coreApplicationEntitlementsList({
...(await this.defaultEndpointConfig()),
app: this.app || "",
});
}
columns(): TableColumn[] {
protected columns(): TableColumn[] {
return [new TableColumn(msg("Name"), "name"), new TableColumn(msg("Actions"))];
}
renderToolbarSelected(): TemplateResult {
protected override renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk
objectLabel=${msg("Application entitlement(s)")}
@@ -69,7 +69,7 @@ export class ApplicationEntitlementsPage extends Table<ApplicationEntitlement> {
</ak-forms-delete-bulk>`;
}
row(item: ApplicationEntitlement): TemplateResult[] {
protected row(item: ApplicationEntitlement): TemplateResult[] {
return [
html`${item.name}`,
html`<ak-forms-modal size=${PFSize.Medium}>
@@ -95,7 +95,7 @@ export class ApplicationEntitlementsPage extends Table<ApplicationEntitlement> {
];
}
renderExpanded(item: ApplicationEntitlement): TemplateResult {
protected override renderExpanded(item: ApplicationEntitlement): TemplateResult {
return html`<td></td>
<td role="cell" colspan="4">
<div class="pf-c-table__expandable-row-content">
@@ -118,7 +118,7 @@ export class ApplicationEntitlementsPage extends Table<ApplicationEntitlement> {
</td>`;
}
renderEmpty(): TemplateResult {
protected override renderEmpty(): TemplateResult {
return super.renderEmpty(
html`<ak-empty-state icon="pf-icon-module"
><span>${msg("No app entitlements created.")}</span>
@@ -133,7 +133,7 @@ export class ApplicationEntitlementsPage extends Table<ApplicationEntitlement> {
);
}
renderToolbar(): TemplateResult {
protected override renderToolbar(): TemplateResult {
return html`<ak-forms-modal size=${PFSize.Medium}>
<span slot="submit"> ${msg("Create")} </span>
<span slot="header"> ${msg("Create Entitlement")} </span>

View File

@@ -21,22 +21,24 @@ import { msg } from "@lit/localize";
import { property, query } from "lit/decorators.js";
export class ApplicationWizardStep<T = Record<string, unknown>> extends WizardStep {
static styles = [...WizardStep.styles, ...styles];
public static override styles = [...WizardStep.styles, ...styles];
@property({ type: Object, attribute: false })
wizard!: ApplicationWizardState;
public wizard!: ApplicationWizardState;
// As recommended in [WizardStep](../../../components/ak-wizard/WizardStep.ts), we override
// these fields and provide them to all the child classes.
wizardTitle = msg("New application");
wizardDescription = msg("Create a new application and configure a provider for it.");
canCancel = true;
public override wizardTitle = msg("New application");
public override wizardDescription = msg(
"Create a new application and configure a provider for it.",
);
public override canCancel = true;
// This should be overridden in the children for more precise targeting.
@query("form")
form!: HTMLFormElement;
protected form!: HTMLFormElement;
get formValues(): T {
public get formValues(): T {
return serializeForm<T>([
...this.form.querySelectorAll("ak-form-element-horizontal"),
...this.form.querySelectorAll("[data-ak-control]"),

View File

@@ -35,19 +35,19 @@ const freshWizardState = (): ApplicationWizardState => ({
@customElement("ak-application-wizard-main")
export class AkApplicationWizardMain extends AKElement {
@state()
wizard: ApplicationWizardState = freshWizardState();
protected wizard: ApplicationWizardState = freshWizardState();
wizardProviderProvider = new ContextProvider(this, {
protected wizardProviderProvider = new ContextProvider(this, {
context: applicationWizardProvidersContext,
initialValue: [],
});
constructor() {
public constructor() {
super();
this.addEventListener(WizardUpdateEvent.eventName, this.handleUpdate);
}
connectedCallback() {
public override connectedCallback() {
super.connectedCallback();
new ProvidersApi(DEFAULT_CONFIG).providersAllTypesList().then((providerTypes) => {
const wizardReadyProviders = Object.keys(providerTypeRenderers);
@@ -70,7 +70,7 @@ export class AkApplicationWizardMain extends AKElement {
// This is the actual top of the Wizard; so this is where we accept the update information and
// incorporate it into the wizard.
handleUpdate(ev: WizardUpdateEvent<ApplicationWizardStateUpdate>) {
protected handleUpdate(ev: WizardUpdateEvent<ApplicationWizardStateUpdate>) {
ev.stopPropagation();
const update = ev.content;
if (update !== undefined) {
@@ -81,7 +81,7 @@ export class AkApplicationWizardMain extends AKElement {
}
}
render() {
public override render() {
return html`<ak-wizard-steps>
<ak-application-wizard-application-step
slot="application"

View File

@@ -1,7 +1,6 @@
import "./ak-application-wizard-main.js";
import { ModalButton } from "#elements/buttons/ModalButton";
import { bound } from "#elements/decorators/bound";
import { WizardCloseEvent } from "#components/ak-wizard/events";
@@ -10,18 +9,17 @@ import { customElement } from "lit/decorators.js";
@customElement("ak-application-wizard")
export class AkApplicationWizard extends ModalButton {
constructor() {
public constructor() {
super();
this.addEventListener(WizardCloseEvent.eventName, this.onCloseEvent);
this.addEventListener(WizardCloseEvent.eventName, this.#closeListener);
}
@bound
onCloseEvent(ev: WizardCloseEvent) {
ev.stopPropagation();
#closeListener = (event: WizardCloseEvent) => {
event.stopPropagation();
this.open = false;
}
};
renderModalInner() {
protected override renderModalInner() {
return html` <ak-application-wizard-main> </ak-application-wizard-main>`;
}
}

View File

@@ -8,7 +8,7 @@ import PFTitle from "@patternfly/patternfly/components/Title/title.css";
@customElement("ak-wizard-title")
export class AkWizardTitle extends AKElement {
static styles = [
public static override styles = [
PFContent,
PFTitle,
css`
@@ -18,7 +18,7 @@ export class AkWizardTitle extends AKElement {
`,
];
render() {
public override render() {
return html`<div class="ak-bottom-spacing pf-c-content">
<h3><slot></slot></h3>
</div>`;

View File

@@ -34,21 +34,21 @@ const isStr = (v: any): v is string => typeof v === "string";
@customElement("ak-application-wizard-application-step")
export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
label = msg("Application");
public override label = msg("Application");
@state()
errors = new Map<string, string>();
protected errors = new Map<string, string>();
@query("form#applicationform")
form!: HTMLFormElement;
protected override form!: HTMLFormElement;
constructor() {
public constructor() {
super();
// This is the first step. Ensure it is always enabled.
this.enabled = true;
}
errorMessages(name: string) {
protected errorMessages(name: string) {
return this.errors.has(name)
? [this.errors.get(name)]
: (this.wizard.errors?.app?.[name] ??
@@ -56,11 +56,11 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
[]);
}
get buttons(): WizardButton[] {
public override get buttons(): WizardButton[] {
return [{ kind: "next", destination: "provider-choice" }, { kind: "cancel" }];
}
get valid() {
public get valid() {
this.errors = new Map();
const values = trimMany(this.formValues ?? {}, ["metaLaunchUrl", "name", "slug"]);
@@ -81,7 +81,7 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
return this.errors.size === 0;
}
override handleButton(button: NavigableButton) {
protected override handleButton(button: NavigableButton) {
if (button.kind === "next") {
if (!this.valid) {
this.handleEnabling({
@@ -109,7 +109,7 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
super.handleButton(button);
}
renderForm(app: Partial<ApplicationRequest>, errors: ValidationRecord) {
protected renderForm(app: Partial<ApplicationRequest>, errors: ValidationRecord) {
return html` <ak-wizard-title>${msg("Configure The Application")}</ak-wizard-title>
<form id="applicationform" class="pf-c-form pf-m-horizontal" slot="form">
<ak-text-input
@@ -177,7 +177,7 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
</form>`;
}
renderMain() {
protected override renderMain() {
if (!(this.wizard.app && this.wizard.errors)) {
throw new Error("Application Step received uninitialized wizard context.");
}

View File

@@ -35,9 +35,9 @@ const COLUMNS = [
@customElement("ak-application-wizard-bindings-step")
export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
label = msg("Configure Bindings");
public override label = msg("Configure Bindings");
get buttons(): WizardButton[] {
public override get buttons(): WizardButton[] {
return [
{ kind: "next", destination: "submit" },
{ kind: "back", destination: "provider" },
@@ -46,9 +46,9 @@ export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
}
@query("ak-select-table")
selectTable!: SelectTable;
protected selectTable!: SelectTable;
static styles = [
public static override styles = [
...super.styles,
PFCard,
css`
@@ -58,7 +58,7 @@ export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
`,
];
get bindingsAsColumns() {
public get bindingsAsColumns() {
return this.wizard.bindings.map((binding, index) => {
const { order, enabled, timeout } = binding;
const isSet = P.union(P.string.minLength(1), P.number);
@@ -85,13 +85,13 @@ export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
// TODO Fix those dispatches so that we handle them here, in this component, and *choose* how to
// forward them.
onBindingEvent(binding?: number) {
protected onBindingEvent(binding?: number) {
this.handleUpdate({ currentBinding: binding ?? -1 }, "edit-binding", {
enable: "edit-binding",
});
}
onDeleteBindings() {
protected onDeleteBindings() {
const toDelete = this.selectTable
.json()
.map((i) => (typeof i === "string" ? parseInt(i, 10) : i));
@@ -99,7 +99,7 @@ export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
this.handleUpdate({ bindings }, "bindings");
}
renderEmptyCollection() {
protected renderEmptyCollection() {
return html`<ak-wizard-title
>${msg("Configure Policy/User/Group Bindings")}</ak-wizard-title
>
@@ -133,7 +133,7 @@ export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
</div>`;
}
renderCollection() {
protected renderCollection() {
return html` <ak-wizard-title>${msg("Configure Policy Bindings")}</ak-wizard-title>
<h6 class="pf-c-title pf-m-md">
${msg("These policies control which users can access this application.")}
@@ -152,7 +152,7 @@ export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
></ak-select-table>`;
}
renderMain() {
protected override renderMain() {
if ((this.wizard.bindings ?? []).length === 0) {
return this.renderEmptyCollection();
}

View File

@@ -44,24 +44,27 @@ const PASS_FAIL = [
@customElement("ak-application-wizard-edit-binding-step")
export class ApplicationWizardEditBindingStep extends ApplicationWizardStep {
label = msg("Edit Binding");
public override label = msg("Edit Binding");
hide = true;
public override hide = true;
@query("form#bindingform")
form!: HTMLFormElement;
protected override form!: HTMLFormElement;
@query(".policy-search-select")
searchSelect!: SearchSelectBase<Policy> | SearchSelectBase<Group> | SearchSelectBase<User>;
protected searchSelect!:
| SearchSelectBase<Policy>
| SearchSelectBase<Group>
| SearchSelectBase<User>;
@state()
policyGroupUser: target = target.policy;
protected policyGroupUser: target = target.policy;
instanceId = -1;
protected instanceId = -1;
instance?: PolicyBinding;
protected instance?: PolicyBinding;
get buttons(): WizardButton[] {
public override get buttons(): WizardButton[] {
return [
{ kind: "next", label: msg("Save Binding"), destination: "bindings" },
{ kind: "back", destination: "bindings" },
@@ -69,7 +72,7 @@ export class ApplicationWizardEditBindingStep extends ApplicationWizardStep {
];
}
override handleButton(button: NavigableButton) {
protected override handleButton(button: NavigableButton) {
if (button.kind === "next") {
if (!this.form.checkValidity()) {
return;
@@ -98,7 +101,7 @@ export class ApplicationWizardEditBindingStep extends ApplicationWizardStep {
// The search select configurations for the three different types of fetches that we care about,
// policy, user, and group, all using the SearchSelectEZ protocol.
searchSelectConfigs(kind: target) {
protected searchSelectConfigs(kind: target) {
switch (kind) {
case target.policy:
return {
@@ -151,7 +154,7 @@ export class ApplicationWizardEditBindingStep extends ApplicationWizardStep {
}
}
renderSearch(title: string, policyKind: target) {
protected renderSearch(title: string, policyKind: target) {
if (policyKind !== this.policyGroupUser) {
return nothing;
}
@@ -165,7 +168,7 @@ export class ApplicationWizardEditBindingStep extends ApplicationWizardStep {
</ak-form-element-horizontal>`;
}
renderForm(instance?: PolicyBinding) {
protected renderForm(instance?: PolicyBinding) {
return html`<ak-wizard-title>${msg("Create a Policy/User/Group Binding")}</ak-wizard-title>
<form id="bindingform" class="pf-c-form pf-m-horizontal" slot="form">
<div class="pf-c-card pf-m-selectable pf-m-selected">
@@ -218,7 +221,7 @@ export class ApplicationWizardEditBindingStep extends ApplicationWizardStep {
</form>`;
}
renderMain() {
protected override renderMain() {
if (!(this.wizard.bindings && this.wizard.errors)) {
throw new Error("Application Step received uninitialized wizard context.");
}

View File

@@ -7,7 +7,6 @@ import "#elements/wizard/TypeCreateWizardPage";
import { applicationWizardProvidersContext } from "../ContextIdentity.js";
import { type LocalTypeCreate } from "./ProviderChoices.js";
import { bound } from "#elements/decorators/bound";
import { WithLicenseSummary } from "#elements/mixins/license";
import { TypeCreateWizardPageLayouts } from "#elements/wizard/TypeCreateWizardPage";
@@ -15,8 +14,6 @@ import type { NavigableButton, WizardButton } from "#components/ak-wizard/types"
import { ApplicationWizardStep } from "#admin/applications/wizard/ApplicationWizardStep";
import { TypeCreate } from "@goauthentik/api";
import { consume } from "@lit/context";
import { msg } from "@lit/localize";
import { html } from "lit";
@@ -24,15 +21,15 @@ import { customElement, state } from "lit/decorators.js";
@customElement("ak-application-wizard-provider-choice-step")
export class ApplicationWizardProviderChoiceStep extends WithLicenseSummary(ApplicationWizardStep) {
label = msg("Choose a Provider");
public override label = msg("Choose a Provider");
@state()
failureMessage = "";
protected failureMessage = "";
@consume({ context: applicationWizardProvidersContext, subscribe: true })
public providerModelsList!: LocalTypeCreate[];
get buttons(): WizardButton[] {
public override get buttons(): WizardButton[] {
return [
{ kind: "next", destination: "provider" },
{ kind: "back", destination: "application" },
@@ -40,7 +37,7 @@ export class ApplicationWizardProviderChoiceStep extends WithLicenseSummary(Appl
];
}
override handleButton(button: NavigableButton) {
protected override handleButton(button: NavigableButton) {
this.failureMessage = "";
if (button.kind === "next") {
if (!this.wizard.providerModel) {
@@ -54,14 +51,7 @@ export class ApplicationWizardProviderChoiceStep extends WithLicenseSummary(Appl
super.handleButton(button);
}
@bound
onSelect(ev: CustomEvent<LocalTypeCreate>) {
ev.stopPropagation();
const detail: TypeCreate = ev.detail;
this.handleUpdate({ providerModel: detail.modelName });
}
renderMain() {
protected override renderMain() {
const selectedTypes = this.providerModelsList.filter(
(t) => t.modelName === this.wizard.providerModel,
);

View File

@@ -30,20 +30,20 @@ const providerToTag = new Map([
@customElement("ak-application-wizard-provider-step")
export class ApplicationWizardProviderStep extends ApplicationWizardStep {
@state()
label = msg("Configure Provider");
public override label = msg("Configure Provider");
@query("#providerform")
element!: ApplicationWizardProviderForm<OneOfProvider>;
protected element!: ApplicationWizardProviderForm<OneOfProvider>;
get valid() {
public get valid() {
return this.element.valid;
}
get formValues() {
public override get formValues() {
return this.element.formValues;
}
override handleButton(button: NavigableButton) {
protected override handleButton(button: NavigableButton) {
if (button.kind === "next") {
if (!this.valid) {
this.handleEnabling({
@@ -66,7 +66,7 @@ export class ApplicationWizardProviderStep extends ApplicationWizardStep {
super.handleButton(button);
}
get buttons(): WizardButton[] {
public override get buttons(): WizardButton[] {
return [
{ kind: "next", destination: "bindings" },
{ kind: "back", destination: "provider-choice" },
@@ -74,7 +74,7 @@ export class ApplicationWizardProviderStep extends ApplicationWizardStep {
];
}
renderMain() {
protected override renderMain() {
if (!this.wizard.providerModel) {
throw new Error("Attempted to access provider page without providing a provider type.");
}
@@ -97,7 +97,7 @@ export class ApplicationWizardProviderStep extends ApplicationWizardStep {
: nothing;
}
updated(changed: PropertyValues<this>) {
public override updated(changed: PropertyValues<this>) {
if (changed.has("wizard")) {
const label = this.element?.label ?? this.label;
if (label !== this.label) {

View File

@@ -80,7 +80,7 @@ const cleanBinding = (binding: PolicyBinding): TransactionPolicyBindingRequest =
@customElement("ak-application-wizard-submit-step")
export class ApplicationWizardSubmitStep extends CustomEmitterElement(ApplicationWizardStep) {
static styles = [
public static styles = [
...ApplicationWizardStep.styles,
PFBullseye,
PFEmptyState,
@@ -95,12 +95,12 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
`,
];
label = msg("Review and Submit Application");
public override label = msg("Review and Submit Application");
@state()
state: SubmitStates = "reviewing";
protected state: SubmitStates = "reviewing";
async send() {
protected async send() {
const app = this.wizard.app;
const provider = this.wizard.provider as ModelRequest;
@@ -177,7 +177,7 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
});
}
override handleButton(button: WizardButton) {
protected override handleButton(button: WizardButton) {
match([button.kind, this.state])
.with([P.union("back", "cancel"), P._], () => {
super.handleButton(button);
@@ -198,7 +198,7 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
});
}
get buttons(): WizardButton[] {
public override get buttons(): WizardButton[] {
const forReview: WizardButton[] = [
{ kind: "next", label: msg("Submit"), destination: "here" },
{ kind: "back", destination: "bindings" },
@@ -214,7 +214,7 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
.exhaustive();
}
renderInfo(
protected renderInfo(
state: string,
label: string,
icons: string[],
@@ -235,7 +235,7 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
</div>`;
}
renderError() {
protected renderError() {
const { errors } = this.wizard;
if (Object.keys(errors).length === 0) return nothing;
@@ -295,7 +295,7 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
)}`;
}
renderReview(app: Partial<ApplicationRequest>, provider: OneOfProvider) {
protected renderReview(app: Partial<ApplicationRequest>, provider: OneOfProvider) {
const renderer = providerRenderers.get(this.wizard.providerModel);
if (!renderer) {
throw new Error(
@@ -338,7 +338,7 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
`;
}
renderMain() {
protected override renderMain() {
const app = this.wizard.app;
const provider = this.wizard.provider;
if (!(this.wizard && app && provider)) {

View File

@@ -1,5 +1,4 @@
import { AKElement } from "#elements/Base";
import { bound } from "#elements/decorators/bound";
import { msg } from "@lit/localize";
import { html } from "lit";
@@ -9,13 +8,12 @@ import PFButton from "@patternfly/patternfly/components/Button/button.css";
@customElement("ak-application-wizard-binding-step-edit-button")
export class ApplicationWizardBindingStepEditButton extends AKElement {
static styles = [PFButton];
public static override styles = [PFButton];
@property({ type: Number })
value = -1;
public value = -1;
@bound
onClick(ev: Event) {
#clickListener = (ev: Event) => {
ev.stopPropagation();
this.dispatchEvent(
new CustomEvent<number>("click-edit", {
@@ -24,10 +22,10 @@ export class ApplicationWizardBindingStepEditButton extends AKElement {
detail: this.value,
}),
);
}
};
render() {
return html`<button class="pf-c-button pf-c-secondary" @click=${this.onClick}>
public override render() {
return html`<button class="pf-c-button pf-c-secondary" @click=${this.#clickListener}>
${msg("Edit")}
</button>`;
}

View File

@@ -10,16 +10,16 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
@customElement("ak-application-wizard-bindings-toolbar")
export class ApplicationWizardBindingsToolbar extends AKElement {
static styles = [PFBase, PFButton, PFToolbar];
public static override styles = [PFBase, PFButton, PFToolbar];
@property({ type: Boolean, attribute: "can-delete", reflect: true })
canDelete = false;
public canDelete = false;
notify(eventName: string) {
protected notify(eventName: string) {
this.dispatchEvent(new Event(eventName, { bubbles: true, composed: true }));
}
render() {
public override render() {
return html`
<div class="pf-c-toolbar">
<div class="pf-c-toolbar__content">

View File

@@ -17,32 +17,32 @@ import { CSSResult } from "lit";
import { property, query } from "lit/decorators.js";
export class ApplicationWizardProviderForm<T extends OneOfProvider> extends AKElement {
static styles: CSSResult[] = [...AwadStyles];
public static override styles: CSSResult[] = [...AwadStyles];
label = "";
public label = "";
@property({ type: Object, attribute: false })
wizard!: ApplicationWizardState;
public wizard!: ApplicationWizardState;
@property({ type: Object, attribute: false })
errors: Record<string | number | symbol, string> = {};
public errors: Record<string | number | symbol, string> = {};
@query("form#providerform")
form!: HTMLFormElement;
protected form!: HTMLFormElement;
get formValues() {
public get formValues() {
return serializeForm([
...this.form.querySelectorAll("ak-form-element-horizontal"),
...this.form.querySelectorAll("[data-ak-control]"),
]);
}
get valid() {
public get valid() {
this.errors = {};
return this.form.checkValidity();
}
errorMessages(name: string) {
protected errorMessages(name: string) {
return name in this.errors
? [this.errors[name]]
: (this.wizard.errors?.provider?.[name] ??
@@ -50,7 +50,7 @@ export class ApplicationWizardProviderForm<T extends OneOfProvider> extends AKEl
[]);
}
isValid(name: keyof T) {
protected isValid(name: keyof T) {
return !(
(this.wizard.errors?.provider?.[name as string] ?? []).length > 0 ||
this.errors?.[name] !== undefined

View File

@@ -17,9 +17,9 @@ import { customElement } from "lit/decorators.js";
export class ApplicationWizardLdapProviderForm extends WithBrandConfig(
ApplicationWizardProviderForm<LDAPProvider>,
) {
label = msg("Configure LDAP Provider");
public override label = msg("Configure LDAP Provider");
renderForm(provider: LDAPProvider, errors: ValidationRecord) {
protected renderForm(provider: LDAPProvider, errors: ValidationRecord) {
return html`
<ak-wizard-title>${this.label}</ak-wizard-title>
<form id="providerform" class="pf-c-form pf-m-horizontal" slot="form">
@@ -28,7 +28,7 @@ export class ApplicationWizardLdapProviderForm extends WithBrandConfig(
`;
}
render() {
public override render() {
if (!(this.wizard.provider && this.wizard.errors)) {
throw new Error("LDAP Provider Step received uninitialized wizard context.");
}

View File

@@ -20,15 +20,15 @@ import { customElement, state } from "lit/decorators.js";
@customElement("ak-application-wizard-provider-for-oauth")
export class ApplicationWizardOauth2ProviderForm extends ApplicationWizardProviderForm<OAuth2ProviderRequest> {
label = msg("Configure OAuth2 Provider");
public override label = msg("Configure OAuth2 Provider");
@state()
showClientSecret = true;
protected showClientSecret = true;
@state()
oauthSources?: PaginatedOAuthSourceList;
protected oauthSources?: PaginatedOAuthSourceList;
constructor() {
public constructor() {
super();
new SourcesApi(DEFAULT_CONFIG)
.sourcesOauthList({
@@ -40,7 +40,7 @@ export class ApplicationWizardOauth2ProviderForm extends ApplicationWizardProvid
});
}
renderForm(provider: OAuth2Provider, errors: ApplicationTransactionValidationError) {
protected renderForm(provider: OAuth2Provider, errors: ApplicationTransactionValidationError) {
const showClientSecretCallback = (show: boolean) => {
this.showClientSecret = show;
};
@@ -55,7 +55,7 @@ export class ApplicationWizardOauth2ProviderForm extends ApplicationWizardProvid
</form>`;
}
render() {
public override render() {
if (!(this.wizard.provider && this.wizard.errors)) {
throw new Error("Oauth2 Provider Step received uninitialized wizard context.");
}

View File

@@ -20,12 +20,12 @@ import { customElement, state } from "lit/decorators.js";
@customElement("ak-application-wizard-provider-for-proxy")
export class ApplicationWizardProxyProviderForm extends ApplicationWizardProviderForm<ProxyProvider> {
label = msg("Configure Proxy Provider");
public override label = msg("Configure Proxy Provider");
@state()
showHttpBasic = true;
protected showHttpBasic = true;
renderForm(provider: ProxyProvider, errors: ValidationRecord) {
protected renderForm(provider: ProxyProvider, errors: ValidationRecord) {
const onSetMode: SetMode = (ev: CustomEvent<ProxyModeValue>) => {
this.dispatchEvent(
new WizardUpdateEvent({ ...this.wizard, proxyMode: ev.detail.value }),
@@ -51,7 +51,7 @@ export class ApplicationWizardProxyProviderForm extends ApplicationWizardProvide
</form>`;
}
render() {
public override render() {
if (!(this.wizard.provider && this.wizard.errors)) {
throw new Error("Proxy Provider Step received uninitialized wizard context.");
}

View File

@@ -21,9 +21,9 @@ import { ifDefined } from "lit/directives/if-defined.js";
@customElement("ak-application-wizard-provider-for-rac")
export class ApplicationWizardRACProviderForm extends ApplicationWizardProviderForm<RACProvider> {
label = msg("Configure Remote Access Provider");
public override label = msg("Configure Remote Access Provider");
renderForm(provider: RACProvider) {
protected renderForm(provider: RACProvider) {
return html`
<ak-wizard-title>${this.label}</ak-wizard-title>
<form id="providerform" class="pf-c-form pf-m-horizontal" slot="form">
@@ -80,7 +80,7 @@ export class ApplicationWizardRACProviderForm extends ApplicationWizardProviderF
`;
}
render() {
public override render() {
if (!(this.wizard.provider && this.wizard.errors)) {
throw new Error("RAC Provider Step received uninitialized wizard context.");
}

View File

@@ -17,16 +17,16 @@ import { html } from "lit";
export class ApplicationWizardRadiusProviderForm extends WithBrandConfig(
ApplicationWizardProviderForm<RadiusProvider>,
) {
label = msg("Configure Radius Provider");
public override label = msg("Configure Radius Provider");
renderForm(provider: RadiusProvider, errors: ValidationRecord) {
protected renderForm(provider: RadiusProvider, errors: ValidationRecord) {
return html` <ak-wizard-title>${this.label}</ak-wizard-title>
<form id="providerform" class="pf-c-form pf-m-horizontal" slot="form">
${renderForm(provider ?? {}, errors, this.brand)}
</form>`;
}
render() {
public override render() {
if (!(this.wizard.provider && this.wizard.errors)) {
throw new Error("RAC Provider Step received uninitialized wizard context.");
}

View File

@@ -14,12 +14,12 @@ import { html } from "lit";
@customElement("ak-application-wizard-provider-for-saml")
export class ApplicationWizardProviderSamlForm extends ApplicationWizardProviderForm<SAMLProvider> {
label = msg("Configure SAML Provider");
public override label = msg("Configure SAML Provider");
@state()
hasSigningKp = false;
protected hasSigningKp = false;
renderForm() {
protected renderForm() {
const setHasSigningKp = (ev: InputEvent) => {
const target = ev.target as AkCryptoCertificateSearch;
if (!target) return;
@@ -37,7 +37,7 @@ export class ApplicationWizardProviderSamlForm extends ApplicationWizardProvider
</form>`;
}
render() {
public override render() {
if (!(this.wizard.provider && this.wizard.errors)) {
throw new Error("SAML Provider Step received uninitialized wizard context.");
}

View File

@@ -13,12 +13,12 @@ import { html } from "lit";
@customElement("ak-application-wizard-provider-for-scim")
export class ApplicationWizardSCIMProvider extends ApplicationWizardProviderForm<SCIMProvider> {
label = msg("Configure SCIM Provider");
public override label = msg("Configure SCIM Provider");
@state()
propertyMappings?: PaginatedSCIMMappingList;
protected propertyMappings?: PaginatedSCIMMappingList;
render() {
public override render() {
return html`<ak-wizard-title>${this.label}</ak-wizard-title>
<form id="providerform" class="pf-c-form pf-m-horizontal" slot="form">
${renderForm(

View File

@@ -30,9 +30,9 @@ enum blueprintSource {
@customElement("ak-blueprint-form")
export class BlueprintForm extends ModelForm<BlueprintInstance, string> {
@state()
source: blueprintSource = blueprintSource.file;
protected source: blueprintSource = blueprintSource.file;
async loadInstance(pk: string): Promise<BlueprintInstance> {
protected async loadInstance(pk: string): Promise<BlueprintInstance> {
const inst = await new ManagedApi(DEFAULT_CONFIG).managedBlueprintsRetrieve({
instanceUuid: pk,
});
@@ -45,15 +45,15 @@ export class BlueprintForm extends ModelForm<BlueprintInstance, string> {
return inst;
}
getSuccessMessage(): string {
public override getSuccessMessage(): string {
return this.instance
? msg("Successfully updated instance.")
: msg("Successfully created instance.");
}
static styles: CSSResult[] = [...super.styles, PFContent];
public static override styles: CSSResult[] = [...super.styles, PFContent];
async send(data: BlueprintInstance): Promise<BlueprintInstance> {
protected async send(data: BlueprintInstance): Promise<BlueprintInstance> {
if (this.instance?.pk) {
return new ManagedApi(DEFAULT_CONFIG).managedBlueprintsUpdate({
instanceUuid: this.instance.pk,
@@ -65,7 +65,7 @@ export class BlueprintForm extends ModelForm<BlueprintInstance, string> {
});
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
<input
type="text"

View File

@@ -46,35 +46,38 @@ export function BlueprintStatus(blueprint?: BlueprintInstance): string {
@customElement("ak-blueprint-list")
export class BlueprintListPage extends TablePage<BlueprintInstance> {
searchEnabled(): boolean {
protected override searchEnabled(): boolean {
return true;
}
pageTitle(): string {
protected pageTitle(): string {
return msg("Blueprints");
}
pageDescription(): string {
protected pageDescription(): string {
return msg("Automate and template configuration within authentik.");
}
pageIcon(): string {
protected pageIcon(): string {
return "pf-icon pf-icon-blueprint";
}
expandable = true;
checkbox = true;
clearOnRefresh = true;
public override expandable = true;
public override checkbox = true;
public override clearOnRefresh = true;
@property()
order = "name";
public override order = "name";
static styles: CSSResult[] = [...super.styles, PFDescriptionList];
public static override styles: CSSResult[] = [...super.styles, PFDescriptionList];
async apiEndpoint(): Promise<PaginatedResponse<BlueprintInstance>> {
protected async apiEndpoint(): Promise<PaginatedResponse<BlueprintInstance>> {
return new ManagedApi(DEFAULT_CONFIG).managedBlueprintsList(
await this.defaultEndpointConfig(),
);
}
columns(): TableColumn[] {
protected columns(): TableColumn[] {
return [
new TableColumn(msg("Name"), "name"),
new TableColumn(msg("Status"), "status"),
@@ -84,7 +87,7 @@ export class BlueprintListPage extends TablePage<BlueprintInstance> {
];
}
renderToolbarSelected(): TemplateResult {
protected override renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk
objectLabel=${msg("Blueprint(s)")}
@@ -109,7 +112,7 @@ export class BlueprintListPage extends TablePage<BlueprintInstance> {
</ak-forms-delete-bulk>`;
}
renderExpanded(item: BlueprintInstance): TemplateResult {
protected override renderExpanded(item: BlueprintInstance): TemplateResult {
const [appLabel, modelName] = ModelEnum.AuthentikBlueprintsBlueprintinstance.split(".");
return html`<td role="cell" colspan="5">
<div class="pf-c-table__expandable-row-content">
@@ -145,7 +148,7 @@ export class BlueprintListPage extends TablePage<BlueprintInstance> {
</td>`;
}
row(item: BlueprintInstance): TemplateResult[] {
protected row(item: BlueprintInstance): TemplateResult[] {
let description = undefined;
const descKey = "blueprints.goauthentik.io/description";
if (
@@ -201,7 +204,7 @@ export class BlueprintListPage extends TablePage<BlueprintInstance> {
];
}
renderObjectCreate(): TemplateResult {
protected override renderObjectCreate(): TemplateResult {
return html`
<ak-forms-modal>
<span slot="submit"> ${msg("Create")} </span>

View File

@@ -31,19 +31,19 @@ import { customElement } from "lit/decorators.js";
@customElement("ak-brand-form")
export class BrandForm extends ModelForm<Brand, string> {
loadInstance(pk: string): Promise<Brand> {
protected loadInstance(pk: string): Promise<Brand> {
return new CoreApi(DEFAULT_CONFIG).coreBrandsRetrieve({
brandUuid: pk,
});
}
getSuccessMessage(): string {
public override getSuccessMessage(): string {
return this.instance
? msg("Successfully updated brand.")
: msg("Successfully created brand.");
}
async send(data: Brand): Promise<Brand> {
protected async send(data: Brand): Promise<Brand> {
if (this.instance?.brandUuid) {
return new CoreApi(DEFAULT_CONFIG).coreBrandsUpdate({
brandUuid: this.instance.brandUuid,
@@ -55,7 +55,7 @@ export class BrandForm extends ModelForm<Brand, string> {
});
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
return html` <ak-form-element-horizontal label=${msg("Domain")} required name="domain">
<input
type="text"

View File

@@ -19,30 +19,33 @@ import { customElement, property } from "lit/decorators.js";
@customElement("ak-brand-list")
export class BrandListPage extends TablePage<Brand> {
searchEnabled(): boolean {
protected override searchEnabled(): boolean {
return true;
}
pageTitle(): string {
protected pageTitle(): string {
return msg("Brands");
}
pageDescription(): string {
protected pageDescription(): string {
return msg("Configure visual settings and defaults for different domains.");
}
pageIcon(): string {
protected pageIcon(): string {
return "pf-icon pf-icon-tenant";
}
checkbox = true;
clearOnRefresh = true;
public override checkbox = true;
public override clearOnRefresh = true;
@property()
order = "domain";
public override order = "domain";
async apiEndpoint(): Promise<PaginatedResponse<Brand>> {
protected async apiEndpoint(): Promise<PaginatedResponse<Brand>> {
return new CoreApi(DEFAULT_CONFIG).coreBrandsList(await this.defaultEndpointConfig());
}
columns(): TableColumn[] {
protected columns(): TableColumn[] {
return [
new TableColumn(msg("Domain"), "domain"),
new TableColumn(msg("Brand name"), "branding_title"),
@@ -51,7 +54,7 @@ export class BrandListPage extends TablePage<Brand> {
];
}
renderToolbarSelected(): TemplateResult {
protected override renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk
objectLabel=${msg("Brand(s)")}
@@ -76,7 +79,7 @@ export class BrandListPage extends TablePage<Brand> {
</ak-forms-delete-bulk>`;
}
row(item: Brand): TemplateResult[] {
protected row(item: Brand): TemplateResult[] {
return [
html`${item.domain}`,
html`${item.brandingTitle}`,
@@ -100,7 +103,7 @@ export class BrandListPage extends TablePage<Brand> {
];
}
renderObjectCreate(): TemplateResult {
protected override renderObjectCreate(): TemplateResult {
return html`
<ak-forms-modal>
<span slot="submit"> ${msg("Create")} </span>

View File

@@ -44,27 +44,27 @@ export class CoreGroupSearch extends CustomListenerElement(AKElement) {
* @attr
*/
@property({ type: String, reflect: true })
group?: string;
public group?: string;
@query("ak-search-select")
search!: SearchSelect<Group>;
protected search!: SearchSelect<Group>;
@property({ type: String })
name: string | null | undefined;
public name: string | null | undefined;
selectedGroup?: Group;
protected selectedGroup?: Group;
constructor() {
public constructor() {
super();
this.selected = this.selected.bind(this);
this.handleSearchUpdate = this.handleSearchUpdate.bind(this);
}
get value() {
public get value() {
return this.selectedGroup ? renderValue(this.selectedGroup) : undefined;
}
connectedCallback() {
public override connectedCallback() {
super.connectedCallback();
const horizontalContainer = this.closest("ak-form-element-horizontal[name]");
if (!horizontalContainer) {
@@ -77,17 +77,17 @@ export class CoreGroupSearch extends CustomListenerElement(AKElement) {
}
}
handleSearchUpdate(ev: CustomEvent) {
protected handleSearchUpdate(ev: CustomEvent) {
ev.stopPropagation();
this.selectedGroup = ev.detail.value;
this.dispatchEvent(new InputEvent("input", { bubbles: true, composed: true }));
}
selected(group: Group) {
protected selected(group: Group) {
return this.group === group.pk;
}
render() {
public override render() {
return html`
<ak-search-select
.fetchObjects=${fetchObjects}

View File

@@ -34,13 +34,13 @@ const renderValue = (item: CertificateKeyPair | undefined): string | undefined =
@customElement("ak-crypto-certificate-search")
export class AkCryptoCertificateSearch extends CustomListenerElement(AKElement) {
@property({ type: String, reflect: true })
certificate?: string;
public certificate?: string;
@query("ak-search-select")
search!: SearchSelect<CertificateKeyPair>;
protected search!: SearchSelect<CertificateKeyPair>;
@property({ type: String })
name: string | null | undefined;
public name: string | null | undefined;
/**
* Set to `true` to allow certificates without private key to show up. When set to `false`,
@@ -48,7 +48,7 @@ export class AkCryptoCertificateSearch extends CustomListenerElement(AKElement)
* @attr
*/
@property({ type: Boolean, attribute: "nokey" })
noKey = false;
public noKey = false;
/**
* Set this to true if, should there be only one certificate available, you want the system to
@@ -57,22 +57,22 @@ export class AkCryptoCertificateSearch extends CustomListenerElement(AKElement)
* @attr
*/
@property({ type: Boolean, attribute: "singleton" })
singleton = false;
public singleton = false;
selectedKeypair?: CertificateKeyPair;
public selectedKeypair?: CertificateKeyPair;
constructor() {
public constructor() {
super();
this.selected = this.selected.bind(this);
this.fetchObjects = this.fetchObjects.bind(this);
this.handleSearchUpdate = this.handleSearchUpdate.bind(this);
}
get value() {
public get value() {
return this.selectedKeypair ? renderValue(this.selectedKeypair) : null;
}
connectedCallback() {
public override connectedCallback() {
super.connectedCallback();
const horizontalContainer = this.closest("ak-form-element-horizontal[name]");
if (!horizontalContainer) {
@@ -85,13 +85,13 @@ export class AkCryptoCertificateSearch extends CustomListenerElement(AKElement)
}
}
handleSearchUpdate(ev: CustomEvent) {
protected handleSearchUpdate(ev: CustomEvent) {
ev.stopPropagation();
this.selectedKeypair = ev.detail.value;
this.dispatchEvent(new InputEvent("input", { bubbles: true, composed: true }));
}
async fetchObjects(query?: string): Promise<CertificateKeyPair[]> {
protected async fetchObjects(query?: string): Promise<CertificateKeyPair[]> {
const args: CryptoCertificatekeypairsListRequest = {
ordering: "name",
hasKey: !this.noKey,
@@ -106,14 +106,14 @@ export class AkCryptoCertificateSearch extends CustomListenerElement(AKElement)
return certificates.results;
}
selected(item: CertificateKeyPair, items: CertificateKeyPair[]) {
protected selected(item: CertificateKeyPair, items: CertificateKeyPair[]) {
return (
(this.singleton && !this.certificate && items.length === 1) ||
(!!this.certificate && this.certificate === item.pk)
);
}
render() {
public override render() {
return html`
<ak-search-select
name=${ifDefined(this.name ?? undefined)}

View File

@@ -43,7 +43,7 @@ export class FlowSearch<T extends Flow> extends CustomListenerElement(AKElement)
* @attr
*/
@property({ type: String })
flowType?: FlowsInstancesListDesignationEnum;
public flowType?: FlowsInstancesListDesignationEnum;
/**
* The id of the current flow, if any. For stages where the flow is already defined.
@@ -51,7 +51,7 @@ export class FlowSearch<T extends Flow> extends CustomListenerElement(AKElement)
* @attr
*/
@property({ type: String })
currentFlow?: string | undefined;
public currentFlow?: string | undefined;
/**
* If true, it is not valid to leave the flow blank.
@@ -59,10 +59,10 @@ export class FlowSearch<T extends Flow> extends CustomListenerElement(AKElement)
* @attr
*/
@property({ type: Boolean })
required?: boolean = false;
public required?: boolean = false;
@query("ak-search-select")
search!: SearchSelect<T>;
protected search!: SearchSelect<T>;
/**
* When specified and the object instance does not have a flow selected, auto-select the flow with the given slug.
@@ -70,31 +70,31 @@ export class FlowSearch<T extends Flow> extends CustomListenerElement(AKElement)
* @attr
*/
@property()
defaultFlowSlug?: string;
public defaultFlowSlug?: string;
@property({ type: String })
name: string | null | undefined;
public name: string | null | undefined;
selectedFlow?: T;
protected selectedFlow?: T;
get value() {
public get value() {
return this.selectedFlow ? getFlowValue(this.selectedFlow) : null;
}
constructor() {
public constructor() {
super();
this.fetchObjects = this.fetchObjects.bind(this);
this.selected = this.selected.bind(this);
this.handleSearchUpdate = this.handleSearchUpdate.bind(this);
}
handleSearchUpdate(ev: CustomEvent) {
protected handleSearchUpdate(ev: CustomEvent) {
ev.stopPropagation();
this.selectedFlow = ev.detail.value;
this.dispatchEvent(new InputEvent("input", { bubbles: true, composed: true }));
}
async fetchObjects(query?: string): Promise<Flow[]> {
protected async fetchObjects(query?: string): Promise<Flow[]> {
const args: FlowsInstancesListRequest = {
ordering: "slug",
designation: this.flowType,
@@ -108,7 +108,7 @@ export class FlowSearch<T extends Flow> extends CustomListenerElement(AKElement)
* use this method, but several have more complex needs, such as relating to the brand, or just
* returning false.
*/
selected(flow: Flow): boolean {
protected selected(flow: Flow): boolean {
let selected = this.currentFlow === flow.pk;
if (!this.currentFlow && this.defaultFlowSlug && flow.slug === this.defaultFlowSlug) {
selected = true;
@@ -116,7 +116,7 @@ export class FlowSearch<T extends Flow> extends CustomListenerElement(AKElement)
return selected;
}
connectedCallback() {
public override connectedCallback() {
super.connectedCallback();
const horizontalContainer = this.closest("ak-form-element-horizontal[name]");
if (!horizontalContainer) {
@@ -129,7 +129,7 @@ export class FlowSearch<T extends Flow> extends CustomListenerElement(AKElement)
}
}
render() {
public override render() {
return html`
<ak-search-select
.fetchObjects=${this.fetchObjects}

View File

@@ -19,14 +19,14 @@ export class AkBrandedFlowSearch<T extends Flow> extends FlowSearch<T> {
* @attr
*/
@property({ attribute: false, type: String })
brandFlow?: string;
public brandFlow?: string;
constructor() {
public constructor() {
super();
this.selected = this.selected.bind(this);
}
selected(flow: Flow): boolean {
public override selected(flow: Flow): boolean {
return super.selected(flow) || flow.pk === this.brandFlow;
}
}

View File

@@ -17,7 +17,7 @@ import { customElement } from "lit/decorators.js";
@customElement("ak-flow-search-no-default")
export class AkFlowSearchNoDefault<T extends Flow> extends FlowSearch<T> {
render() {
public override render() {
return html`
<ak-search-select
.fetchObjects=${this.fetchObjects}

View File

@@ -20,7 +20,7 @@ export class AkSourceFlowSearch<T extends Flow> extends FlowSearch<T> {
*/
@property({ type: String })
fallback: string | undefined;
public fallback: string | undefined;
/**
* The primary key of the Source (not the Flow). Mostly the instancePk itself, used to affirm
@@ -29,16 +29,16 @@ export class AkSourceFlowSearch<T extends Flow> extends FlowSearch<T> {
* @attr
*/
@property({ type: String })
instanceId: string | undefined;
public instanceId: string | undefined;
constructor() {
public constructor() {
super();
this.selected = this.selected.bind(this);
}
// If there's no instance or no currentFlowId for it and the flow resembles the fallback,
// otherwise defer to the parent class.
selected(flow: Flow): boolean {
public override selected(flow: Flow): boolean {
return (
(!this.instanceId && !this.currentFlow && flow.slug === this.fallback) ||
super.selected(flow)

View File

@@ -11,7 +11,7 @@ import { customElement, property } from "lit/decorators.js";
@customElement("ak-license-notice")
export class AKLicenceNotice extends WithLicenseSummary(AKElement) {
static styles = [$PFBase];
public static styles = [$PFBase];
@property()
public label = msg("Enterprise only");
@@ -19,7 +19,7 @@ export class AKLicenceNotice extends WithLicenseSummary(AKElement) {
@property()
public description = msg("Learn more about the enterprise license.");
render() {
public override render() {
if (this.hasEnterpriseLicense) {
return nothing;
}

View File

@@ -17,17 +17,17 @@ import { customElement } from "lit/decorators.js";
@customElement("ak-crypto-certificate-generate-form")
export class CertificateKeyPairForm extends Form<CertificateGenerationRequest> {
getSuccessMessage(): string {
public override getSuccessMessage(): string {
return msg("Successfully generated certificate-key pair.");
}
async send(data: CertificateGenerationRequest): Promise<CertificateKeyPair> {
protected async send(data: CertificateGenerationRequest): Promise<CertificateKeyPair> {
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsGenerateCreate({
certificateGenerationRequest: data,
});
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
return html`<ak-form-element-horizontal
label=${msg("Common Name")}
name="commonName"

View File

@@ -15,19 +15,19 @@ import { ifDefined } from "lit/directives/if-defined.js";
@customElement("ak-crypto-certificate-form")
export class CertificateKeyPairForm extends ModelForm<CertificateKeyPair, string> {
loadInstance(pk: string): Promise<CertificateKeyPair> {
protected loadInstance(pk: string): Promise<CertificateKeyPair> {
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsRetrieve({
kpUuid: pk,
});
}
getSuccessMessage(): string {
public override getSuccessMessage(): string {
return this.instance
? msg("Successfully updated certificate-key pair.")
: msg("Successfully created certificate-key pair.");
}
async send(data: CertificateKeyPair): Promise<CertificateKeyPair> {
protected async send(data: CertificateKeyPair): Promise<CertificateKeyPair> {
if (this.instance) {
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsPartialUpdate({
kpUuid: this.instance.pk || "",
@@ -39,7 +39,7 @@ export class CertificateKeyPairForm extends ModelForm<CertificateKeyPair, string
});
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
return html` <ak-form-element-horizontal label=${msg("Name")} name="name" required>
<input
type="text"

View File

@@ -27,37 +27,40 @@ import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList
@customElement("ak-crypto-certificate-list")
export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
expandable = true;
checkbox = true;
clearOnRefresh = true;
public override expandable = true;
public override checkbox = true;
public override clearOnRefresh = true;
searchEnabled(): boolean {
protected override searchEnabled(): boolean {
return true;
}
pageTitle(): string {
protected pageTitle(): string {
return msg("Certificate-Key Pairs");
}
pageDescription(): string {
protected pageDescription(): string {
return msg(
"Import certificates of external providers or create certificates to sign requests with.",
);
}
pageIcon(): string {
protected pageIcon(): string {
return "pf-icon pf-icon-key";
}
@property()
order = "name";
public override order = "name";
static styles: CSSResult[] = [...super.styles, PFDescriptionList];
public static override styles: CSSResult[] = [...super.styles, PFDescriptionList];
async apiEndpoint(): Promise<PaginatedResponse<CertificateKeyPair>> {
protected async apiEndpoint(): Promise<PaginatedResponse<CertificateKeyPair>> {
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList(
await this.defaultEndpointConfig(),
);
}
columns(): TableColumn[] {
protected columns(): TableColumn[] {
return [
new TableColumn(msg("Name"), "name"),
new TableColumn(msg("Private key available?")),
@@ -66,7 +69,7 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
];
}
renderToolbarSelected(): TemplateResult {
protected override renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk
objectLabel=${msg("Certificate-Key Pair(s)")}
@@ -94,7 +97,7 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
</ak-forms-delete-bulk>`;
}
row(item: CertificateKeyPair): TemplateResult[] {
protected row(item: CertificateKeyPair): TemplateResult[] {
let managedSubText = msg("Managed by authentik");
if (item.managed && item.managed.startsWith("goauthentik.io/crypto/discovered")) {
managedSubText = msg("Managed by authentik (Discovered)");
@@ -140,7 +143,7 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
];
}
renderExpanded(item: CertificateKeyPair): TemplateResult {
protected override renderExpanded(item: CertificateKeyPair): TemplateResult {
return html`<td role="cell" colspan="4">
<div class="pf-c-table__expandable-row-content">
<dl class="pf-c-description-list pf-m-horizontal">
@@ -210,7 +213,7 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
<td></td>`;
}
renderObjectCreate(): TemplateResult {
protected override renderObjectCreate(): TemplateResult {
return html`
<ak-forms-modal>
<span slot="submit"> ${msg("Import")} </span>

View File

@@ -17,27 +17,27 @@ import { ifDefined } from "lit/directives/if-defined.js";
@customElement("ak-enterprise-license-form")
export class EnterpriseLicenseForm extends ModelForm<License, string> {
@state()
installID?: string;
protected installID?: string;
loadInstance(pk: string): Promise<License> {
protected loadInstance(pk: string): Promise<License> {
return new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseRetrieve({
licenseUuid: pk,
});
}
getSuccessMessage(): string {
public override getSuccessMessage(): string {
return this.instance
? msg("Successfully updated license.")
: msg("Successfully created license.");
}
async load(): Promise<void> {
public override async load(): Promise<void> {
this.installID = (
await new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseInstallIdRetrieve()
).installId;
}
async send(data: License): Promise<License> {
protected async send(data: License): Promise<License> {
return (
this.instance
? new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicensePartialUpdate({
@@ -53,7 +53,7 @@ export class EnterpriseLicenseForm extends ModelForm<License, string> {
});
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
return html` <ak-form-element-horizontal label=${msg("Install ID")}>
<input
class="pf-c-form-control pf-m-monospace"

View File

@@ -36,35 +36,38 @@ import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
@customElement("ak-enterprise-license-list")
export class EnterpriseLicenseListPage extends TablePage<License> {
checkbox = true;
clearOnRefresh = true;
public override checkbox = true;
public override clearOnRefresh = true;
searchEnabled(): boolean {
protected override searchEnabled(): boolean {
return true;
}
pageTitle(): string {
protected pageTitle(): string {
return msg("Licenses");
}
pageDescription(): string {
protected pageDescription(): string {
return msg("Manage enterprise licenses");
}
pageIcon(): string {
protected pageIcon(): string {
return "pf-icon pf-icon-key";
}
@property()
order = "name";
public override order = "name";
@state()
forecast?: LicenseForecast;
protected forecast?: LicenseForecast;
@state()
summary?: LicenseSummary;
protected summary?: LicenseSummary;
@state()
installID?: string;
protected installID?: string;
static styles: CSSResult[] = [
public static override styles: CSSResult[] = [
...super.styles,
PFGrid,
PFBanner,
@@ -81,7 +84,7 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
`,
];
async apiEndpoint(): Promise<PaginatedResponse<License>> {
protected async apiEndpoint(): Promise<PaginatedResponse<License>> {
this.forecast = await new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseForecastRetrieve();
this.summary = await new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseSummaryRetrieve({
cached: false,
@@ -94,7 +97,7 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
);
}
columns(): TableColumn[] {
protected columns(): TableColumn[] {
return [
new TableColumn(msg("Name"), "name"),
new TableColumn(msg("Users")),
@@ -105,7 +108,7 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
// TODO: Make this more generic, maybe automatically get the plural name
// of the object to use in the renderEmpty
renderEmpty(inner?: TemplateResult): TemplateResult {
protected override renderEmpty(inner?: TemplateResult): TemplateResult {
return super.renderEmpty(html`
${inner
? inner
@@ -119,7 +122,7 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
`);
}
renderToolbarSelected(): TemplateResult {
protected override renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk
objectLabel=${msg("License(s)")}
@@ -147,7 +150,7 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
</ak-forms-delete-bulk>`;
}
renderSectionBefore(): TemplateResult {
protected override renderSectionBefore(): TemplateResult {
return html`
<section class="pf-c-page__main-section pf-m-no-padding-bottom">
<div
@@ -199,7 +202,7 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
`;
}
row(item: License): TemplateResult[] {
protected row(item: License): TemplateResult[] {
let color = PFColor.Green;
if (item.expiry) {
const now = new Date();
@@ -236,7 +239,7 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
];
}
renderGetLicenseCard() {
protected renderGetLicenseCard() {
const renderSpinner = () =>
html` <div class="pf-c-card__body">
<ak-spinner></ak-spinner>
@@ -273,7 +276,7 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
</div> `;
}
renderObjectCreate(): TemplateResult {
protected override renderObjectCreate(): TemplateResult {
return html`
<ak-forms-modal>
<span slot="submit"> ${msg("Install")} </span>

View File

@@ -16,14 +16,20 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
@customElement("ak-enterprise-status-card")
export class EnterpriseStatusCard extends AKElement {
@state()
forecast?: LicenseForecast;
protected forecast?: LicenseForecast;
@state()
summary?: LicenseSummary;
protected summary?: LicenseSummary;
static styles: CSSResult[] = [PFBase, PFDescriptionList, PFCard, PFSplit, PFProgress];
public static override styles: CSSResult[] = [
PFBase,
PFDescriptionList,
PFCard,
PFSplit,
PFProgress,
];
renderSummaryBadge() {
protected renderSummaryBadge() {
switch (this.summary?.status) {
case LicenseSummaryStatusEnum.Expired:
return html`<ak-label color=${PFColor.Red}>${msg("Expired")}</ak-label>`;
@@ -40,13 +46,13 @@ export class EnterpriseStatusCard extends AKElement {
}
}
calcUserPercentage(licensed: number, current: number) {
protected calcUserPercentage(licensed: number, current: number) {
const percentage = licensed > 0 ? Math.ceil(current / (licensed / 100)) : 0;
if (current > 0 && licensed === 0) return Infinity;
return percentage;
}
render() {
public override render() {
if (!this.forecast || !this.summary) {
return html`${msg("Loading")}`;
}

View File

@@ -26,26 +26,29 @@ import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
@customElement("ak-event-list")
export class EventListPage extends WithLicenseSummary(TablePage<Event>) {
expandable = true;
supportsQL = true;
public override expandable = true;
public override supportsQL = true;
pageTitle(): string {
protected pageTitle(): string {
return msg("Event Log");
}
pageDescription(): string | undefined {
protected pageDescription(): string | undefined {
return;
}
pageIcon(): string {
protected pageIcon(): string {
return "pf-icon pf-icon-catalog";
}
searchEnabled(): boolean {
protected override searchEnabled(): boolean {
return true;
}
@property()
order = "-created";
public override order = "-created";
static styles: CSSResult[] = [
public static styles: CSSResult[] = [
...TablePage.styles,
PFGrid,
css`
@@ -55,11 +58,11 @@ export class EventListPage extends WithLicenseSummary(TablePage<Event>) {
`,
];
async apiEndpoint(): Promise<PaginatedResponse<Event>> {
protected async apiEndpoint(): Promise<PaginatedResponse<Event>> {
return new EventsApi(DEFAULT_CONFIG).eventsEventsList(await this.defaultEndpointConfig());
}
columns(): TableColumn[] {
protected columns(): TableColumn[] {
return [
new TableColumn(msg("Action"), "action"),
new TableColumn(msg("User"), "user"),
@@ -70,7 +73,7 @@ export class EventListPage extends WithLicenseSummary(TablePage<Event>) {
];
}
renderSectionBefore(): TemplateResult {
protected override renderSectionBefore(): TemplateResult {
if (this.hasEnterpriseLicense) {
return html`<div
class="pf-l-grid pf-m-gutter pf-c-page__main-section pf-m-no-padding-bottom"
@@ -104,7 +107,7 @@ export class EventListPage extends WithLicenseSummary(TablePage<Event>) {
</div>`;
}
row(item: EventWithContext): SlottedTemplateResult[] {
protected row(item: EventWithContext): SlottedTemplateResult[] {
return [
html`<div>${actionToLabel(item.action)}</div>
<small>${item.app}</small>`,
@@ -122,7 +125,7 @@ export class EventListPage extends WithLicenseSummary(TablePage<Event>) {
];
}
renderExpanded(item: Event): TemplateResult {
protected override renderExpanded(item: Event): TemplateResult {
return html` <td role="cell" colspan="5">
<div class="pf-c-table__expandable-row-content">
<ak-event-info .event=${item as EventWithContext}></ak-event-info>

View File

@@ -28,7 +28,7 @@ import OL from "ol/ol.css";
@customElement("ak-map")
export class Map extends OlMap {
public render() {
public override render() {
return html`
<style>
${OL}
@@ -57,18 +57,18 @@ export class Map extends OlMap {
@customElement("ak-events-map")
export class EventMap extends AKElement {
@property({ attribute: false })
events?: PaginatedResponse<Event>;
public events?: PaginatedResponse<Event>;
@query("ol-layer-vector")
vectorLayer?: OlLayerVector;
protected vectorLayer?: OlLayerVector;
@query("ak-map")
map?: Map;
protected map?: Map;
@property({ type: Number })
zoomPaddingPx = 100;
public zoomPaddingPx = 100;
static styles: CSSResult[] = [
public static override styles: CSSResult[] = [
PFBase,
PFCard,
css`
@@ -82,7 +82,7 @@ export class EventMap extends AKElement {
`,
];
updated(_changedProperties: PropertyValues<this>): void {
public override updated(_changedProperties: PropertyValues<this>): void {
if (!_changedProperties.has("events")) {
return;
}
@@ -141,7 +141,7 @@ export class EventMap extends AKElement {
});
}
render(): TemplateResult {
public override render(): TemplateResult {
return html`<div class="pf-c-card">
<ak-map>
<ol-select

View File

@@ -26,26 +26,33 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
@customElement("ak-event-view")
export class EventViewPage extends AKElement {
@property({ type: String })
eventID?: string;
public eventID?: string;
@state()
event!: EventWithContext;
protected event!: EventWithContext;
static styles: CSSResult[] = [PFBase, PFGrid, PFDescriptionList, PFPage, PFContent, PFCard];
public static override styles: CSSResult[] = [
PFBase,
PFGrid,
PFDescriptionList,
PFPage,
PFContent,
PFCard,
];
fetchEvent(eventUuid: string) {
protected fetchEvent(eventUuid: string) {
new EventsApi(DEFAULT_CONFIG).eventsEventsRetrieve({ eventUuid }).then((ev) => {
this.event = ev as EventWithContext;
});
}
willUpdate(changedProperties: PropertyValues<this>) {
public override willUpdate(changedProperties: PropertyValues<this>) {
if (changedProperties.has("eventID") && this.eventID) {
this.fetchEvent(this.eventID);
}
}
render(): TemplateResult {
public override render(): TemplateResult {
if (!this.event) {
return html`<ak-page-header icon="pf-icon pf-icon-catalog" header=${msg("Loading")}>
</ak-page-header> `;

View File

@@ -14,18 +14,18 @@ import PFCard from "@patternfly/patternfly/components/Card/card.css";
@customElement("ak-events-volume-chart")
export class EventVolumeChart extends EventChart {
@property({ attribute: "with-map", type: Boolean })
withMap = false;
public withMap = false;
_query?: EventsEventsListRequest;
#query?: EventsEventsListRequest;
@property({ attribute: false })
set query(value: EventsEventsListRequest | undefined) {
if (JSON.stringify(value) !== JSON.stringify(this._query)) return;
this._query = value;
public set query(value: EventsEventsListRequest | undefined) {
if (JSON.stringify(value) !== JSON.stringify(this.#query)) return;
this.#query = value;
this.refreshHandler();
}
static styles: CSSResult[] = [
public static override styles: CSSResult[] = [
...super.styles,
PFCard,
css`
@@ -38,20 +38,20 @@ export class EventVolumeChart extends EventChart {
`,
];
apiRequest(): Promise<EventVolume[]> {
protected apiRequest(): Promise<EventVolume[]> {
return new EventsApi(DEFAULT_CONFIG).eventsEventsVolumeList({
historyDays: 7,
...this._query,
...this.#query,
});
}
getChartData(data: EventVolume[]): ChartData {
protected getChartData(data: EventVolume[]): ChartData {
return this.eventVolume(data, {
padToDays: 7,
});
}
render(): TemplateResult {
public override render(): TemplateResult {
return html`<div class="pf-c-card">
<div class="pf-c-card__body">${super.render()}</div>
</div>`;

View File

@@ -27,27 +27,27 @@ import { ifDefined } from "lit/directives/if-defined.js";
@customElement("ak-event-rule-form")
export class RuleForm extends ModelForm<NotificationRule, string> {
eventTransports?: PaginatedNotificationTransportList;
protected eventTransports?: PaginatedNotificationTransportList;
loadInstance(pk: string): Promise<NotificationRule> {
protected loadInstance(pk: string): Promise<NotificationRule> {
return new EventsApi(DEFAULT_CONFIG).eventsRulesRetrieve({
pbmUuid: pk,
});
}
async load(): Promise<void> {
public override async load(): Promise<void> {
this.eventTransports = await new EventsApi(DEFAULT_CONFIG).eventsTransportsList({
ordering: "name",
});
}
getSuccessMessage(): string {
public override getSuccessMessage(): string {
return this.instance
? msg("Successfully updated rule.")
: msg("Successfully created rule.");
}
async send(data: NotificationRule): Promise<NotificationRule> {
protected async send(data: NotificationRule): Promise<NotificationRule> {
if (this.instance) {
return new EventsApi(DEFAULT_CONFIG).eventsRulesUpdate({
pbmUuid: this.instance.pk || "",
@@ -59,7 +59,7 @@ export class RuleForm extends ModelForm<NotificationRule, string> {
});
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
<input
type="text"

View File

@@ -27,33 +27,36 @@ import { customElement, property } from "lit/decorators.js";
@customElement("ak-event-rule-list")
export class RuleListPage extends TablePage<NotificationRule> {
expandable = true;
checkbox = true;
clearOnRefresh = true;
public override expandable = true;
public override checkbox = true;
public override clearOnRefresh = true;
searchEnabled(): boolean {
protected override searchEnabled(): boolean {
return true;
}
pageTitle(): string {
protected pageTitle(): string {
return msg("Notification Rules");
}
pageDescription(): string {
protected pageDescription(): string {
return msg(
"Send notifications whenever a specific Event is created and matched by policies.",
);
}
pageIcon(): string {
protected pageIcon(): string {
return "pf-icon pf-icon-attention-bell";
}
@property()
order = "name";
public override order = "name";
async apiEndpoint(): Promise<PaginatedResponse<NotificationRule>> {
protected async apiEndpoint(): Promise<PaginatedResponse<NotificationRule>> {
return new EventsApi(DEFAULT_CONFIG).eventsRulesList(await this.defaultEndpointConfig());
}
columns(): TableColumn[] {
protected columns(): TableColumn[] {
return [
new TableColumn(msg("Enabled")),
new TableColumn(msg("Name"), "name"),
@@ -63,7 +66,7 @@ export class RuleListPage extends TablePage<NotificationRule> {
];
}
renderToolbarSelected(): TemplateResult {
protected override renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk
objectLabel=${msg("Notification rule(s)")}
@@ -85,7 +88,7 @@ export class RuleListPage extends TablePage<NotificationRule> {
</ak-forms-delete-bulk>`;
}
row(item: NotificationRule): TemplateResult[] {
protected row(item: NotificationRule): TemplateResult[] {
const enabled = !!item.destinationGroupObj || item.destinationEventUser;
return [
html`<ak-status-label type="warning" ?good=${enabled}></ak-status-label>`,
@@ -115,7 +118,7 @@ export class RuleListPage extends TablePage<NotificationRule> {
];
}
renderObjectCreate(): TemplateResult {
protected override renderObjectCreate(): TemplateResult {
return html`
<ak-forms-modal>
<span slot="submit"> ${msg("Create")} </span>
@@ -126,7 +129,7 @@ export class RuleListPage extends TablePage<NotificationRule> {
`;
}
renderExpanded(item: NotificationRule): TemplateResult {
protected override renderExpanded(item: NotificationRule): TemplateResult {
const [appLabel, modelName] = ModelEnum.AuthentikEventsNotificationrule.split(".");
return html` <td role="cell" colspan="4">
<div class="pf-c-table__expandable-row-content">

View File

@@ -23,7 +23,7 @@ import { ifDefined } from "lit/directives/if-defined.js";
@customElement("ak-event-transport-form")
export class TransportForm extends ModelForm<NotificationTransport, string> {
loadInstance(pk: string): Promise<NotificationTransport> {
protected loadInstance(pk: string): Promise<NotificationTransport> {
return new EventsApi(DEFAULT_CONFIG)
.eventsTransportsRetrieve({
uuid: pk,
@@ -35,15 +35,15 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
}
@property({ type: Boolean })
showWebhook = false;
public showWebhook = false;
getSuccessMessage(): string {
public override getSuccessMessage(): string {
return this.instance
? msg("Successfully updated transport.")
: msg("Successfully created transport.");
}
async send(data: NotificationTransport): Promise<NotificationTransport> {
protected async send(data: NotificationTransport): Promise<NotificationTransport> {
if (this.instance) {
return new EventsApi(DEFAULT_CONFIG).eventsTransportsUpdate({
uuid: this.instance.pk || "",
@@ -55,7 +55,7 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
});
}
onModeChange(mode: string | undefined): void {
protected onModeChange(mode: string | undefined): void {
if (
mode === NotificationTransportModeEnum.Webhook ||
mode === NotificationTransportModeEnum.WebhookSlack
@@ -66,7 +66,7 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
}
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
<input
type="text"

View File

@@ -25,33 +25,36 @@ import { customElement, property } from "lit/decorators.js";
@customElement("ak-event-transport-list")
export class TransportListPage extends TablePage<NotificationTransport> {
searchEnabled(): boolean {
protected override searchEnabled(): boolean {
return true;
}
pageTitle(): string {
protected pageTitle(): string {
return msg("Notification Transports");
}
pageDescription(): string {
protected pageDescription(): string {
return msg("Define how notifications are sent to users, like Email or Webhook.");
}
pageIcon(): string {
protected pageIcon(): string {
return "pf-icon pf-icon-export";
}
checkbox = true;
clearOnRefresh = true;
expandable = true;
public override checkbox = true;
public override clearOnRefresh = true;
public override expandable = true;
@property()
order = "name";
public override order = "name";
async apiEndpoint(): Promise<PaginatedResponse<NotificationTransport>> {
protected async apiEndpoint(): Promise<PaginatedResponse<NotificationTransport>> {
return new EventsApi(DEFAULT_CONFIG).eventsTransportsList(
await this.defaultEndpointConfig(),
);
}
columns(): TableColumn[] {
protected columns(): TableColumn[] {
return [
new TableColumn(msg("Name"), "name"),
new TableColumn(msg("Mode"), "mode"),
@@ -59,7 +62,7 @@ export class TransportListPage extends TablePage<NotificationTransport> {
];
}
renderToolbarSelected(): TemplateResult {
protected override renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk
objectLabel=${msg("Notification transport(s)")}
@@ -81,7 +84,7 @@ export class TransportListPage extends TablePage<NotificationTransport> {
</ak-forms-delete-bulk>`;
}
row(item: NotificationTransport): TemplateResult[] {
protected row(item: NotificationTransport): TemplateResult[] {
return [
html`${item.name}`,
html`${item.modeVerbose}`,
@@ -117,7 +120,7 @@ export class TransportListPage extends TablePage<NotificationTransport> {
];
}
renderExpanded(item: NotificationTransport): TemplateResult {
protected override renderExpanded(item: NotificationTransport): TemplateResult {
const [appLabel, modelName] = ModelEnum.AuthentikEventsNotificationtransport.split(".");
return html`<td role="cell" colspan="5">
<div class="pf-c-table__expandable-row-content">
@@ -141,7 +144,7 @@ export class TransportListPage extends TablePage<NotificationTransport> {
</td>`;
}
renderObjectCreate(): TemplateResult {
protected override renderObjectCreate(): TemplateResult {
return html`
<ak-forms-modal>
<span slot="submit"> ${msg("Create")} </span>

View File

@@ -24,23 +24,23 @@ import { ifDefined } from "lit/directives/if-defined.js";
@customElement("ak-bound-stages-list")
export class BoundStagesList extends Table<FlowStageBinding> {
expandable = true;
checkbox = true;
clearOnRefresh = true;
public override expandable = true;
public override checkbox = true;
public override clearOnRefresh = true;
order = "order";
public override order = "order";
@property()
target?: string;
public target?: string;
async apiEndpoint(): Promise<PaginatedResponse<FlowStageBinding>> {
protected async apiEndpoint(): Promise<PaginatedResponse<FlowStageBinding>> {
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsList({
...(await this.defaultEndpointConfig()),
target: this.target || "",
});
}
columns(): TableColumn[] {
protected columns(): TableColumn[] {
return [
new TableColumn(msg("Order"), "order"),
new TableColumn(msg("Name"), "stage__name"),
@@ -49,7 +49,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
];
}
renderToolbarSelected(): TemplateResult {
protected override renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk
objectLabel=${msg("Stage binding(s)")}
@@ -77,7 +77,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
</ak-forms-delete-bulk>`;
}
row(item: FlowStageBinding): TemplateResult[] {
protected row(item: FlowStageBinding): TemplateResult[] {
return [
html`<pre>${item.order}</pre>`,
html`${item.stageObj?.name}`,
@@ -114,7 +114,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
];
}
renderExpanded(item: FlowStageBinding): TemplateResult {
protected override renderExpanded(item: FlowStageBinding): TemplateResult {
return html` <td></td>
<td role="cell" colspan="4">
<div class="pf-c-table__expandable-row-content">
@@ -134,7 +134,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
</td>`;
}
renderEmpty(): TemplateResult {
protected override renderEmpty(): TemplateResult {
return super.renderEmpty(
html`<ak-empty-state icon="pf-icon-module">
<span>${msg("No Stages bound")}</span>
@@ -159,7 +159,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
);
}
renderToolbar(): TemplateResult {
protected override renderToolbar(): TemplateResult {
return html`
<ak-stage-wizard
createText=${msg("Create and bind Stage")}

View File

@@ -11,9 +11,9 @@ import { customElement, property } from "lit/decorators.js";
@customElement("ak-flow-diagram")
export class FlowDiagram extends Diagram {
@property()
flowSlug?: string;
public flowSlug?: string;
refreshHandler = (): void => {
protected override refreshHandler = (): void => {
this.diagram = undefined;
new FlowsApi(DEFAULT_CONFIG)
.flowsInstancesDiagramRetrieve({

View File

@@ -27,7 +27,7 @@ import { ifDefined } from "lit/directives/if-defined.js";
@customElement("ak-flow-form")
export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
async loadInstance(pk: string): Promise<Flow> {
protected async loadInstance(pk: string): Promise<Flow> {
const flow = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesRetrieve({
slug: pk,
});
@@ -35,16 +35,16 @@ export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
return flow;
}
getSuccessMessage(): string {
public override getSuccessMessage(): string {
return this.instance
? msg("Successfully updated flow.")
: msg("Successfully created flow.");
}
@property({ type: Boolean })
clearBackground = false;
public clearBackground = false;
async send(data: Flow): Promise<void | Flow> {
protected async send(data: Flow): Promise<void | Flow> {
let flow: Flow;
if (this.instance) {
flow = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesUpdate({
@@ -77,7 +77,7 @@ export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
return flow;
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
<input
type="text"

View File

@@ -18,15 +18,15 @@ import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList
@customElement("ak-flow-import-form")
export class FlowImportForm extends Form<Flow> {
@state()
result?: FlowImportResult;
protected result?: FlowImportResult;
getSuccessMessage(): string {
public override getSuccessMessage(): string {
return msg("Successfully imported flow.");
}
static styles: CSSResult[] = [...super.styles, PFDescriptionList];
public static override styles: CSSResult[] = [...super.styles, PFDescriptionList];
async send(): Promise<FlowImportResult> {
protected async send(): Promise<FlowImportResult> {
const file = this.files().get("flow");
if (!file) {
throw new SentryIgnoredError("No form data");
@@ -41,7 +41,7 @@ export class FlowImportForm extends Form<Flow> {
return result;
}
renderResult(): TemplateResult {
protected renderResult(): TemplateResult {
return html`
<ak-form-element-horizontal label=${msg("Successful")}>
<div class="pf-c-form__group-label">
@@ -64,7 +64,7 @@ export class FlowImportForm extends Form<Flow> {
`;
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
return html`<ak-form-element-horizontal label=${msg("Flow")} name="flow">
<input type="file" value="" class="pf-c-form-control" />
<p class="pf-c-form__helper-text">

View File

@@ -22,38 +22,41 @@ import { customElement, property } from "lit/decorators.js";
@customElement("ak-flow-list")
export class FlowListPage extends TablePage<Flow> {
searchEnabled(): boolean {
protected override searchEnabled(): boolean {
return true;
}
pageTitle(): string {
protected pageTitle(): string {
return msg("Flows");
}
pageDescription(): string {
protected pageDescription(): string {
return msg(
"Flows describe a chain of Stages to authenticate, enroll or recover a user. Stages are chosen based on policies applied to them.",
);
}
pageIcon(): string {
protected pageIcon(): string {
return "pf-icon pf-icon-process-automation";
}
checkbox = true;
clearOnRefresh = true;
public override checkbox = true;
public override clearOnRefresh = true;
@property()
order = "slug";
public override order = "slug";
async apiEndpoint(): Promise<PaginatedResponse<Flow>> {
protected async apiEndpoint(): Promise<PaginatedResponse<Flow>> {
return new FlowsApi(DEFAULT_CONFIG).flowsInstancesList(await this.defaultEndpointConfig());
}
groupBy(items: Flow[]): [string, Flow[]][] {
public override groupBy(items: Flow[]): [string, Flow[]][] {
return groupBy(items, (flow) => {
return DesignationToLabel(flow.designation);
});
}
columns(): TableColumn[] {
protected columns(): TableColumn[] {
return [
new TableColumn(msg("Identifier"), "slug"),
new TableColumn(msg("Name"), "name"),
@@ -63,7 +66,7 @@ export class FlowListPage extends TablePage<Flow> {
];
}
renderToolbarSelected(): TemplateResult {
protected override renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk
objectLabel=${msg("Flow(s)")}
@@ -85,7 +88,7 @@ export class FlowListPage extends TablePage<Flow> {
</ak-forms-delete-bulk>`;
}
row(item: Flow): TemplateResult[] {
protected row(item: Flow): TemplateResult[] {
return [
html`<div>
<a href="#/flow/flows/${item.slug}">
@@ -127,7 +130,7 @@ export class FlowListPage extends TablePage<Flow> {
];
}
renderObjectCreate(): TemplateResult {
protected override renderObjectCreate(): TemplateResult {
return html`
<ak-forms-modal>
<span slot="submit"> ${msg("Create")} </span>
@@ -144,7 +147,7 @@ export class FlowListPage extends TablePage<Flow> {
`;
}
renderToolbar(): TemplateResult {
protected override renderToolbar(): TemplateResult {
return html`
${super.renderToolbar()}
<ak-forms-confirm

View File

@@ -32,12 +32,12 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
@customElement("ak-flow-view")
export class FlowViewPage extends AKElement {
@property({ type: String })
flowSlug?: string;
public flowSlug?: string;
@state()
flow!: Flow;
protected flow!: Flow;
static styles: CSSResult[] = [
public static override styles: CSSResult[] = [
PFBase,
PFPage,
PFDescriptionList,
@@ -55,19 +55,19 @@ export class FlowViewPage extends AKElement {
`,
];
fetchFlow(slug: string) {
new FlowsApi(DEFAULT_CONFIG).flowsInstancesRetrieve({ slug }).then((flow) => {
protected fetchFlow(slug: string) {
return new FlowsApi(DEFAULT_CONFIG).flowsInstancesRetrieve({ slug }).then((flow) => {
this.flow = flow;
});
}
willUpdate(changedProperties: PropertyValues<this>) {
public override willUpdate(changedProperties: PropertyValues<this>) {
if (changedProperties.has("flowSlug") && this.flowSlug) {
this.fetchFlow(this.flowSlug);
}
}
render(): TemplateResult {
public override render(): TemplateResult {
if (!this.flow) {
return html``;
}

View File

@@ -24,7 +24,7 @@ import { customElement, property, state } from "lit/decorators.js";
@customElement("ak-stage-binding-form")
export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
async loadInstance(pk: string): Promise<FlowStageBinding> {
protected async loadInstance(pk: string): Promise<FlowStageBinding> {
const binding = await new FlowsApi(DEFAULT_CONFIG).flowsBindingsRetrieve({
fsbUuid: pk,
});
@@ -33,19 +33,19 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
}
@property()
targetPk?: string;
public targetPk?: string;
@state()
defaultOrder = 0;
protected defaultOrder = 0;
getSuccessMessage(): string {
public override getSuccessMessage(): string {
if (this.instance?.pk) {
return msg("Successfully updated binding.");
}
return msg("Successfully created binding.");
}
send(data: FlowStageBinding): Promise<unknown> {
protected send(data: FlowStageBinding): Promise<unknown> {
if (this.instance?.pk) {
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsPartialUpdate({
fsbUuid: this.instance.pk,
@@ -60,7 +60,7 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
});
}
async getOrder(): Promise<number> {
protected async getOrder(): Promise<number> {
if (this.instance?.pk) {
return this.instance.order;
}
@@ -74,7 +74,7 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
return Math.max(...orders) + 1;
}
renderTarget(): TemplateResult {
protected renderTarget(): TemplateResult {
if (this.instance?.target || this.targetPk) {
return html``;
}
@@ -87,7 +87,7 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
</ak-form-element-horizontal>`;
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
return html` ${this.renderTarget()}
<ak-form-element-horizontal label=${msg("Stage")} required name="stage">
<ak-search-select

View File

@@ -27,7 +27,7 @@ export function rbacRolePair(item: Role): DualSelectPair {
@customElement("ak-group-form")
export class GroupForm extends ModelForm<Group, string> {
static styles: CSSResult[] = [
public static override styles: CSSResult[] = [
...super.styles,
css`
.pf-c-button.pf-m-control {
@@ -39,20 +39,20 @@ export class GroupForm extends ModelForm<Group, string> {
`,
];
loadInstance(pk: string): Promise<Group> {
protected loadInstance(pk: string): Promise<Group> {
return new CoreApi(DEFAULT_CONFIG).coreGroupsRetrieve({
groupUuid: pk,
includeUsers: false,
});
}
getSuccessMessage(): string {
public override getSuccessMessage(): string {
return this.instance
? msg("Successfully updated group.")
: msg("Successfully created group.");
}
async send(data: Group): Promise<Group> {
protected async send(data: Group): Promise<Group> {
if (this.instance?.pk) {
return new CoreApi(DEFAULT_CONFIG).coreGroupsPartialUpdate({
groupUuid: this.instance.pk,
@@ -65,7 +65,7 @@ export class GroupForm extends ModelForm<Group, string> {
});
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
<input
type="text"

View File

@@ -18,32 +18,36 @@ import { customElement, property } from "lit/decorators.js";
@customElement("ak-group-list")
export class GroupListPage extends TablePage<Group> {
checkbox = true;
clearOnRefresh = true;
searchEnabled(): boolean {
public override checkbox = true;
public override clearOnRefresh = true;
protected override searchEnabled(): boolean {
return true;
}
pageTitle(): string {
protected pageTitle(): string {
return msg("Groups");
}
pageDescription(): string {
protected pageDescription(): string {
return msg("Group users together and give them permissions based on the membership.");
}
pageIcon(): string {
protected pageIcon(): string {
return "pf-icon pf-icon-users";
}
@property()
order = "name";
public override order = "name";
async apiEndpoint(): Promise<PaginatedResponse<Group>> {
protected async apiEndpoint(): Promise<PaginatedResponse<Group>> {
return new CoreApi(DEFAULT_CONFIG).coreGroupsList({
...(await this.defaultEndpointConfig()),
includeUsers: false,
});
}
columns(): TableColumn[] {
protected columns(): TableColumn[] {
return [
new TableColumn(msg("Name"), "name"),
new TableColumn(msg("Parent"), "parent"),
@@ -53,7 +57,7 @@ export class GroupListPage extends TablePage<Group> {
];
}
renderToolbarSelected(): TemplateResult {
protected override renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk
objectLabel=${msg("Group(s)")}
@@ -75,7 +79,7 @@ export class GroupListPage extends TablePage<Group> {
</ak-forms-delete-bulk>`;
}
row(item: Group): TemplateResult[] {
protected row(item: Group): TemplateResult[] {
return [
html`<a href="#/identity/groups/${item.pk}">${item.name}</a>`,
html`${item.parentName || msg("-")}`,
@@ -94,7 +98,7 @@ export class GroupListPage extends TablePage<Group> {
];
}
renderObjectCreate(): TemplateResult {
protected override renderObjectCreate(): TemplateResult {
return html`
<ak-forms-modal>
<span slot="submit"> ${msg("Create")} </span>

View File

@@ -35,7 +35,7 @@ import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css";
@customElement("ak-group-view")
export class GroupViewPage extends AKElement {
@property({ type: String })
set groupId(id: string) {
public set groupId(id: string) {
new CoreApi(DEFAULT_CONFIG)
.coreGroupsRetrieve({
groupUuid: id,
@@ -47,9 +47,9 @@ export class GroupViewPage extends AKElement {
}
@property({ attribute: false })
group?: Group;
public group?: Group;
static styles: CSSResult[] = [
public static override styles: CSSResult[] = [
PFBase,
PFPage,
PFButton,
@@ -62,7 +62,7 @@ export class GroupViewPage extends AKElement {
PFSizing,
];
constructor() {
public constructor() {
super();
this.addEventListener(EVENT_REFRESH, () => {
if (!this.group?.pk) return;
@@ -70,7 +70,7 @@ export class GroupViewPage extends AKElement {
});
}
render(): TemplateResult {
public override render(): TemplateResult {
return html`<ak-page-header
icon="pf-icon pf-icon-users"
header=${msg(str`Group ${this.group?.name || ""}`)}
@@ -80,7 +80,7 @@ export class GroupViewPage extends AKElement {
${this.renderBody()}`;
}
renderBody(): TemplateResult {
protected renderBody(): TemplateResult {
if (!this.group) {
return html``;
}

View File

@@ -22,7 +22,7 @@ type UserListRequestFilter = Partial<Pick<CoreUsersListRequest, "isActive">>;
@customElement("ak-group-member-select-table")
export class MemberSelectTable extends TableModal<User> {
static styles = [
public static override styles = [
...super.styles,
css`
.show-disabled-toggle-group {
@@ -31,24 +31,24 @@ export class MemberSelectTable extends TableModal<User> {
`,
];
checkbox = true;
checkboxChip = true;
public override checkbox = true;
public override checkboxChip = true;
searchEnabled(): boolean {
protected override searchEnabled(): boolean {
return true;
}
@property()
confirm!: (selectedItems: User[]) => Promise<unknown>;
public confirm!: (selectedItems: User[]) => Promise<unknown>;
userListFilter: UserListFilter = "active";
protected userListFilter: UserListFilter = "active";
order = "username";
public override order = "username";
// The `userListRequestFilter` clause is necessary because the back-end for searches is
// tri-state: `isActive: true` will only show active users, `isActive: false` will show only
// inactive users; only when it's _missing_ will you get all users.
async apiEndpoint(): Promise<PaginatedResponse<User>> {
protected async apiEndpoint(): Promise<PaginatedResponse<User>> {
const userListRequestFilter: UserListRequestFilter = match(this.userListFilter)
.with("all", () => ({}))
.with("active", () => ({ isActive: true }))
@@ -61,7 +61,7 @@ export class MemberSelectTable extends TableModal<User> {
});
}
columns(): TableColumn[] {
protected columns(): TableColumn[] {
return [
new TableColumn(msg("Name"), "username"),
new TableColumn(msg("Active"), "is_active"),
@@ -69,7 +69,7 @@ export class MemberSelectTable extends TableModal<User> {
];
}
renderToolbarAfter() {
protected override renderToolbarAfter() {
const toggleShowDisabledUsers = () => {
this.userListFilter = this.userListFilter === "all" ? "active" : "all";
this.page = 1;
@@ -99,7 +99,7 @@ export class MemberSelectTable extends TableModal<User> {
</div>`;
}
row(item: User): TemplateResult[] {
protected row(item: User): TemplateResult[] {
return [
html`<div>${item.username}</div>
<small>${item.name}</small>`,
@@ -111,11 +111,11 @@ export class MemberSelectTable extends TableModal<User> {
];
}
renderSelectedChip(item: User): TemplateResult {
protected override renderSelectedChip(item: User): TemplateResult {
return html`${item.username}`;
}
renderModalInner(): TemplateResult {
protected override renderModalInner(): TemplateResult {
return html`<section class="pf-c-modal-box__header pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1 class="pf-c-title pf-m-2xl">${msg("Select users to add")}</h1>

View File

@@ -22,16 +22,16 @@ import { ifDefined } from "lit/directives/if-defined.js";
@customElement("ak-group-related-add")
export class RelatedGroupAdd extends Form<{ groups: string[] }> {
@property({ attribute: false })
user?: User;
public user?: User;
@state()
groupsToAdd: Group[] = [];
protected groupsToAdd: Group[] = [];
getSuccessMessage(): string {
public override getSuccessMessage(): string {
return msg("Successfully added user to group(s).");
}
async send(data: { groups: string[] }): Promise<unknown> {
protected async send(data: { groups: string[] }): Promise<unknown> {
await Promise.all(
data.groups.map((group) => {
return new CoreApi(DEFAULT_CONFIG).coreGroupsAddUserCreate({
@@ -45,7 +45,7 @@ export class RelatedGroupAdd extends Form<{ groups: string[] }> {
return data;
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
return html`<ak-form-element-horizontal label=${msg("Groups to add")} name="groups">
<div class="pf-c-input-group">
<ak-user-group-select-table
@@ -85,19 +85,20 @@ export class RelatedGroupAdd extends Form<{ groups: string[] }> {
@customElement("ak-group-related-list")
export class RelatedGroupList extends Table<Group> {
checkbox = true;
clearOnRefresh = true;
searchEnabled(): boolean {
public override checkbox = true;
public override clearOnRefresh = true;
protected override searchEnabled(): boolean {
return true;
}
@property()
order = "name";
public override order = "name";
@property({ attribute: false })
targetUser?: User;
public targetUser?: User;
async apiEndpoint(): Promise<PaginatedResponse<Group>> {
protected async apiEndpoint(): Promise<PaginatedResponse<Group>> {
return new CoreApi(DEFAULT_CONFIG).coreGroupsList({
...(await this.defaultEndpointConfig()),
membersByPk: this.targetUser ? [this.targetUser.pk] : [],
@@ -105,7 +106,7 @@ export class RelatedGroupList extends Table<Group> {
});
}
columns(): TableColumn[] {
protected columns(): TableColumn[] {
return [
new TableColumn(msg("Name"), "name"),
new TableColumn(msg("Parent"), "parent"),
@@ -114,7 +115,7 @@ export class RelatedGroupList extends Table<Group> {
];
}
renderToolbarSelected(): TemplateResult {
protected override renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk
objectLabel=${msg("Group(s)")}
@@ -140,7 +141,7 @@ export class RelatedGroupList extends Table<Group> {
</ak-forms-delete-bulk>`;
}
row(item: Group): TemplateResult[] {
protected row(item: Group): TemplateResult[] {
return [
html`<a href="#/identity/groups/${item.pk}">${item.name}</a>`,
html`${item.parentName || msg("-")}`,
@@ -158,7 +159,7 @@ export class RelatedGroupList extends Table<Group> {
];
}
renderToolbar(): TemplateResult {
protected override renderToolbar(): TemplateResult {
return html`
${this.targetUser
? html`<ak-forms-modal>

View File

@@ -41,16 +41,16 @@ import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList
@customElement("ak-user-related-add")
export class RelatedUserAdd extends Form<{ users: number[] }> {
@property({ attribute: false })
group?: Group;
public group?: Group;
@state()
usersToAdd: User[] = [];
protected usersToAdd: User[] = [];
getSuccessMessage(): string {
public override getSuccessMessage(): string {
return msg("Successfully added user(s).");
}
async send(data: { users: number[] }): Promise<{ users: number[] }> {
protected async send(data: { users: number[] }): Promise<{ users: number[] }> {
await Promise.all(
data.users.map((user) => {
return new CoreApi(DEFAULT_CONFIG).coreGroupsAddUserCreate({
@@ -64,7 +64,7 @@ export class RelatedUserAdd extends Form<{ users: number[] }> {
return data;
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
return html` <ak-form-element-horizontal label=${msg("Users to add")} name="users">
<div class="pf-c-input-group">
<ak-group-member-select-table
@@ -104,29 +104,29 @@ export class RelatedUserAdd extends Form<{ users: number[] }> {
@customElement("ak-user-related-list")
export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Table<User>)) {
expandable = true;
checkbox = true;
clearOnRefresh = true;
public override expandable = true;
public override checkbox = true;
public override clearOnRefresh = true;
searchEnabled(): boolean {
protected override searchEnabled(): boolean {
return true;
}
@property({ attribute: false })
targetGroup?: Group;
public targetGroup?: Group;
@property()
order = "last_login";
public override order = "last_login";
@property({ type: Boolean })
hideServiceAccounts = getURLParam<boolean>("hideServiceAccounts", true);
public hideServiceAccounts = getURLParam<boolean>("hideServiceAccounts", true);
@state()
me?: SessionUser;
protected me?: SessionUser;
static styles: CSSResult[] = [...Table.styles, PFDescriptionList, PFAlert, PFBanner];
public static styles: CSSResult[] = [...Table.styles, PFDescriptionList, PFAlert, PFBanner];
async apiEndpoint(): Promise<PaginatedResponse<User>> {
protected async apiEndpoint(): Promise<PaginatedResponse<User>> {
const users = await new CoreApi(DEFAULT_CONFIG).coreUsersList({
...(await this.defaultEndpointConfig()),
groupsByPk: this.targetGroup ? [this.targetGroup.pk] : [],
@@ -139,7 +139,7 @@ export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Tabl
return users;
}
columns(): TableColumn[] {
protected columns(): TableColumn[] {
return [
new TableColumn(msg("Name"), "username"),
new TableColumn(msg("Active"), "is_active"),
@@ -148,7 +148,7 @@ export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Tabl
];
}
renderToolbarSelected(): TemplateResult {
protected override renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk
objectLabel=${msg("User(s)")}
@@ -180,7 +180,7 @@ export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Tabl
</ak-forms-delete-bulk>`;
}
row(item: User): TemplateResult[] {
protected row(item: User): TemplateResult[] {
const canImpersonate =
this.can(CapabilitiesEnum.CanImpersonate) && item.pk !== this.me?.user.pk;
return [
@@ -226,7 +226,7 @@ export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Tabl
];
}
renderExpanded(item: User): TemplateResult {
protected override renderExpanded(item: User): TemplateResult {
return html`<td role="cell" colspan="3">
<div class="pf-c-table__expandable-row-content">
<dl class="pf-c-description-list pf-m-horizontal">
@@ -374,7 +374,7 @@ export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Tabl
<td></td>`;
}
renderToolbar(): TemplateResult {
protected override renderToolbar(): TemplateResult {
return html`
${this.targetGroup
? html`<ak-forms-modal>
@@ -450,7 +450,7 @@ export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Tabl
`;
}
renderToolbarAfter(): TemplateResult {
protected override renderToolbarAfter(): TemplateResult {
return html`&nbsp;
<div class="pf-c-toolbar__group pf-m-filter-group">
<div class="pf-c-toolbar__item pf-m-search-filter">

View File

@@ -14,9 +14,9 @@ import { ifDefined } from "lit/directives/if-defined.js";
@customElement("ak-outpost-deployment-modal")
export class OutpostDeploymentModal extends ModalButton {
@property({ attribute: false })
outpost?: Outpost;
public outpost?: Outpost;
renderModalInner(): TemplateResult {
protected override renderModalInner(): TemplateResult {
return html`<div class="pf-c-modal-box__header">
<h1 class="pf-c-title pf-m-2xl">${msg("Outpost Deployment Info")}</h1>
</div>

View File

@@ -94,17 +94,17 @@ function providerProvider(type: OutpostTypeEnum): DataProvider {
@customElement("ak-outpost-form")
export class OutpostForm extends ModelForm<Outpost, string> {
@property()
type: OutpostTypeEnum = OutpostTypeEnum.Proxy;
public type: OutpostTypeEnum = OutpostTypeEnum.Proxy;
@property({ type: Boolean })
embedded = false;
public embedded = false;
@state()
providers: DataProvider = providerProvider(this.type);
protected providers: DataProvider = providerProvider(this.type);
defaultConfig?: OutpostDefaultConfig;
protected defaultConfig?: OutpostDefaultConfig;
async loadInstance(pk: string): Promise<Outpost> {
protected async loadInstance(pk: string): Promise<Outpost> {
const o = await new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesRetrieve({
uuid: pk,
});
@@ -113,20 +113,20 @@ export class OutpostForm extends ModelForm<Outpost, string> {
return o;
}
async load(): Promise<void> {
public override async load(): Promise<void> {
this.defaultConfig = await new OutpostsApi(
DEFAULT_CONFIG,
).outpostsInstancesDefaultSettingsRetrieve();
this.providers = providerProvider(this.type);
}
getSuccessMessage(): string {
public override getSuccessMessage(): string {
return this.instance
? msg("Successfully updated outpost.")
: msg("Successfully created outpost.");
}
async send(data: Outpost): Promise<Outpost> {
protected async send(data: Outpost): Promise<Outpost> {
if (this.instance) {
return new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesUpdate({
uuid: this.instance.pk || "",
@@ -138,7 +138,7 @@ export class OutpostForm extends ModelForm<Outpost, string> {
});
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
const typeOptions = [
[OutpostTypeEnum.Proxy, msg("Proxy")],
[OutpostTypeEnum.Ldap, msg("LDAP")],

View File

@@ -17,9 +17,9 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
@customElement("ak-outpost-health")
export class OutpostHealthElement extends AKElement {
@property({ attribute: false })
outpostHealth?: OutpostHealth;
public outpostHealth?: OutpostHealth;
static styles: CSSResult[] = [
public static override styles: CSSResult[] = [
PFBase,
PFDescriptionList,
css`
@@ -29,7 +29,7 @@ export class OutpostHealthElement extends AKElement {
`,
];
render(): TemplateResult {
public override render(): TemplateResult {
if (!this.outpostHealth) {
return html`<ak-spinner></ak-spinner>`;
}

View File

@@ -18,20 +18,20 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
@customElement("ak-outpost-health-simple")
export class OutpostHealthSimpleElement extends AKElement {
@property()
outpostId?: string;
public outpostId?: string;
@state()
outpostHealths: OutpostHealth[] = [];
protected outpostHealths: OutpostHealth[] = [];
@property({ attribute: false })
loaded = false;
public loaded = false;
@property({ attribute: false })
showVersion = true;
public showVersion = true;
static styles: CSSResult[] = [PFBase];
public static override styles: CSSResult[] = [PFBase];
constructor() {
public constructor() {
super();
window.addEventListener(EVENT_REFRESH, () => {
this.outpostHealths = [];
@@ -39,7 +39,7 @@ export class OutpostHealthSimpleElement extends AKElement {
});
}
firstUpdated(): void {
public override firstUpdated(): void {
if (!this.outpostId) return;
new OutpostsApi(DEFAULT_CONFIG)
.outpostsInstancesHealthList({
@@ -51,7 +51,7 @@ export class OutpostHealthSimpleElement extends AKElement {
});
}
render(): TemplateResult {
public override render(): TemplateResult {
if (!this.outpostId || !this.loaded) {
return html`<ak-spinner></ak-spinner>`;
}

View File

@@ -51,24 +51,27 @@ export function TypeToLabel(type?: OutpostTypeEnum): string {
@customElement("ak-outpost-list")
export class OutpostListPage extends TablePage<Outpost> {
expandable = true;
public override expandable = true;
pageTitle(): string {
protected pageTitle(): string {
return msg("Outposts");
}
pageDescription(): string | undefined {
protected pageDescription(): string | undefined {
return msg(
"Outposts are deployments of authentik components to support different environments and protocols, like reverse proxies.",
);
}
pageIcon(): string {
protected pageIcon(): string {
return "pf-icon pf-icon-zone";
}
searchEnabled(): boolean {
protected override searchEnabled(): boolean {
return true;
}
async apiEndpoint(): Promise<PaginatedResponse<Outpost>> {
protected async apiEndpoint(): Promise<PaginatedResponse<Outpost>> {
const outposts = await new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesList(
await this.defaultEndpointConfig(),
);
@@ -87,9 +90,9 @@ export class OutpostListPage extends TablePage<Outpost> {
}
@state()
health: { [key: string]: OutpostHealth[] } = {};
protected health: { [key: string]: OutpostHealth[] } = {};
columns(): TableColumn[] {
protected columns(): TableColumn[] {
return [
new TableColumn(msg("Name"), "name"),
new TableColumn(msg("Type"), "type"),
@@ -100,15 +103,15 @@ export class OutpostListPage extends TablePage<Outpost> {
];
}
static styles: CSSResult[] = [...super.styles, PFDescriptionList];
public static override styles: CSSResult[] = [...super.styles, PFDescriptionList];
checkbox = true;
clearOnRefresh = true;
public override checkbox = true;
public override clearOnRefresh = true;
@property()
order = "name";
public override order = "name";
row(item: Outpost): TemplateResult[] {
protected row(item: Outpost): TemplateResult[] {
return [
html`<div>${item.name}</div>
${item.config.authentik_host === ""
@@ -162,7 +165,7 @@ export class OutpostListPage extends TablePage<Outpost> {
];
}
renderExpanded(item: Outpost): TemplateResult {
protected override renderExpanded(item: Outpost): TemplateResult {
const [appLabel, modelName] = ModelEnum.AuthentikOutpostsOutpost.split(".");
return html`<td role="cell" colspan="7">
<div class="pf-c-table__expandable-row-content">
@@ -218,7 +221,7 @@ export class OutpostListPage extends TablePage<Outpost> {
</td>`;
}
renderToolbarSelected(): TemplateResult {
protected override renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk
objectLabel=${msg("Outpost(s)")}
@@ -240,7 +243,7 @@ export class OutpostListPage extends TablePage<Outpost> {
</ak-forms-delete-bulk>`;
}
renderObjectCreate(): TemplateResult {
protected override renderObjectCreate(): TemplateResult {
return html`
<ak-forms-modal>
<span slot="submit"> ${msg("Create")} </span>

View File

@@ -15,19 +15,19 @@ import { ifDefined } from "lit/directives/if-defined.js";
@customElement("ak-service-connection-docker-form")
export class ServiceConnectionDockerForm extends ModelForm<DockerServiceConnection, string> {
loadInstance(pk: string): Promise<DockerServiceConnection> {
protected loadInstance(pk: string): Promise<DockerServiceConnection> {
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsDockerRetrieve({
uuid: pk,
});
}
getSuccessMessage(): string {
public override getSuccessMessage(): string {
return this.instance
? msg("Successfully updated integration.")
: msg("Successfully created integration.");
}
async send(data: DockerServiceConnection): Promise<DockerServiceConnection> {
protected async send(data: DockerServiceConnection): Promise<DockerServiceConnection> {
if (this.instance) {
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsDockerUpdate({
uuid: this.instance.pk || "",
@@ -39,7 +39,7 @@ export class ServiceConnectionDockerForm extends ModelForm<DockerServiceConnecti
});
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
<input
type="text"

View File

@@ -20,19 +20,19 @@ export class ServiceConnectionKubernetesForm extends ModelForm<
KubernetesServiceConnection,
string
> {
loadInstance(pk: string): Promise<KubernetesServiceConnection> {
protected loadInstance(pk: string): Promise<KubernetesServiceConnection> {
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsKubernetesRetrieve({
uuid: pk,
});
}
getSuccessMessage(): string {
public override getSuccessMessage(): string {
return this.instance
? msg("Successfully updated integration.")
: msg("Successfully created integration.");
}
async send(data: KubernetesServiceConnection): Promise<KubernetesServiceConnection> {
protected async send(data: KubernetesServiceConnection): Promise<KubernetesServiceConnection> {
if (this.instance) {
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsKubernetesUpdate({
uuid: this.instance.pk || "",
@@ -44,7 +44,7 @@ export class ServiceConnectionKubernetesForm extends ModelForm<
});
}
renderForm(): TemplateResult {
protected override renderForm(): TemplateResult {
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
<input
type="text"

View File

@@ -27,26 +27,29 @@ import { ifDefined } from "lit/directives/if-defined.js";
@customElement("ak-outpost-service-connection-list")
export class OutpostServiceConnectionListPage extends TablePage<ServiceConnection> {
pageTitle(): string {
protected pageTitle(): string {
return msg("Outpost integrations");
}
pageDescription(): string | undefined {
protected pageDescription(): string | undefined {
return msg(
"Outpost integrations define how authentik connects to external platforms to manage and deploy Outposts.",
);
}
pageIcon(): string {
protected pageIcon(): string {
return "pf-icon pf-icon-integration";
}
searchEnabled(): boolean {
protected override searchEnabled(): boolean {
return true;
}
checkbox = true;
expandable = true;
clearOnRefresh = true;
public override checkbox = true;
public override expandable = true;
public override clearOnRefresh = true;
async apiEndpoint(): Promise<PaginatedResponse<ServiceConnection>> {
protected async apiEndpoint(): Promise<PaginatedResponse<ServiceConnection>> {
const connections = await new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllList(
await this.defaultEndpointConfig(),
);
@@ -65,9 +68,9 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio
}
@state()
state: { [key: string]: ServiceConnectionState } = {};
protected state: { [key: string]: ServiceConnectionState } = {};
columns(): TableColumn[] {
protected columns(): TableColumn[] {
return [
new TableColumn(msg("Name"), "name"),
new TableColumn(msg("Type")),
@@ -78,9 +81,9 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio
}
@property()
order = "name";
public override order = "name";
row(item: ServiceConnection): TemplateResult[] {
protected row(item: ServiceConnection): TemplateResult[] {
const itemState = this.state[item.pk];
return [
html`${item.name}`,
@@ -113,7 +116,7 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio
];
}
renderExpanded(item: ServiceConnection): TemplateResult {
protected override renderExpanded(item: ServiceConnection): TemplateResult {
const [appLabel, modelName] = item.metaModelName.split(".");
return html` <td role="cell" colspan="5">
<div class="pf-c-table__expandable-row-content">
@@ -153,7 +156,7 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio
</td>`;
}
renderToolbarSelected(): TemplateResult {
protected override renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk
objectLabel=${msg("Outpost integration(s)")}
@@ -175,7 +178,7 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio
</ak-forms-delete-bulk>`;
}
renderObjectCreate(): TemplateResult {
protected override renderObjectCreate(): TemplateResult {
return html`<ak-service-connection-wizard></ak-service-connection-wizard> `;
}
}

Some files were not shown because too many files have changed in this diff Show More