feat: change CRLF to LF for some files

This commit is contained in:
Yang Luo
2025-06-24 09:55:00 +08:00
parent ca224fdd4c
commit d9b97d70be
10 changed files with 976 additions and 976 deletions

8
.gitattributes vendored
View File

@ -1,5 +1,5 @@
*.go linguist-detectable=true *.go linguist-detectable=true
*.js linguist-detectable=false *.js linguist-detectable=false
# Declare files that will always have LF line endings on checkout. # Declare files that will always have LF line endings on checkout.
# Git will always convert line endings to LF on checkout. You should use this for files that must keep LF endings, even on Windows. # Git will always convert line endings to LF on checkout. You should use this for files that must keep LF endings, even on Windows.
*.sh text eol=lf *.sh text eol=lf

204
README.md
View File

@ -1,102 +1,102 @@
<h1 align="center" style="border-bottom: none;">📦⚡️ Casdoor</h1> <h1 align="center" style="border-bottom: none;">📦⚡️ Casdoor</h1>
<h3 align="center">An open-source UI-first Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML, CAS, LDAP, SCIM, WebAuthn, TOTP, MFA and RADIUS</h3> <h3 align="center">An open-source UI-first Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML, CAS, LDAP, SCIM, WebAuthn, TOTP, MFA and RADIUS</h3>
<p align="center"> <p align="center">
<a href="#badge"> <a href="#badge">
<img alt="semantic-release" src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg"> <img alt="semantic-release" src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg">
</a> </a>
<a href="https://hub.docker.com/r/casbin/casdoor"> <a href="https://hub.docker.com/r/casbin/casdoor">
<img alt="docker pull casbin/casdoor" src="https://img.shields.io/docker/pulls/casbin/casdoor.svg"> <img alt="docker pull casbin/casdoor" src="https://img.shields.io/docker/pulls/casbin/casdoor.svg">
</a> </a>
<a href="https://github.com/casdoor/casdoor/actions/workflows/build.yml"> <a href="https://github.com/casdoor/casdoor/actions/workflows/build.yml">
<img alt="GitHub Workflow Status (branch)" src="https://github.com/casdoor/casdoor/workflows/Build/badge.svg?style=flat-square"> <img alt="GitHub Workflow Status (branch)" src="https://github.com/casdoor/casdoor/workflows/Build/badge.svg?style=flat-square">
</a> </a>
<a href="https://github.com/casdoor/casdoor/releases/latest"> <a href="https://github.com/casdoor/casdoor/releases/latest">
<img alt="GitHub Release" src="https://img.shields.io/github/v/release/casdoor/casdoor.svg"> <img alt="GitHub Release" src="https://img.shields.io/github/v/release/casdoor/casdoor.svg">
</a> </a>
<a href="https://hub.docker.com/r/casbin/casdoor"> <a href="https://hub.docker.com/r/casbin/casdoor">
<img alt="Docker Image Version (latest semver)" src="https://img.shields.io/badge/Docker%20Hub-latest-brightgreen"> <img alt="Docker Image Version (latest semver)" src="https://img.shields.io/badge/Docker%20Hub-latest-brightgreen">
</a> </a>
</p> </p>
<p align="center"> <p align="center">
<a href="https://goreportcard.com/report/github.com/casdoor/casdoor"> <a href="https://goreportcard.com/report/github.com/casdoor/casdoor">
<img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/casdoor/casdoor?style=flat-square"> <img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/casdoor/casdoor?style=flat-square">
</a> </a>
<a href="https://github.com/casdoor/casdoor/blob/master/LICENSE"> <a href="https://github.com/casdoor/casdoor/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/casdoor/casdoor?style=flat-square" alt="license"> <img src="https://img.shields.io/github/license/casdoor/casdoor?style=flat-square" alt="license">
</a> </a>
<a href="https://github.com/casdoor/casdoor/issues"> <a href="https://github.com/casdoor/casdoor/issues">
<img alt="GitHub issues" src="https://img.shields.io/github/issues/casdoor/casdoor?style=flat-square"> <img alt="GitHub issues" src="https://img.shields.io/github/issues/casdoor/casdoor?style=flat-square">
</a> </a>
<a href="#"> <a href="#">
<img alt="GitHub stars" src="https://img.shields.io/github/stars/casdoor/casdoor?style=flat-square"> <img alt="GitHub stars" src="https://img.shields.io/github/stars/casdoor/casdoor?style=flat-square">
</a> </a>
<a href="https://github.com/casdoor/casdoor/network"> <a href="https://github.com/casdoor/casdoor/network">
<img alt="GitHub forks" src="https://img.shields.io/github/forks/casdoor/casdoor?style=flat-square"> <img alt="GitHub forks" src="https://img.shields.io/github/forks/casdoor/casdoor?style=flat-square">
</a> </a>
<a href="https://crowdin.com/project/casdoor-site"> <a href="https://crowdin.com/project/casdoor-site">
<img alt="Crowdin" src="https://badges.crowdin.net/casdoor-site/localized.svg"> <img alt="Crowdin" src="https://badges.crowdin.net/casdoor-site/localized.svg">
</a> </a>
<a href="https://discord.gg/5rPsrAzK7S"> <a href="https://discord.gg/5rPsrAzK7S">
<img alt="Discord" src="https://img.shields.io/discord/1022748306096537660?style=flat-square&logo=discord&label=discord&color=5865F2"> <img alt="Discord" src="https://img.shields.io/discord/1022748306096537660?style=flat-square&logo=discord&label=discord&color=5865F2">
</a> </a>
</p> </p>
<p align="center"> <p align="center">
<sup>Sponsored by</sup> <sup>Sponsored by</sup>
<br> <br>
<a href="https://stytch.com/docs?utm_source=oss-sponsorship&utm_medium=paid_sponsorship&utm_campaign=casbin"> <a href="https://stytch.com/docs?utm_source=oss-sponsorship&utm_medium=paid_sponsorship&utm_campaign=casbin">
<picture> <picture>
<source media="(prefers-color-scheme: dark)" srcset="https://cdn.casbin.org/img/stytch-white.png"> <source media="(prefers-color-scheme: dark)" srcset="https://cdn.casbin.org/img/stytch-white.png">
<source media="(prefers-color-scheme: light)" srcset="https://cdn.casbin.org/img/stytch-charcoal.png"> <source media="(prefers-color-scheme: light)" srcset="https://cdn.casbin.org/img/stytch-charcoal.png">
<img src="https://cdn.casbin.org/img/stytch-charcoal.png" width="275"> <img src="https://cdn.casbin.org/img/stytch-charcoal.png" width="275">
</picture> </picture>
</a><br/> </a><br/>
<a href="https://stytch.com/docs?utm_source=oss-sponsorship&utm_medium=paid_sponsorship&utm_campaign=casbin"><b>Build auth with fraud prevention, faster.</b><br/> Try Stytch for API-first authentication, user & org management, multi-tenant SSO, MFA, device fingerprinting, and more.</a> <a href="https://stytch.com/docs?utm_source=oss-sponsorship&utm_medium=paid_sponsorship&utm_campaign=casbin"><b>Build auth with fraud prevention, faster.</b><br/> Try Stytch for API-first authentication, user & org management, multi-tenant SSO, MFA, device fingerprinting, and more.</a>
<br> <br>
</p> </p>
## Online demo ## Online demo
- Read-only site: https://door.casdoor.com (any modification operation will fail) - Read-only site: https://door.casdoor.com (any modification operation will fail)
- Writable site: https://demo.casdoor.com (original data will be restored for every 5 minutes) - Writable site: https://demo.casdoor.com (original data will be restored for every 5 minutes)
## Documentation ## Documentation
https://casdoor.org https://casdoor.org
## Install ## Install
- By source code: https://casdoor.org/docs/basic/server-installation - By source code: https://casdoor.org/docs/basic/server-installation
- By Docker: https://casdoor.org/docs/basic/try-with-docker - By Docker: https://casdoor.org/docs/basic/try-with-docker
- By Kubernetes Helm: https://casdoor.org/docs/basic/try-with-helm - By Kubernetes Helm: https://casdoor.org/docs/basic/try-with-helm
## How to connect to Casdoor? ## How to connect to Casdoor?
https://casdoor.org/docs/how-to-connect/overview https://casdoor.org/docs/how-to-connect/overview
## Casdoor Public API ## Casdoor Public API
- Docs: https://casdoor.org/docs/basic/public-api - Docs: https://casdoor.org/docs/basic/public-api
- Swagger: https://door.casdoor.com/swagger - Swagger: https://door.casdoor.com/swagger
## Integrations ## Integrations
https://casdoor.org/docs/category/integrations https://casdoor.org/docs/category/integrations
## How to contact? ## How to contact?
- Discord: https://discord.gg/5rPsrAzK7S - Discord: https://discord.gg/5rPsrAzK7S
- Contact: https://casdoor.org/help - Contact: https://casdoor.org/help
## Contribute ## Contribute
For casdoor, if you have any questions, you can give Issues, or you can also directly start Pull Requests(but we recommend giving issues first to communicate with the community). For casdoor, if you have any questions, you can give Issues, or you can also directly start Pull Requests(but we recommend giving issues first to communicate with the community).
### I18n translation ### I18n translation
If you are contributing to casdoor, please note that we use [Crowdin](https://crowdin.com/project/casdoor-site) as translating platform and i18next as translating tool. When you add some words using i18next in the `web/` directory, please remember to add what you have added to the `web/src/locales/en/data.json` file. If you are contributing to casdoor, please note that we use [Crowdin](https://crowdin.com/project/casdoor-site) as translating platform and i18next as translating tool. When you add some words using i18next in the `web/` directory, please remember to add what you have added to the `web/src/locales/en/data.json` file.
## License ## License
[Apache-2.0](https://github.com/casdoor/casdoor/blob/master/LICENSE) [Apache-2.0](https://github.com/casdoor/casdoor/blob/master/LICENSE)

View File

@ -1,97 +1,97 @@
const CracoLessPlugin = require("craco-less"); const CracoLessPlugin = require("craco-less");
const path = require("path"); const path = require("path");
module.exports = { module.exports = {
devServer: { devServer: {
proxy: { proxy: {
"/api": { "/api": {
target: "http://localhost:8000", target: "http://localhost:8000",
changeOrigin: true, changeOrigin: true,
}, },
"/swagger": { "/swagger": {
target: "http://localhost:8000", target: "http://localhost:8000",
changeOrigin: true, changeOrigin: true,
}, },
"/files": { "/files": {
target: "http://localhost:8000", target: "http://localhost:8000",
changeOrigin: true, changeOrigin: true,
}, },
"/.well-known/openid-configuration": { "/.well-known/openid-configuration": {
target: "http://localhost:8000", target: "http://localhost:8000",
changeOrigin: true, changeOrigin: true,
}, },
"/cas/serviceValidate": { "/cas/serviceValidate": {
target: "http://localhost:8000", target: "http://localhost:8000",
changeOrigin: true, changeOrigin: true,
}, },
"/cas/proxyValidate": { "/cas/proxyValidate": {
target: "http://localhost:8000", target: "http://localhost:8000",
changeOrigin: true, changeOrigin: true,
}, },
"/cas/proxy": { "/cas/proxy": {
target: "http://localhost:8000", target: "http://localhost:8000",
changeOrigin: true, changeOrigin: true,
}, },
"/cas/validate": { "/cas/validate": {
target: "http://localhost:8000", target: "http://localhost:8000",
changeOrigin: true, changeOrigin: true,
}, },
"/scim": { "/scim": {
target: "http://localhost:8000", target: "http://localhost:8000",
changeOrigin: true, changeOrigin: true,
} }
}, },
}, },
plugins: [ plugins: [
{ {
plugin: CracoLessPlugin, plugin: CracoLessPlugin,
options: { options: {
lessLoaderOptions: { lessLoaderOptions: {
lessOptions: { lessOptions: {
modifyVars: {"@primary-color": "rgb(89,54,213)", "@border-radius-base": "5px"}, modifyVars: {"@primary-color": "rgb(89,54,213)", "@border-radius-base": "5px"},
javascriptEnabled: true, javascriptEnabled: true,
}, },
}, },
}, },
}, },
], ],
webpack: { webpack: {
configure: (webpackConfig, { env, paths }) => { configure: (webpackConfig, { env, paths }) => {
paths.appBuild = path.resolve(__dirname, "build-temp"); paths.appBuild = path.resolve(__dirname, "build-temp");
webpackConfig.output.path = path.resolve(__dirname, "build-temp"); webpackConfig.output.path = path.resolve(__dirname, "build-temp");
// ignore webpack warnings by source-map-loader // ignore webpack warnings by source-map-loader
// https://github.com/facebook/create-react-app/pull/11752#issuecomment-1345231546 // https://github.com/facebook/create-react-app/pull/11752#issuecomment-1345231546
webpackConfig.ignoreWarnings = [ webpackConfig.ignoreWarnings = [
function ignoreSourcemapsloaderWarnings(warning) { function ignoreSourcemapsloaderWarnings(warning) {
return ( return (
warning.module && warning.module &&
warning.module.resource.includes("node_modules") && warning.module.resource.includes("node_modules") &&
warning.details && warning.details &&
warning.details.includes("source-map-loader") warning.details.includes("source-map-loader")
); );
}, },
]; ];
// use polyfill Buffer with Webpack 5 // use polyfill Buffer with Webpack 5
// https://viglucci.io/articles/how-to-polyfill-buffer-with-webpack-5 // https://viglucci.io/articles/how-to-polyfill-buffer-with-webpack-5
// https://craco.js.org/docs/configuration/webpack/ // https://craco.js.org/docs/configuration/webpack/
webpackConfig.resolve.fallback = { webpackConfig.resolve.fallback = {
buffer: require.resolve("buffer/"), buffer: require.resolve("buffer/"),
process: false, process: false,
util: false, util: false,
url: false, url: false,
zlib: false, zlib: false,
stream: false, stream: false,
http: false, http: false,
https: false, https: false,
assert: false, assert: false,
crypto: false, crypto: false,
os: false, os: false,
fs: false, fs: false,
}; };
return webpackConfig; return webpackConfig;
}, },
}, },
}; };

View File

@ -1,21 +1,21 @@
const fs = require("fs"); const fs = require("fs");
const path = require("path"); const path = require("path");
const sourceDir = path.join(__dirname, "build-temp"); const sourceDir = path.join(__dirname, "build-temp");
const targetDir = path.join(__dirname, "build"); const targetDir = path.join(__dirname, "build");
if (!fs.existsSync(sourceDir)) { if (!fs.existsSync(sourceDir)) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error(`Source directory "${sourceDir}" does not exist.`); console.error(`Source directory "${sourceDir}" does not exist.`);
process.exit(1); process.exit(1);
} }
if (fs.existsSync(targetDir)) { if (fs.existsSync(targetDir)) {
fs.rmSync(targetDir, {recursive: true, force: true}); fs.rmSync(targetDir, {recursive: true, force: true});
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(`Target directory "${targetDir}" has been deleted successfully.`); console.log(`Target directory "${targetDir}" has been deleted successfully.`);
} }
fs.renameSync(sourceDir, targetDir); fs.renameSync(sourceDir, targetDir);
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(`Renamed "${sourceDir}" to "${targetDir}" successfully.`); console.log(`Renamed "${sourceDir}" to "${targetDir}" successfully.`);

View File

@ -1,36 +1,36 @@
// Copyright 2021 The Casdoor Authors. All Rights Reserved. // Copyright 2021 The Casdoor Authors. All Rights Reserved.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
export const DefaultApplication = "app-built-in"; export const DefaultApplication = "app-built-in";
export const CasvisorUrl = ""; export const CasvisorUrl = "";
export const ShowGithubCorner = false; export const ShowGithubCorner = false;
export const IsDemoMode = false; export const IsDemoMode = false;
export const ForceLanguage = ""; export const ForceLanguage = "";
export const DefaultLanguage = "en"; export const DefaultLanguage = "en";
export const InitThemeAlgorithm = true; export const InitThemeAlgorithm = true;
export const ThemeDefault = { export const ThemeDefault = {
themeType: "default", themeType: "default",
colorPrimary: "#5734d3", colorPrimary: "#5734d3",
borderRadius: 6, borderRadius: 6,
isCompact: false, isCompact: false,
}; };
export const CustomFooter = null; export const CustomFooter = null;
// Blank or null to hide Ai Assistant button // Blank or null to hide Ai Assistant button
export const AiAssistantUrl = "https://ai.casbin.com"; export const AiAssistantUrl = "https://ai.casbin.com";

View File

@ -1,341 +1,341 @@
// Copyright 2021 The Casdoor Authors. All Rights Reserved. // Copyright 2021 The Casdoor Authors. All Rights Reserved.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {Button, Image, Table, Upload} from "antd"; import {Button, Image, Table, Upload} from "antd";
import {UploadOutlined} from "@ant-design/icons"; import {UploadOutlined} from "@ant-design/icons";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import * as ResourceBackend from "./backend/ResourceBackend"; import * as ResourceBackend from "./backend/ResourceBackend";
import i18next from "i18next"; import i18next from "i18next";
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
import BaseListPage from "./BaseListPage"; import BaseListPage from "./BaseListPage";
import PopconfirmModal from "./common/modal/PopconfirmModal"; import PopconfirmModal from "./common/modal/PopconfirmModal";
class ResourceListPage extends BaseListPage { class ResourceListPage extends BaseListPage {
constructor(props) { constructor(props) {
super(props); super(props);
} }
componentDidMount() { componentDidMount() {
this.setState({ this.setState({
fileList: [], fileList: [],
uploading: false, uploading: false,
}); });
} }
deleteResource(i) { deleteResource(i) {
ResourceBackend.deleteResource(this.state.data[i]) ResourceBackend.deleteResource(this.state.data[i])
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted")); Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch({ this.fetch({
pagination: { pagination: {
...this.state.pagination, ...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current, current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
}, },
}); });
} else { } else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
} }
}) })
.catch(error => { .catch(error => {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`); Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
}); });
} }
handleUpload(info) { handleUpload(info) {
this.setState({uploading: true}); this.setState({uploading: true});
const filename = info.fileList[0].name; const filename = info.fileList[0].name;
const fullFilePath = `resource/${this.props.account.owner}/${this.props.account.name}/${filename}`; const fullFilePath = `resource/${this.props.account.owner}/${this.props.account.name}/${filename}`;
ResourceBackend.uploadResource(this.props.account.owner, this.props.account.name, "custom", "ResourceListPage", fullFilePath, info.file) ResourceBackend.uploadResource(this.props.account.owner, this.props.account.name, "custom", "ResourceListPage", fullFilePath, info.file)
.then(res => { .then(res => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("application:File uploaded successfully")); Setting.showMessage("success", i18next.t("application:File uploaded successfully"));
const {pagination} = this.state; const {pagination} = this.state;
this.fetch({pagination}); this.fetch({pagination});
} else { } else {
Setting.showMessage("error", res.msg); Setting.showMessage("error", res.msg);
} }
}).finally(() => { }).finally(() => {
this.setState({uploading: false}); this.setState({uploading: false});
}); });
} }
renderUpload() { renderUpload() {
return ( return (
<Upload maxCount={1} accept="image/*,video/*,audio/*,.pdf,.doc,.docx,.csv,.xls,.xlsx" showUploadList={false} <Upload maxCount={1} accept="image/*,video/*,audio/*,.pdf,.doc,.docx,.csv,.xls,.xlsx" showUploadList={false}
beforeUpload={file => {return false;}} onChange={info => {this.handleUpload(info);}}> beforeUpload={file => {return false;}} onChange={info => {this.handleUpload(info);}}>
<Button id="upload-button" icon={<UploadOutlined />} loading={this.state.uploading} type="primary" size="small"> <Button id="upload-button" icon={<UploadOutlined />} loading={this.state.uploading} type="primary" size="small">
{i18next.t("resource:Upload a file...")} {i18next.t("resource:Upload a file...")}
</Button> </Button>
</Upload> </Upload>
); );
} }
renderTable(resources) { renderTable(resources) {
const columns = [ const columns = [
{ {
title: i18next.t("general:Provider"), title: i18next.t("general:Provider"),
dataIndex: "provider", dataIndex: "provider",
key: "provider", key: "provider",
width: "150px", width: "150px",
sorter: true, sorter: true,
...this.getColumnSearchProps("provider"), ...this.getColumnSearchProps("provider"),
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
<Link to={`/providers/${record.owner}/${text}`}> <Link to={`/providers/${record.owner}/${text}`}>
{text} {text}
</Link> </Link>
); );
}, },
}, },
{ {
title: i18next.t("general:Organization"), title: i18next.t("general:Organization"),
dataIndex: "owner", dataIndex: "owner",
key: "owner", key: "owner",
width: "120px", width: "120px",
sorter: true, sorter: true,
...this.getColumnSearchProps("owner"), ...this.getColumnSearchProps("owner"),
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
<Link to={`/organizations/${text}`}> <Link to={`/organizations/${text}`}>
{text} {text}
</Link> </Link>
); );
}, },
}, },
{ {
title: i18next.t("general:Application"), title: i18next.t("general:Application"),
dataIndex: "application", dataIndex: "application",
key: "application", key: "application",
width: "80px", width: "80px",
sorter: true, sorter: true,
...this.getColumnSearchProps("application"), ...this.getColumnSearchProps("application"),
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
<Link to={`/applications/${record.owner}/${text}`}> <Link to={`/applications/${record.owner}/${text}`}>
{text} {text}
</Link> </Link>
); );
}, },
}, },
{ {
title: i18next.t("general:User"), title: i18next.t("general:User"),
dataIndex: "user", dataIndex: "user",
key: "user", key: "user",
width: "80px", width: "80px",
sorter: true, sorter: true,
...this.getColumnSearchProps("user"), ...this.getColumnSearchProps("user"),
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
<Link to={`/users/${record.owner}/${record.user}`}> <Link to={`/users/${record.owner}/${record.user}`}>
{text} {text}
</Link> </Link>
); );
}, },
}, },
{ {
title: i18next.t("resource:Parent"), title: i18next.t("resource:Parent"),
dataIndex: "parent", dataIndex: "parent",
key: "parent", key: "parent",
width: "80px", width: "80px",
sorter: true, sorter: true,
...this.getColumnSearchProps("parent"), ...this.getColumnSearchProps("parent"),
}, },
{ {
title: i18next.t("general:Name"), title: i18next.t("general:Name"),
dataIndex: "name", dataIndex: "name",
key: "name", key: "name",
width: "150px", width: "150px",
sorter: true, sorter: true,
...this.getColumnSearchProps("name"), ...this.getColumnSearchProps("name"),
}, },
{ {
title: i18next.t("general:Created time"), title: i18next.t("general:Created time"),
dataIndex: "createdTime", dataIndex: "createdTime",
key: "createdTime", key: "createdTime",
width: "150px", width: "150px",
sorter: true, sorter: true,
render: (text, record, index) => { render: (text, record, index) => {
return Setting.getFormattedDate(text); return Setting.getFormattedDate(text);
}, },
}, },
{ {
title: i18next.t("user:Tag"), title: i18next.t("user:Tag"),
dataIndex: "tag", dataIndex: "tag",
key: "tag", key: "tag",
width: "80px", width: "80px",
sorter: true, sorter: true,
...this.getColumnSearchProps("tag"), ...this.getColumnSearchProps("tag"),
}, },
// { // {
// title: i18next.t("resource:File name"), // title: i18next.t("resource:File name"),
// dataIndex: 'fileName', // dataIndex: 'fileName',
// key: 'fileName', // key: 'fileName',
// width: '120px', // width: '120px',
// sorter: (a, b) => a.fileName.localeCompare(b.fileName), // sorter: (a, b) => a.fileName.localeCompare(b.fileName),
// }, // },
{ {
title: i18next.t("provider:Type"), title: i18next.t("provider:Type"),
dataIndex: "fileType", dataIndex: "fileType",
key: "fileType", key: "fileType",
width: "80px", width: "80px",
sorter: true, sorter: true,
...this.getColumnSearchProps("fileType"), ...this.getColumnSearchProps("fileType"),
}, },
{ {
title: i18next.t("resource:Format"), title: i18next.t("resource:Format"),
dataIndex: "fileFormat", dataIndex: "fileFormat",
key: "fileFormat", key: "fileFormat",
width: "80px", width: "80px",
sorter: true, sorter: true,
...this.getColumnSearchProps("fileFormat"), ...this.getColumnSearchProps("fileFormat"),
}, },
{ {
title: i18next.t("resource:File size"), title: i18next.t("resource:File size"),
dataIndex: "fileSize", dataIndex: "fileSize",
key: "fileSize", key: "fileSize",
width: "100px", width: "100px",
sorter: true, sorter: true,
render: (text, record, index) => { render: (text, record, index) => {
return Setting.getFriendlyFileSize(text); return Setting.getFriendlyFileSize(text);
}, },
}, },
{ {
title: i18next.t("general:Preview"), title: i18next.t("general:Preview"),
dataIndex: "preview", dataIndex: "preview",
key: "preview", key: "preview",
width: "100px", width: "100px",
fixed: (Setting.isMobile()) ? "false" : "right", fixed: (Setting.isMobile()) ? "false" : "right",
render: (text, record, index) => { render: (text, record, index) => {
if (record.fileType === "image") { if (record.fileType === "image") {
const errorImage = "data:image/png;base64,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"; const errorImage = "data:image/png;base64,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";
return ( return (
<Image <Image
width={200} width={200}
src={record.url} src={record.url}
fallback={errorImage} fallback={errorImage}
/> />
); );
} else if (record.fileType === "video") { } else if (record.fileType === "video") {
return ( return (
<video width={200} controls> <video width={200} controls>
<source src={record.url} type="video/mp4" /> <source src={record.url} type="video/mp4" />
</video> </video>
); );
} }
}, },
}, },
{ {
title: i18next.t("general:URL"), title: i18next.t("general:URL"),
dataIndex: "url", dataIndex: "url",
key: "url", key: "url",
width: "120px", width: "120px",
fixed: (Setting.isMobile()) ? "false" : "right", fixed: (Setting.isMobile()) ? "false" : "right",
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
<div> <div>
<Button onClick={() => { <Button onClick={() => {
copy(record.url); copy(record.url);
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully")); Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
}} }}
> >
{i18next.t("resource:Copy Link")} {i18next.t("resource:Copy Link")}
</Button> </Button>
</div> </div>
); );
}, },
}, },
{ {
title: i18next.t("general:Action"), title: i18next.t("general:Action"),
dataIndex: "", dataIndex: "",
key: "op", key: "op",
width: "70px", width: "70px",
fixed: (Setting.isMobile()) ? "false" : "right", fixed: (Setting.isMobile()) ? "false" : "right",
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
<div> <div>
<PopconfirmModal <PopconfirmModal
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`} title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
onConfirm={() => this.deleteResource(index)} onConfirm={() => this.deleteResource(index)}
okText={i18next.t("general:OK")} okText={i18next.t("general:OK")}
cancelText={i18next.t("general:Cancel")} cancelText={i18next.t("general:Cancel")}
> >
</PopconfirmModal> </PopconfirmModal>
</div> </div>
); );
}, },
}, },
]; ];
const paginationProps = { const paginationProps = {
total: this.state.pagination.total, total: this.state.pagination.total,
showQuickJumper: true, showQuickJumper: true,
showSizeChanger: true, showSizeChanger: true,
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total), showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
}; };
return ( return (
<div> <div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={resources} rowKey="name" size="middle" bordered pagination={paginationProps} <Table scroll={{x: "max-content"}} columns={columns} dataSource={resources} rowKey="name" size="middle" bordered pagination={paginationProps}
title={() => ( title={() => (
<div> <div>
{i18next.t("general:Resources")}&nbsp;&nbsp;&nbsp;&nbsp; {i18next.t("general:Resources")}&nbsp;&nbsp;&nbsp;&nbsp;
{/* <Button type="primary" size="small" onClick={this.addResource.bind(this)}>{i18next.t("general:Add")}</Button>*/} {/* <Button type="primary" size="small" onClick={this.addResource.bind(this)}>{i18next.t("general:Add")}</Button>*/}
{ {
this.renderUpload() this.renderUpload()
} }
</div> </div>
)} )}
loading={this.state.loading} loading={this.state.loading}
onChange={this.handleTableChange} onChange={this.handleTableChange}
/> />
</div> </div>
); );
} }
fetch = (params = {}) => { fetch = (params = {}) => {
const field = params.searchedColumn, value = params.searchText; const field = params.searchedColumn, value = params.searchText;
const sortField = params.sortField, sortOrder = params.sortOrder; const sortField = params.sortField, sortOrder = params.sortOrder;
this.setState({loading: true}); this.setState({loading: true});
ResourceBackend.getResources(Setting.isDefaultOrganizationSelected(this.props.account) ? "" : Setting.getRequestOrganization(this.props.account), this.props.account.name, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder) ResourceBackend.getResources(Setting.isDefaultOrganizationSelected(this.props.account) ? "" : Setting.getRequestOrganization(this.props.account), this.props.account.name, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
.then((res) => { .then((res) => {
this.setState({ this.setState({
loading: false, loading: false,
}); });
if (res.status === "ok") { if (res.status === "ok") {
this.setState({ this.setState({
data: res.data, data: res.data,
pagination: { pagination: {
...params.pagination, ...params.pagination,
total: res.data2, total: res.data2,
}, },
searchText: params.searchText, searchText: params.searchText,
searchedColumn: params.searchedColumn, searchedColumn: params.searchedColumn,
}); });
} else { } else {
if (res.data.includes("Please login first")) { if (res.data.includes("Please login first")) {
this.setState({ this.setState({
loading: false, loading: false,
isAuthorized: false, isAuthorized: false,
}); });
} }
} }
}); });
}; };
} }
export default ResourceListPage; export default ResourceListPage;

