sources: add custom icon support (#4022)
* add source icon Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * add to oauth form Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * add to other browser sources Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * add migration, return icon in UI challenges Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * deduplicate file upload Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
import { UserMatchingModeToLabel } from "@goauthentik/admin/sources/oauth/utils";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
@ -9,11 +9,12 @@ import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
import { until } from "lit/directives/until.js";
|
||||
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
FlowsApi,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
OAuthSource,
|
||||
@ -57,6 +58,9 @@ export class OAuthSourceForm extends ModelForm<OAuthSource, string> {
|
||||
@property({ attribute: false })
|
||||
providerType: SourceType | null = null;
|
||||
|
||||
@state()
|
||||
clearIcon = false;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
if (this.instance) {
|
||||
return t`Successfully updated source.`;
|
||||
@ -65,18 +69,38 @@ export class OAuthSourceForm extends ModelForm<OAuthSource, string> {
|
||||
}
|
||||
}
|
||||
|
||||
send = (data: OAuthSource): Promise<OAuthSource> => {
|
||||
send = async (data: OAuthSource): Promise<OAuthSource> => {
|
||||
data.providerType = (this.providerType?.slug || "") as ProviderTypeEnum;
|
||||
if (this.instance?.slug) {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesOauthPartialUpdate({
|
||||
let source: OAuthSource;
|
||||
if (this.instance) {
|
||||
source = await new SourcesApi(DEFAULT_CONFIG).sourcesOauthPartialUpdate({
|
||||
slug: this.instance.slug,
|
||||
patchedOAuthSourceRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesOauthCreate({
|
||||
source = await new SourcesApi(DEFAULT_CONFIG).sourcesOauthCreate({
|
||||
oAuthSourceRequest: data as unknown as OAuthSourceRequest,
|
||||
});
|
||||
}
|
||||
const c = await config();
|
||||
if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) {
|
||||
const icon = this.getFormFiles()["icon"];
|
||||
if (icon || this.clearIcon) {
|
||||
await new SourcesApi(DEFAULT_CONFIG).sourcesAllSetIconCreate({
|
||||
slug: source.slug,
|
||||
file: icon,
|
||||
clear: this.clearIcon,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
await new SourcesApi(DEFAULT_CONFIG).sourcesAllSetIconUrlCreate({
|
||||
slug: source.slug,
|
||||
filePathRequest: {
|
||||
url: data.icon || "",
|
||||
},
|
||||
});
|
||||
}
|
||||
return source;
|
||||
};
|
||||
|
||||
renderUrlOptions(): TemplateResult {
|
||||
@ -282,6 +306,54 @@ export class OAuthSourceForm extends ModelForm<OAuthSource, string> {
|
||||
${t`Path template for users created. Use placeholders like \`%(slug)s\` to insert the source slug.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
${until(
|
||||
config().then((c) => {
|
||||
if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) {
|
||||
return html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
|
||||
<input type="file" value="" class="pf-c-form-control" />
|
||||
${this.instance?.icon
|
||||
? html`
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Currently set to:`} ${this.instance?.icon}
|
||||
</p>
|
||||
`
|
||||
: html``}
|
||||
</ak-form-element-horizontal>
|
||||
${this.instance?.icon
|
||||
? html`
|
||||
<ak-form-element-horizontal>
|
||||
<div class="pf-c-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
@change=${(ev: Event) => {
|
||||
const target = ev.target as HTMLInputElement;
|
||||
this.clearIcon = target.checked;
|
||||
}}
|
||||
/>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Clear icon`}
|
||||
</label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Delete currently set icon.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
`
|
||||
: html``}`;
|
||||
}
|
||||
return html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.icon, "")}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
}),
|
||||
)}
|
||||
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header"> ${t`Protocol settings`} </span>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { UserMatchingModeToLabel } from "@goauthentik/admin/sources/oauth/utils";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||
import { PlexAPIClient, PlexResource, popupCenterScreen } from "@goauthentik/common/helpers/plex";
|
||||
import { first, randomString } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
@ -9,11 +9,12 @@ import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
import { until } from "lit/directives/until.js";
|
||||
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
FlowsApi,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
PlexSource,
|
||||
@ -35,6 +36,9 @@ export class PlexSourceForm extends ModelForm<PlexSource, string> {
|
||||
});
|
||||
}
|
||||
|
||||
@state()
|
||||
clearIcon = false;
|
||||
|
||||
@property()
|
||||
plexToken?: string;
|
||||
|
||||
@ -55,18 +59,38 @@ export class PlexSourceForm extends ModelForm<PlexSource, string> {
|
||||
}
|
||||
}
|
||||
|
||||
send = (data: PlexSource): Promise<PlexSource> => {
|
||||
send = async (data: PlexSource): Promise<PlexSource> => {
|
||||
data.plexToken = this.plexToken || "";
|
||||
if (this.instance?.slug) {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesPlexUpdate({
|
||||
let source: PlexSource;
|
||||
if (this.instance) {
|
||||
source = await new SourcesApi(DEFAULT_CONFIG).sourcesPlexUpdate({
|
||||
slug: this.instance.slug,
|
||||
plexSourceRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesPlexCreate({
|
||||
source = await new SourcesApi(DEFAULT_CONFIG).sourcesPlexCreate({
|
||||
plexSourceRequest: data,
|
||||
});
|
||||
}
|
||||
const c = await config();
|
||||
if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) {
|
||||
const icon = this.getFormFiles()["icon"];
|
||||
if (icon || this.clearIcon) {
|
||||
await new SourcesApi(DEFAULT_CONFIG).sourcesAllSetIconCreate({
|
||||
slug: source.slug,
|
||||
file: icon,
|
||||
clear: this.clearIcon,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
await new SourcesApi(DEFAULT_CONFIG).sourcesAllSetIconUrlCreate({
|
||||
slug: source.slug,
|
||||
filePathRequest: {
|
||||
url: data.icon || "",
|
||||
},
|
||||
});
|
||||
}
|
||||
return source;
|
||||
};
|
||||
|
||||
async doAuth(): Promise<void> {
|
||||
@ -229,6 +253,54 @@ export class PlexSourceForm extends ModelForm<PlexSource, string> {
|
||||
${t`Path template for users created. Use placeholders like \`%(slug)s\` to insert the source slug.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
${until(
|
||||
config().then((c) => {
|
||||
if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) {
|
||||
return html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
|
||||
<input type="file" value="" class="pf-c-form-control" />
|
||||
${this.instance?.icon
|
||||
? html`
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Currently set to:`} ${this.instance?.icon}
|
||||
</p>
|
||||
`
|
||||
: html``}
|
||||
</ak-form-element-horizontal>
|
||||
${this.instance?.icon
|
||||
? html`
|
||||
<ak-form-element-horizontal>
|
||||
<div class="pf-c-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
@change=${(ev: Event) => {
|
||||
const target = ev.target as HTMLInputElement;
|
||||
this.clearIcon = target.checked;
|
||||
}}
|
||||
/>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Clear icon`}
|
||||
</label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Delete currently set icon.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
`
|
||||
: html``}`;
|
||||
}
|
||||
return html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.icon, "")}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
}),
|
||||
)}
|
||||
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header"> ${t`Protocol settings`} </span>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { UserMatchingModeToLabel } from "@goauthentik/admin/sources/oauth/utils";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
@ -9,12 +9,13 @@ import "@goauthentik/elements/utils/TimeDeltaHelp";
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
import { until } from "lit/directives/until.js";
|
||||
|
||||
import {
|
||||
BindingTypeEnum,
|
||||
CapabilitiesEnum,
|
||||
CryptoApi,
|
||||
DigestAlgorithmEnum,
|
||||
FlowsApi,
|
||||
@ -28,6 +29,9 @@ import {
|
||||
|
||||
@customElement("ak-source-saml-form")
|
||||
export class SAMLSourceForm extends ModelForm<SAMLSource, string> {
|
||||
@state()
|
||||
clearIcon = false;
|
||||
|
||||
loadInstance(pk: string): Promise<SAMLSource> {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesSamlRetrieve({
|
||||
slug: pk,
|
||||
@ -42,17 +46,37 @@ export class SAMLSourceForm extends ModelForm<SAMLSource, string> {
|
||||
}
|
||||
}
|
||||
|
||||
send = (data: SAMLSource): Promise<SAMLSource> => {
|
||||
send = async (data: SAMLSource): Promise<SAMLSource> => {
|
||||
let source: SAMLSource;
|
||||
if (this.instance) {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesSamlUpdate({
|
||||
source = await new SourcesApi(DEFAULT_CONFIG).sourcesSamlUpdate({
|
||||
slug: this.instance.slug,
|
||||
sAMLSourceRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesSamlCreate({
|
||||
source = await new SourcesApi(DEFAULT_CONFIG).sourcesSamlCreate({
|
||||
sAMLSourceRequest: data,
|
||||
});
|
||||
}
|
||||
const c = await config();
|
||||
if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) {
|
||||
const icon = this.getFormFiles()["icon"];
|
||||
if (icon || this.clearIcon) {
|
||||
await new SourcesApi(DEFAULT_CONFIG).sourcesAllSetIconCreate({
|
||||
slug: source.slug,
|
||||
file: icon,
|
||||
clear: this.clearIcon,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
await new SourcesApi(DEFAULT_CONFIG).sourcesAllSetIconUrlCreate({
|
||||
slug: source.slug,
|
||||
filePathRequest: {
|
||||
url: data.icon || "",
|
||||
},
|
||||
});
|
||||
}
|
||||
return source;
|
||||
};
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
@ -126,6 +150,54 @@ export class SAMLSourceForm extends ModelForm<SAMLSource, string> {
|
||||
</option>
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
${until(
|
||||
config().then((c) => {
|
||||
if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) {
|
||||
return html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
|
||||
<input type="file" value="" class="pf-c-form-control" />
|
||||
${this.instance?.icon
|
||||
? html`
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Currently set to:`} ${this.instance?.icon}
|
||||
</p>
|
||||
`
|
||||
: html``}
|
||||
</ak-form-element-horizontal>
|
||||
${this.instance?.icon
|
||||
? html`
|
||||
<ak-form-element-horizontal>
|
||||
<div class="pf-c-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="pf-c-check__input"
|
||||
@change=${(ev: Event) => {
|
||||
const target = ev.target as HTMLInputElement;
|
||||
this.clearIcon = target.checked;
|
||||
}}
|
||||
/>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Clear icon`}
|
||||
</label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Delete currently set icon.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
`
|
||||
: html``}`;
|
||||
}
|
||||
return html`<ak-form-element-horizontal label=${t`Icon`} name="icon">
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.icon, "")}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
}),
|
||||
)}
|
||||
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header"> ${t`Protocol settings`} </span>
|
||||
|
Reference in New Issue
Block a user