forked from casdoor/casdoor
Compare commits
1 Commits
copilot/fi
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d755e5376 |
12
Dockerfile
12
Dockerfile
@@ -51,14 +51,22 @@ COPY --from=FRONT --chown=$USER:$USER /web/build ./web/build
|
||||
ENTRYPOINT ["/server"]
|
||||
|
||||
|
||||
FROM debian:latest AS ALLINONE
|
||||
FROM debian:latest AS db
|
||||
RUN apt update \
|
||||
&& apt install -y \
|
||||
mariadb-server \
|
||||
mariadb-client \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
FROM db AS ALLINONE
|
||||
LABEL MAINTAINER="https://casdoor.org/"
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
ENV BUILDX_ARCH="${TARGETOS:-linux}_${TARGETARCH:-amd64}"
|
||||
|
||||
RUN apt update
|
||||
RUN apt install -y ca-certificates lsof && update-ca-certificates
|
||||
RUN apt install -y ca-certificates && update-ca-certificates
|
||||
|
||||
WORKDIR /
|
||||
COPY --from=BACK /go/src/casdoor/server_${BUILDX_ARCH} ./server
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
#!/bin/bash
|
||||
if [ "${MYSQL_ROOT_PASSWORD}" = "" ] ;then MYSQL_ROOT_PASSWORD=123456 ;fi
|
||||
|
||||
if [ -z "${driverName:-}" ]; then
|
||||
export driverName=sqlite
|
||||
fi
|
||||
if [ -z "${dataSourceName:-}" ]; then
|
||||
export dataSourceName="file:casdoor.db?cache=shared"
|
||||
fi
|
||||
service mariadb start
|
||||
|
||||
exec /server
|
||||
mysqladmin -u root password ${MYSQL_ROOT_PASSWORD}
|
||||
|
||||
exec /server --createDatabase=true
|
||||
|
||||
@@ -216,16 +216,6 @@ func handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
|
||||
e.AddAttribute("mobile", message.AttributeValue(user.Phone))
|
||||
e.AddAttribute("sn", message.AttributeValue(user.LastName))
|
||||
e.AddAttribute("givenName", message.AttributeValue(user.FirstName))
|
||||
// Add POSIX attributes for Linux machine login support
|
||||
e.AddAttribute("loginShell", getAttribute("loginShell", user))
|
||||
e.AddAttribute("gecos", getAttribute("gecos", user))
|
||||
// Add SSH public key if available
|
||||
sshKey := getAttribute("sshPublicKey", user)
|
||||
if sshKey != "" {
|
||||
e.AddAttribute("sshPublicKey", sshKey)
|
||||
}
|
||||
// Add objectClass for posixAccount
|
||||
e.AddAttribute("objectClass", "posixAccount")
|
||||
for _, group := range user.Groups {
|
||||
e.AddAttribute(ldapMemberOfAttr, message.AttributeValue(group))
|
||||
}
|
||||
|
||||
39
ldap/util.go
39
ldap/util.go
@@ -83,45 +83,6 @@ var ldapAttributesMapping = map[string]FieldRelation{
|
||||
return message.AttributeValue(getUserPasswordWithType(user))
|
||||
},
|
||||
},
|
||||
"loginShell": {
|
||||
userField: "loginShell",
|
||||
notSearchable: true,
|
||||
fieldMapper: func(user *object.User) message.AttributeValue {
|
||||
// Check user properties first, otherwise return default shell
|
||||
if user.Properties != nil {
|
||||
if shell, ok := user.Properties["loginShell"]; ok && shell != "" {
|
||||
return message.AttributeValue(shell)
|
||||
}
|
||||
}
|
||||
return message.AttributeValue("/bin/bash")
|
||||
},
|
||||
},
|
||||
"gecos": {
|
||||
userField: "gecos",
|
||||
notSearchable: true,
|
||||
fieldMapper: func(user *object.User) message.AttributeValue {
|
||||
// GECOS field typically contains full name and other user info
|
||||
// Format: Full Name,Room Number,Work Phone,Home Phone,Other
|
||||
gecos := user.DisplayName
|
||||
if gecos == "" {
|
||||
gecos = user.Name
|
||||
}
|
||||
return message.AttributeValue(gecos)
|
||||
},
|
||||
},
|
||||
"sshPublicKey": {
|
||||
userField: "sshPublicKey",
|
||||
notSearchable: true,
|
||||
fieldMapper: func(user *object.User) message.AttributeValue {
|
||||
// Return SSH public key from user properties
|
||||
if user.Properties != nil {
|
||||
if sshKey, ok := user.Properties["sshPublicKey"]; ok && sshKey != "" {
|
||||
return message.AttributeValue(sshKey)
|
||||
}
|
||||
}
|
||||
return message.AttributeValue("")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const ldapMemberOfAttr = "memberOf"
|
||||
|
||||
@@ -32,7 +32,6 @@ type AccountItem struct {
|
||||
ViewRule string `json:"viewRule"`
|
||||
ModifyRule string `json:"modifyRule"`
|
||||
Regex string `json:"regex"`
|
||||
Tab string `json:"tab"`
|
||||
}
|
||||
|
||||
type ThemeData struct {
|
||||
@@ -89,7 +88,6 @@ type Organization struct {
|
||||
|
||||
MfaItems []*MfaItem `xorm:"varchar(300)" json:"mfaItems"`
|
||||
MfaRememberInHours int `json:"mfaRememberInHours"`
|
||||
AccountMenu string `xorm:"varchar(20)" json:"accountMenu"`
|
||||
AccountItems []*AccountItem `xorm:"mediumtext" json:"accountItems"`
|
||||
|
||||
OrgBalance float64 `json:"orgBalance"`
|
||||
|
||||
@@ -50,8 +50,7 @@ type Payment struct {
|
||||
InvoiceRemark string `xorm:"varchar(100)" json:"invoiceRemark"`
|
||||
InvoiceUrl string `xorm:"varchar(255)" json:"invoiceUrl"`
|
||||
// Order Info
|
||||
Order string `xorm:"varchar(100)" json:"order"` // Internal order name
|
||||
OrderObj *Order `xorm:"-" json:"orderObj,omitempty"`
|
||||
Order string `xorm:"varchar(100)" json:"order"` // Internal order name
|
||||
OutOrderId string `xorm:"varchar(100)" json:"outOrderId"` // External payment provider's order ID
|
||||
PayUrl string `xorm:"varchar(2000)" json:"payUrl"`
|
||||
SuccessUrl string `xorm:"varchar(2000)" json:"successUrl"` // `successUrl` is redirected from `payUrl` after pay success
|
||||
@@ -71,11 +70,6 @@ func GetPayments(owner string) ([]*Payment, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ExtendPaymentWithOrder(payments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return payments, nil
|
||||
}
|
||||
|
||||
@@ -86,11 +80,6 @@ func GetUserPayments(owner, user string) ([]*Payment, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ExtendPaymentWithOrder(payments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return payments, nil
|
||||
}
|
||||
|
||||
@@ -102,49 +91,9 @@ func GetPaginationPayments(owner string, offset, limit int, field, value, sortFi
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ExtendPaymentWithOrder(payments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return payments, nil
|
||||
}
|
||||
|
||||
func ExtendPaymentWithOrder(payments []*Payment) error {
|
||||
ownerOrdersMap := make(map[string][]string)
|
||||
for _, payment := range payments {
|
||||
if payment.Order != "" {
|
||||
ownerOrdersMap[payment.Owner] = append(ownerOrdersMap[payment.Owner], payment.Order)
|
||||
}
|
||||
}
|
||||
|
||||
ordersMap := make(map[string]*Order)
|
||||
for owner, orderNames := range ownerOrdersMap {
|
||||
if len(orderNames) == 0 {
|
||||
continue
|
||||
}
|
||||
var orders []*Order
|
||||
err := ormer.Engine.In("name", orderNames).Find(&orders, &Order{Owner: owner})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, order := range orders {
|
||||
ordersMap[util.GetId(order.Owner, order.Name)] = order
|
||||
}
|
||||
}
|
||||
|
||||
for _, payment := range payments {
|
||||
if payment.Order != "" {
|
||||
orderId := util.GetId(payment.Owner, payment.Order)
|
||||
if order, ok := ordersMap[orderId]; ok {
|
||||
payment.OrderObj = order
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPayment(owner string, name string) (*Payment, error) {
|
||||
if owner == "" || name == "" {
|
||||
return nil, nil
|
||||
@@ -261,29 +210,18 @@ func NotifyPayment(body []byte, owner string, paymentName string, lang string) (
|
||||
}
|
||||
|
||||
// Check if payment is already in a terminal state to prevent duplicate processing
|
||||
if pp.IsTerminalState(payment.State) {
|
||||
if payment.State == pp.PaymentStatePaid || payment.State == pp.PaymentStateError ||
|
||||
payment.State == pp.PaymentStateCanceled || payment.State == pp.PaymentStateTimeout {
|
||||
return payment, nil
|
||||
}
|
||||
|
||||
// Determine the new payment state
|
||||
var newState pp.PaymentState
|
||||
var newMessage string
|
||||
if err != nil {
|
||||
newState = pp.PaymentStateError
|
||||
newMessage = err.Error()
|
||||
payment.State = pp.PaymentStateError
|
||||
payment.Message = err.Error()
|
||||
} else {
|
||||
newState = notifyResult.PaymentStatus
|
||||
newMessage = notifyResult.NotifyMessage
|
||||
payment.State = notifyResult.PaymentStatus
|
||||
payment.Message = notifyResult.NotifyMessage
|
||||
}
|
||||
|
||||
// Check if the payment state would actually change
|
||||
// This prevents duplicate webhook events when providers send redundant notifications
|
||||
if payment.State == newState {
|
||||
return payment, nil
|
||||
}
|
||||
|
||||
payment.State = newState
|
||||
payment.Message = newMessage
|
||||
_, err = UpdatePayment(payment.GetId(), payment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -918,8 +918,6 @@ func StringArrayToStruct[T any](stringArray [][]string) ([]*T, error) {
|
||||
err = setReflectAttr[[]MfaAccount](&fv, v)
|
||||
case reflect.TypeOf([]webauthn.Credential{}):
|
||||
err = setReflectAttr[[]webauthn.Credential](&fv, v)
|
||||
case reflect.TypeOf(map[string]string{}):
|
||||
err = setReflectAttr[map[string]string](&fv, v)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -24,12 +24,6 @@ const (
|
||||
PaymentStateError PaymentState = "Error"
|
||||
)
|
||||
|
||||
// IsTerminalState checks if a payment state is terminal (cannot transition to other states)
|
||||
func IsTerminalState(state PaymentState) bool {
|
||||
return state == PaymentStatePaid || state == PaymentStateError ||
|
||||
state == PaymentStateCanceled || state == PaymentStateTimeout
|
||||
}
|
||||
|
||||
const (
|
||||
PaymentEnvWechatBrowser = "WechatBrowser"
|
||||
)
|
||||
|
||||
@@ -144,8 +144,8 @@ class OrderListPage extends BaseListPage {
|
||||
key: "products",
|
||||
...this.getColumnSearchProps("products"),
|
||||
render: (text, record, index) => {
|
||||
const productInfos = record?.productInfos || [];
|
||||
if (productInfos.length === 0) {
|
||||
const products = record?.products || [];
|
||||
if (products.length === 0) {
|
||||
return `(${i18next.t("general:empty")})`;
|
||||
}
|
||||
return (
|
||||
@@ -153,26 +153,21 @@ class OrderListPage extends BaseListPage {
|
||||
<List
|
||||
size="small"
|
||||
locale={{emptyText: " "}}
|
||||
dataSource={productInfos}
|
||||
dataSource={products}
|
||||
style={{
|
||||
paddingTop: 8,
|
||||
paddingBottom: 8,
|
||||
}}
|
||||
renderItem={(productInfo, i) => {
|
||||
const price = productInfo.price * (productInfo.quantity || 1);
|
||||
const currency = record.currency || "USD";
|
||||
renderItem={(productName, i) => {
|
||||
return (
|
||||
<List.Item>
|
||||
<div style={{display: "inline"}}>
|
||||
<Tooltip placement="topLeft" title={i18next.t("general:Edit")}>
|
||||
<Button style={{marginRight: "5px"}} icon={<EditOutlined />} size="small" onClick={() => Setting.goToLinkSoft(this, `/products/${record.owner}/${productInfo.name}`)} />
|
||||
<Button style={{marginRight: "5px"}} icon={<EditOutlined />} size="small" onClick={() => Setting.goToLinkSoft(this, `/products/${record.owner}/${productName}`)} />
|
||||
</Tooltip>
|
||||
<Link to={`/products/${record.owner}/${productInfo.name}`}>
|
||||
{productInfo.displayName || productInfo.name}
|
||||
<Link to={`/products/${record.owner}/${productName}`}>
|
||||
{productName}
|
||||
</Link>
|
||||
<span style={{marginLeft: "8px", color: "#666"}}>
|
||||
{Setting.getPriceDisplay(price, currency)}
|
||||
</span>
|
||||
</div>
|
||||
</List.Item>
|
||||
);
|
||||
|
||||
@@ -678,16 +678,6 @@ class OrganizationEditPage extends React.Component {
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("organization:Account menu"), i18next.t("organization:Account menu - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.organization.accountMenu || "Horizontal"} onChange={(value => {this.updateOrganizationField("accountMenu", value);})}
|
||||
options={[{value: "Horizontal", label: i18next.t("general:Horizontal")}, {value: "Vertical", label: i18next.t("general:Vertical")}].map(item => Setting.getOption(item.label, item.value))}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("organization:Account items"), i18next.t("organization:Account items - Tooltip"))} :
|
||||
|
||||
@@ -180,8 +180,8 @@ class PaymentListPage extends BaseListPage {
|
||||
key: "products",
|
||||
...this.getColumnSearchProps("products"),
|
||||
render: (text, record, index) => {
|
||||
const productInfos = record?.orderObj?.productInfos || [];
|
||||
if (productInfos.length === 0) {
|
||||
const products = record?.products || [];
|
||||
if (products.length === 0) {
|
||||
return `(${i18next.t("general:empty")})`;
|
||||
}
|
||||
return (
|
||||
@@ -189,26 +189,21 @@ class PaymentListPage extends BaseListPage {
|
||||
<List
|
||||
size="small"
|
||||
locale={{emptyText: " "}}
|
||||
dataSource={productInfos}
|
||||
dataSource={products}
|
||||
style={{
|
||||
paddingTop: 8,
|
||||
paddingBottom: 8,
|
||||
}}
|
||||
renderItem={(productInfo, i) => {
|
||||
const price = productInfo.price * (productInfo.quantity || 1);
|
||||
const currency = record.currency || "USD";
|
||||
renderItem={(productName, i) => {
|
||||
return (
|
||||
<List.Item>
|
||||
<div style={{display: "inline"}}>
|
||||
<Tooltip placement="topLeft" title={i18next.t("general:Edit")}>
|
||||
<Button style={{marginRight: "5px"}} icon={<EditOutlined />} size="small" onClick={() => Setting.goToLinkSoft(this, `/products/${record.owner}/${productInfo.name}`)} />
|
||||
<Button style={{marginRight: "5px"}} icon={<EditOutlined />} size="small" onClick={() => Setting.goToLinkSoft(this, `/products/${record.owner}/${productName}`)} />
|
||||
</Tooltip>
|
||||
<Link to={`/products/${record.owner}/${productInfo.name}`}>
|
||||
{productInfo.displayName || productInfo.name}
|
||||
<Link to={`/products/${record.owner}/${productName}`}>
|
||||
{productName}
|
||||
</Link>
|
||||
<span style={{marginLeft: "8px", color: "#666"}}>
|
||||
{Setting.getPriceDisplay(price, currency)}
|
||||
</span>
|
||||
</div>
|
||||
</List.Item>
|
||||
);
|
||||
|
||||
@@ -340,7 +340,7 @@ class ProductBuyPage extends React.Component {
|
||||
{i18next.t("order:Place Order")}
|
||||
</Button>
|
||||
<Button
|
||||
type="default"
|
||||
type="primary"
|
||||
size="large"
|
||||
style={{
|
||||
height: "50px",
|
||||
|
||||
@@ -162,7 +162,7 @@ class ProductStorePage extends React.Component {
|
||||
{!product.isRecharge && (
|
||||
<Button
|
||||
key="add"
|
||||
type="default"
|
||||
type="primary"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
this.addToCart(product);
|
||||
|
||||
@@ -13,10 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import {
|
||||
Button, Card, Col, Form, Input, InputNumber, Layout, List,
|
||||
Menu, Result, Row, Select, Space, Spin, Switch, Tabs, Tag, Tooltip
|
||||
} from "antd";
|
||||
import {Button, Card, Col, Form, Input, InputNumber, List, Result, Row, Select, Space, Spin, Switch, Tag, Tooltip} from "antd";
|
||||
import {withRouter} from "react-router-dom";
|
||||
import {TotpMfaType} from "./auth/MfaSetupPage";
|
||||
import * as GroupBackend from "./backend/GroupBackend";
|
||||
@@ -49,8 +46,6 @@ import MfaAccountTable from "./table/MfaAccountTable";
|
||||
import MfaTable from "./table/MfaTable";
|
||||
import TransactionTable from "./table/TransactionTable";
|
||||
import * as TransactionBackend from "./backend/TransactionBackend";
|
||||
import {Content, Header} from "antd/es/layout/layout";
|
||||
import Sider from "antd/es/layout/Sider";
|
||||
|
||||
const {Option} = Select;
|
||||
|
||||
@@ -72,8 +67,6 @@ class UserEditPage extends React.Component {
|
||||
idCardInfo: ["ID card front", "ID card back", "ID card with person"],
|
||||
openFaceRecognitionModal: false,
|
||||
transactions: [],
|
||||
activeMenuKey: window.location.hash?.slice(1) || "",
|
||||
menuMode: "Horizontal",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -182,7 +175,6 @@ class UserEditPage extends React.Component {
|
||||
}
|
||||
|
||||
this.setState({
|
||||
menuMode: res.data?.organizationObj?.accountMenu ?? "Horizontal",
|
||||
application: res.data,
|
||||
});
|
||||
});
|
||||
@@ -1341,152 +1333,6 @@ class UserEditPage extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
isAccountItemVisible(item) {
|
||||
if (!item.visible) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isAdmin = Setting.isLocalAdminUser(this.props.account);
|
||||
if (item.viewRule === "Self") {
|
||||
if (!this.isSelfOrAdmin()) {
|
||||
return false;
|
||||
}
|
||||
} else if (item.viewRule === "Admin") {
|
||||
if (!isAdmin) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getAccountItemsByTab(tab) {
|
||||
const accountItems = this.getUserOrganization()?.accountItems || [];
|
||||
return accountItems.filter(item => {
|
||||
if (!this.isAccountItemVisible(item)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const itemTab = item.tab || "";
|
||||
return itemTab === tab;
|
||||
});
|
||||
}
|
||||
|
||||
getUniqueTabs() {
|
||||
const accountItems = this.getUserOrganization()?.accountItems || [];
|
||||
const tabs = new Set();
|
||||
|
||||
accountItems.forEach(item => {
|
||||
if (this.isAccountItemVisible(item)) {
|
||||
tabs.add(item.tab || "");
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(tabs).sort((a, b) => {
|
||||
// Empty string (default tab) comes first
|
||||
if (a === "") {
|
||||
return -1;
|
||||
}
|
||||
if (b === "") {
|
||||
return 1;
|
||||
}
|
||||
return a.localeCompare(b);
|
||||
});
|
||||
}
|
||||
|
||||
renderUserForm() {
|
||||
const tabs = this.getUniqueTabs();
|
||||
|
||||
// If there are no tabs or only one tab (default), render without tab navigation
|
||||
if (tabs.length === 0 || (tabs.length === 1 && tabs[0] === "")) {
|
||||
const accountItems = this.getAccountItemsByTab("");
|
||||
return (
|
||||
<Form>
|
||||
{accountItems.map(accountItem => (
|
||||
<React.Fragment key={accountItem.name}>
|
||||
<Form.Item name={accountItem.name}
|
||||
validateTrigger="onChange"
|
||||
rules={[
|
||||
{
|
||||
pattern: accountItem.regex ? new RegExp(accountItem.regex, "g") : null,
|
||||
message: i18next.t("user:This field value doesn't match the pattern rule"),
|
||||
},
|
||||
]}
|
||||
style={{margin: 0}}>
|
||||
{this.renderAccountItem(accountItem)}
|
||||
</Form.Item>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
// Render with tabs
|
||||
const activeKey = this.state.activeMenuKey || tabs[0] || "";
|
||||
|
||||
return (
|
||||
<Layout style={{background: "inherit"}}>
|
||||
{
|
||||
this.state.menuMode === "Vertical" ? null : (
|
||||
<Header style={{background: "inherit", padding: "0px"}}>
|
||||
<Tabs
|
||||
onChange={(key) => {
|
||||
this.setState({activeMenuKey: key});
|
||||
window.location.hash = key;
|
||||
}}
|
||||
type="card"
|
||||
activeKey={activeKey}
|
||||
items={tabs.map(tab => ({
|
||||
label: tab === "" ? i18next.t("user:Default") : tab,
|
||||
key: tab,
|
||||
}))}
|
||||
/>
|
||||
</Header>
|
||||
)
|
||||
}
|
||||
<Layout style={{background: "inherit", maxHeight: "70vh", overflow: "auto"}}>
|
||||
{
|
||||
this.state.menuMode === "Vertical" ? (
|
||||
<Sider width={200} style={{background: "inherit", position: "sticky", top: 0}}>
|
||||
<Menu
|
||||
mode="vertical"
|
||||
selectedKeys={[activeKey]}
|
||||
onClick={({key}) => {
|
||||
this.setState({activeMenuKey: key});
|
||||
window.location.hash = key;
|
||||
}}
|
||||
style={{marginBottom: "20px", height: "100%"}}
|
||||
items={tabs.map(tab => ({
|
||||
label: tab === "" ? i18next.t("user:Default") : tab,
|
||||
key: tab,
|
||||
}))}
|
||||
/>
|
||||
</Sider>) : null
|
||||
}
|
||||
<Content style={{padding: "15px"}}>
|
||||
<Form>
|
||||
{this.getAccountItemsByTab(activeKey).map(accountItem => (
|
||||
<React.Fragment key={accountItem.name}>
|
||||
<Form.Item name={accountItem.name}
|
||||
validateTrigger="onChange"
|
||||
rules={[
|
||||
{
|
||||
pattern: accountItem.regex ? new RegExp(accountItem.regex, "g") : null,
|
||||
message: i18next.t("user:This field value doesn't match the pattern rule"),
|
||||
},
|
||||
]}
|
||||
style={{margin: 0}}>
|
||||
{this.renderAccountItem(accountItem)}
|
||||
</Form.Item>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Form>
|
||||
</Content>
|
||||
</Layout>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
renderUser() {
|
||||
return (
|
||||
<div>
|
||||
@@ -1500,7 +1346,42 @@ class UserEditPage extends React.Component {
|
||||
</div>
|
||||
)
|
||||
} style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner">
|
||||
{this.renderUserForm()}
|
||||
<Form>
|
||||
{
|
||||
this.getUserOrganization()?.accountItems?.map(accountItem => {
|
||||
if (!accountItem.visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isAdmin = Setting.isLocalAdminUser(this.props.account);
|
||||
|
||||
if (accountItem.viewRule === "Self") {
|
||||
if (!this.isSelfOrAdmin()) {
|
||||
return null;
|
||||
}
|
||||
} else if (accountItem.viewRule === "Admin") {
|
||||
if (!isAdmin) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<React.Fragment key={accountItem.name}>
|
||||
<Form.Item name={accountItem.name}
|
||||
validateTrigger="onChange"
|
||||
rules={[
|
||||
{
|
||||
pattern: accountItem.regex ? new RegExp(accountItem.regex, "g") : null,
|
||||
message: i18next.t("user:This field value doesn't match the pattern rule"),
|
||||
},
|
||||
]}
|
||||
style={{margin: 0}}>
|
||||
{this.renderAccountItem(accountItem)}
|
||||
</Form.Item>
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
}
|
||||
</Form>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -38,7 +38,7 @@ class AccountTable extends React.Component {
|
||||
}
|
||||
|
||||
addRow(table) {
|
||||
const row = {name: Setting.getNewRowNameForTable(table, "Please select an account item"), visible: true, viewRule: "Public", modifyRule: "Self", tab: ""};
|
||||
const row = {name: Setting.getNewRowNameForTable(table, "Please select an account item"), visible: true, viewRule: "Public", modifyRule: "Self"};
|
||||
if (table === undefined) {
|
||||
table = [];
|
||||
}
|
||||
@@ -93,19 +93,6 @@ class AccountTable extends React.Component {
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Tab"),
|
||||
dataIndex: "tab",
|
||||
key: "tab",
|
||||
width: "150px",
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Input value={text} placeholder={i18next.t("user:Default")} onChange={e => {
|
||||
this.updateField(table, index, "tab", e.target.value);
|
||||
}} />
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("signup:Regex"),
|
||||
dataIndex: "regex",
|
||||
|
||||
@@ -275,7 +275,7 @@ export function getTransactionTableColumns(options = {}) {
|
||||
title: i18next.t("transaction:Amount"),
|
||||
dataIndex: "amount",
|
||||
key: "amount",
|
||||
width: "180px",
|
||||
width: "160px",
|
||||
sorter: getSorter("amount"),
|
||||
...(getColumnSearchProps ? getColumnSearchProps("amount") : {}),
|
||||
fixed: (Setting.isMobile()) ? "false" : "right",
|
||||
|
||||
Reference in New Issue
Block a user