View File

@ -1,30 +1,30 @@
// Copyright 2021 The Casdoor Authors. All Rights Reserved. // Copyright 2021 The Casdoor Authors. All Rights Reserved.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import * as Setting from "../Setting"; import * as Setting from "../Setting";
class OdicDiscoveryPage extends React.Component { class OdicDiscoveryPage extends React.Component {
UNSAFE_componentWillMount() { UNSAFE_componentWillMount() {
if (Setting.isLocalhost()) { if (Setting.isLocalhost()) {
Setting.goToLink(`${Setting.ServerUrl}/.well-known/openid-configuration`); Setting.goToLink(`${Setting.ServerUrl}/.well-known/openid-configuration`);
} }
} }
render() { render() {
return null; return null;
} }
} }
export default OdicDiscoveryPage; export default OdicDiscoveryPage;

View File

@ -1,31 +1,31 @@
// Copyright 2021 The Casdoor Authors. All Rights Reserved. // Copyright 2021 The Casdoor Authors. All Rights Reserved.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import {createButton} from "react-social-login-buttons"; import {createButton} from "react-social-login-buttons";
function Icon({width = 24, height = 24, color}) { function Icon({width = 24, height = 24, color}) {
return <svg xmlns="http://www.w3.org/2000/svg" height="48" width="32" viewBox="-18.15 -35.9725 157.3 215.835"><path fill="#faab07" d="M60.503 142.237c-12.533 0-24.038-4.195-31.445-10.46-3.762 1.124-8.574 2.932-11.61 5.175-2.6 1.918-2.275 3.874-1.807 4.663 2.056 3.47 35.273 2.216 44.862 1.136zm0 0c12.535 0 24.039-4.195 31.447-10.46 3.76 1.124 8.573 2.932 11.61 5.175 2.598 1.918 2.274 3.874 1.805 4.663-2.056 3.47-35.272 2.216-44.862 1.136zm0 0" /><path d="M60.576 67.119c20.698-.14 37.286-4.147 42.907-5.683 1.34-.367 2.056-1.024 2.056-1.024.005-.189.085-3.37.085-5.01C105.624 27.768 92.58.001 60.5 0 28.42.001 15.375 27.769 15.375 55.401c0 1.642.08 4.822.086 5.01 0 0 .583.615 1.65.913 5.19 1.444 22.09 5.65 43.312 5.795zm56.245 23.02c-1.283-4.129-3.034-8.944-4.808-13.568 0 0-1.02-.126-1.537.023-15.913 4.623-35.202 7.57-49.9 7.392h-.153c-14.616.175-33.774-2.737-49.634-7.315-.606-.175-1.802-.1-1.802-.1-1.774 4.624-3.525 9.44-4.808 13.568-6.119 19.69-4.136 27.838-2.627 28.02 3.239.392 12.606-14.821 12.606-14.821 0 15.459 13.957 39.195 45.918 39.413h.848c31.96-.218 45.917-23.954 45.917-39.413 0 0 9.368 15.213 12.607 14.822 1.508-.183 3.491-8.332-2.627-28.021" /><path fill="#fff" d="M49.085 40.824c-4.352.197-8.07-4.76-8.304-11.063-.236-6.305 3.098-11.576 7.45-11.773 4.347-.195 8.064 4.76 8.3 11.065.238 6.306-3.097 11.577-7.446 11.771m31.133-11.063c-.233 6.302-3.951 11.26-8.303 11.063-4.35-.195-7.684-5.465-7.446-11.77.236-6.305 3.952-11.26 8.3-11.066 4.352.197 7.686 5.468 7.449 11.773" /><path fill="#faab07" d="M87.952 49.725C86.79 47.15 75.077 44.28 60.578 44.28h-.156c-14.5 0-26.212 2.87-27.375 5.446a.863.863 0 00-.085.367.88.88 0 00.16.496c.98 1.427 13.985 8.487 27.3 8.487h.156c13.314 0 26.319-7.058 27.299-8.487a.873.873 0 00.16-.498.856.856 0 00-.085-.365" /><path d="M54.434 29.854c.199 2.49-1.167 4.702-3.046 4.943-1.883.242-3.568-1.58-3.768-4.07-.197-2.492 1.167-4.704 3.043-4.944 1.886-.244 3.574 1.58 3.771 4.07m11.956.833c.385-.689 3.004-4.312 8.427-2.993 1.425.347 2.084.857 2.223 1.057.205.296.262.718.053 1.286-.412 1.126-1.263 1.095-1.734.875-.305-.142-4.082-2.66-7.562 1.097-.24.257-.668.346-1.073.04-.407-.308-.574-.93-.334-1.362" /><path fill="#fff" d="M60.576 83.08h-.153c-9.996.12-22.116-1.204-33.854-3.518-1.004 5.818-1.61 13.132-1.09 21.853 1.316 22.043 14.407 35.9 34.614 36.1h.82c20.208-.2 33.298-14.057 34.616-36.1.52-8.723-.087-16.035-1.092-21.854-11.739 2.315-23.862 3.64-33.86 3.518" /><path fill="#eb1923" d="M32.102 81.235v21.693s9.937 2.004 19.893.616V83.535c-6.307-.357-13.109-1.152-19.893-2.3" /><path fill="#eb1923" d="M105.539 60.412s-19.33 6.102-44.963 6.275h-.153c-25.591-.172-44.896-6.255-44.962-6.275L8.987 76.57c16.193 4.882 36.261 8.028 51.436 7.845h.153c15.175.183 35.242-2.963 51.437-7.845zm0 0" /></svg>; return <svg xmlns="http://www.w3.org/2000/svg" height="48" width="32" viewBox="-18.15 -35.9725 157.3 215.835"><path fill="#faab07" d="M60.503 142.237c-12.533 0-24.038-4.195-31.445-10.46-3.762 1.124-8.574 2.932-11.61 5.175-2.6 1.918-2.275 3.874-1.807 4.663 2.056 3.47 35.273 2.216 44.862 1.136zm0 0c12.535 0 24.039-4.195 31.447-10.46 3.76 1.124 8.573 2.932 11.61 5.175 2.598 1.918 2.274 3.874 1.805 4.663-2.056 3.47-35.272 2.216-44.862 1.136zm0 0" /><path d="M60.576 67.119c20.698-.14 37.286-4.147 42.907-5.683 1.34-.367 2.056-1.024 2.056-1.024.005-.189.085-3.37.085-5.01C105.624 27.768 92.58.001 60.5 0 28.42.001 15.375 27.769 15.375 55.401c0 1.642.08 4.822.086 5.01 0 0 .583.615 1.65.913 5.19 1.444 22.09 5.65 43.312 5.795zm56.245 23.02c-1.283-4.129-3.034-8.944-4.808-13.568 0 0-1.02-.126-1.537.023-15.913 4.623-35.202 7.57-49.9 7.392h-.153c-14.616.175-33.774-2.737-49.634-7.315-.606-.175-1.802-.1-1.802-.1-1.774 4.624-3.525 9.44-4.808 13.568-6.119 19.69-4.136 27.838-2.627 28.02 3.239.392 12.606-14.821 12.606-14.821 0 15.459 13.957 39.195 45.918 39.413h.848c31.96-.218 45.917-23.954 45.917-39.413 0 0 9.368 15.213 12.607 14.822 1.508-.183 3.491-8.332-2.627-28.021" /><path fill="#fff" d="M49.085 40.824c-4.352.197-8.07-4.76-8.304-11.063-.236-6.305 3.098-11.576 7.45-11.773 4.347-.195 8.064 4.76 8.3 11.065.238 6.306-3.097 11.577-7.446 11.771m31.133-11.063c-.233 6.302-3.951 11.26-8.303 11.063-4.35-.195-7.684-5.465-7.446-11.77.236-6.305 3.952-11.26 8.3-11.066 4.352.197 7.686 5.468 7.449 11.773" /><path fill="#faab07" d="M87.952 49.725C86.79 47.15 75.077 44.28 60.578 44.28h-.156c-14.5 0-26.212 2.87-27.375 5.446a.863.863 0 00-.085.367.88.88 0 00.16.496c.98 1.427 13.985 8.487 27.3 8.487h.156c13.314 0 26.319-7.058 27.299-8.487a.873.873 0 00.16-.498.856.856 0 00-.085-.365" /><path d="M54.434 29.854c.199 2.49-1.167 4.702-3.046 4.943-1.883.242-3.568-1.58-3.768-4.07-.197-2.492 1.167-4.704 3.043-4.944 1.886-.244 3.574 1.58 3.771 4.07m11.956.833c.385-.689 3.004-4.312 8.427-2.993 1.425.347 2.084.857 2.223 1.057.205.296.262.718.053 1.286-.412 1.126-1.263 1.095-1.734.875-.305-.142-4.082-2.66-7.562 1.097-.24.257-.668.346-1.073.04-.407-.308-.574-.93-.334-1.362" /><path fill="#fff" d="M60.576 83.08h-.153c-9.996.12-22.116-1.204-33.854-3.518-1.004 5.818-1.61 13.132-1.09 21.853 1.316 22.043 14.407 35.9 34.614 36.1h.82c20.208-.2 33.298-14.057 34.616-36.1.52-8.723-.087-16.035-1.092-21.854-11.739 2.315-23.862 3.64-33.86 3.518" /><path fill="#eb1923" d="M32.102 81.235v21.693s9.937 2.004 19.893.616V83.535c-6.307-.357-13.109-1.152-19.893-2.3" /><path fill="#eb1923" d="M105.539 60.412s-19.33 6.102-44.963 6.275h-.153c-25.591-.172-44.896-6.255-44.962-6.275L8.987 76.57c16.193 4.882 36.261 8.028 51.436 7.845h.153c15.175.183 35.242-2.963 51.437-7.845zm0 0" /></svg>;
} }
const config = { const config = {
text: "Sign in with QQ", text: "Sign in with QQ",
icon: Icon, icon: Icon,
iconFormat: name => `fa fa-${name}`, iconFormat: name => `fa fa-${name}`,
style: {background: "rgb(94,188,249)"}, style: {background: "rgb(94,188,249)"},
activeStyle: {background: "rgb(76,143,208)"}, activeStyle: {background: "rgb(76,143,208)"},
}; };
const QqLoginButton = createButton(config); const QqLoginButton = createButton(config);
export default QqLoginButton; export default QqLoginButton;

