diff --git a/.gitattributes b/.gitattributes
index b5a9e8e4..b1f2c5cf 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,5 +1,5 @@
-*.go linguist-detectable=true
-*.js linguist-detectable=false
-# 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.
+*.go linguist-detectable=true
+*.js linguist-detectable=false
+# 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.
*.sh text eol=lf
\ No newline at end of file
diff --git a/README.md b/README.md
index a7f33583..b1397443 100644
--- a/README.md
+++ b/README.md
@@ -1,102 +1,102 @@
-
📦⚡️ Casdoor
-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
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Sponsored by
-
-
-
-
-
-
-
-
- Build auth with fraud prevention, faster. Try Stytch for API-first authentication, user & org management, multi-tenant SSO, MFA, device fingerprinting, and more.
-
-
-
-## Online demo
-
-- 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)
-
-## Documentation
-
-https://casdoor.org
-
-## Install
-
-- By source code: https://casdoor.org/docs/basic/server-installation
-- By Docker: https://casdoor.org/docs/basic/try-with-docker
-- By Kubernetes Helm: https://casdoor.org/docs/basic/try-with-helm
-
-## How to connect to Casdoor?
-
-https://casdoor.org/docs/how-to-connect/overview
-
-## Casdoor Public API
-
-- Docs: https://casdoor.org/docs/basic/public-api
-- Swagger: https://door.casdoor.com/swagger
-
-## Integrations
-
-https://casdoor.org/docs/category/integrations
-
-## How to contact?
-
-- Discord: https://discord.gg/5rPsrAzK7S
-- Contact: https://casdoor.org/help
-
-## 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).
-
-### 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.
-
-## License
-
-[Apache-2.0](https://github.com/casdoor/casdoor/blob/master/LICENSE)
+📦⚡️ Casdoor
+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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Sponsored by
+
+
+
+
+
+
+
+
+ Build auth with fraud prevention, faster. Try Stytch for API-first authentication, user & org management, multi-tenant SSO, MFA, device fingerprinting, and more.
+
+
+
+## Online demo
+
+- 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)
+
+## Documentation
+
+https://casdoor.org
+
+## Install
+
+- By source code: https://casdoor.org/docs/basic/server-installation
+- By Docker: https://casdoor.org/docs/basic/try-with-docker
+- By Kubernetes Helm: https://casdoor.org/docs/basic/try-with-helm
+
+## How to connect to Casdoor?
+
+https://casdoor.org/docs/how-to-connect/overview
+
+## Casdoor Public API
+
+- Docs: https://casdoor.org/docs/basic/public-api
+- Swagger: https://door.casdoor.com/swagger
+
+## Integrations
+
+https://casdoor.org/docs/category/integrations
+
+## How to contact?
+
+- Discord: https://discord.gg/5rPsrAzK7S
+- Contact: https://casdoor.org/help
+
+## 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).
+
+### 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.
+
+## License
+
+[Apache-2.0](https://github.com/casdoor/casdoor/blob/master/LICENSE)
diff --git a/web/craco.config.js b/web/craco.config.js
index a2f55393..643a1385 100644
--- a/web/craco.config.js
+++ b/web/craco.config.js
@@ -1,97 +1,97 @@
-const CracoLessPlugin = require("craco-less");
-const path = require("path");
-
-module.exports = {
- devServer: {
- proxy: {
- "/api": {
- target: "http://localhost:8000",
- changeOrigin: true,
- },
- "/swagger": {
- target: "http://localhost:8000",
- changeOrigin: true,
- },
- "/files": {
- target: "http://localhost:8000",
- changeOrigin: true,
- },
- "/.well-known/openid-configuration": {
- target: "http://localhost:8000",
- changeOrigin: true,
- },
- "/cas/serviceValidate": {
- target: "http://localhost:8000",
- changeOrigin: true,
- },
- "/cas/proxyValidate": {
- target: "http://localhost:8000",
- changeOrigin: true,
- },
- "/cas/proxy": {
- target: "http://localhost:8000",
- changeOrigin: true,
- },
- "/cas/validate": {
- target: "http://localhost:8000",
- changeOrigin: true,
- },
- "/scim": {
- target: "http://localhost:8000",
- changeOrigin: true,
- }
- },
- },
- plugins: [
- {
- plugin: CracoLessPlugin,
- options: {
- lessLoaderOptions: {
- lessOptions: {
- modifyVars: {"@primary-color": "rgb(89,54,213)", "@border-radius-base": "5px"},
- javascriptEnabled: true,
- },
- },
- },
- },
- ],
- webpack: {
- configure: (webpackConfig, { env, paths }) => {
- paths.appBuild = path.resolve(__dirname, "build-temp");
- webpackConfig.output.path = path.resolve(__dirname, "build-temp");
-
- // ignore webpack warnings by source-map-loader
- // https://github.com/facebook/create-react-app/pull/11752#issuecomment-1345231546
- webpackConfig.ignoreWarnings = [
- function ignoreSourcemapsloaderWarnings(warning) {
- return (
- warning.module &&
- warning.module.resource.includes("node_modules") &&
- warning.details &&
- warning.details.includes("source-map-loader")
- );
- },
- ];
-
- // use polyfill Buffer with Webpack 5
- // https://viglucci.io/articles/how-to-polyfill-buffer-with-webpack-5
- // https://craco.js.org/docs/configuration/webpack/
- webpackConfig.resolve.fallback = {
- buffer: require.resolve("buffer/"),
- process: false,
- util: false,
- url: false,
- zlib: false,
- stream: false,
- http: false,
- https: false,
- assert: false,
- crypto: false,
- os: false,
- fs: false,
- };
-
- return webpackConfig;
- },
- },
-};
+const CracoLessPlugin = require("craco-less");
+const path = require("path");
+
+module.exports = {
+ devServer: {
+ proxy: {
+ "/api": {
+ target: "http://localhost:8000",
+ changeOrigin: true,
+ },
+ "/swagger": {
+ target: "http://localhost:8000",
+ changeOrigin: true,
+ },
+ "/files": {
+ target: "http://localhost:8000",
+ changeOrigin: true,
+ },
+ "/.well-known/openid-configuration": {
+ target: "http://localhost:8000",
+ changeOrigin: true,
+ },
+ "/cas/serviceValidate": {
+ target: "http://localhost:8000",
+ changeOrigin: true,
+ },
+ "/cas/proxyValidate": {
+ target: "http://localhost:8000",
+ changeOrigin: true,
+ },
+ "/cas/proxy": {
+ target: "http://localhost:8000",
+ changeOrigin: true,
+ },
+ "/cas/validate": {
+ target: "http://localhost:8000",
+ changeOrigin: true,
+ },
+ "/scim": {
+ target: "http://localhost:8000",
+ changeOrigin: true,
+ }
+ },
+ },
+ plugins: [
+ {
+ plugin: CracoLessPlugin,
+ options: {
+ lessLoaderOptions: {
+ lessOptions: {
+ modifyVars: {"@primary-color": "rgb(89,54,213)", "@border-radius-base": "5px"},
+ javascriptEnabled: true,
+ },
+ },
+ },
+ },
+ ],
+ webpack: {
+ configure: (webpackConfig, { env, paths }) => {
+ paths.appBuild = path.resolve(__dirname, "build-temp");
+ webpackConfig.output.path = path.resolve(__dirname, "build-temp");
+
+ // ignore webpack warnings by source-map-loader
+ // https://github.com/facebook/create-react-app/pull/11752#issuecomment-1345231546
+ webpackConfig.ignoreWarnings = [
+ function ignoreSourcemapsloaderWarnings(warning) {
+ return (
+ warning.module &&
+ warning.module.resource.includes("node_modules") &&
+ warning.details &&
+ warning.details.includes("source-map-loader")
+ );
+ },
+ ];
+
+ // use polyfill Buffer with Webpack 5
+ // https://viglucci.io/articles/how-to-polyfill-buffer-with-webpack-5
+ // https://craco.js.org/docs/configuration/webpack/
+ webpackConfig.resolve.fallback = {
+ buffer: require.resolve("buffer/"),
+ process: false,
+ util: false,
+ url: false,
+ zlib: false,
+ stream: false,
+ http: false,
+ https: false,
+ assert: false,
+ crypto: false,
+ os: false,
+ fs: false,
+ };
+
+ return webpackConfig;
+ },
+ },
+};
diff --git a/web/mv.js b/web/mv.js
index dbbfd7cf..9f78ab43 100644
--- a/web/mv.js
+++ b/web/mv.js
@@ -1,21 +1,21 @@
-const fs = require("fs");
-const path = require("path");
-
-const sourceDir = path.join(__dirname, "build-temp");
-const targetDir = path.join(__dirname, "build");
-
-if (!fs.existsSync(sourceDir)) {
- // eslint-disable-next-line no-console
- console.error(`Source directory "${sourceDir}" does not exist.`);
- process.exit(1);
-}
-
-if (fs.existsSync(targetDir)) {
- fs.rmSync(targetDir, {recursive: true, force: true});
- // eslint-disable-next-line no-console
- console.log(`Target directory "${targetDir}" has been deleted successfully.`);
-}
-
-fs.renameSync(sourceDir, targetDir);
-// eslint-disable-next-line no-console
-console.log(`Renamed "${sourceDir}" to "${targetDir}" successfully.`);
+const fs = require("fs");
+const path = require("path");
+
+const sourceDir = path.join(__dirname, "build-temp");
+const targetDir = path.join(__dirname, "build");
+
+if (!fs.existsSync(sourceDir)) {
+ // eslint-disable-next-line no-console
+ console.error(`Source directory "${sourceDir}" does not exist.`);
+ process.exit(1);
+}
+
+if (fs.existsSync(targetDir)) {
+ fs.rmSync(targetDir, {recursive: true, force: true});
+ // eslint-disable-next-line no-console
+ console.log(`Target directory "${targetDir}" has been deleted successfully.`);
+}
+
+fs.renameSync(sourceDir, targetDir);
+// eslint-disable-next-line no-console
+console.log(`Renamed "${sourceDir}" to "${targetDir}" successfully.`);
diff --git a/web/src/Conf.js b/web/src/Conf.js
index df9cab56..2148c606 100644
--- a/web/src/Conf.js
+++ b/web/src/Conf.js
@@ -1,36 +1,36 @@
-// Copyright 2021 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.
-
-export const DefaultApplication = "app-built-in";
-
-export const CasvisorUrl = "";
-
-export const ShowGithubCorner = false;
-export const IsDemoMode = false;
-
-export const ForceLanguage = "";
-export const DefaultLanguage = "en";
-
-export const InitThemeAlgorithm = true;
-export const ThemeDefault = {
- themeType: "default",
- colorPrimary: "#5734d3",
- borderRadius: 6,
- isCompact: false,
-};
-
-export const CustomFooter = null;
-
-// Blank or null to hide Ai Assistant button
-export const AiAssistantUrl = "https://ai.casbin.com";
+// Copyright 2021 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.
+
+export const DefaultApplication = "app-built-in";
+
+export const CasvisorUrl = "";
+
+export const ShowGithubCorner = false;
+export const IsDemoMode = false;
+
+export const ForceLanguage = "";
+export const DefaultLanguage = "en";
+
+export const InitThemeAlgorithm = true;
+export const ThemeDefault = {
+ themeType: "default",
+ colorPrimary: "#5734d3",
+ borderRadius: 6,
+ isCompact: false,
+};
+
+export const CustomFooter = null;
+
+// Blank or null to hide Ai Assistant button
+export const AiAssistantUrl = "https://ai.casbin.com";
diff --git a/web/src/ResourceListPage.js b/web/src/ResourceListPage.js
index f8a8fb91..42dceca2 100644
--- a/web/src/ResourceListPage.js
+++ b/web/src/ResourceListPage.js
@@ -1,341 +1,341 @@
-// Copyright 2021 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 React from "react";
-import {Button, Image, Table, Upload} from "antd";
-import {UploadOutlined} from "@ant-design/icons";
-import copy from "copy-to-clipboard";
-import * as Setting from "./Setting";
-import * as ResourceBackend from "./backend/ResourceBackend";
-import i18next from "i18next";
-import {Link} from "react-router-dom";
-import BaseListPage from "./BaseListPage";
-import PopconfirmModal from "./common/modal/PopconfirmModal";
-
-class ResourceListPage extends BaseListPage {
- constructor(props) {
- super(props);
- }
-
- componentDidMount() {
- this.setState({
- fileList: [],
- uploading: false,
- });
- }
-
- deleteResource(i) {
- ResourceBackend.deleteResource(this.state.data[i])
- .then((res) => {
- if (res.status === "ok") {
- Setting.showMessage("success", i18next.t("general:Successfully deleted"));
- this.fetch({
- pagination: {
- ...this.state.pagination,
- current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
- },
- });
- } else {
- Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
- }
- })
- .catch(error => {
- Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
- });
- }
-
- handleUpload(info) {
- this.setState({uploading: true});
- const filename = info.fileList[0].name;
- 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)
- .then(res => {
- if (res.status === "ok") {
- Setting.showMessage("success", i18next.t("application:File uploaded successfully"));
-
- const {pagination} = this.state;
- this.fetch({pagination});
- } else {
- Setting.showMessage("error", res.msg);
- }
- }).finally(() => {
- this.setState({uploading: false});
- });
- }
-
- renderUpload() {
- return (
- {return false;}} onChange={info => {this.handleUpload(info);}}>
- } loading={this.state.uploading} type="primary" size="small">
- {i18next.t("resource:Upload a file...")}
-
-
- );
- }
-
- renderTable(resources) {
- const columns = [
- {
- title: i18next.t("general:Provider"),
- dataIndex: "provider",
- key: "provider",
- width: "150px",
- sorter: true,
- ...this.getColumnSearchProps("provider"),
- render: (text, record, index) => {
- return (
-
- {text}
-
- );
- },
- },
- {
- title: i18next.t("general:Organization"),
- dataIndex: "owner",
- key: "owner",
- width: "120px",
- sorter: true,
- ...this.getColumnSearchProps("owner"),
- render: (text, record, index) => {
- return (
-
- {text}
-
- );
- },
- },
- {
- title: i18next.t("general:Application"),
- dataIndex: "application",
- key: "application",
- width: "80px",
- sorter: true,
- ...this.getColumnSearchProps("application"),
- render: (text, record, index) => {
- return (
-
- {text}
-
- );
- },
- },
- {
- title: i18next.t("general:User"),
- dataIndex: "user",
- key: "user",
- width: "80px",
- sorter: true,
- ...this.getColumnSearchProps("user"),
- render: (text, record, index) => {
- return (
-
- {text}
-
- );
- },
- },
- {
- title: i18next.t("resource:Parent"),
- dataIndex: "parent",
- key: "parent",
- width: "80px",
- sorter: true,
- ...this.getColumnSearchProps("parent"),
- },
- {
- title: i18next.t("general:Name"),
- dataIndex: "name",
- key: "name",
- width: "150px",
- sorter: true,
- ...this.getColumnSearchProps("name"),
- },
- {
- title: i18next.t("general:Created time"),
- dataIndex: "createdTime",
- key: "createdTime",
- width: "150px",
- sorter: true,
- render: (text, record, index) => {
- return Setting.getFormattedDate(text);
- },
- },
- {
- title: i18next.t("user:Tag"),
- dataIndex: "tag",
- key: "tag",
- width: "80px",
- sorter: true,
- ...this.getColumnSearchProps("tag"),
- },
- // {
- // title: i18next.t("resource:File name"),
- // dataIndex: 'fileName',
- // key: 'fileName',
- // width: '120px',
- // sorter: (a, b) => a.fileName.localeCompare(b.fileName),
- // },
- {
- title: i18next.t("provider:Type"),
- dataIndex: "fileType",
- key: "fileType",
- width: "80px",
- sorter: true,
- ...this.getColumnSearchProps("fileType"),
- },
- {
- title: i18next.t("resource:Format"),
- dataIndex: "fileFormat",
- key: "fileFormat",
- width: "80px",
- sorter: true,
- ...this.getColumnSearchProps("fileFormat"),
- },
- {
- title: i18next.t("resource:File size"),
- dataIndex: "fileSize",
- key: "fileSize",
- width: "100px",
- sorter: true,
- render: (text, record, index) => {
- return Setting.getFriendlyFileSize(text);
- },
- },
- {
- title: i18next.t("general:Preview"),
- dataIndex: "preview",
- key: "preview",
- width: "100px",
- fixed: (Setting.isMobile()) ? "false" : "right",
- render: (text, record, index) => {
- if (record.fileType === "image") {
- const errorImage = "data:image/png;base64,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";
- return (
-
- );
- } else if (record.fileType === "video") {
- return (
-
-
-
- );
- }
- },
- },
- {
- title: i18next.t("general:URL"),
- dataIndex: "url",
- key: "url",
- width: "120px",
- fixed: (Setting.isMobile()) ? "false" : "right",
- render: (text, record, index) => {
- return (
-
- {
- copy(record.url);
- Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
- }}
- >
- {i18next.t("resource:Copy Link")}
-
-
- );
- },
- },
- {
- title: i18next.t("general:Action"),
- dataIndex: "",
- key: "op",
- width: "70px",
- fixed: (Setting.isMobile()) ? "false" : "right",
- render: (text, record, index) => {
- return (
-
-
this.deleteResource(index)}
- okText={i18next.t("general:OK")}
- cancelText={i18next.t("general:Cancel")}
- >
-
-
- );
- },
- },
- ];
-
- const paginationProps = {
- total: this.state.pagination.total,
- showQuickJumper: true,
- showSizeChanger: true,
- showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
- };
-
- return (
-
-
(
-
- {i18next.t("general:Resources")}
- {/* {i18next.t("general:Add")} */}
- {
- this.renderUpload()
- }
-
- )}
- loading={this.state.loading}
- onChange={this.handleTableChange}
- />
-
- );
- }
-
- fetch = (params = {}) => {
- const field = params.searchedColumn, value = params.searchText;
- const sortField = params.sortField, sortOrder = params.sortOrder;
- 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)
- .then((res) => {
- this.setState({
- loading: false,
- });
- if (res.status === "ok") {
- this.setState({
- data: res.data,
- pagination: {
- ...params.pagination,
- total: res.data2,
- },
- searchText: params.searchText,
- searchedColumn: params.searchedColumn,
- });
- } else {
- if (res.data.includes("Please login first")) {
- this.setState({
- loading: false,
- isAuthorized: false,
- });
- }
- }
- });
- };
-}
-
-export default ResourceListPage;
+// Copyright 2021 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 React from "react";
+import {Button, Image, Table, Upload} from "antd";
+import {UploadOutlined} from "@ant-design/icons";
+import copy from "copy-to-clipboard";
+import * as Setting from "./Setting";
+import * as ResourceBackend from "./backend/ResourceBackend";
+import i18next from "i18next";
+import {Link} from "react-router-dom";
+import BaseListPage from "./BaseListPage";
+import PopconfirmModal from "./common/modal/PopconfirmModal";
+
+class ResourceListPage extends BaseListPage {
+ constructor(props) {
+ super(props);
+ }
+
+ componentDidMount() {
+ this.setState({
+ fileList: [],
+ uploading: false,
+ });
+ }
+
+ deleteResource(i) {
+ ResourceBackend.deleteResource(this.state.data[i])
+ .then((res) => {
+ if (res.status === "ok") {
+ Setting.showMessage("success", i18next.t("general:Successfully deleted"));
+ this.fetch({
+ pagination: {
+ ...this.state.pagination,
+ current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
+ },
+ });
+ } else {
+ Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
+ }
+ })
+ .catch(error => {
+ Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
+ });
+ }
+
+ handleUpload(info) {
+ this.setState({uploading: true});
+ const filename = info.fileList[0].name;
+ 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)
+ .then(res => {
+ if (res.status === "ok") {
+ Setting.showMessage("success", i18next.t("application:File uploaded successfully"));
+
+ const {pagination} = this.state;
+ this.fetch({pagination});
+ } else {
+ Setting.showMessage("error", res.msg);
+ }
+ }).finally(() => {
+ this.setState({uploading: false});
+ });
+ }
+
+ renderUpload() {
+ return (
+ {return false;}} onChange={info => {this.handleUpload(info);}}>
+ } loading={this.state.uploading} type="primary" size="small">
+ {i18next.t("resource:Upload a file...")}
+
+
+ );
+ }
+
+ renderTable(resources) {
+ const columns = [
+ {
+ title: i18next.t("general:Provider"),
+ dataIndex: "provider",
+ key: "provider",
+ width: "150px",
+ sorter: true,
+ ...this.getColumnSearchProps("provider"),
+ render: (text, record, index) => {
+ return (
+
+ {text}
+
+ );
+ },
+ },
+ {
+ title: i18next.t("general:Organization"),
+ dataIndex: "owner",
+ key: "owner",
+ width: "120px",
+ sorter: true,
+ ...this.getColumnSearchProps("owner"),
+ render: (text, record, index) => {
+ return (
+
+ {text}
+
+ );
+ },
+ },
+ {
+ title: i18next.t("general:Application"),
+ dataIndex: "application",
+ key: "application",
+ width: "80px",
+ sorter: true,
+ ...this.getColumnSearchProps("application"),
+ render: (text, record, index) => {
+ return (
+
+ {text}
+
+ );
+ },
+ },
+ {
+ title: i18next.t("general:User"),
+ dataIndex: "user",
+ key: "user",
+ width: "80px",
+ sorter: true,
+ ...this.getColumnSearchProps("user"),
+ render: (text, record, index) => {
+ return (
+
+ {text}
+
+ );
+ },
+ },
+ {
+ title: i18next.t("resource:Parent"),
+ dataIndex: "parent",
+ key: "parent",
+ width: "80px",
+ sorter: true,
+ ...this.getColumnSearchProps("parent"),
+ },
+ {
+ title: i18next.t("general:Name"),
+ dataIndex: "name",
+ key: "name",
+ width: "150px",
+ sorter: true,
+ ...this.getColumnSearchProps("name"),
+ },
+ {
+ title: i18next.t("general:Created time"),
+ dataIndex: "createdTime",
+ key: "createdTime",
+ width: "150px",
+ sorter: true,
+ render: (text, record, index) => {
+ return Setting.getFormattedDate(text);
+ },
+ },
+ {
+ title: i18next.t("user:Tag"),
+ dataIndex: "tag",
+ key: "tag",
+ width: "80px",
+ sorter: true,
+ ...this.getColumnSearchProps("tag"),
+ },
+ // {
+ // title: i18next.t("resource:File name"),
+ // dataIndex: 'fileName',
+ // key: 'fileName',
+ // width: '120px',
+ // sorter: (a, b) => a.fileName.localeCompare(b.fileName),
+ // },
+ {
+ title: i18next.t("provider:Type"),
+ dataIndex: "fileType",
+ key: "fileType",
+ width: "80px",
+ sorter: true,
+ ...this.getColumnSearchProps("fileType"),
+ },
+ {
+ title: i18next.t("resource:Format"),
+ dataIndex: "fileFormat",
+ key: "fileFormat",
+ width: "80px",
+ sorter: true,
+ ...this.getColumnSearchProps("fileFormat"),
+ },
+ {
+ title: i18next.t("resource:File size"),
+ dataIndex: "fileSize",
+ key: "fileSize",
+ width: "100px",
+ sorter: true,
+ render: (text, record, index) => {
+ return Setting.getFriendlyFileSize(text);
+ },
+ },
+ {
+ title: i18next.t("general:Preview"),
+ dataIndex: "preview",
+ key: "preview",
+ width: "100px",
+ fixed: (Setting.isMobile()) ? "false" : "right",
+ render: (text, record, index) => {
+ if (record.fileType === "image") {
+ const errorImage = "data:image/png;base64,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";
+ return (
+
+ );
+ } else if (record.fileType === "video") {
+ return (
+
+
+
+ );
+ }
+ },
+ },
+ {
+ title: i18next.t("general:URL"),
+ dataIndex: "url",
+ key: "url",
+ width: "120px",
+ fixed: (Setting.isMobile()) ? "false" : "right",
+ render: (text, record, index) => {
+ return (
+
+ {
+ copy(record.url);
+ Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
+ }}
+ >
+ {i18next.t("resource:Copy Link")}
+
+
+ );
+ },
+ },
+ {
+ title: i18next.t("general:Action"),
+ dataIndex: "",
+ key: "op",
+ width: "70px",
+ fixed: (Setting.isMobile()) ? "false" : "right",
+ render: (text, record, index) => {
+ return (
+
+
this.deleteResource(index)}
+ okText={i18next.t("general:OK")}
+ cancelText={i18next.t("general:Cancel")}
+ >
+
+
+ );
+ },
+ },
+ ];
+
+ const paginationProps = {
+ total: this.state.pagination.total,
+ showQuickJumper: true,
+ showSizeChanger: true,
+ showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
+ };
+
+ return (
+
+
(
+
+ {i18next.t("general:Resources")}
+ {/* {i18next.t("general:Add")} */}
+ {
+ this.renderUpload()
+ }
+
+ )}
+ loading={this.state.loading}
+ onChange={this.handleTableChange}
+ />
+
+ );
+ }
+
+ fetch = (params = {}) => {
+ const field = params.searchedColumn, value = params.searchText;
+ const sortField = params.sortField, sortOrder = params.sortOrder;
+ 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)
+ .then((res) => {
+ this.setState({
+ loading: false,
+ });
+ if (res.status === "ok") {
+ this.setState({
+ data: res.data,
+ pagination: {
+ ...params.pagination,
+ total: res.data2,
+ },
+ searchText: params.searchText,
+ searchedColumn: params.searchedColumn,
+ });
+ } else {
+ if (res.data.includes("Please login first")) {
+ this.setState({
+ loading: false,
+ isAuthorized: false,
+ });
+ }
+ }
+ });
+ };
+}
+
+export default ResourceListPage;
diff --git a/web/src/auth/OidcDiscoveryPage.js b/web/src/auth/OidcDiscoveryPage.js
index 7680179f..8a0793c4 100644
--- a/web/src/auth/OidcDiscoveryPage.js
+++ b/web/src/auth/OidcDiscoveryPage.js
@@ -1,30 +1,30 @@
-// Copyright 2021 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 React from "react";
-import * as Setting from "../Setting";
-
-class OdicDiscoveryPage extends React.Component {
- UNSAFE_componentWillMount() {
- if (Setting.isLocalhost()) {
- Setting.goToLink(`${Setting.ServerUrl}/.well-known/openid-configuration`);
- }
- }
-
- render() {
- return null;
- }
-}
-
-export default OdicDiscoveryPage;
+// Copyright 2021 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 React from "react";
+import * as Setting from "../Setting";
+
+class OdicDiscoveryPage extends React.Component {
+ UNSAFE_componentWillMount() {
+ if (Setting.isLocalhost()) {
+ Setting.goToLink(`${Setting.ServerUrl}/.well-known/openid-configuration`);
+ }
+ }
+
+ render() {
+ return null;
+ }
+}
+
+export default OdicDiscoveryPage;
diff --git a/web/src/auth/QqLoginButton.js b/web/src/auth/QqLoginButton.js
index 34092f75..59da252e 100644
--- a/web/src/auth/QqLoginButton.js
+++ b/web/src/auth/QqLoginButton.js
@@ -1,31 +1,31 @@
-// Copyright 2021 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 {createButton} from "react-social-login-buttons";
-
-function Icon({width = 24, height = 24, color}) {
- return ;
-}
-
-const config = {
- text: "Sign in with QQ",
- icon: Icon,
- iconFormat: name => `fa fa-${name}`,
- style: {background: "rgb(94,188,249)"},
- activeStyle: {background: "rgb(76,143,208)"},
-};
-
-const QqLoginButton = createButton(config);
-
-export default QqLoginButton;
+// Copyright 2021 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 {createButton} from "react-social-login-buttons";
+
+function Icon({width = 24, height = 24, color}) {
+ return ;
+}
+
+const config = {
+ text: "Sign in with QQ",
+ icon: Icon,
+ iconFormat: name => `fa fa-${name}`,
+ style: {background: "rgb(94,188,249)"},
+ activeStyle: {background: "rgb(76,143,208)"},
+};
+
+const QqLoginButton = createButton(config);
+
+export default QqLoginButton;
diff --git a/web/src/backend/ResourceBackend.js b/web/src/backend/ResourceBackend.js
index 844e9c18..4afe37d2 100644
--- a/web/src/backend/ResourceBackend.js
+++ b/web/src/backend/ResourceBackend.js
@@ -1,85 +1,85 @@
-// Copyright 2021 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 * as Setting from "../Setting";
-
-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}`, {
- method: "GET",
- credentials: "include",
- headers: {
- "Accept-Language": Setting.getAcceptLanguage(),
- },
- }).then(res => res.json());
-}
-
-export function getResource(owner, name) {
- return fetch(`${Setting.ServerUrl}/api/get-resource?id=${owner}/${encodeURIComponent(name)}`, {
- method: "GET",
- credentials: "include",
- headers: {
- "Accept-Language": Setting.getAcceptLanguage(),
- },
- }).then(res => res.json());
-}
-
-export function updateResource(owner, name, resource) {
- const newResource = Setting.deepCopy(resource);
- return fetch(`${Setting.ServerUrl}/api/update-resource?id=${owner}/${encodeURIComponent(name)}`, {
- method: "POST",
- credentials: "include",
- body: JSON.stringify(newResource),
- headers: {
- "Accept-Language": Setting.getAcceptLanguage(),
- },
- }).then(res => res.json());
-}
-
-export function addResource(resource) {
- const newResource = Setting.deepCopy(resource);
- return fetch(`${Setting.ServerUrl}/api/add-resource`, {
- method: "POST",
- credentials: "include",
- body: JSON.stringify(newResource),
- headers: {
- "Accept-Language": Setting.getAcceptLanguage(),
- },
- }).then(res => res.json());
-}
-
-export function deleteResource(resource, provider = "") {
- const newResource = Setting.deepCopy(resource);
- return fetch(`${Setting.ServerUrl}/api/delete-resource?provider=${provider}`, {
- method: "POST",
- credentials: "include",
- body: JSON.stringify(newResource),
- headers: {
- "Accept-Language": Setting.getAcceptLanguage(),
- },
- }).then(res => res.json());
-}
-
-export function uploadResource(owner, user, tag, parent, fullFilePath, file, provider = "") {
- const application = "app-built-in";
- const formData = new FormData();
- 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}`, {
- body: formData,
- method: "POST",
- credentials: "include",
- headers: {
- "Accept-Language": Setting.getAcceptLanguage(),
- },
- }).then(res => res.json());
-}
+// Copyright 2021 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 * as Setting from "../Setting";
+
+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}`, {
+ method: "GET",
+ credentials: "include",
+ headers: {
+ "Accept-Language": Setting.getAcceptLanguage(),
+ },
+ }).then(res => res.json());
+}
+
+export function getResource(owner, name) {
+ return fetch(`${Setting.ServerUrl}/api/get-resource?id=${owner}/${encodeURIComponent(name)}`, {
+ method: "GET",
+ credentials: "include",
+ headers: {
+ "Accept-Language": Setting.getAcceptLanguage(),
+ },
+ }).then(res => res.json());
+}
+
+export function updateResource(owner, name, resource) {
+ const newResource = Setting.deepCopy(resource);
+ return fetch(`${Setting.ServerUrl}/api/update-resource?id=${owner}/${encodeURIComponent(name)}`, {
+ method: "POST",
+ credentials: "include",
+ body: JSON.stringify(newResource),
+ headers: {
+ "Accept-Language": Setting.getAcceptLanguage(),
+ },
+ }).then(res => res.json());
+}
+
+export function addResource(resource) {
+ const newResource = Setting.deepCopy(resource);
+ return fetch(`${Setting.ServerUrl}/api/add-resource`, {
+ method: "POST",
+ credentials: "include",
+ body: JSON.stringify(newResource),
+ headers: {
+ "Accept-Language": Setting.getAcceptLanguage(),
+ },
+ }).then(res => res.json());
+}
+
+export function deleteResource(resource, provider = "") {
+ const newResource = Setting.deepCopy(resource);
+ return fetch(`${Setting.ServerUrl}/api/delete-resource?provider=${provider}`, {
+ method: "POST",
+ credentials: "include",
+ body: JSON.stringify(newResource),
+ headers: {
+ "Accept-Language": Setting.getAcceptLanguage(),
+ },
+ }).then(res => res.json());
+}
+
+export function uploadResource(owner, user, tag, parent, fullFilePath, file, provider = "") {
+ const application = "app-built-in";
+ const formData = new FormData();
+ 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}`, {
+ body: formData,
+ method: "POST",
+ credentials: "include",
+ headers: {
+ "Accept-Language": Setting.getAcceptLanguage(),
+ },
+ }).then(res => res.json());
+}
diff --git a/web/src/common/OAuthWidget.js b/web/src/common/OAuthWidget.js
index a0f06d2b..84fed699 100644
--- a/web/src/common/OAuthWidget.js
+++ b/web/src/common/OAuthWidget.js
@@ -1,229 +1,229 @@
-// Copyright 2021 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 React from "react";
-import {Button, Col, Row} from "antd";
-import i18next from "i18next";
-import * as UserBackend from "../backend/UserBackend";
-import * as Setting from "../Setting";
-import * as Provider from "../auth/Provider";
-import * as AuthBackend from "../auth/AuthBackend";
-import {goToWeb3Url} from "../auth/ProviderButton";
-import AccountAvatar from "../account/AccountAvatar";
-import {WechatOfficialAccountModal} from "../auth/Util";
-
-class OAuthWidget extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- classes: props,
- addressOptions: [],
- affiliationOptions: [],
- };
- }
-
- UNSAFE_componentWillMount() {
- this.getAddressOptions(this.props.application);
- this.getAffiliationOptions(this.props.application, this.props.user);
- }
-
- getAddressOptions(application) {
- if (application.affiliationUrl === "") {
- return;
- }
-
- const addressUrl = application.affiliationUrl.split("|")[0];
- UserBackend.getAddressOptions(addressUrl)
- .then((addressOptions) => {
- this.setState({
- addressOptions: addressOptions,
- });
- });
- }
-
- getAffiliationOptions(application, user) {
- if (application.affiliationUrl === "") {
- return;
- }
-
- const affiliationUrl = application.affiliationUrl.split("|")[1];
- const code = user.address[user.address.length - 1];
- UserBackend.getAffiliationOptions(affiliationUrl, code)
- .then((affiliationOptions) => {
- this.setState({
- affiliationOptions: affiliationOptions,
- });
- });
- }
-
- updateUserField(key, value) {
- this.props.onUpdateUserField(key, value);
- }
-
- unlinked() {
- this.props.onUnlinked();
- }
-
- getProviderLink(user, provider) {
- if (provider.type === "GitHub") {
- return `https://github.com/${this.getUserProperty(user, provider.type, "username")}`;
- } else if (provider.type === "Google") {
- return "https://mail.google.com";
- } else {
- return "";
- }
- }
-
- getUserProperty(user, providerType, propertyName) {
- const key = `oauth_${providerType}_${propertyName}`;
- if (user.properties === null) {return "";}
- return user.properties[key];
- }
-
- unlinkUser(providerType, linkedValue) {
- const body = {
- 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.
- user: this.props.user,
- };
- if (providerType === "MetaMask" || providerType === "Web3Onboard") {
- import("../auth/Web3Auth")
- .then(module => {
- const delWeb3AuthToken = module.delWeb3AuthToken;
- delWeb3AuthToken(linkedValue);
- AuthBackend.unlink(body)
- .then((res) => {
- if (res.status === "ok") {
- Setting.showMessage("success", "Unlinked successfully");
-
- this.unlinked();
- } else {
- Setting.showMessage("error", `Failed to unlink: ${res.msg}`);
- }
- });
- });
- return;
- }
- AuthBackend.unlink(body)
- .then((res) => {
- if (res.status === "ok") {
- Setting.showMessage("success", "Unlinked successfully");
-
- this.unlinked();
- } else {
- Setting.showMessage("error", `Failed to unlink: ${res.msg}`);
- }
- });
- }
-
- renderIdp(user, application, providerItem) {
- const provider = providerItem.provider;
- const linkedValue = user[provider.type.toLowerCase()];
- const profileUrl = this.getProviderLink(user, provider);
- const id = this.getUserProperty(user, provider.type, "id");
- const username = this.getUserProperty(user, provider.type, "username");
- const displayName = this.getUserProperty(user, provider.type, "displayName");
- const email = this.getUserProperty(user, provider.type, "email");
- let avatarUrl = this.getUserProperty(user, provider.type, "avatarUrl");
- // the account user
- const account = this.props.account;
-
- if (avatarUrl === "" || avatarUrl === undefined) {
- avatarUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAQAAACROWYpAAAAHElEQVR42mNkoAAwjmoe1TyqeVTzqOZRzcNZMwB18wAfEFQkPQAAAABJRU5ErkJggg==";
- }
-
- let name = (username === undefined) ? displayName : `${displayName} (${username})`;
- if (name === undefined) {
- if (id !== undefined) {
- name = id;
- } else if (email !== undefined) {
- name = email;
- } else {
- name = linkedValue;
- }
- }
-
- let linkButtonWidth = "110px";
- if (Setting.getLanguage() === "id") {
- linkButtonWidth = "160px";
- }
-
- return (
-
-
- {
- Setting.getProviderLogo(provider)
- }
-
- {
- `${provider.type}:`
- }
-
-
-
-
-
- {
- linkedValue === "" ? (
- `(${i18next.t("general:empty")})`
- ) : (
- profileUrl === "" ? name : (
-
- {
- name
- }
-
- )
- )
- }
-
- {
- linkedValue === "" ? (
- provider.category === "Web3" ? (
- goToWeb3Url(application, provider, "link")}>{i18next.t("user:Link")}
- ) : (
- provider.type === "WeChat" && provider.clientId2 !== "" && provider.clientSecret2 !== "" && provider.disableSsl === true && !navigator.userAgent.includes("MicroMessenger") ? (
-
- {
- WechatOfficialAccountModal(application, provider, "link");
- }
- }>{i18next.t("user:Link")}
-
- ) : (
-
- {i18next.t("user:Link")}
-
- )
- )
- ) : (
- this.unlinkUser(provider.type, linkedValue)}>{i18next.t("user:Unlink")}
- )
- }
-
-
- );
- }
-
- render() {
- return this.renderIdp(this.props.user, this.props.application, this.props.providerItem);
- }
-}
-
-export default OAuthWidget;
+// Copyright 2021 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 React from "react";
+import {Button, Col, Row} from "antd";
+import i18next from "i18next";
+import * as UserBackend from "../backend/UserBackend";
+import * as Setting from "../Setting";
+import * as Provider from "../auth/Provider";
+import * as AuthBackend from "../auth/AuthBackend";
+import {goToWeb3Url} from "../auth/ProviderButton";
+import AccountAvatar from "../account/AccountAvatar";
+import {WechatOfficialAccountModal} from "../auth/Util";
+
+class OAuthWidget extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ classes: props,
+ addressOptions: [],
+ affiliationOptions: [],
+ };
+ }
+
+ UNSAFE_componentWillMount() {
+ this.getAddressOptions(this.props.application);
+ this.getAffiliationOptions(this.props.application, this.props.user);
+ }
+
+ getAddressOptions(application) {
+ if (application.affiliationUrl === "") {
+ return;
+ }
+
+ const addressUrl = application.affiliationUrl.split("|")[0];
+ UserBackend.getAddressOptions(addressUrl)
+ .then((addressOptions) => {
+ this.setState({
+ addressOptions: addressOptions,
+ });
+ });
+ }
+
+ getAffiliationOptions(application, user) {
+ if (application.affiliationUrl === "") {
+ return;
+ }
+
+ const affiliationUrl = application.affiliationUrl.split("|")[1];
+ const code = user.address[user.address.length - 1];
+ UserBackend.getAffiliationOptions(affiliationUrl, code)
+ .then((affiliationOptions) => {
+ this.setState({
+ affiliationOptions: affiliationOptions,
+ });
+ });
+ }
+
+ updateUserField(key, value) {
+ this.props.onUpdateUserField(key, value);
+ }
+
+ unlinked() {
+ this.props.onUnlinked();
+ }
+
+ getProviderLink(user, provider) {
+ if (provider.type === "GitHub") {
+ return `https://github.com/${this.getUserProperty(user, provider.type, "username")}`;
+ } else if (provider.type === "Google") {
+ return "https://mail.google.com";
+ } else {
+ return "";
+ }
+ }
+
+ getUserProperty(user, providerType, propertyName) {
+ const key = `oauth_${providerType}_${propertyName}`;
+ if (user.properties === null) {return "";}
+ return user.properties[key];
+ }
+
+ unlinkUser(providerType, linkedValue) {
+ const body = {
+ 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.
+ user: this.props.user,
+ };
+ if (providerType === "MetaMask" || providerType === "Web3Onboard") {
+ import("../auth/Web3Auth")
+ .then(module => {
+ const delWeb3AuthToken = module.delWeb3AuthToken;
+ delWeb3AuthToken(linkedValue);
+ AuthBackend.unlink(body)
+ .then((res) => {
+ if (res.status === "ok") {
+ Setting.showMessage("success", "Unlinked successfully");
+
+ this.unlinked();
+ } else {
+ Setting.showMessage("error", `Failed to unlink: ${res.msg}`);
+ }
+ });
+ });
+ return;
+ }
+ AuthBackend.unlink(body)
+ .then((res) => {
+ if (res.status === "ok") {
+ Setting.showMessage("success", "Unlinked successfully");
+
+ this.unlinked();
+ } else {
+ Setting.showMessage("error", `Failed to unlink: ${res.msg}`);
+ }
+ });
+ }
+
+ renderIdp(user, application, providerItem) {
+ const provider = providerItem.provider;
+ const linkedValue = user[provider.type.toLowerCase()];
+ const profileUrl = this.getProviderLink(user, provider);
+ const id = this.getUserProperty(user, provider.type, "id");
+ const username = this.getUserProperty(user, provider.type, "username");
+ const displayName = this.getUserProperty(user, provider.type, "displayName");
+ const email = this.getUserProperty(user, provider.type, "email");
+ let avatarUrl = this.getUserProperty(user, provider.type, "avatarUrl");
+ // the account user
+ const account = this.props.account;
+
+ if (avatarUrl === "" || avatarUrl === undefined) {
+ avatarUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAQAAACROWYpAAAAHElEQVR42mNkoAAwjmoe1TyqeVTzqOZRzcNZMwB18wAfEFQkPQAAAABJRU5ErkJggg==";
+ }
+
+ let name = (username === undefined) ? displayName : `${displayName} (${username})`;
+ if (name === undefined) {
+ if (id !== undefined) {
+ name = id;
+ } else if (email !== undefined) {
+ name = email;
+ } else {
+ name = linkedValue;
+ }
+ }
+
+ let linkButtonWidth = "110px";
+ if (Setting.getLanguage() === "id") {
+ linkButtonWidth = "160px";
+ }
+
+ return (
+
+
+ {
+ Setting.getProviderLogo(provider)
+ }
+
+ {
+ `${provider.type}:`
+ }
+
+
+
+
+
+ {
+ linkedValue === "" ? (
+ `(${i18next.t("general:empty")})`
+ ) : (
+ profileUrl === "" ? name : (
+
+ {
+ name
+ }
+
+ )
+ )
+ }
+
+ {
+ linkedValue === "" ? (
+ provider.category === "Web3" ? (
+ goToWeb3Url(application, provider, "link")}>{i18next.t("user:Link")}
+ ) : (
+ provider.type === "WeChat" && provider.clientId2 !== "" && provider.clientSecret2 !== "" && provider.disableSsl === true && !navigator.userAgent.includes("MicroMessenger") ? (
+
+ {
+ WechatOfficialAccountModal(application, provider, "link");
+ }
+ }>{i18next.t("user:Link")}
+
+ ) : (
+
+ {i18next.t("user:Link")}
+
+ )
+ )
+ ) : (
+ this.unlinkUser(provider.type, linkedValue)}>{i18next.t("user:Unlink")}
+ )
+ }
+
+
+ );
+ }
+
+ render() {
+ return this.renderIdp(this.props.user, this.props.application, this.props.providerItem);
+ }
+}
+
+export default OAuthWidget;