feat: support custom header HTML in entry pages (#2731)

This commit is contained in:
DacongDA 2024-02-22 17:56:47 +08:00 committed by GitHub
parent 53b8424a1f
commit 44abfb3430
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 162 additions and 28 deletions

View File

@ -66,6 +66,7 @@ type Application struct {
Description string `xorm:"varchar(100)" json:"description"`
Organization string `xorm:"varchar(100)" json:"organization"`
Cert string `xorm:"varchar(100)" json:"cert"`
HeaderHtml string `xorm:"mediumtext" json:"headerHtml"`
EnablePassword bool `json:"enablePassword"`
EnableSignUp bool `json:"enableSignUp"`
EnableSigninSession bool `json:"enableSigninSession"`

View File

@ -865,6 +865,28 @@ class ApplicationEditPage extends React.Component {
}
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Header HTML"), i18next.t("application:Header HTML - Tooltip"))} :
</Col>
<Col span={22} >
<Popover placement="right" content={
<div style={{width: "900px", height: "300px"}} >
<CodeMirror
value={this.state.application.headerHtml}
options={{mode: "htmlmixed", theme: "material-darker"}}
onBeforeChange={(editor, data, value) => {
this.updateApplicationField("headerHtml", value);
}}
/>
</div>
} title={i18next.t("application:Header HTML - Edit")} trigger="click">
<Input value={this.state.application.headerHtml} style={{marginBottom: "10px"}} onChange={e => {
this.updateApplicationField("headerHtml", e.target.value);
}} />
</Popover>
</Col>
</Row>
{
<React.Fragment>
<Row style={{marginTop: "20px"}} >

View File

@ -32,6 +32,7 @@ import {authConfig} from "./auth/Auth";
import ProductBuyPage from "./ProductBuyPage";
import PaymentResultPage from "./PaymentResultPage";
import QrCodePage from "./QrCodePage";
import CustomHead from "./basic/CustomHead";
class EntryPage extends React.Component {
constructor(props) {
@ -66,7 +67,6 @@ class EntryPage extends React.Component {
this.setState({
application: application,
});
const themeData = application !== null ? Setting.getThemeData(application.organizationObj, application) : Conf.ThemeDefault;
this.props.updataThemeData(themeData);
};
@ -82,7 +82,6 @@ class EntryPage extends React.Component {
Setting.showMessage("error", res.msg);
return;
}
const application = res.data;
const themeData = application !== null ? Setting.getThemeData(application.organizationObj, application) : Conf.ThemeDefault;
this.props.updataThemeData(themeData);
@ -90,32 +89,35 @@ class EntryPage extends React.Component {
};
return (
<div className="loginBackground"
style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${this.state.application?.formBackgroundUrl})`}}>
<Spin size="large" spinning={this.state.application === undefined && this.state.pricing === undefined} tip={i18next.t("login:Loading")}
style={{margin: "0 auto"}} />
<Switch>
<Route exact path="/signup" render={(props) => this.renderHomeIfLoggedIn(<SignupPage {...this.props} application={this.state.application} applicationName={authConfig.appName} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/signup/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/login" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/login/:owner" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/signup/oauth/authorize" render={(props) => <SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/login/oauth/authorize" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"code"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/login/saml/authorize/:owner/:applicationName" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"saml"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/forget" render={(props) => this.renderHomeIfLoggedIn(<SelfForgetPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/forget/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ForgetPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/prompt" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/prompt/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/result" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/result/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/cas/:owner/:casApplicationName/logout" render={(props) => this.renderHomeIfLoggedIn(<CasLogout {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/cas/:owner/:casApplicationName/login" render={(props) => {return (<LoginPage {...this.props} application={this.state.application} type={"cas"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />);}} />
<Route exact path="/select-plan/:owner/:pricingName" render={(props) => <PricingPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
<Route exact path="/buy-plan/:owner/:pricingName" render={(props) => <ProductBuyPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
<Route exact path="/buy-plan/:owner/:pricingName/result" render={(props) => <PaymentResultPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
<Route exact path="/qrcode/:owner/:paymentName" render={(props) => <QrCodePage {...this.props} onUpdateApplication={onUpdateApplication} {...props} />} />
</Switch>
</div>
<React.Fragment>
<CustomHead headerHtml={this.state.application?.headerHtml} />
<div className="loginBackground"
style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${this.state.application?.formBackgroundUrl})`}}>
<Spin size="large" spinning={this.state.application === undefined && this.state.pricing === undefined} tip={i18next.t("login:Loading")}
style={{margin: "0 auto"}} />
<Switch>
<Route exact path="/signup" render={(props) => this.renderHomeIfLoggedIn(<SignupPage {...this.props} application={this.state.application} applicationName={authConfig.appName} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/signup/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/login" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/login/:owner" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/signup/oauth/authorize" render={(props) => <SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/login/oauth/authorize" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"code"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/login/saml/authorize/:owner/:applicationName" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"saml"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/forget" render={(props) => this.renderHomeIfLoggedIn(<SelfForgetPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/forget/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ForgetPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/prompt" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/prompt/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/result" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/result/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/cas/:owner/:casApplicationName/logout" render={(props) => this.renderHomeIfLoggedIn(<CasLogout {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/cas/:owner/:casApplicationName/login" render={(props) => {return (<LoginPage {...this.props} application={this.state.application} type={"cas"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />);}} />
<Route exact path="/select-plan/:owner/:pricingName" render={(props) => <PricingPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
<Route exact path="/buy-plan/:owner/:pricingName" render={(props) => <ProductBuyPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
<Route exact path="/buy-plan/:owner/:pricingName/result" render={(props) => <PaymentResultPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
<Route exact path="/qrcode/:owner/:paymentName" render={(props) => <QrCodePage {...this.props} onUpdateApplication={onUpdateApplication} {...props} />} />
</Switch>
</div>
</React.Fragment>
);
}
}

View File

@ -0,0 +1,40 @@
// Copyright 2024 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {useEffect} from "react";
function CustomHead(props) {
useEffect(() => {
const suffix = new Date().getTime().toString();
if (!props.headerHtml) {return;}
const node = document.createElement("div");
node.innerHTML = props.headerHtml;
node.childNodes.forEach(el => {
el.setAttribute("app-custom-head" + suffix, "");
document.head.appendChild(el);
});
return () => {
for (const el of document.head.children) {
if (el.getAttribute("app-custom-head" + suffix) !== null) {
document.head.removeChild(el);
}
}
};
});
}
export default CustomHead;

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Position der Anmelde-, Registrierungs- und Passwort-vergessen-Formulare",
"Grant types": "Grant-Typen",
"Grant types - Tooltip": "Wählen Sie aus, welche Grant-Typen im OAuth-Protokoll zulässig sind",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Ubicación de los formularios de registro, inicio de sesión y olvido de contraseña",
"Grant types": "Tipos de subvenciones",
"Grant types - Tooltip": "Selecciona cuáles tipos de subvenciones están permitidas en el protocolo OAuth",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Emplacement des formulaires d'inscription, de connexion et de récupération de mot de passe",
"Grant types": "Types d'autorisation",
"Grant types - Tooltip": "Sélectionnez les types d'autorisations autorisés dans le protocole OAuth",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incrémentale",
"Input": "Saisie",
"Invitation code": "Code d'invitation",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Tempat pendaftaran, masuk, dan lupa kata sandi",
"Grant types": "Jenis-jenis hibah",
"Grant types - Tooltip": "Pilih jenis hibah apa yang diperbolehkan dalam protokol OAuth",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "登録、ログイン、パスワード忘れフォームの位置",
"Grant types": "グラント種類",
"Grant types - Tooltip": "OAuthプロトコルで許可されているグラントタイプを選択してください",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "가입, 로그인 및 비밀번호 재설정 양식의 위치",
"Grant types": "Grant types: 부여 유형",
"Grant types - Tooltip": "OAuth 프로토콜에서 허용되는 그란트 유형을 선택하십시오",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Localização dos formulários de registro, login e recuperação de senha",
"Grant types": "Tipos de concessão",
"Grant types - Tooltip": "Selecione quais tipos de concessão são permitidos no protocolo OAuth",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Código de convite",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Местоположение форм регистрации, входа и восстановления пароля",
"Grant types": "Типы грантов",
"Grant types - Tooltip": "Выберите, какие типы грантов разрешены в протоколе OAuth",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Последовательный",
"Input": "Input",
"Invitation code": "Код приглашения",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Davet Kodu",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "Vị trí của các biểu mẫu đăng ký, đăng nhập và quên mật khẩu",
"Grant types": "Loại hỗ trợ",
"Grant types - Tooltip": "Chọn loại hỗ trợ được cho phép trong giao thức OAuth",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Tăng",
"Input": "Input",
"Invitation code": "Invitation code",

View File

@ -62,6 +62,9 @@
"Form position - Tooltip": "注册、登录、忘记密码等表单的位置",
"Grant types": "OAuth授权类型",
"Grant types - Tooltip": "选择允许哪些OAuth协议中的grant types",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - 编辑",
"Header HTML - Tooltip": "自定义应用页面的head标签",
"Incremental": "递增",
"Input": "输入",
"Invitation code": "邀请码",