View File

@ -1,85 +1,85 @@
// Copyright 2021 The Casdoor Authors. All Rights Reserved. // Copyright 2021 The Casdoor Authors. All Rights Reserved.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import * as Setting from "../Setting"; import * as Setting from "../Setting";
export function getResources(owner, user, page = "", pageSize = "", field = "", value = "", sortField = "", sortOrder = "") { export function getResources(owner, user, page = "", pageSize = "", field = "", value = "", sortField = "", sortOrder = "") {
return fetch(`${Setting.ServerUrl}/api/get-resources?owner=${owner}&user=${user}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { return fetch(`${Setting.ServerUrl}/api/get-resources?owner=${owner}&user=${user}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: { headers: {
"Accept-Language": Setting.getAcceptLanguage(), "Accept-Language": Setting.getAcceptLanguage(),
}, },
}).then(res => res.json()); }).then(res => res.json());
} }
export function getResource(owner, name) { export function getResource(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-resource?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/get-resource?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: { headers: {
"Accept-Language": Setting.getAcceptLanguage(), "Accept-Language": Setting.getAcceptLanguage(),
}, },
}).then(res => res.json()); }).then(res => res.json());
} }
export function updateResource(owner, name, resource) { export function updateResource(owner, name, resource) {
const newResource = Setting.deepCopy(resource); const newResource = Setting.deepCopy(resource);
return fetch(`${Setting.ServerUrl}/api/update-resource?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/update-resource?id=${owner}/${encodeURIComponent(name)}`, {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newResource), body: JSON.stringify(newResource),
headers: { headers: {
"Accept-Language": Setting.getAcceptLanguage(), "Accept-Language": Setting.getAcceptLanguage(),
}, },
}).then(res => res.json()); }).then(res => res.json());
} }
export function addResource(resource) { export function addResource(resource) {
const newResource = Setting.deepCopy(resource); const newResource = Setting.deepCopy(resource);
return fetch(`${Setting.ServerUrl}/api/add-resource`, { return fetch(`${Setting.ServerUrl}/api/add-resource`, {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newResource), body: JSON.stringify(newResource),
headers: { headers: {
"Accept-Language": Setting.getAcceptLanguage(), "Accept-Language": Setting.getAcceptLanguage(),
}, },
}).then(res => res.json()); }).then(res => res.json());
} }
export function deleteResource(resource, provider = "") { export function deleteResource(resource, provider = "") {
const newResource = Setting.deepCopy(resource); const newResource = Setting.deepCopy(resource);
return fetch(`${Setting.ServerUrl}/api/delete-resource?provider=${provider}`, { return fetch(`${Setting.ServerUrl}/api/delete-resource?provider=${provider}`, {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newResource), body: JSON.stringify(newResource),
headers: { headers: {
"Accept-Language": Setting.getAcceptLanguage(), "Accept-Language": Setting.getAcceptLanguage(),
}, },
}).then(res => res.json()); }).then(res => res.json());
} }
export function uploadResource(owner, user, tag, parent, fullFilePath, file, provider = "") { export function uploadResource(owner, user, tag, parent, fullFilePath, file, provider = "") {
const application = "app-built-in"; const application = "app-built-in";
const formData = new FormData(); const formData = new FormData();
formData.append("file", file); formData.append("file", file);
return fetch(`${Setting.ServerUrl}/api/upload-resource?owner=${owner}&user=${user}&application=${application}&tag=${tag}&parent=${parent}&fullFilePath=${encodeURIComponent(fullFilePath)}&provider=${provider}`, { return fetch(`${Setting.ServerUrl}/api/upload-resource?owner=${owner}&user=${user}&application=${application}&tag=${tag}&parent=${parent}&fullFilePath=${encodeURIComponent(fullFilePath)}&provider=${provider}`, {
body: formData, body: formData,
method: "POST", method: "POST",
credentials: "include", credentials: "include",
headers: { headers: {
"Accept-Language": Setting.getAcceptLanguage(), "Accept-Language": Setting.getAcceptLanguage(),
}, },
}).then(res => res.json()); }).then(res => res.json());
} }

View File

@ -1,229 +1,229 @@
// Copyright 2021 The Casdoor Authors. All Rights Reserved. // Copyright 2021 The Casdoor Authors. All Rights Reserved.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {Button, Col, Row} from "antd"; import {Button, Col, Row} from "antd";
import i18next from "i18next"; import i18next from "i18next";
import * as UserBackend from "../backend/UserBackend"; import * as UserBackend from "../backend/UserBackend";
import * as Setting from "../Setting"; import * as Setting from "../Setting";
import * as Provider from "../auth/Provider"; import * as Provider from "../auth/Provider";
import * as AuthBackend from "../auth/AuthBackend"; import * as AuthBackend from "../auth/AuthBackend";
import {goToWeb3Url} from "../auth/ProviderButton"; import {goToWeb3Url} from "../auth/ProviderButton";
import AccountAvatar from "../account/AccountAvatar"; import AccountAvatar from "../account/AccountAvatar";
import {WechatOfficialAccountModal} from "../auth/Util"; import {WechatOfficialAccountModal} from "../auth/Util";
class OAuthWidget extends React.Component { class OAuthWidget extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
classes: props, classes: props,
addressOptions: [], addressOptions: [],
affiliationOptions: [], affiliationOptions: [],
}; };
} }
UNSAFE_componentWillMount() { UNSAFE_componentWillMount() {
this.getAddressOptions(this.props.application); this.getAddressOptions(this.props.application);
this.getAffiliationOptions(this.props.application, this.props.user); this.getAffiliationOptions(this.props.application, this.props.user);
} }
getAddressOptions(application) { getAddressOptions(application) {
if (application.affiliationUrl === "") { if (application.affiliationUrl === "") {
return; return;
} }
const addressUrl = application.affiliationUrl.split("|")[0]; const addressUrl = application.affiliationUrl.split("|")[0];
UserBackend.getAddressOptions(addressUrl) UserBackend.getAddressOptions(addressUrl)
.then((addressOptions) => { .then((addressOptions) => {
this.setState({ this.setState({
addressOptions: addressOptions, addressOptions: addressOptions,
}); });
}); });
} }
getAffiliationOptions(application, user) { getAffiliationOptions(application, user) {
if (application.affiliationUrl === "") { if (application.affiliationUrl === "") {
return; return;
} }
const affiliationUrl = application.affiliationUrl.split("|")[1]; const affiliationUrl = application.affiliationUrl.split("|")[1];
const code = user.address[user.address.length - 1]; const code = user.address[user.address.length - 1];
UserBackend.getAffiliationOptions(affiliationUrl, code) UserBackend.getAffiliationOptions(affiliationUrl, code)
.then((affiliationOptions) => { .then((affiliationOptions) => {
this.setState({ this.setState({
affiliationOptions: affiliationOptions, affiliationOptions: affiliationOptions,
}); });
}); });
} }
updateUserField(key, value) { updateUserField(key, value) {
this.props.onUpdateUserField(key, value); this.props.onUpdateUserField(key, value);
} }
unlinked() { unlinked() {
this.props.onUnlinked(); this.props.onUnlinked();
} }
getProviderLink(user, provider) { getProviderLink(user, provider) {
if (provider.type === "GitHub") { if (provider.type === "GitHub") {
return `https://github.com/${this.getUserProperty(user, provider.type, "username")}`; return `https://github.com/${this.getUserProperty(user, provider.type, "username")}`;
} else if (provider.type === "Google") { } else if (provider.type === "Google") {
return "https://mail.google.com"; return "https://mail.google.com";
} else { } else {
return ""; return "";
} }
} }
getUserProperty(user, providerType, propertyName) { getUserProperty(user, providerType, propertyName) {
const key = `oauth_${providerType}_${propertyName}`; const key = `oauth_${providerType}_${propertyName}`;
if (user.properties === null) {return "";} if (user.properties === null) {return "";}
return user.properties[key]; return user.properties[key];
} }
unlinkUser(providerType, linkedValue) { unlinkUser(providerType, linkedValue) {
const body = { const body = {
providerType: providerType, providerType: providerType,
// should add the unlink user's info, cause the user may not be logged in, but a admin want to unlink the user. // should add the unlink user's info, cause the user may not be logged in, but a admin want to unlink the user.
user: this.props.user, user: this.props.user,
}; };
if (providerType === "MetaMask" || providerType === "Web3Onboard") { if (providerType === "MetaMask" || providerType === "Web3Onboard") {
import("../auth/Web3Auth") import("../auth/Web3Auth")
.then(module => { .then(module => {
const delWeb3AuthToken = module.delWeb3AuthToken; const delWeb3AuthToken = module.delWeb3AuthToken;
delWeb3AuthToken(linkedValue); delWeb3AuthToken(linkedValue);
AuthBackend.unlink(body) AuthBackend.unlink(body)
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", "Unlinked successfully"); Setting.showMessage("success", "Unlinked successfully");
this.unlinked(); this.unlinked();
} else { } else {
Setting.showMessage("error", `Failed to unlink: ${res.msg}`); Setting.showMessage("error", `Failed to unlink: ${res.msg}`);
} }
}); });
}); });
return; return;
} }
AuthBackend.unlink(body) AuthBackend.unlink(body)
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", "Unlinked successfully"); Setting.showMessage("success", "Unlinked successfully");
this.unlinked(); this.unlinked();
} else { } else {
Setting.showMessage("error", `Failed to unlink: ${res.msg}`); Setting.showMessage("error", `Failed to unlink: ${res.msg}`);
} }
}); });
} }
renderIdp(user, application, providerItem) { renderIdp(user, application, providerItem) {
const provider = providerItem.provider; const provider = providerItem.provider;
const linkedValue = user[provider.type.toLowerCase()]; const linkedValue = user[provider.type.toLowerCase()];
const profileUrl = this.getProviderLink(user, provider); const profileUrl = this.getProviderLink(user, provider);
const id = this.getUserProperty(user, provider.type, "id"); const id = this.getUserProperty(user, provider.type, "id");
const username = this.getUserProperty(user, provider.type, "username"); const username = this.getUserProperty(user, provider.type, "username");
const displayName = this.getUserProperty(user, provider.type, "displayName"); const displayName = this.getUserProperty(user, provider.type, "displayName");
const email = this.getUserProperty(user, provider.type, "email"); const email = this.getUserProperty(user, provider.type, "email");
let avatarUrl = this.getUserProperty(user, provider.type, "avatarUrl"); let avatarUrl = this.getUserProperty(user, provider.type, "avatarUrl");
// the account user // the account user
const account = this.props.account; const account = this.props.account;
if (avatarUrl === "" || avatarUrl === undefined) { if (avatarUrl === "" || avatarUrl === undefined) {
avatarUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAQAAACROWYpAAAAHElEQVR42mNkoAAwjmoe1TyqeVTzqOZRzcNZMwB18wAfEFQkPQAAAABJRU5ErkJggg=="; avatarUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAQAAACROWYpAAAAHElEQVR42mNkoAAwjmoe1TyqeVTzqOZRzcNZMwB18wAfEFQkPQAAAABJRU5ErkJggg==";
} }
let name = (username === undefined) ? displayName : `${displayName} (${username})`; let name = (username === undefined) ? displayName : `${displayName} (${username})`;
if (name === undefined) { if (name === undefined) {
if (id !== undefined) { if (id !== undefined) {
name = id; name = id;
} else if (email !== undefined) { } else if (email !== undefined) {
name = email; name = email;
} else { } else {
name = linkedValue; name = linkedValue;
} }
} }
let linkButtonWidth = "110px"; let linkButtonWidth = "110px";
if (Setting.getLanguage() === "id") { if (Setting.getLanguage() === "id") {
linkButtonWidth = "160px"; linkButtonWidth = "160px";
} }
return ( return (
<Row key={provider.name} style={{marginTop: "20px"}} > <Row key={provider.name} style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={this.props.labelSpan}> <Col style={{marginTop: "5px"}} span={this.props.labelSpan}>
{ {
Setting.getProviderLogo(provider) Setting.getProviderLogo(provider)
} }
<span style={{marginLeft: "5px"}}> <span style={{marginLeft: "5px"}}>
{ {
`${provider.type}:` `${provider.type}:`
} }
</span> </span>
</Col> </Col>
<Col span={24 - this.props.labelSpan} > <Col span={24 - this.props.labelSpan} >
<AccountAvatar style={{marginRight: "10px"}} size={30} src={avatarUrl} alt={name} referrerPolicy="no-referrer" /> <AccountAvatar style={{marginRight: "10px"}} size={30} src={avatarUrl} alt={name} referrerPolicy="no-referrer" />
<span style={{ <span style={{
width: this.props.labelSpan === 3 ? "300px" : "200px", width: this.props.labelSpan === 3 ? "300px" : "200px",
display: (Setting.isMobile()) ? "inline" : "inline-block", display: (Setting.isMobile()) ? "inline" : "inline-block",
overflow: "hidden", overflow: "hidden",
textOverflow: "ellipsis", textOverflow: "ellipsis",
}} title={name}> }} title={name}>
{ {
linkedValue === "" ? ( linkedValue === "" ? (
`(${i18next.t("general:empty")})` `(${i18next.t("general:empty")})`
) : ( ) : (
profileUrl === "" ? name : ( profileUrl === "" ? name : (
<a target="_blank" rel="noreferrer" href={profileUrl}> <a target="_blank" rel="noreferrer" href={profileUrl}>
{ {
name name
} }
</a> </a>
) )
) )
} }
</span> </span>
{ {
linkedValue === "" ? ( linkedValue === "" ? (
provider.category === "Web3" ? ( provider.category === "Web3" ? (
<Button style={{marginLeft: "20px", width: linkButtonWidth}} type="primary" disabled={user.id !== account.id} onClick={() => goToWeb3Url(application, provider, "link")}>{i18next.t("user:Link")}</Button> <Button style={{marginLeft: "20px", width: linkButtonWidth}} type="primary" disabled={user.id !== account.id} onClick={() => goToWeb3Url(application, provider, "link")}>{i18next.t("user:Link")}</Button>
) : ( ) : (
provider.type === "WeChat" && provider.clientId2 !== "" && provider.clientSecret2 !== "" && provider.disableSsl === true && !navigator.userAgent.includes("MicroMessenger") ? ( provider.type === "WeChat" && provider.clientId2 !== "" && provider.clientSecret2 !== "" && provider.disableSsl === true && !navigator.userAgent.includes("MicroMessenger") ? (
<a key={provider.displayName}> <a key={provider.displayName}>
<Button style={{marginLeft: "20px", width: linkButtonWidth}} type="primary" disabled={user.id !== account.id} onClick={ <Button style={{marginLeft: "20px", width: linkButtonWidth}} type="primary" disabled={user.id !== account.id} onClick={
() => { () => {
WechatOfficialAccountModal(application, provider, "link"); WechatOfficialAccountModal(application, provider, "link");
} }
}>{i18next.t("user:Link")}</Button> }>{i18next.t("user:Link")}</Button>
</a> </a>
) : ( ) : (
<a key={provider.displayName} href={user.id !== account.id ? null : Provider.getAuthUrl(application, provider, "link")}> <a key={provider.displayName} href={user.id !== account.id ? null : Provider.getAuthUrl(application, provider, "link")}>
<Button style={{marginLeft: "20px", width: linkButtonWidth}} type="primary" disabled={user.id !== account.id}>{i18next.t("user:Link")}</Button> <Button style={{marginLeft: "20px", width: linkButtonWidth}} type="primary" disabled={user.id !== account.id}>{i18next.t("user:Link")}</Button>
</a> </a>
) )
) )
) : ( ) : (
<Button disabled={!providerItem.canUnlink && !Setting.isAdminUser(account)} style={{marginLeft: "20px", width: linkButtonWidth}} onClick={() => this.unlinkUser(provider.type, linkedValue)}>{i18next.t("user:Unlink")}</Button> <Button disabled={!providerItem.canUnlink && !Setting.isAdminUser(account)} style={{marginLeft: "20px", width: linkButtonWidth}} onClick={() => this.unlinkUser(provider.type, linkedValue)}>{i18next.t("user:Unlink")}</Button>
) )
} }
</Col> </Col>
</Row> </Row>
); );
} }
render() { render() {
return this.renderIdp(this.props.user, this.props.application, this.props.providerItem); return this.renderIdp(this.props.user, this.props.application, this.props.providerItem);
} }
} }
export default OAuthWidget; export default OAuthWidget;