This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch 389-ds-base-1.4.0
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.4.0 by this push:
new 894d1b6 Issue 50592 - Port Replication Tab to ReactJS
894d1b6 is described below
commit 894d1b6fb8c70c39085eb76bbc2316d82340ba13
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Fri Nov 1 15:10:34 2019 -0400
Issue 50592 - Port Replication Tab to ReactJS
Description: Ported the replication tab to React. Made many
other improvements throughout the UI:
- Protected "Treeviews" by disable/enable as components are
relaoded
- Add a new Double Confirmation Modal/Popup
- Added a script (buildAndWatch.sh) for faster/more convenient
developing
- Added a new RUV fucntion for the CLI, and made other lib389
improvements:
- Added support for not only "dc" suffixes, but also
"o", "ou", and "cn"
- Also rebase cockpit and lib389 with 1.4.1 as things have gotten out of
sync
relates:
https://pagure.io/389-ds-base/issue/50592
Reviewed by: spichugi(Thanks!)
---
src/cockpit/389-console/buildAndRun.sh | 45 +
src/cockpit/389-console/node_modules.mk | 5 +-
src/cockpit/389-console/package-lock.json | 1831 +++++++++++++++++++-
src/cockpit/389-console/package.json | 6 +-
src/cockpit/389-console/src/banner.html | 4 +-
src/cockpit/389-console/src/css/ds.css | 1022 ++---------
src/cockpit/389-console/src/database.jsx | 171 +-
src/cockpit/389-console/src/ds.js | 76 +-
src/cockpit/389-console/src/index.es6 | 7 +
src/cockpit/389-console/src/index.html | 114 +-
src/cockpit/389-console/src/lib/customCollapse.jsx | 2 +-
.../src/lib/database/attrEncryption.jsx | 3 +-
.../389-console/src/lib/database/backups.jsx | 227 ++-
.../389-console/src/lib/database/chaining.jsx | 616 ++++---
.../src/lib/database/databaseConfig.jsx | 226 ++-
.../389-console/src/lib/database/databaseModal.jsx | 52 +-
.../src/lib/database/databaseTables.jsx | 7 +-
.../389-console/src/lib/database/indexes.jsx | 4 +-
.../389-console/src/lib/database/referrals.jsx | 29 +-
.../389-console/src/lib/database/suffix.jsx | 167 +-
.../389-console/src/lib/database/suffixConfig.jsx | 120 +-
.../389-console/src/lib/database/vlvIndexes.jsx | 23 +-
.../389-console/src/lib/monitor/accesslog.jsx | 13 +-
.../389-console/src/lib/monitor/auditfaillog.jsx | 17 +-
.../389-console/src/lib/monitor/auditlog.jsx | 17 +-
.../src/lib/monitor/chainingMonitor.jsx | 15 +-
.../389-console/src/lib/monitor/dbMonitor.jsx | 124 +-
.../389-console/src/lib/monitor/errorlog.jsx | 16 +-
.../389-console/src/lib/monitor/monitorModals.jsx | 15 +-
.../389-console/src/lib/monitor/replMonitor.jsx | 12 +-
.../389-console/src/lib/monitor/serverMonitor.jsx | 199 ++-
.../389-console/src/lib/monitor/snmpMonitor.jsx | 119 +-
.../389-console/src/lib/monitor/suffixMonitor.jsx | 111 +-
src/cockpit/389-console/src/lib/notifications.jsx | 123 ++
.../389-console/src/lib/plugins/accountPolicy.jsx | 7 +-
.../src/lib/plugins/attributeUniqueness.jsx | 2 +-
.../389-console/src/lib/plugins/autoMembership.jsx | 2 +-
src/cockpit/389-console/src/lib/plugins/dna.jsx | 3 +-
.../src/lib/plugins/linkedAttributes.jsx | 2 +-
.../389-console/src/lib/plugins/managedEntries.jsx | 3 +-
.../389-console/src/lib/plugins/memberOf.jsx | 86 +-
.../src/lib/plugins/passthroughAuthentication.jsx | 4 +-
.../src/lib/plugins/pluginBasicConfig.jsx | 29 +-
.../src/lib/plugins/referentialIntegrity.jsx | 17 +-
.../389-console/src/lib/plugins/retroChangelog.jsx | 8 +-
.../src/lib/plugins/rootDNAccessControl.jsx | 14 +-
src/cockpit/389-console/src/lib/plugins/usn.jsx | 1 -
.../389-console/src/lib/plugins/winsync.jsx | 179 +-
.../389-console/src/lib/replication/replAgmts.jsx | 1150 ++++++++++++
.../src/lib/replication/replChangelog.jsx | 395 +++++
.../389-console/src/lib/replication/replConfig.jsx | 640 +++++++
.../389-console/src/lib/replication/replModals.jsx | 1684 ++++++++++++++++++
.../389-console/src/lib/replication/replSuffix.jsx | 456 +++++
.../replTables.jsx} | 374 ++--
.../389-console/src/lib/replication/replTasks.jsx | 314 ++++
.../src/lib/replication/winsyncAgmts.jsx | 1210 +++++++++++++
.../src/lib/security/certificateManagement.jsx | 53 +-
.../389-console/src/lib/security/ciphers.jsx | 2 +-
.../src/lib/security/securityTables.jsx | 6 +-
src/cockpit/389-console/src/lib/tools.jsx | 28 +-
src/cockpit/389-console/src/monitor.jsx | 69 +-
src/cockpit/389-console/src/plugins.jsx | 10 +-
src/cockpit/389-console/src/replication.jsx | 1148 ++++++++++++
src/cockpit/389-console/src/schema.html | 249 ++-
src/cockpit/389-console/src/schema.js | 206 ++-
src/cockpit/389-console/src/security.jsx | 72 +-
src/cockpit/389-console/src/servers.html | 147 +-
src/cockpit/389-console/src/servers.js | 248 ++-
src/cockpit/389-console/webpack.config.js | 13 +-
src/lib389/cli/dsctl | 3 +-
src/lib389/cli/dsidm | 6 +-
src/lib389/lib389/__init__.py | 11 +-
src/lib389/lib389/_mapped_object.py | 19 +-
src/lib389/lib389/agreement.py | 10 +-
src/lib389/lib389/backend.py | 3 +-
src/lib389/lib389/chaining.py | 4 +-
src/lib389/lib389/cli_base/__init__.py | 10 +-
src/lib389/lib389/cli_conf/backend.py | 65 +-
src/lib389/lib389/cli_conf/monitor.py | 4 +-
.../lib389/cli_conf/plugins/posix_winsync.py | 22 +
src/lib389/lib389/cli_conf/replication.py | 34 +-
src/lib389/lib389/cli_conf/security.py | 5 +-
src/lib389/lib389/cli_idm/__init__.py | 2 -
src/lib389/lib389/cli_idm/account.py | 116 +-
src/lib389/lib389/cli_idm/role.py | 126 ++
src/lib389/lib389/config.py | 2 +-
.../lib389/configurations/config_001004000.py | 35 +-
src/lib389/lib389/configurations/sample.py | 53 +
src/lib389/lib389/dbgen.py | 2 +
src/lib389/lib389/idm/account.py | 170 +-
src/lib389/lib389/idm/group.py | 13 +-
src/lib389/lib389/idm/posixgroup.py | 9 +-
src/lib389/lib389/idm/role.py | 377 ++--
src/lib389/lib389/instance/options.py | 7 +-
src/lib389/lib389/instance/remove.py | 12 +-
src/lib389/lib389/instance/setup.py | 57 +-
src/lib389/lib389/ldclt.py | 53 +-
src/lib389/lib389/mappingTree.py | 30 +-
src/lib389/lib389/monitor.py | 13 +
src/lib389/lib389/plugins.py | 53 +
src/lib389/lib389/replica.py | 60 +-
src/lib389/lib389/schema.py | 8 +-
src/lib389/lib389/tasks.py | 15 +
src/lib389/lib389/tools.py | 99 +-
src/lib389/lib389/utils.py | 86 +-
src/lib389/setup.py | 1 +
106 files changed, 13153 insertions(+), 2811 deletions(-)
diff --git a/src/cockpit/389-console/buildAndRun.sh
b/src/cockpit/389-console/buildAndRun.sh
new file mode 100755
index 0000000..8021003
--- /dev/null
+++ b/src/cockpit/389-console/buildAndRun.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+#
+# Do a fresh build of the UI, and run it. While in this state all updates made
+# are built immediately. Just refresh the browser to test them.
+
+AUDIT=0
+while (( "$#" )); do
+ case "$1" in
+ -a|--audit)
+ AUDIT=1
+ break
+ ;;
+ -h|--help)
+ echo Usage:
+ echo This is a development script to quickly refresh the UI and watch it live
+ echo Options:
+ echo -a|--audit Audit the build
+ exit 0
+ ;;
+ -*|--*=)
+ echo "Error: Unsupported argument $1" >&2
+ echo "Available Options:" >&2
+ echo " -a|--audit Audit the build" >&2
+ exit 1
+ ;;
+ esac
+done
+
+printf "\nCleaning and installing npm packages ...\n\n"
+make -f node_modules.mk clean > /dev/null
+make -f node_modules.mk install > /dev/null
+if [ $? != 0 ]; then
+ exit 1
+fi
+
+if [ $AUDIT == 1 ]; then
+ printf "\nAuditing npm packages ...\n\n"
+ make -f node_modules.mk build-cockpit-plugin
+ if [ $? != 0 ]; then
+ exit 1
+ fi
+fi
+
+printf "\nBuilding and watching ...\n"
+node_modules/webpack/bin/webpack.js --watch
diff --git a/src/cockpit/389-console/node_modules.mk
b/src/cockpit/389-console/node_modules.mk
index 307e8e3..e88b994 100644
--- a/src/cockpit/389-console/node_modules.mk
+++ b/src/cockpit/389-console/node_modules.mk
@@ -2,7 +2,10 @@ install: package.json
npm ci
build-cockpit-plugin: webpack.config.js
- npm run audit-ci && npm run build && cp -r dist cockpit_dist
+ifndef SKIP_AUDIT_CI
+ npm run audit-ci
+endif
+ npm run build && cp -r dist cockpit_dist
eslint-fix:
npm run eslint:fix
diff --git a/src/cockpit/389-console/package-lock.json
b/src/cockpit/389-console/package-lock.json
index f8ca714..6b4ebfb 100644
--- a/src/cockpit/389-console/package-lock.json
+++ b/src/cockpit/389-console/package-lock.json
@@ -266,7 +266,6 @@
"version": "7.0.0",
"resolved":
"https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz",
"integrity":
"sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==",
- "dev": true,
"requires": {
"@babel/types": "^7.0.0"
}
@@ -297,8 +296,7 @@
"@babel/helper-plugin-utils": {
"version": "7.0.0",
"resolved":
"https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz",
- "integrity":
"sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==",
- "dev": true
+ "integrity":
"sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA=="
},
"@babel/helper-regex": {
"version": "7.0.0",
@@ -951,13 +949,144 @@
"version": "7.1.3",
"resolved":
"https://registry.npmjs.org/@babel/types/-/types-7.1.3.tgz",
"integrity":
"sha512-RpPOVfK+yatXyn8n4PB1NW6k9qjinrXrRR8ugBN8fD6hCy5RXI6PSbVqpOJBO9oSaY7Nom4ohj35feb0UR9hSA==",
- "dev": true,
"requires": {
"esutils": "^2.0.2",
"lodash": "^4.17.10",
"to-fast-properties": "^2.0.0"
}
},
+ "@emotion/babel-utils": {
+ "version": "0.6.10",
+ "resolved":
"https://registry.npmjs.org/@emotion/babel-utils/-/babel-utils-0.6.10.tgz",
+ "integrity":
"sha512-/fnkM/LTEp3jKe++T0KyTszVGWNKPNOUJfjNKLO17BzQ6QPxgbg3whayom1Qr2oLFH3V92tDymU+dT5q676uow==",
+ "requires": {
+ "@emotion/hash": "^0.6.6",
+ "@emotion/memoize": "^0.6.6",
+ "@emotion/serialize": "^0.9.1",
+ "convert-source-map": "^1.5.1",
+ "find-root": "^1.1.0",
+ "source-map": "^0.7.2"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.7.3",
+ "resolved":
"https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+ "integrity":
"sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
+ }
+ }
+ },
+ "@emotion/hash": {
+ "version": "0.6.6",
+ "resolved":
"https://registry.npmjs.org/@emotion/hash/-/hash-0.6.6.tgz",
+ "integrity":
"sha512-ojhgxzUHZ7am3D2jHkMzPpsBAiB005GF5YU4ea+8DNPybMk01JJUM9V9YRlF/GE95tcOm8DxQvWA2jq19bGalQ=="
+ },
+ "@emotion/memoize": {
+ "version": "0.6.6",
+ "resolved":
"https://registry.npmjs.org/@emotion/memoize/-/memoize-0.6.6.tgz",
+ "integrity":
"sha512-h4t4jFjtm1YV7UirAFuSuFGyLa+NNxjdkq6DpFLANNQY5rHueFZHVY+8Cu1HYVP6DrheB0kv4m5xPjo7eKT7yQ=="
+ },
+ "@emotion/serialize": {
+ "version": "0.9.1",
+ "resolved":
"https://registry.npmjs.org/@emotion/serialize/-/serialize-0.9.1.tgz",
+ "integrity":
"sha512-zTuAFtyPvCctHBEL8KZ5lJuwBanGSutFEncqLn/m9T1a6a93smBStK+bZzcNPgj4QS8Rkw9VTwJGhRIUVO8zsQ==",
+ "requires": {
+ "@emotion/hash": "^0.6.6",
+ "@emotion/memoize": "^0.6.6",
+ "@emotion/unitless": "^0.6.7",
+ "@emotion/utils": "^0.8.2"
+ }
+ },
+ "@emotion/stylis": {
+ "version": "0.7.1",
+ "resolved":
"https://registry.npmjs.org/@emotion/stylis/-/stylis-0.7.1.tgz",
+ "integrity":
"sha512-/SLmSIkN13M//53TtNxgxo57mcJk/UJIDFRKwOiLIBEyBHEcipgR6hNMQ/59Sl4VjCJ0Z/3zeAZyvnSLPG/1HQ=="
+ },
+ "@emotion/unitless": {
+ "version": "0.6.7",
+ "resolved":
"https://registry.npmjs.org/@emotion/unitless/-/unitless-0.6.7.tgz",
+ "integrity":
"sha512-Arj1hncvEVqQ2p7Ega08uHLr1JuRYBuO5cIvcA+WWEQ5+VmkOE3ZXzl04NbQxeQpWX78G7u6MqxKuNX3wvYZxg=="
+ },
+ "@emotion/utils": {
+ "version": "0.8.2",
+ "resolved":
"https://registry.npmjs.org/@emotion/utils/-/utils-0.8.2.tgz",
+ "integrity":
"sha512-rLu3wcBWH4P5q1CGoSSH/i9hrXs7SlbRLkoq9IGuoPYNGQvDJ3pt/wmOM+XgYjIDRMVIdkUWt0RsfzF50JfnCw=="
+ },
+ "@fortawesome/fontawesome-common-types": {
+ "version": "0.2.19",
+ "resolved":
"https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.19.tgz",
+ "integrity":
"sha512-nd2Ul/CUs8U9sjofQYAALzOGpgkVJQgEhIJnOHaoyVR/LeC3x2mVg4eB910a4kS6WgLPebAY0M2fApEI497raQ=="
+ },
+ "@fortawesome/free-brands-svg-icons": {
+ "version": "5.9.0",
+ "resolved":
"https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.9.0.tgz",
+ "integrity":
"sha512-sOz1wFyslaHUak8tY6IEhSAV1mAWbCLssBR8yFQV6f065k8nUCkjyrcxW4RVl9+wiLXmeG1CJUABUJV9DiW+7Q==",
+ "requires": {
+ "@fortawesome/fontawesome-common-types": "^0.2.19"
+ }
+ },
+ "@patternfly/react-core": {
+ "version": "3.58.1",
+ "resolved":
"https://registry.npmjs.org/@patternfly/react-core/-/react-core-3.58.1.tgz",
+ "integrity":
"sha512-ahb7cbWsLWZu69sHnn3ZdB5tcpXAbgHcUbAttWuG01KVpleK50qkwZMWW0JzuDlUfnelpW3kbaVfNE14HZo+Eg==",
+ "requires": {
+ "@patternfly/react-icons": "^3.10.6",
+ "@patternfly/react-styles": "^3.4.6",
+ "@patternfly/react-tokens": "^2.6.5",
+ "(a)tippy.js/react": "^1.1.1",
+ "emotion": "^9.2.9",
+ "exenv": "^1.2.2",
+ "focus-trap-react": "^4.0.1"
+ }
+ },
+ "@patternfly/react-icons": {
+ "version": "3.10.13",
+ "resolved":
"https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-3.10.13.tgz",
+ "integrity":
"sha512-UFiofUekWTAAaoFKVYvasj2KsFAirnMHofgabv9FPHVfJREHY9hU7aV1LAX+bO16lSh8/Qo88TjcVY1nawOnuQ==",
+ "requires": {
+ "@fortawesome/free-brands-svg-icons": "^5.8.1"
+ }
+ },
+ "@patternfly/react-styles": {
+ "version": "3.4.6",
+ "resolved":
"https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-3.4.6.tgz",
+ "integrity":
"sha512-m9ynr85EMmBAxGhCguwRiGgpLDA9+YKtwF83ORK7ylHHsrK9JC/RFPvTj8d/80oUMmlzcL765ZwG7sT9qR3ybA==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0-beta.48",
+ "camel-case": "^3.0.0",
+ "css": "^2.2.3",
+ "cssom": "^0.3.4",
+ "cssstyle": "^0.3.1",
+ "emotion": "^9.2.9",
+ "emotion-server": "^9.2.9",
+ "fbjs-scripts": "^0.8.3",
+ "fs-extra": "^6.0.1",
+ "jsdom": "^15.1.0",
+ "relative": "^3.0.2",
+ "resolve-from": "^4.0.0",
+ "typescript": "3.4.5"
+ },
+ "dependencies": {
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved":
"https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity":
"sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
+ }
+ }
+ },
+ "@patternfly/react-tokens": {
+ "version": "2.6.5",
+ "resolved":
"https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-2.6.5.tgz",
+ "integrity":
"sha512-HZ0LLAOugMmnD9n5T5Hp6vDnZ86ikm+MXu7kbfNqK9dTOVmEroPai0rUN6hnnPP4mCzFrLXmh4rzecDr2273yg=="
+ },
+ "(a)tippy.js/react": {
+ "version": "1.1.1",
+ "resolved":
"https://registry.npmjs.org/@tippy.js/react/-/react-1.1.1.tgz",
+ "integrity":
"sha512-TkL1VufxgUvTMouDoBGv2vTdtUxtLUaRpspI4Rv0DsoKe2Ex1E5bl/qISk434mhuAhEnXuemrcgTaPWrfDvmGw==",
+ "requires": {
+ "prop-types": "^15.6.2",
+ "tippy.js": "^3.2.0"
+ }
+ },
"@types/c3": {
"version": "0.6.4",
"resolved":
"https://registry.npmjs.org/@types/c3/-/c3-0.6.4.tgz",
@@ -1420,6 +1549,11 @@
"integrity":
"sha512-FZdkNBDqBRHKQ2MEbSC17xnPFOhZxeJ2YGSfr2BKf3sujG49Qe3bB+rGCwQfIaA7WHnGeGkSijX4FuBCdrzW/g==",
"dev": true
},
+ "abab": {
+ "version": "2.0.0",
+ "resolved":
"https://registry.npmjs.org/abab/-/abab-2.0.0.tgz",
+ "integrity":
"sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w=="
+ },
"abbrev": {
"version": "1.1.1",
"resolved":
"https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@@ -1440,6 +1574,22 @@
"acorn": "^5.0.0"
}
},
+ "acorn-globals": {
+ "version": "4.3.2",
+ "resolved":
"https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.2.tgz",
+ "integrity":
"sha512-BbzvZhVtZP+Bs1J1HcwrQe8ycfO0wStkSGxuul3He3GkHOIZ6eTqOkPuw9IP1X3+IkOo4wiJmwkobzXYz4wewQ==",
+ "requires": {
+ "acorn": "^6.0.1",
+ "acorn-walk": "^6.0.1"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "6.2.1",
+ "resolved":
"https://registry.npmjs.org/acorn/-/acorn-6.2.1.tgz",
+ "integrity":
"sha512-JD0xT5FCRDNyjDda3Lrg/IxFscp9q4tiYtxE1/nOzlKCk7hIRuYjhq1kCNkbPjMRMZuFq20HNQn1I9k8Oj0E+Q=="
+ }
+ }
+ },
"acorn-jsx": {
"version": "4.1.1",
"resolved":
"https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-4.1.1.tgz",
@@ -1449,11 +1599,15 @@
"acorn": "^5.0.3"
}
},
+ "acorn-walk": {
+ "version": "6.2.0",
+ "resolved":
"https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz",
+ "integrity":
"sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA=="
+ },
"ajv": {
"version": "6.5.4",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.4.tgz",
"integrity":
"sha512-4Wyjt8+t6YszqaXnLDfMmG/8AlO5Zbcsy3ATHncCzjW/NoPzAId8AK6749Ybjmdt+kUY1gP60fCu46oDxPv/mg==",
- "dev": true,
"requires": {
"fast-deep-equal": "^2.0.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -1470,20 +1624,51 @@
"ajv-keywords": {
"version": "3.2.0",
"resolved":
"https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz",
- "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=",
- "dev": true
+ "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo="
},
"amdefine": {
"version": "1.0.1",
"resolved":
"https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU="
},
+ "ansi-colors": {
+ "version": "1.1.0",
+ "resolved":
"https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
+ "integrity":
"sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==",
+ "requires": {
+ "ansi-wrap": "^0.1.0"
+ }
+ },
+ "ansi-cyan": {
+ "version": "0.1.1",
+ "resolved":
"https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz",
+ "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=",
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
"ansi-escapes": {
"version": "3.1.0",
"resolved":
"https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
"integrity":
"sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==",
"dev": true
},
+ "ansi-gray": {
+ "version": "0.1.1",
+ "resolved":
"https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz",
+ "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=",
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-red": {
+ "version": "0.1.1",
+ "resolved":
"https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz",
+ "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=",
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
"ansi-regex": {
"version": "2.1.1",
"resolved":
"https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
@@ -1494,6 +1679,11 @@
"resolved":
"https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
},
+ "ansi-wrap": {
+ "version": "0.1.0",
+ "resolved":
"https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz",
+ "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768="
+ },
"anymatch": {
"version": "2.0.0",
"resolved":
"https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
@@ -1522,7 +1712,6 @@
"version": "1.0.10",
"resolved":
"https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity":
"sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
- "dev": true,
"requires": {
"sprintf-js": "~1.0.2"
}
@@ -1536,8 +1725,7 @@
"arr-flatten": {
"version": "1.1.0",
"resolved":
"https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
- "integrity":
"sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
- "dev": true
+ "integrity":
"sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg=="
},
"arr-union": {
"version": "3.1.0",
@@ -1545,6 +1733,11 @@
"integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
"dev": true
},
+ "array-equal": {
+ "version": "1.0.0",
+ "resolved":
"https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
+ "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM="
+ },
"array-find-index": {
"version": "1.0.2",
"resolved":
"https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
@@ -1560,6 +1753,11 @@
"es-abstract": "^1.7.0"
}
},
+ "array-slice": {
+ "version": "0.2.3",
+ "resolved":
"https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
+ "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU="
+ },
"array-union": {
"version": "1.0.2",
"resolved":
"https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
@@ -1671,8 +1869,7 @@
"async-limiter": {
"version": "1.0.0",
"resolved":
"https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
- "integrity":
"sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
- "dev": true
+ "integrity":
"sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
},
"asynckit": {
"version": "0.4.0",
@@ -1682,8 +1879,7 @@
"atob": {
"version": "2.1.2",
"resolved":
"https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
- "integrity":
"sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
- "dev": true
+ "integrity":
"sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
},
"audit-ci": {
"version": "1.7.0",
@@ -1929,6 +2125,69 @@
"resolved":
"https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
"integrity":
"sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
},
+ "babel-code-frame": {
+ "version": "6.26.0",
+ "resolved":
"https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
+ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+ "requires": {
+ "chalk": "^1.1.3",
+ "esutils": "^2.0.2",
+ "js-tokens": "^3.0.2"
+ },
+ "dependencies": {
+ "js-tokens": {
+ "version": "3.0.2",
+ "resolved":
"https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+ "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
+ }
+ }
+ },
+ "babel-core": {
+ "version": "6.26.3",
+ "resolved":
"https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz",
+ "integrity":
"sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==",
+ "requires": {
+ "babel-code-frame": "^6.26.0",
+ "babel-generator": "^6.26.0",
+ "babel-helpers": "^6.24.1",
+ "babel-messages": "^6.23.0",
+ "babel-register": "^6.26.0",
+ "babel-runtime": "^6.26.0",
+ "babel-template": "^6.26.0",
+ "babel-traverse": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "convert-source-map": "^1.5.1",
+ "debug": "^2.6.9",
+ "json5": "^0.5.1",
+ "lodash": "^4.17.4",
+ "minimatch": "^3.0.4",
+ "path-is-absolute": "^1.0.1",
+ "private": "^0.1.8",
+ "slash": "^1.0.0",
+ "source-map": "^0.5.7"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved":
"https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity":
"sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved":
"https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved":
"https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+ }
+ }
+ },
"babel-eslint": {
"version": "9.0.0",
"resolved":
"https://registry.npmjs.org/babel-eslint/-/babel-eslint-9.0.0.tgz",
@@ -1943,6 +2202,126 @@
"eslint-visitor-keys": "^1.0.0"
}
},
+ "babel-generator": {
+ "version": "6.26.1",
+ "resolved":
"https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz",
+ "integrity":
"sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==",
+ "requires": {
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "detect-indent": "^4.0.0",
+ "jsesc": "^1.3.0",
+ "lodash": "^4.17.4",
+ "source-map": "^0.5.7",
+ "trim-right": "^1.0.1"
+ },
+ "dependencies": {
+ "jsesc": {
+ "version": "1.3.0",
+ "resolved":
"https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
+ "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s="
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved":
"https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+ }
+ }
+ },
+ "babel-helper-builder-react-jsx": {
+ "version": "6.26.0",
+ "resolved":
"https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz",
+ "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=",
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "esutils": "^2.0.2"
+ }
+ },
+ "babel-helper-call-delegate": {
+ "version": "6.24.1",
+ "resolved":
"https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz",
+ "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=",
+ "requires": {
+ "babel-helper-hoist-variables": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-helper-define-map": {
+ "version": "6.26.0",
+ "resolved":
"https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz",
+ "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=",
+ "requires": {
+ "babel-helper-function-name": "^6.24.1",
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "lodash": "^4.17.4"
+ }
+ },
+ "babel-helper-function-name": {
+ "version": "6.24.1",
+ "resolved":
"https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz",
+ "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=",
+ "requires": {
+ "babel-helper-get-function-arity": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-helper-get-function-arity": {
+ "version": "6.24.1",
+ "resolved":
"https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz",
+ "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=",
+ "requires": {
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-helper-hoist-variables": {
+ "version": "6.24.1",
+ "resolved":
"https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz",
+ "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=",
+ "requires": {
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-helper-optimise-call-expression": {
+ "version": "6.24.1",
+ "resolved":
"https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz",
+ "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=",
+ "requires": {
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-helper-replace-supers": {
+ "version": "6.24.1",
+ "resolved":
"https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz",
+ "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=",
+ "requires": {
+ "babel-helper-optimise-call-expression": "^6.24.1",
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-helpers": {
+ "version": "6.24.1",
+ "resolved":
"https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz",
+ "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=",
+ "requires": {
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1"
+ }
+ },
"babel-loader": {
"version": "8.0.4",
"resolved":
"https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.4.tgz",
@@ -1955,6 +2334,372 @@
"util.promisify": "^1.0.0"
}
},
+ "babel-messages": {
+ "version": "6.23.0",
+ "resolved":
"https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
+ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-check-es2015-constants": {
+ "version": "6.22.0",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz",
+ "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=",
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-emotion": {
+ "version": "9.2.11",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-9.2.11.tgz",
+ "integrity":
"sha512-dgCImifnOPPSeXod2znAmgc64NhaaOjGEHROR/M+lmStb3841yK1sgaDYAYMnlvWNz8GnpwIPN0VmNpbWYZ+VQ==",
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@emotion/babel-utils": "^0.6.4",
+ "@emotion/hash": "^0.6.2",
+ "@emotion/memoize": "^0.6.1",
+ "@emotion/stylis": "^0.7.0",
+ "babel-plugin-macros": "^2.0.0",
+ "babel-plugin-syntax-jsx": "^6.18.0",
+ "convert-source-map": "^1.5.0",
+ "find-root": "^1.1.0",
+ "mkdirp": "^0.5.1",
+ "source-map": "^0.5.7",
+ "touch": "^2.0.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved":
"https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+ }
+ }
+ },
+ "babel-plugin-macros": {
+ "version": "2.6.1",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.6.1.tgz",
+ "integrity":
"sha512-6W2nwiXme6j1n2erPOnmRiWfObUhWH7Qw1LMi9XZy8cj+KtESu3T6asZvtk5bMQQjX8te35o7CFueiSdL/2NmQ==",
+ "requires": {
+ "@babel/runtime": "^7.4.2",
+ "cosmiconfig": "^5.2.0",
+ "resolve": "^1.10.0"
+ },
+ "dependencies": {
+ "@babel/runtime": {
+ "version": "7.5.5",
+ "resolved":
"https://registry.npmjs.org/@babel/runtime/-/runtime-7.5.5.tgz",
+ "integrity":
"sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==",
+ "requires": {
+ "regenerator-runtime": "^0.13.2"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.13.3",
+ "resolved":
"https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
+ "integrity":
"sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
+ },
+ "resolve": {
+ "version": "1.11.1",
+ "resolved":
"https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz",
+ "integrity":
"sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==",
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ }
+ }
+ },
+ "babel-plugin-syntax-class-properties": {
+ "version": "6.13.0",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz",
+ "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94="
+ },
+ "babel-plugin-syntax-flow": {
+ "version": "6.18.0",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz",
+ "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0="
+ },
+ "babel-plugin-syntax-jsx": {
+ "version": "6.18.0",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
+ "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY="
+ },
+ "babel-plugin-syntax-object-rest-spread": {
+ "version": "6.13.0",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz",
+ "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U="
+ },
+ "babel-plugin-syntax-trailing-function-commas": {
+ "version": "6.22.0",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz",
+ "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM="
+ },
+ "babel-plugin-transform-class-properties": {
+ "version": "6.24.1",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz",
+ "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=",
+ "requires": {
+ "babel-helper-function-name": "^6.24.1",
+ "babel-plugin-syntax-class-properties": "^6.8.0",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1"
+ }
+ },
+ "babel-plugin-transform-es2015-arrow-functions": {
+ "version": "6.22.0",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
+ "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=",
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-es2015-block-scoped-functions": {
+ "version": "6.22.0",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz",
+ "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=",
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-es2015-block-scoping": {
+ "version": "6.26.0",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz",
+ "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=",
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "babel-template": "^6.26.0",
+ "babel-traverse": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "lodash": "^4.17.4"
+ }
+ },
+ "babel-plugin-transform-es2015-classes": {
+ "version": "6.24.1",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz",
+ "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=",
+ "requires": {
+ "babel-helper-define-map": "^6.24.1",
+ "babel-helper-function-name": "^6.24.1",
+ "babel-helper-optimise-call-expression": "^6.24.1",
+ "babel-helper-replace-supers": "^6.24.1",
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-plugin-transform-es2015-computed-properties": {
+ "version": "6.24.1",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz",
+ "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=",
+ "requires": {
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1"
+ }
+ },
+ "babel-plugin-transform-es2015-destructuring": {
+ "version": "6.23.0",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz",
+ "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=",
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-es2015-for-of": {
+ "version": "6.23.0",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz",
+ "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=",
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-es2015-function-name": {
+ "version": "6.24.1",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz",
+ "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=",
+ "requires": {
+ "babel-helper-function-name": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-plugin-transform-es2015-literals": {
+ "version": "6.22.0",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz",
+ "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=",
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-es2015-modules-commonjs": {
+ "version": "6.26.2",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz",
+ "integrity":
"sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==",
+ "requires": {
+ "babel-plugin-transform-strict-mode": "^6.24.1",
+ "babel-runtime": "^6.26.0",
+ "babel-template": "^6.26.0",
+ "babel-types": "^6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-object-super": {
+ "version": "6.24.1",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz",
+ "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=",
+ "requires": {
+ "babel-helper-replace-supers": "^6.24.1",
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-es2015-parameters": {
+ "version": "6.24.1",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz",
+ "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=",
+ "requires": {
+ "babel-helper-call-delegate": "^6.24.1",
+ "babel-helper-get-function-arity": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-plugin-transform-es2015-shorthand-properties": {
+ "version": "6.24.1",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz",
+ "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=",
+ "requires": {
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-plugin-transform-es2015-spread": {
+ "version": "6.22.0",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz",
+ "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=",
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-es2015-template-literals": {
+ "version": "6.22.0",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz",
+ "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=",
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-es3-member-expression-literals": {
+ "version": "6.22.0",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-es3-member-expression-literals/-/babel-plugin-transform-es3-member-expression-literals-6.22.0.tgz",
+ "integrity": "sha1-cz00RPPsxBvvjtGmpOCWV7iWnrs=",
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-es3-property-literals": {
+ "version": "6.22.0",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-es3-property-literals/-/babel-plugin-transform-es3-property-literals-6.22.0.tgz",
+ "integrity": "sha1-sgeNWELiKr9A9z6M3pzTcRq9V1g=",
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-flow-strip-types": {
+ "version": "6.22.0",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz",
+ "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=",
+ "requires": {
+ "babel-plugin-syntax-flow": "^6.18.0",
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-object-rest-spread": {
+ "version": "6.26.0",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz",
+ "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=",
+ "requires": {
+ "babel-plugin-syntax-object-rest-spread": "^6.8.0",
+ "babel-runtime": "^6.26.0"
+ }
+ },
+ "babel-plugin-transform-react-display-name": {
+ "version": "6.25.0",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz",
+ "integrity": "sha1-Z+K/Hx6ck6sI25Z5LgU5K/LMKNE=",
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-react-jsx": {
+ "version": "6.24.1",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz",
+ "integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=",
+ "requires": {
+ "babel-helper-builder-react-jsx": "^6.24.1",
+ "babel-plugin-syntax-jsx": "^6.8.0",
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-strict-mode": {
+ "version": "6.24.1",
+ "resolved":
"https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz",
+ "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=",
+ "requires": {
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-preset-fbjs": {
+ "version": "2.3.0",
+ "resolved":
"https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-2.3.0.tgz",
+ "integrity":
"sha512-ZOpAI1/bN0Y3J1ZAK9gRsFkHy9gGgJoDRUjtUCla/129LC7uViq9nIK22YdHfey8szohYoZY3f9L2lGOv0Edqw==",
+ "requires": {
+ "babel-plugin-check-es2015-constants": "^6.8.0",
+ "babel-plugin-syntax-class-properties": "^6.8.0",
+ "babel-plugin-syntax-flow": "^6.8.0",
+ "babel-plugin-syntax-jsx": "^6.8.0",
+ "babel-plugin-syntax-object-rest-spread": "^6.8.0",
+ "babel-plugin-syntax-trailing-function-commas": "^6.8.0",
+ "babel-plugin-transform-class-properties": "^6.8.0",
+ "babel-plugin-transform-es2015-arrow-functions": "^6.8.0",
+ "babel-plugin-transform-es2015-block-scoped-functions":
"^6.8.0",
+ "babel-plugin-transform-es2015-block-scoping": "^6.8.0",
+ "babel-plugin-transform-es2015-classes": "^6.8.0",
+ "babel-plugin-transform-es2015-computed-properties":
"^6.8.0",
+ "babel-plugin-transform-es2015-destructuring": "^6.8.0",
+ "babel-plugin-transform-es2015-for-of": "^6.8.0",
+ "babel-plugin-transform-es2015-function-name": "^6.8.0",
+ "babel-plugin-transform-es2015-literals": "^6.8.0",
+ "babel-plugin-transform-es2015-modules-commonjs": "^6.8.0",
+ "babel-plugin-transform-es2015-object-super": "^6.8.0",
+ "babel-plugin-transform-es2015-parameters": "^6.8.0",
+ "babel-plugin-transform-es2015-shorthand-properties":
"^6.8.0",
+ "babel-plugin-transform-es2015-spread": "^6.8.0",
+ "babel-plugin-transform-es2015-template-literals": "^6.8.0",
+ "babel-plugin-transform-es3-member-expression-literals":
"^6.8.0",
+ "babel-plugin-transform-es3-property-literals": "^6.8.0",
+ "babel-plugin-transform-flow-strip-types": "^6.8.0",
+ "babel-plugin-transform-object-rest-spread": "^6.8.0",
+ "babel-plugin-transform-react-display-name": "^6.8.0",
+ "babel-plugin-transform-react-jsx": "^6.8.0"
+ }
+ },
+ "babel-register": {
+ "version": "6.26.0",
+ "resolved":
"https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz",
+ "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=",
+ "requires": {
+ "babel-core": "^6.26.0",
+ "babel-runtime": "^6.26.0",
+ "core-js": "^2.5.0",
+ "home-or-tmp": "^2.0.0",
+ "lodash": "^4.17.4",
+ "mkdirp": "^0.5.1",
+ "source-map-support": "^0.4.15"
+ }
+ },
"babel-runtime": {
"version": "6.26.0",
"resolved":
"https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
@@ -1971,6 +2716,77 @@
}
}
},
+ "babel-template": {
+ "version": "6.26.0",
+ "resolved":
"https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz",
+ "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=",
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "babel-traverse": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "lodash": "^4.17.4"
+ }
+ },
+ "babel-traverse": {
+ "version": "6.26.0",
+ "resolved":
"https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
+ "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
+ "requires": {
+ "babel-code-frame": "^6.26.0",
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "debug": "^2.6.8",
+ "globals": "^9.18.0",
+ "invariant": "^2.2.2",
+ "lodash": "^4.17.4"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved":
"https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity":
"sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "globals": {
+ "version": "9.18.0",
+ "resolved":
"https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
+ "integrity":
"sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ=="
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved":
"https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ }
+ }
+ },
+ "babel-types": {
+ "version": "6.26.0",
+ "resolved":
"https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
+ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.4",
+ "to-fast-properties": "^1.0.3"
+ },
+ "dependencies": {
+ "to-fast-properties": {
+ "version": "1.0.3",
+ "resolved":
"https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
+ "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc="
+ }
+ }
+ },
+ "babylon": {
+ "version": "6.18.0",
+ "resolved":
"https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
+ "integrity":
"sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ=="
+ },
"balanced-match": {
"version": "1.0.0",
"resolved":
"https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@@ -2178,6 +2994,11 @@
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
"dev": true
},
+ "browser-process-hrtime": {
+ "version": "0.1.3",
+ "resolved":
"https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz",
+ "integrity":
"sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw=="
+ },
"browserify-aes": {
"version": "1.2.0",
"resolved":
"http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
@@ -2354,6 +3175,21 @@
"unset-value": "^1.0.0"
}
},
+ "caller-callsite": {
+ "version": "2.0.0",
+ "resolved":
"https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
+ "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
+ "requires": {
+ "callsites": "^2.0.0"
+ },
+ "dependencies": {
+ "callsites": {
+ "version": "2.0.0",
+ "resolved":
"https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
+ "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA="
+ }
+ }
+ },
"caller-path": {
"version": "0.1.0",
"resolved":
"https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
@@ -2369,6 +3205,15 @@
"integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
"dev": true
},
+ "camel-case": {
+ "version": "3.0.0",
+ "resolved":
"https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
+ "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
+ "requires": {
+ "no-case": "^2.2.0",
+ "upper-case": "^1.1.1"
+ }
+ },
"camelcase": {
"version": "2.1.1",
"resolved":
"https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
@@ -2574,6 +3419,11 @@
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
},
+ "color-support": {
+ "version": "1.1.3",
+ "resolved":
"https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+ "integrity":
"sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="
+ },
"combined-stream": {
"version": "1.0.7",
"resolved":
"https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
@@ -2660,7 +3510,6 @@
"version": "1.6.0",
"resolved":
"https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz",
"integrity":
"sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==",
- "dev": true,
"requires": {
"safe-buffer": "~5.1.1"
}
@@ -2711,6 +3560,28 @@
"resolved":
"https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
+ "cosmiconfig": {
+ "version": "5.2.1",
+ "resolved":
"https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
+ "integrity":
"sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
+ "requires": {
+ "import-fresh": "^2.0.0",
+ "is-directory": "^0.3.1",
+ "js-yaml": "^3.13.1",
+ "parse-json": "^4.0.0"
+ },
+ "dependencies": {
+ "parse-json": {
+ "version": "4.0.0",
+ "resolved":
"https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+ "requires": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ }
+ }
+ }
+ },
"create-ecdh": {
"version": "4.0.3",
"resolved":
"https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
@@ -2721,6 +3592,30 @@
"elliptic": "^6.0.0"
}
},
+ "create-emotion": {
+ "version": "9.2.12",
+ "resolved":
"https://registry.npmjs.org/create-emotion/-/create-emotion-9.2.12.tgz",
+ "integrity":
"sha512-P57uOF9NL2y98Xrbl2OuiDQUZ30GVmASsv5fbsjF4Hlraip2kyAvMm+2PoYUvFFw03Fhgtxk3RqZSm2/qHL9hA==",
+ "requires": {
+ "@emotion/hash": "^0.6.2",
+ "@emotion/memoize": "^0.6.1",
+ "@emotion/stylis": "^0.7.0",
+ "@emotion/unitless": "^0.6.2",
+ "csstype": "^2.5.2",
+ "stylis": "^3.5.0",
+ "stylis-rule-sheet": "^0.0.10"
+ }
+ },
+ "create-emotion-server": {
+ "version": "9.2.12",
+ "resolved":
"https://registry.npmjs.org/create-emotion-server/-/create-emotion-server-9.2.12.tgz",
+ "integrity":
"sha512-ET+E6A5MkQTEBNDYAnjh6+0cB33qStFXhtflkZNPEaOmvzYlB/xcPnpUk4J7ul3MVa8PCQx2Ei5g2MGY/y1n+g==",
+ "requires": {
+ "html-tokenize": "^2.0.0",
+ "multipipe": "^1.0.2",
+ "through": "^2.3.8"
+ }
+ },
"create-hash": {
"version": "1.2.0",
"resolved":
"http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
@@ -2785,6 +3680,24 @@
"randomfill": "^1.0.3"
}
},
+ "css": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz",
+ "integrity":
"sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==",
+ "requires": {
+ "inherits": "^2.0.3",
+ "source-map": "^0.6.1",
+ "source-map-resolve": "^0.5.2",
+ "urix": "^0.1.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved":
"https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity":
"sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+ }
+ }
+ },
"css-element-queries": {
"version": "1.2.0",
"resolved":
"https://registry.npmjs.org/css-element-queries/-/css-element-queries-1.2.0.tgz",
@@ -2866,6 +3779,19 @@
"integrity":
"sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
"dev": true
},
+ "cssom": {
+ "version": "0.3.8",
+ "resolved":
"https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
+ "integrity":
"sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="
+ },
+ "cssstyle": {
+ "version": "0.3.1",
+ "resolved":
"https://registry.npmjs.org/cssstyle/-/cssstyle-0.3.1.tgz",
+ "integrity":
"sha512-tNvaxM5blOnxanyxI6panOsnfiyLRj3HV4qjqqS45WPNS1usdYWRUQjqTEEELK73lpeP/1KoIGYUwrBn/VcECA==",
+ "requires": {
+ "cssom": "0.3.x"
+ }
+ },
"csstype": {
"version": "2.5.8",
"resolved":
"https://registry.npmjs.org/csstype/-/csstype-2.5.8.tgz",
@@ -2898,6 +3824,16 @@
"assert-plus": "^1.0.0"
}
},
+ "data-urls": {
+ "version": "1.1.0",
+ "resolved":
"https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz",
+ "integrity":
"sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==",
+ "requires": {
+ "abab": "^2.0.0",
+ "whatwg-mimetype": "^2.2.0",
+ "whatwg-url": "^7.0.0"
+ }
+ },
"datatables.net": {
"version": "1.10.19",
"resolved":
"https://registry.npmjs.org/datatables.net/-/datatables.net-1.10.19.tgz",
@@ -2971,14 +3907,12 @@
"decode-uri-component": {
"version": "0.2.0",
"resolved":
"https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
- "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
- "dev": true
+ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
},
"deep-is": {
"version": "0.1.3",
"resolved":
"https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
- "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
- "dev": true
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
},
"define-properties": {
"version": "1.1.3",
@@ -3081,6 +4015,14 @@
"minimalistic-assert": "^1.0.0"
}
},
+ "detect-indent": {
+ "version": "4.0.0",
+ "resolved":
"https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz",
+ "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=",
+ "requires": {
+ "repeating": "^2.0.0"
+ }
+ },
"diffie-hellman": {
"version": "5.0.3",
"resolved":
"http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
@@ -3142,6 +4084,14 @@
"integrity":
"sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
"dev": true
},
+ "domexception": {
+ "version": "1.0.1",
+ "resolved":
"https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
+ "integrity":
"sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
+ "requires": {
+ "webidl-conversions": "^4.0.2"
+ }
+ },
"drmonty-datatables-colvis": {
"version": "1.1.2",
"resolved":
"https://registry.npmjs.org/drmonty-datatables-colvis/-/drmonty-datatables-colvis-1.1.2.tgz",
@@ -3151,6 +4101,14 @@
"jquery": ">=1.7.0"
}
},
+ "duplexer2": {
+ "version": "0.1.4",
+ "resolved":
"https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+ "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
+ "requires": {
+ "readable-stream": "^2.0.2"
+ }
+ },
"duplexify": {
"version": "3.6.1",
"resolved":
"https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz",
@@ -3196,8 +4154,24 @@
"emojis-list": {
"version": "2.1.0",
"resolved":
"https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
- "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
- "dev": true
+ "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k="
+ },
+ "emotion": {
+ "version": "9.2.12",
+ "resolved":
"https://registry.npmjs.org/emotion/-/emotion-9.2.12.tgz",
+ "integrity":
"sha512-hcx7jppaI8VoXxIWEhxpDW7I+B4kq9RNzQLmsrF6LY8BGKqe2N+gFAQr0EfuFucFlPs2A9HM4+xNj4NeqEWIOQ==",
+ "requires": {
+ "babel-plugin-emotion": "^9.2.11",
+ "create-emotion": "^9.2.12"
+ }
+ },
+ "emotion-server": {
+ "version": "9.2.12",
+ "resolved":
"https://registry.npmjs.org/emotion-server/-/emotion-server-9.2.12.tgz",
+ "integrity":
"sha512-Bhjdl7eNoIeiAVa2QPP5d+1nP/31SiO/K1P/qI9cdXCydg91NwGYmteqhhge8u7PF8fLGTEVQfcPwj21815eBw==",
+ "requires": {
+ "create-emotion-server": "^9.2.12"
+ }
},
"encoding": {
"version": "0.1.12",
@@ -3293,6 +4267,31 @@
"resolved":
"https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
+ "escodegen": {
+ "version": "1.11.1",
+ "resolved":
"https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz",
+ "integrity":
"sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==",
+ "requires": {
+ "esprima": "^3.1.3",
+ "estraverse": "^4.2.0",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1",
+ "source-map": "~0.6.1"
+ },
+ "dependencies": {
+ "esprima": {
+ "version": "3.1.3",
+ "resolved":
"https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
+ "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM="
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved":
"https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity":
"sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "optional": true
+ }
+ }
+ },
"eslint": {
"version": "5.7.0",
"resolved":
"https://registry.npmjs.org/eslint/-/eslint-5.7.0.tgz",
@@ -3763,6 +4762,11 @@
"acorn-jsx": "^4.1.1"
}
},
+ "esprima": {
+ "version": "4.0.1",
+ "resolved":
"https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity":
"sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
+ },
"esquery": {
"version": "1.0.1",
"resolved":
"https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
@@ -3784,14 +4788,12 @@
"estraverse": {
"version": "4.2.0",
"resolved":
"https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
- "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
- "dev": true
+ "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM="
},
"esutils": {
"version": "2.0.2",
"resolved":
"https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
- "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
- "dev": true
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
},
"events": {
"version": "1.1.1",
@@ -3839,6 +4841,11 @@
}
}
},
+ "exenv": {
+ "version": "1.2.2",
+ "resolved":
"https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
+ "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50="
+ },
"expand-brackets": {
"version": "2.1.4",
"resolved":
"https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
@@ -4008,6 +5015,17 @@
"resolved":
"https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
},
+ "fancy-log": {
+ "version": "1.3.3",
+ "resolved":
"https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz",
+ "integrity":
"sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==",
+ "requires": {
+ "ansi-gray": "^0.1.1",
+ "color-support": "^1.1.3",
+ "parse-node-version": "^1.0.0",
+ "time-stamp": "^1.0.0"
+ }
+ },
"fast-deep-equal": {
"version": "2.0.1",
"resolved":
"https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
@@ -4021,8 +5039,7 @@
"fast-levenshtein": {
"version": "2.0.6",
"resolved":
"https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
- "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
- "dev": true
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
},
"fbjs": {
"version": "0.8.17",
@@ -4045,6 +5062,35 @@
}
}
},
+ "fbjs-scripts": {
+ "version": "0.8.3",
+ "resolved":
"https://registry.npmjs.org/fbjs-scripts/-/fbjs-scripts-0.8.3.tgz",
+ "integrity":
"sha512-aUJ/uEzMIiBYuj/blLp4sVNkQQ7ZEB/lyplG1IzzOmZ83meiWecrGg5jBo4wWrxXmO4RExdtsSV1QkTjPt2Gag==",
+ "requires": {
+ "ansi-colors": "^1.0.1",
+ "babel-core": "^6.7.2",
+ "babel-preset-fbjs": "^2.1.2",
+ "core-js": "^2.4.1",
+ "cross-spawn": "^5.1.0",
+ "fancy-log": "^1.3.2",
+ "object-assign": "^4.0.1",
+ "plugin-error": "^0.1.2",
+ "semver": "^5.1.0",
+ "through2": "^2.0.0"
+ },
+ "dependencies": {
+ "cross-spawn": {
+ "version": "5.1.0",
+ "resolved":
"https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+ "requires": {
+ "lru-cache": "^4.0.1",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ }
+ }
+ },
"figures": {
"version": "2.0.0",
"resolved":
"https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
@@ -4064,6 +5110,49 @@
"object-assign": "^4.0.1"
}
},
+ "file-loader": {
+ "version": "4.1.0",
+ "resolved":
"https://registry.npmjs.org/file-loader/-/file-loader-4.1.0.tgz",
+ "integrity":
"sha512-ajDk1nlByoalZAGR4b0H6oD+EGlWnyW1qbSxzaUc7RFiqmn+RbXQQRbTc72jsiUIlVusJ4Et58ltds8ZwTfnAw==",
+ "requires": {
+ "loader-utils": "^1.2.3",
+ "schema-utils": "^2.0.0"
+ },
+ "dependencies": {
+ "big.js": {
+ "version": "5.2.2",
+ "resolved":
"https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
+ "integrity":
"sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ=="
+ },
+ "json5": {
+ "version": "1.0.1",
+ "resolved":
"https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity":
"sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "loader-utils": {
+ "version": "1.2.3",
+ "resolved":
"https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
+ "integrity":
"sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^2.0.0",
+ "json5": "^1.0.1"
+ }
+ },
+ "schema-utils": {
+ "version": "2.0.1",
+ "resolved":
"https://registry.npmjs.org/schema-utils/-/schema-utils-2.0.1.tgz",
+ "integrity":
"sha512-HJFKJ4JixDpRur06QHwi8uu2kZbng318ahWEKgBjc0ZklcE4FDvmm2wghb448q0IRaABxIESt8vqPFvwgMB80A==",
+ "requires": {
+ "ajv": "^6.1.0",
+ "ajv-keywords": "^3.1.0"
+ }
+ }
+ }
+ },
"fill-range": {
"version": "4.0.0",
"resolved":
"https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
@@ -4098,6 +5187,11 @@
"pkg-dir": "^2.0.0"
}
},
+ "find-root": {
+ "version": "1.1.0",
+ "resolved":
"https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+ "integrity":
"sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
+ },
"find-up": {
"version": "1.1.2",
"resolved":
"https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
@@ -4129,6 +5223,23 @@
"readable-stream": "^2.0.4"
}
},
+ "focus-trap": {
+ "version": "3.0.0",
+ "resolved":
"https://registry.npmjs.org/focus-trap/-/focus-trap-3.0.0.tgz",
+ "integrity":
"sha512-jTFblf0tLWbleGjj2JZsAKbgtZTdL1uC48L8FcmSDl4c2vDoU4NycN1kgV5vJhuq1mxNFkw7uWZ1JAGlINWvyw==",
+ "requires": {
+ "tabbable": "^3.1.0",
+ "xtend": "^4.0.1"
+ }
+ },
+ "focus-trap-react": {
+ "version": "4.0.1",
+ "resolved":
"https://registry.npmjs.org/focus-trap-react/-/focus-trap-react-4.0.1.tgz",
+ "integrity":
"sha512-UUZKVEn5cFbF6yUnW7lbXNW0iqN617ShSqYKgxctUvWw1wuylLtyVmC0RmPQNnJ/U+zoKc/djb0tZMs0uN/0QQ==",
+ "requires": {
+ "focus-trap": "^3.0.0"
+ }
+ },
"font-awesome": {
"version": "4.7.0",
"resolved":
"https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
@@ -4189,6 +5300,16 @@
"readable-stream": "^2.0.0"
}
},
+ "fs-extra": {
+ "version": "6.0.1",
+ "resolved":
"https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz",
+ "integrity":
"sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==",
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
"fs-write-stream-atomic": {
"version": "1.0.10",
"resolved":
"https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
@@ -4931,6 +6052,40 @@
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
"integrity":
"sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
},
+ "handlebars": {
+ "version": "4.3.3",
+ "resolved":
"https://registry.npmjs.org/handlebars/-/handlebars-4.3.3.tgz",
+ "integrity":
"sha512-VupOxR91xcGojfINrzMqrvlyYbBs39sXIrWa7YdaQWeBudOlvKEGvCczMfJPgnuwHE/zyH1M6J+IUP6cgDVyxg==",
+ "requires": {
+ "neo-async": "^2.6.0",
+ "optimist": "^0.6.1",
+ "source-map": "^0.6.1",
+ "uglify-js": "^3.1.4"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.0",
+ "resolved":
"https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
+ "integrity":
"sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
+ "optional": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved":
"https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity":
"sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+ },
+ "uglify-js": {
+ "version": "3.6.0",
+ "resolved":
"https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz",
+ "integrity":
"sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==",
+ "optional": true,
+ "requires": {
+ "commander": "~2.20.0",
+ "source-map": "~0.6.1"
+ }
+ }
+ }
+ },
"har-schema": {
"version": "2.0.0",
"resolved":
"https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
@@ -5060,11 +6215,95 @@
"resolved":
"https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
"integrity":
"sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
},
+ "home-or-tmp": {
+ "version": "2.0.0",
+ "resolved":
"https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
+ "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=",
+ "requires": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.1"
+ }
+ },
"hosted-git-info": {
"version": "2.7.1",
"resolved":
"https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
"integrity":
"sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w=="
},
+ "html-encoding-sniffer": {
+ "version": "1.0.2",
+ "resolved":
"https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
+ "integrity":
"sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
+ "requires": {
+ "whatwg-encoding": "^1.0.1"
+ }
+ },
+ "html-tokenize": {
+ "version": "2.0.0",
+ "resolved":
"https://registry.npmjs.org/html-tokenize/-/html-tokenize-2.0.0.tgz",
+ "integrity": "sha1-izqaXetHXK5qb5ZxYA0sIKspglE=",
+ "requires": {
+ "buffer-from": "~0.1.1",
+ "inherits": "~2.0.1",
+ "minimist": "~0.0.8",
+ "readable-stream": "~1.0.27-1",
+ "through2": "~0.4.1"
+ },
+ "dependencies": {
+ "buffer-from": {
+ "version": "0.1.2",
+ "resolved":
"https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz",
+ "integrity":
"sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg=="
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved":
"https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+ },
+ "minimist": {
+ "version": "0.0.10",
+ "resolved":
"https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+ "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
+ },
+ "object-keys": {
+ "version": "0.4.0",
+ "resolved":
"https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz",
+ "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY="
+ },
+ "readable-stream": {
+ "version": "1.0.34",
+ "resolved":
"https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved":
"https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+ },
+ "through2": {
+ "version": "0.4.2",
+ "resolved":
"https://registry.npmjs.org/through2/-/through2-0.4.2.tgz",
+ "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=",
+ "requires": {
+ "readable-stream": "~1.0.17",
+ "xtend": "~2.1.1"
+ }
+ },
+ "xtend": {
+ "version": "2.1.2",
+ "resolved":
"https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz",
+ "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=",
+ "requires": {
+ "object-keys": "~0.4.0"
+ }
+ }
+ }
+ },
"htmlparser": {
"version": "1.7.7",
"resolved":
"https://registry.npmjs.org/htmlparser/-/htmlparser-1.7.7.tgz",
@@ -5128,6 +6367,30 @@
"integrity":
"sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==",
"dev": true
},
+ "import-fresh": {
+ "version": "2.0.0",
+ "resolved":
"https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
+ "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
+ "requires": {
+ "caller-path": "^2.0.0",
+ "resolve-from": "^3.0.0"
+ },
+ "dependencies": {
+ "caller-path": {
+ "version": "2.0.0",
+ "resolved":
"https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
+ "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
+ "requires": {
+ "caller-callsite": "^2.0.0"
+ }
+ },
+ "resolve-from": {
+ "version": "3.0.0",
+ "resolved":
"https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+ "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
+ }
+ }
+ },
"import-local": {
"version": "2.0.0",
"resolved":
"https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
@@ -5345,6 +6608,11 @@
"resolved":
"https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
"integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
},
+ "ip-regex": {
+ "version": "2.1.0",
+ "resolved":
"https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
+ "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk="
+ },
"is-accessor-descriptor": {
"version": "0.1.6",
"resolved":
"https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
@@ -5444,6 +6712,11 @@
}
}
},
+ "is-directory": {
+ "version": "0.3.1",
+ "resolved":
"https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
+ "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE="
+ },
"is-extendable": {
"version": "0.1.1",
"resolved":
"https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
@@ -5648,11 +6921,91 @@
"resolved":
"https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity":
"sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved":
"https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity":
"sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
"jsbn": {
"version": "0.1.1",
"resolved":
"https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
},
+ "jsdom": {
+ "version": "15.1.1",
+ "resolved":
"https://registry.npmjs.org/jsdom/-/jsdom-15.1.1.tgz",
+ "integrity":
"sha512-cQZRBB33arrDAeCrAEWn1U3SvrvC8XysBua9Oqg1yWrsY/gYcusloJC3RZJXuY5eehSCmws8f2YeliCqGSkrtQ==",
+ "requires": {
+ "abab": "^2.0.0",
+ "acorn": "^6.1.1",
+ "acorn-globals": "^4.3.2",
+ "array-equal": "^1.0.0",
+ "cssom": "^0.3.6",
+ "cssstyle": "^1.2.2",
+ "data-urls": "^1.1.0",
+ "domexception": "^1.0.1",
+ "escodegen": "^1.11.1",
+ "html-encoding-sniffer": "^1.0.2",
+ "nwsapi": "^2.1.4",
+ "parse5": "5.1.0",
+ "pn": "^1.1.0",
+ "request": "^2.88.0",
+ "request-promise-native": "^1.0.7",
+ "saxes": "^3.1.9",
+ "symbol-tree": "^3.2.2",
+ "tough-cookie": "^3.0.1",
+ "w3c-hr-time": "^1.0.1",
+ "w3c-xmlserializer": "^1.1.2",
+ "webidl-conversions": "^4.0.2",
+ "whatwg-encoding": "^1.0.5",
+ "whatwg-mimetype": "^2.3.0",
+ "whatwg-url": "^7.0.0",
+ "ws": "^7.0.0",
+ "xml-name-validator": "^3.0.0"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "6.2.1",
+ "resolved":
"https://registry.npmjs.org/acorn/-/acorn-6.2.1.tgz",
+ "integrity":
"sha512-JD0xT5FCRDNyjDda3Lrg/IxFscp9q4tiYtxE1/nOzlKCk7hIRuYjhq1kCNkbPjMRMZuFq20HNQn1I9k8Oj0E+Q=="
+ },
+ "cssstyle": {
+ "version": "1.4.0",
+ "resolved":
"https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz",
+ "integrity":
"sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==",
+ "requires": {
+ "cssom": "0.3.x"
+ }
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved":
"https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity":
"sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
+ },
+ "tough-cookie": {
+ "version": "3.0.1",
+ "resolved":
"https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
+ "integrity":
"sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==",
+ "requires": {
+ "ip-regex": "^2.1.0",
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ }
+ },
+ "ws": {
+ "version": "7.1.1",
+ "resolved":
"https://registry.npmjs.org/ws/-/ws-7.1.1.tgz",
+ "integrity":
"sha512-o41D/WmDeca0BqYhsr3nJzQyg9NF5X8l/UdnFNux9cS3lwB+swm8qGWX5rn+aD6xfBU3rGmtHij7g7x6LxFU3A==",
+ "requires": {
+ "async-limiter": "^1.0.0"
+ }
+ }
+ }
+ },
"jsesc": {
"version": "2.5.1",
"resolved":
"https://registry.npmjs.org/jsesc/-/jsesc-2.5.1.tgz",
@@ -5662,8 +7015,7 @@
"json-parse-better-errors": {
"version": "1.0.2",
"resolved":
"https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
- "integrity":
"sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
- "dev": true
+ "integrity":
"sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
},
"json-schema": {
"version": "0.2.3",
@@ -5689,8 +7041,15 @@
"json5": {
"version": "0.5.1",
"resolved":
"http://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
- "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
- "dev": true
+ "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE="
+ },
+ "jsonfile": {
+ "version": "4.0.0",
+ "resolved":
"https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
},
"jsprim": {
"version": "1.4.1",
@@ -5735,7 +7094,6 @@
"version": "0.3.0",
"resolved":
"https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
"integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
- "dev": true,
"requires": {
"prelude-ls": "~1.1.2",
"type-check": "~0.3.2"
@@ -5845,6 +7203,11 @@
"resolved":
"https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
"integrity":
"sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ=="
},
+ "lodash.sortby": {
+ "version": "4.7.0",
+ "resolved":
"https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg="
+ },
"lodash.tail": {
"version": "4.1.1",
"resolved":
"https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz",
@@ -5868,6 +7231,11 @@
"signal-exit": "^3.0.0"
}
},
+ "lower-case": {
+ "version": "1.1.4",
+ "resolved":
"https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
+ "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw="
+ },
"lru-cache": {
"version": "4.1.3",
"resolved":
"https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz",
@@ -6003,6 +7371,11 @@
"brorand": "^1.0.1"
}
},
+ "mime": {
+ "version": "2.4.4",
+ "resolved":
"https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
+ "integrity":
"sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA=="
+ },
"mime-db": {
"version": "1.37.0",
"resolved":
"https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
@@ -6154,6 +7527,15 @@
"integrity":
"sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"dev": true
},
+ "multipipe": {
+ "version": "1.0.2",
+ "resolved":
"https://registry.npmjs.org/multipipe/-/multipipe-1.0.2.tgz",
+ "integrity": "sha1-zBPv2DPJzamfIk+GhGG44aP9k50=",
+ "requires": {
+ "duplexer2": "^0.1.2",
+ "object-assign": "^4.1.0"
+ }
+ },
"mute-stream": {
"version": "0.0.7",
"resolved":
"https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
@@ -6193,8 +7575,7 @@
"neo-async": {
"version": "2.6.0",
"resolved":
"https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz",
- "integrity":
"sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==",
- "dev": true
+ "integrity":
"sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA=="
},
"nice-try": {
"version": "1.0.5",
@@ -6202,6 +7583,14 @@
"integrity":
"sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"dev": true
},
+ "no-case": {
+ "version": "2.3.2",
+ "resolved":
"https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
+ "integrity":
"sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==",
+ "requires": {
+ "lower-case": "^1.1.1"
+ }
+ },
"node-fetch": {
"version": "1.7.3",
"resolved":
"https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
@@ -6356,6 +7745,11 @@
"resolved":
"https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
},
+ "nwsapi": {
+ "version": "2.1.4",
+ "resolved":
"https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.4.tgz",
+ "integrity":
"sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw=="
+ },
"oauth-sign": {
"version": "0.9.0",
"resolved":
"https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
@@ -6454,11 +7848,31 @@
"mimic-fn": "^1.0.0"
}
},
+ "optimist": {
+ "version": "0.6.1",
+ "resolved":
"https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+ "requires": {
+ "minimist": "~0.0.1",
+ "wordwrap": "~0.0.2"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.10",
+ "resolved":
"https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+ "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
+ },
+ "wordwrap": {
+ "version": "0.0.3",
+ "resolved":
"https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+ "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
+ }
+ }
+ },
"optionator": {
"version": "0.8.2",
"resolved":
"https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
"integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
- "dev": true,
"requires": {
"deep-is": "~0.1.3",
"fast-levenshtein": "~2.0.4",
@@ -6581,6 +7995,16 @@
"error-ex": "^1.2.0"
}
},
+ "parse-node-version": {
+ "version": "1.0.1",
+ "resolved":
"https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
+ "integrity":
"sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA=="
+ },
+ "parse5": {
+ "version": "5.1.0",
+ "resolved":
"https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
+ "integrity":
"sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ=="
+ },
"pascalcase": {
"version": "0.1.1",
"resolved":
"https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
@@ -6627,8 +8051,7 @@
"path-parse": {
"version": "1.0.6",
"resolved":
"https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
- "integrity":
"sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
- "dev": true
+ "integrity":
"sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
},
"path-type": {
"version": "1.1.0",
@@ -6831,12 +8254,58 @@
}
}
},
+ "plugin-error": {
+ "version": "0.1.2",
+ "resolved":
"https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz",
+ "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=",
+ "requires": {
+ "ansi-cyan": "^0.1.1",
+ "ansi-red": "^0.1.1",
+ "arr-diff": "^1.0.1",
+ "arr-union": "^2.0.1",
+ "extend-shallow": "^1.1.2"
+ },
+ "dependencies": {
+ "arr-diff": {
+ "version": "1.1.0",
+ "resolved":
"https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz",
+ "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=",
+ "requires": {
+ "arr-flatten": "^1.0.1",
+ "array-slice": "^0.2.3"
+ }
+ },
+ "arr-union": {
+ "version": "2.1.0",
+ "resolved":
"https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz",
+ "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0="
+ },
+ "extend-shallow": {
+ "version": "1.1.4",
+ "resolved":
"https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz",
+ "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=",
+ "requires": {
+ "kind-of": "^1.1.0"
+ }
+ },
+ "kind-of": {
+ "version": "1.1.0",
+ "resolved":
"https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
+ "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ="
+ }
+ }
+ },
"pluralize": {
"version": "7.0.0",
"resolved":
"https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
"integrity":
"sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
"dev": true
},
+ "pn": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
+ "integrity":
"sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA=="
+ },
"popper.js": {
"version": "1.15.0",
"resolved":
"https://registry.npmjs.org/popper.js/-/popper.js-1.15.0.tgz",
@@ -6967,14 +8436,12 @@
"prelude-ls": {
"version": "1.1.2",
"resolved":
"https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
- "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
- "dev": true
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
},
"private": {
"version": "0.1.8",
"resolved":
"https://registry.npmjs.org/private/-/private-0.1.8.tgz",
- "integrity":
"sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==",
- "dev": true
+ "integrity":
"sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg=="
},
"process": {
"version": "0.11.10",
@@ -7489,6 +8956,24 @@
}
}
},
+ "relative": {
+ "version": "3.0.2",
+ "resolved":
"https://registry.npmjs.org/relative/-/relative-3.0.2.tgz",
+ "integrity": "sha1-Dc2OxUpdNaPBXhBFA9ZTdbWlNn8=",
+ "requires": {
+ "isobject": "^2.0.0"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "2.1.0",
+ "resolved":
"https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+ "requires": {
+ "isarray": "1.0.0"
+ }
+ }
+ }
+ },
"remove-trailing-separator": {
"version": "1.1.0",
"resolved":
"https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
@@ -7549,6 +9034,24 @@
}
}
},
+ "request-promise-core": {
+ "version": "1.1.2",
+ "resolved":
"https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz",
+ "integrity":
"sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==",
+ "requires": {
+ "lodash": "^4.17.11"
+ }
+ },
+ "request-promise-native": {
+ "version": "1.0.7",
+ "resolved":
"https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz",
+ "integrity":
"sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==",
+ "requires": {
+ "request-promise-core": "1.1.2",
+ "stealthy-require": "^1.1.1",
+ "tough-cookie": "^2.3.3"
+ }
+ },
"require-directory": {
"version": "2.1.1",
"resolved":
"https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -7604,8 +9107,7 @@
"resolve-url": {
"version": "0.2.1",
"resolved":
"https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
- "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
- "dev": true
+ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
},
"restore-cursor": {
"version": "2.0.0",
@@ -7720,6 +9222,14 @@
}
}
},
+ "saxes": {
+ "version": "3.1.11",
+ "resolved":
"https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz",
+ "integrity":
"sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==",
+ "requires": {
+ "xmlchars": "^2.1.1"
+ }
+ },
"scheduler": {
"version": "0.11.3",
"resolved":
"https://registry.npmjs.org/scheduler/-/scheduler-0.11.3.tgz",
@@ -7851,7 +9361,6 @@
"version": "1.2.0",
"resolved":
"https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
"integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
- "dev": true,
"requires": {
"shebang-regex": "^1.0.0"
}
@@ -7859,8 +9368,7 @@
"shebang-regex": {
"version": "1.0.0",
"resolved":
"https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
- "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
- "dev": true
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
},
"signal-exit": {
"version": "3.0.2",
@@ -7876,8 +9384,7 @@
"slash": {
"version": "1.0.0",
"resolved":
"https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
- "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
- "dev": true
+ "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU="
},
"slice-ansi": {
"version": "1.0.0",
@@ -8048,7 +9555,6 @@
"version": "0.5.2",
"resolved":
"https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz",
"integrity":
"sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==",
- "dev": true,
"requires": {
"atob": "^2.1.1",
"decode-uri-component": "^0.2.0",
@@ -8057,11 +9563,25 @@
"urix": "^0.1.0"
}
},
+ "source-map-support": {
+ "version": "0.4.18",
+ "resolved":
"https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
+ "integrity":
"sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+ "requires": {
+ "source-map": "^0.5.6"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved":
"https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+ }
+ }
+ },
"source-map-url": {
"version": "0.4.0",
"resolved":
"https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
- "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
- "dev": true
+ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM="
},
"spdx-correct": {
"version": "3.0.2",
@@ -8103,8 +9623,7 @@
"sprintf-js": {
"version": "1.0.3",
"resolved":
"https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
- "dev": true
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"sshpk": {
"version": "1.16.0",
@@ -8166,6 +9685,11 @@
"readable-stream": "^2.0.1"
}
},
+ "stealthy-require": {
+ "version": "1.1.1",
+ "resolved":
"https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
+ "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
+ },
"stream-browserify": {
"version": "2.0.1",
"resolved":
"https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz",
@@ -8282,6 +9806,16 @@
}
}
},
+ "stylis": {
+ "version": "3.5.4",
+ "resolved":
"https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz",
+ "integrity":
"sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q=="
+ },
+ "stylis-rule-sheet": {
+ "version": "0.0.10",
+ "resolved":
"https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz",
+ "integrity":
"sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw=="
+ },
"supports-color": {
"version": "2.0.0",
"resolved":
"https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
@@ -8292,6 +9826,16 @@
"resolved":
"https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
"integrity":
"sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
},
+ "symbol-tree": {
+ "version": "3.2.4",
+ "resolved":
"https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+ "integrity":
"sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="
+ },
+ "tabbable": {
+ "version": "3.1.2",
+ "resolved":
"https://registry.npmjs.org/tabbable/-/tabbable-3.1.2.tgz",
+ "integrity":
"sha512-wjB6puVXTYO0BSFtCmWQubA/KIn7Xvajw0x0l6eJUudMG/EAiJvIUnyNX6xO4NpGrJ16lbD0eUseB9WxW0vlpQ=="
+ },
"table": {
"version": "5.1.0",
"resolved":
"https://registry.npmjs.org/table/-/table-5.1.0.tgz",
@@ -8394,19 +9938,22 @@
"through": {
"version": "2.3.8",
"resolved":
"http://registry.npmjs.org/through/-/through-2.3.8.tgz",
- "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
- "dev": true
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
"through2": {
"version": "2.0.3",
"resolved":
"https://registry.npmjs.org/through2/-/through2-2.0.3.tgz",
"integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=",
- "dev": true,
"requires": {
"readable-stream": "^2.1.5",
"xtend": "~4.0.1"
}
},
+ "time-stamp": {
+ "version": "1.1.0",
+ "resolved":
"https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz",
+ "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM="
+ },
"timers-browserify": {
"version": "2.0.10",
"resolved":
"https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz",
@@ -8416,6 +9963,14 @@
"setimmediate": "^1.0.4"
}
},
+ "tippy.js": {
+ "version": "3.4.1",
+ "resolved":
"https://registry.npmjs.org/tippy.js/-/tippy.js-3.4.1.tgz",
+ "integrity":
"sha512-ZiyGP9WZyCCcjxKM4G88cm4U1r1ytjeMDGa5FSKPaPzwc/3yZJVZsb1ffcmqUMCpryRp5LNxRNGKLzbs11sb/Q==",
+ "requires": {
+ "popper.js": "^1.14.6"
+ }
+ },
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@@ -8434,8 +9989,7 @@
"to-fast-properties": {
"version": "2.0.0",
"resolved":
"https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
- "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
- "dev": true
+ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
},
"to-object-path": {
"version": "0.3.0",
@@ -8479,6 +10033,24 @@
"repeat-string": "^1.6.1"
}
},
+ "touch": {
+ "version": "2.0.2",
+ "resolved":
"https://registry.npmjs.org/touch/-/touch-2.0.2.tgz",
+ "integrity":
"sha512-qjNtvsFXTRq7IuMLweVgFxmEuQ6gLbRs2jQxL80TtZ31dEKWYIxRXquij6w6VimyDek5hD3PytljHmEtAs2u0A==",
+ "requires": {
+ "nopt": "~1.0.10"
+ },
+ "dependencies": {
+ "nopt": {
+ "version": "1.0.10",
+ "resolved":
"https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+ "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
+ "requires": {
+ "abbrev": "1"
+ }
+ }
+ }
+ },
"tough-cookie": {
"version": "2.4.3",
"resolved":
"https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
@@ -8488,6 +10060,21 @@
"punycode": "^1.4.1"
}
},
+ "tr46": {
+ "version": "1.0.1",
+ "resolved":
"https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+ "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+ "requires": {
+ "punycode": "^2.1.0"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "2.1.1",
+ "resolved":
"https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity":
"sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
+ }
+ }
+ },
"trim-newlines": {
"version": "1.0.0",
"resolved":
"https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
@@ -8496,8 +10083,7 @@
"trim-right": {
"version": "1.0.1",
"resolved":
"https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
- "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=",
- "dev": true
+ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM="
},
"true-case-path": {
"version": "1.0.3",
@@ -8536,7 +10122,6 @@
"version": "0.3.2",
"resolved":
"https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
"integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
- "dev": true,
"requires": {
"prelude-ls": "~1.1.2"
}
@@ -8552,6 +10137,11 @@
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
"dev": true
},
+ "typescript": {
+ "version": "3.4.5",
+ "resolved":
"https://registry.npmjs.org/typescript/-/typescript-3.4.5.tgz",
+ "integrity":
"sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw=="
+ },
"ua-parser-js": {
"version": "0.7.19",
"resolved":
"https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz",
@@ -8683,6 +10273,11 @@
"imurmurhash": "^0.1.4"
}
},
+ "universalify": {
+ "version": "0.1.2",
+ "resolved":
"https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity":
"sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
+ },
"unset-value": {
"version": "1.0.0",
"resolved":
"https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
@@ -8729,6 +10324,11 @@
"integrity":
"sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==",
"dev": true
},
+ "upper-case": {
+ "version": "1.1.3",
+ "resolved":
"https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
+ "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg="
+ },
"uri-js": {
"version": "4.2.2",
"resolved":
"https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
@@ -8747,8 +10347,7 @@
"urix": {
"version": "0.1.0",
"resolved":
"https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
- "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
- "dev": true
+ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI="
},
"url": {
"version": "0.11.0",
@@ -8768,6 +10367,50 @@
}
}
},
+ "url-loader": {
+ "version": "2.1.0",
+ "resolved":
"https://registry.npmjs.org/url-loader/-/url-loader-2.1.0.tgz",
+ "integrity":
"sha512-kVrp/8VfEm5fUt+fl2E0FQyrpmOYgMEkBsv8+UDP1wFhszECq5JyGF33I7cajlVY90zRZ6MyfgKXngLvHYZX8A==",
+ "requires": {
+ "loader-utils": "^1.2.3",
+ "mime": "^2.4.4",
+ "schema-utils": "^2.0.0"
+ },
+ "dependencies": {
+ "big.js": {
+ "version": "5.2.2",
+ "resolved":
"https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
+ "integrity":
"sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ=="
+ },
+ "json5": {
+ "version": "1.0.1",
+ "resolved":
"https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity":
"sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "loader-utils": {
+ "version": "1.2.3",
+ "resolved":
"https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
+ "integrity":
"sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^2.0.0",
+ "json5": "^1.0.1"
+ }
+ },
+ "schema-utils": {
+ "version": "2.0.1",
+ "resolved":
"https://registry.npmjs.org/schema-utils/-/schema-utils-2.0.1.tgz",
+ "integrity":
"sha512-HJFKJ4JixDpRur06QHwi8uu2kZbng318ahWEKgBjc0ZklcE4FDvmm2wghb448q0IRaABxIESt8vqPFvwgMB80A==",
+ "requires": {
+ "ajv": "^6.1.0",
+ "ajv-keywords": "^3.1.0"
+ }
+ }
+ }
+ },
"use": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
@@ -8837,6 +10480,24 @@
"indexof": "0.0.1"
}
},
+ "w3c-hr-time": {
+ "version": "1.0.1",
+ "resolved":
"https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz",
+ "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=",
+ "requires": {
+ "browser-process-hrtime": "^0.1.2"
+ }
+ },
+ "w3c-xmlserializer": {
+ "version": "1.1.2",
+ "resolved":
"https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz",
+ "integrity":
"sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==",
+ "requires": {
+ "domexception": "^1.0.1",
+ "webidl-conversions": "^4.0.2",
+ "xml-name-validator": "^3.0.0"
+ }
+ },
"warning": {
"version": "3.0.0",
"resolved":
"https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
@@ -8856,6 +10517,11 @@
"neo-async": "^2.5.0"
}
},
+ "webidl-conversions": {
+ "version": "4.0.2",
+ "resolved":
"https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+ "integrity":
"sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="
+ },
"webpack": {
"version": "4.22.0",
"resolved":
"https://registry.npmjs.org/webpack/-/webpack-4.22.0.tgz",
@@ -9171,11 +10837,34 @@
}
}
},
+ "whatwg-encoding": {
+ "version": "1.0.5",
+ "resolved":
"https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
+ "integrity":
"sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==",
+ "requires": {
+ "iconv-lite": "0.4.24"
+ }
+ },
"whatwg-fetch": {
"version": "3.0.0",
"resolved":
"https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
"integrity":
"sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q=="
},
+ "whatwg-mimetype": {
+ "version": "2.3.0",
+ "resolved":
"https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
+ "integrity":
"sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g=="
+ },
+ "whatwg-url": {
+ "version": "7.0.0",
+ "resolved":
"https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz",
+ "integrity":
"sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==",
+ "requires": {
+ "lodash.sortby": "^4.7.0",
+ "tr46": "^1.0.1",
+ "webidl-conversions": "^4.0.2"
+ }
+ },
"which": {
"version": "1.3.1",
"resolved":
"https://registry.npmjs.org/which/-/which-1.3.1.tgz",
@@ -9200,8 +10889,7 @@
"wordwrap": {
"version": "1.0.0",
"resolved":
"https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
- "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
- "dev": true
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
},
"worker-farm": {
"version": "1.6.0",
@@ -9246,6 +10934,16 @@
"ultron": "~1.1.0"
}
},
+ "xml-name-validator": {
+ "version": "3.0.0",
+ "resolved":
"https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
+ "integrity":
"sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw=="
+ },
+ "xmlchars": {
+ "version": "2.1.1",
+ "resolved":
"https://registry.npmjs.org/xmlchars/-/xmlchars-2.1.1.tgz",
+ "integrity":
"sha512-7hew1RPJ1iIuje/Y01bGD/mXokXxegAgVS+e+E0wSi2ILHQkYAH1+JXARwTjZSM4Z4Z+c73aKspEcqj+zPPL/w=="
+ },
"xregexp": {
"version": "4.0.0",
"resolved":
"https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz",
@@ -9255,8 +10953,7 @@
"xtend": {
"version": "4.0.1",
"resolved":
"https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
- "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
- "dev": true
+ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
},
"y18n": {
"version": "3.2.1",
diff --git a/src/cockpit/389-console/package.json b/src/cockpit/389-console/package.json
index ddf44b5..420fb6f 100644
--- a/src/cockpit/389-console/package.json
+++ b/src/cockpit/389-console/package.json
@@ -49,7 +49,10 @@
"webpack-cli": "^3.1.0"
},
"dependencies": {
+ "@patternfly/react-core": "^3.58.1",
"bootstrap": "^4.3.1",
+ "file-loader": "^4.1.0",
+ "handlebars": "^4.3.3",
"node-sass": "4.11.0",
"patternfly": "^3.59.3",
"patternfly-react": "^2.34.3",
@@ -60,6 +63,7 @@
"react-dom": "16.6.1",
"react-switch": "^5.0.0",
"recompose": "0.30.0",
- "table-resolver": "4.1.1"
+ "table-resolver": "4.1.1",
+ "url-loader": "^2.1.0"
}
}
diff --git a/src/cockpit/389-console/src/banner.html
b/src/cockpit/389-console/src/banner.html
index d609aec..16b4107 100644
--- a/src/cockpit/389-console/src/banner.html
+++ b/src/cockpit/389-console/src/banner.html
@@ -1,6 +1,6 @@
-389 Directory Server Management <div class="dropdown ds-server-action">
<select class="btn btn-default dropdown ds-dropdown-server"
+389 Directory Server Management <div class="dropdown ds-server-action">
<select class="btn btn-default dropdown"
title="Directory Server Instance List" id="select-server">
- </select></div><div class="dropdown
ds-float-right"><button class="btn btn-primary dropdown-toggle
ds-action-btn"
+ </select></div><div class="dropdown
ds-float-right"><button class="btn btn-primary ds-action-btn"
type="button" id="server-list-menu"
data-toggle="dropdown">Actions<span
class="caret"></span>
</button><ul class="dropdown-menu pull-right"
role="menu">
<li role=""><a role="menuitem"
id="start-server-btn" href="#">Start Instance</a></li>
diff --git a/src/cockpit/389-console/src/css/ds.css
b/src/cockpit/389-console/src/css/ds.css
index 8082d1e..fdc79e7 100644
--- a/src/cockpit/389-console/src/css/ds.css
+++ b/src/cockpit/389-console/src/css/ds.css
@@ -8,19 +8,6 @@
margin-bottom: 20px;
}
-.ds-logo-hr {
- padding: 0 !important;
- margin-top: -20px !important;
- line-height: 0;
-}
-
-.ds-title-hr {
- margin: 0 !important;
- padding: 0 !important;
- margin-right: 10px !important;
- margin-bottom: 10px !important;
-}
-
.ds-container {
margin: 0;
padding: 0 !important;
@@ -32,13 +19,8 @@
vertical-align: top !important;
}
-.ds-fixed {
- padding: 0 !important;
- padding-bottom: 10px;
- width: 175px !important;
- min-width: 175px !important;
- margin: 2px 2px 2px 0px !important;
- line-height: 0;
+.ds-left-align {
+ right: 0 !important;
}
/* Main nav page index.html */
@@ -47,101 +29,14 @@
padding-top: 115px; /* this pushes the content below fixed nav bar */
padding-bottom: 50px;
margin-top: 0;
- margin-left: 25px;
margin-right: 10px;
margin-bottom: 10px;
}
-.ds-flex {
- /* background-color: #f7f7f7; */
- background-color: white !important;
- flex-grow: 1;
- border: 1px groove black;
- border-style: groove;
- margin: 6px 2px 2px 10px;
- margin-left: 0px !important;
- margin-right: 10px;
- margin-bottom: 10px;
- padding-left: 15px;
- padding-right: 15px;
- padding-top: 0px;
- padding-bottom: 0px;
- min-height: 500px;
- line-height: 0;
- border-radius: 5px;
-}
-
-.ds-repl-state-good {
- margin: auto;
- width: 30px;
- height: 30px;
- border-radius: 50%;
- font-size: 12px;
- font-weight: bold;
- color: white;
- line-height: 1;
- text-align: center;
- background-color: #4dac26;
- border-color: black;
- padding-top: 8px;
- align-content: center !important;
- vertical-align: middle !important;
-}
-
-.ds-repl-state-bad {
- margin: auto;
- width: 30px;
- height: 30px;
- border-radius: 50%;
- font-size: 12px;
- font-weight: bold;
- color: white;
- line-height: 1;
- text-align: center;
- background-color: #d01c8b;
- border-color: black;
- padding-top: 8px;
- align-content: center !important;
- vertical-align: middle !important;
-}
-
.ds-chart-right {
margin-left: 65px;
}
-.ds-chart-left {
- margin-left: 25px;
- margin-bottom: 9px;
-}
-
-.ds-chart-center {
- margin-left: 100px;
- margin-bottom: 15px;
-}
-
-.ds-repl-lag-bad {
- font-weight: bold;
- color: #7b3294;
-}
-
-.ds-button-border {
- line-height: 0;
- margin: 0px;
- padding-top: 0px;
- padding-bottom: 10px;
-}
-
-.ds-button {
- height: 30px;
- width: 160px !important;
- margin-top: 5px;
- float: none;
- left: auto;
- line-height: normal;
- overflow: visible;
- position: static;
-}
-
.ds-oc-add-del-btn {
padding: 0 !important;
margin: 10px !important;
@@ -159,6 +54,11 @@
margin-right: 9px;
}
+/* This can probably be removed once we switch to PF React 4 - suffix action button */
+.ds-action-button {
+ color: white;
+}
+
.ds-oc-may-buttons {
padding0: !important;
padding-left: 3px;
@@ -167,72 +67,6 @@
margin-right: 9px;
}
-.ds-panel {
- margin: 0px 0px 0px 0px;
- padding-top: 10px;
- padding-left: 10px;
- line-height: 0;
-}
-
-.ds-dropdown {
- padding: 0px;
- padding-left: 5px;
- height: 35px;
- width: 300px !important;
- margin-top: 10px;
- text-align: left;
- outline: 0 !important;
-}
-
-.ds-dropdown-server {
- margin-top: 0px !important;
- margin-left: 6px;
- padding: 0px !important;
- padding-left: 10px !important;
- padding-right: 10px !important;
- height: 30px;
- text-align: left;
-}
-
-.ds-passwd-dropdown {
- height: 35px !important;
- padding: 0px !important;
- outline: 0 !important;
- width: 150px !important;
-}
-
-.ds-log-dropdown {
- margin-left: 10px;
- padding: 0px !important;
- outline: 0 !important;
- width: 65px !important;
-}
-
-.ds-rootdn-passwd-dropdown {
- margin-left: 30px;
- margin-top: 5px;
- padding: 10px;
- outline: 0 !important;
- width: 175px;
- border-radius: 5px;
-}
-
-.ds-table {
- padding: 0px;
- border: 1px solid #909090;
- line-height: 1;
- margin-top: 10px !important;
- margin-bottom: 10px !important;
-}
-
-.ds-referral-table {
- padding: 0px !important;
- margin-top: 10px !important;
- margin-bottom: 10px !important;
- border: 1px solid #909090;
- line-height: 1;
-}
-
.ds-refresh {
background-color: #0088ce;
background-image: linear-gradient(to bottom,#39a5dc 0,#0088ce 100%);
@@ -246,6 +80,10 @@
font-size: 13px !important;
}
+.ds-switch {
+ margin-top: 2px;
+}
+
.ds-refresh:hover {
color: DarkGray;
background-color: white;
@@ -282,15 +120,8 @@ td {
width: 525px;
}
-.ds-hr-pwp {
- display: block;
- margin-top: 0.5em;
- margin-bottom: 0.5em;
- margin-left: 0 !important;
- border-style: inset;
- border-width: 1px;
+.ds-no-padding {
padding: 0px !important;
- width: 835px;
}
.ds-tree {
@@ -303,45 +134,20 @@ td {
min-width: 300px;
min-height: 400px;
max-height: 400px;
- margin: 25px 10px 2px 0px;
+ margin-bottom: 5px;
overflow: auto;
padding-top: 5px;
}
-.ds-monitor-tree {
- font-size: 0.5;
- background-color: #f8f8f8;
- border: 1px solid #909090;
- color: black;
- line-height: 2;
- width: 275px;
- min-width: 275px;
- min-height: 400px;
- max-height: 400px;
- margin: 10px;
- overflow: auto;
-}
-
-.ds-treenode {
- border: 0 !important;
-}
-
-.ds-treenode ul {
- border: 0 !important;
-}
-
-.ds-treenode li {
- border: 0 !important;
+.ds-disabled {
+ pointer-events: none;
+ opacity: 0.7;
}
.ds-split {
width: 400px;
}
-.ds-modal-header {
- font-size: 16px;
-}
-
.ds-input {
margin-top: 5px !important;
padding-right: 5px !important;
@@ -349,12 +155,12 @@ td {
}
.ds-input-bad {
- border-color: red;
+ border-color: red !important;
}
.ds-input-auto-bad {
width: 100%;
- border-color: red;
+ border-color: red !important;
padding-left: 5px;
}
@@ -376,40 +182,6 @@ td {
max-width: 65px !important;
}
-.ds-pw-list-input {
- margin-top: 5px;
- padding-right: 5px;
- padding-left: 5px !important;
- min-width: 150px !important;
- max-width: 150px !important;
-}
-
-.ds-ro-input {
- margin-top: 5px;
- padding-left: 5px;
- margin-right: 5px;
- background-color: #f8f8f8 !important;
-}
-
-.ds-input-lrg {
- width: 315px !important;
- height: 37px !important;
- margin-top: 5px;
- padding-right: 10px;
- height: 37px !important;
-}
-
-.ds-input-long {
- width: 500px !important;
-}
-
-.ds-input-max {
- margin-left: 10px;
- margin-bottom: 20px;
- width: 500px;
- max-width: 600px;
-}
-
.ds-lag-report {
min-width: 800px !important;
}
@@ -418,25 +190,12 @@ td {
max-width: 600px !important;
}
-.ds-role-input {
- height: 30px !important;
- min-height: 30px;
- margin-top: 5px;
- margin-left: 7px;
- border-radius: 3px;
- padding-left: 5px !important;
-}
-
.ds-history-input {
margin-top: !important;
margin-right: 5px;
margin-left: 40px;
}
-.ds-form-input {
- margin-left: 10px;
-}
-
.ds-divider {
width: 35px;
}
@@ -449,49 +208,6 @@ td {
width: 15px;
}
-.ds-div-right {
- width: 50% !important;
- margin: 2px 2px 2px 2px;
-}
-
-.ds-div {
- width: 50% !important;
- margin: 2px 2px 2px 2px;
-}
-
-.ds-button-save {
- width: 100px;
- margin: 2px 2px 2px 215px;
-}
-
-.ds-changelog-button {
- width: 150px;
- height: 32px;
- margin-top: 0px !important;
- margin-left: 5px;
- margin-bottom: 4px;
-}
-
-.ds-ref-button {
- margin-right: 20px;
- min-width: 110px !important;
- max-width: 110px !important;
-}
-
-.ds-repl-manager {
- margin-top: 5px;
- padding: 5px;
- resize: none;
- width: 305px;
- height: 100px;
- white-space: pre !important;
-}
-
-.ds-roles {
- padding: 5px !important;
- margin-bottom: 5px;
-}
-
.ds-repl-managers-list {
width: 420px;
height: 200px;
@@ -535,20 +251,6 @@ td {
overflow: auto;
}
-.ds-agmt-wiz-dropdown {
- width: 175px !important;
- text-align: left;
-}
-
-.ds-agmt-wiz-dropdown a {
- padding: 0px !important;
- padding-left: 10px !important;
- line-height: 0 !important;
- height: 40px;
- width: 175px !important;
- text-align: left;
-}
-
.ds-dblink-dropdown {
width: 140px !important;
text-align: left;
@@ -569,20 +271,6 @@ td {
height: 37px !important;
}
-.ds-agmt-dropdown-button {
- width: 120px;
-}
-
-.ds-agmt-button {
- height: 40px;
- width: 210px;
-}
-
-.ds-sasl-button {
- height: 35px;
- width: 175px;
-}
-
.ds-repl-table {
background-color: white !important;
padding: 0px !important;
@@ -623,117 +311,16 @@ td {
line-height: 1;
}
-.ds-table-header th {
- text-align: center !important;
-}
-
-.ds-plugin-table {
- padding: 0px;
- border: 1px solid #909090;
- line-height: 1;
- table-layout: fixed;
- clear: both;
- word-wrap: break-word;
- text-align: center;
-}
-
-.ds-plugin-header th {
- width: 100%;
- text-align: center !important;
-}
-
-.ds-plugin-panel {
- background-color: #f7f7f7;
- flex-grow: 1;
- border: 1px groove black;
- border-style: groove;
- margin: 6px 2px 2px 10px;
- margin-left: 10px;
- margin-right: 10px;
- margin-bottom: 10px;
- padding-left: 15px;
- padding-right: 15px;
- padding-top: 0px;
- padding-bottom: 0px;
-}
-
-.ds-monitor-panel {
- background-color: #f7f7f7;
- flex-grow: 1;
- border: 1px groove black;
- border-style: groove;
- margin: 6px 2px 2px 10px;
- margin-left: 10px;
- margin-right: 10px;
- margin-bottom: 10px;
- padding-left: 15px;
- padding-right: 15px;
- padding-top: 0px;
- padding-bottom: 10px;
-}
-
.ds-cipher-width {
max-width: 350px !important;
min-width: 350px !important;
}
-/*
- * Popup modal stuff
-*/
-
-/* Extra styles for the cancel button */
-.cancelbtn {
- padding: 14px 20px;
- background-color: #f44336;
-}
-
-/* Float cancel and signup buttons and add an equal width */
-.cancelbtn,
-.savebtn {
- float: left;
- width: 50%;
-}
-
-/* Add padding to container elements */
-.container {
- width: 650px;
- padding-left: 10px;
- padding-right: 10px;
- padding-bottom: 10px;
-}
-
-.ds-modal {
- width: 450px;
-}
-
.ds-modal-wide {
width: 875px !important;
min-width: 875px !important;
vertical-align: middle;
-}
-
-.ds-log-panel {
- padding: 0 18px;
- max-height: 0 !important;
- overflow: hidden !important;
- transition: max-height 0.2s ease-out !important;
- margin: 2px;
- display: none;
-}
-
-.ds-suffix-panel {
- padding: 0 18px;
- max-height: 0 !important;
- overflow: hidden !important;
- transition: max-height 0.2s ease-out !important;
- margin: 2px;
- display: none !important;
-}
-
-.ds-agmt-wiz-button {
- height: 40px;
- width: 630px;
- font-weight: bold;
+ margin-left: -100px !important;
}
.ds-button-right {
@@ -743,14 +330,6 @@ td {
display: inline-block !important;
}
-.ds-close-button-right {
- height: 40px;
- width: 150px !important;
- margin-top: 5px;
- margin-bottom: 5px;
- float: right !important;
-}
-
.ds-button-left {
width: 120px;
max-width: 150px;
@@ -791,12 +370,6 @@ td {
margin: 5px 5px 5px 10px;
}
-.ds-agmt-schedule-time {
- width: 75px;
- padding: 10px;
- margin: 5px;
-}
-
.ds-agmt-schedule-checkbox {
margin: 8px !important;
padding: 5px;
@@ -808,13 +381,6 @@ td {
margin-right: 5px !important;
}
-.ds-mgr-checkbox {
- margin-left: 10px !important;
- margin-top: 10px !important;
- margin-bottom: 10px !important;
- margin-right: 5px !important;
-}
-
.ds-server-checkbox {
margin-top: 15px !important;
margin-right: 12px !important;
@@ -824,171 +390,74 @@ td {
.ds-send-expiring-checkbox {
margin-top: 12px !important;
margin-right: 12px !important;
- padding: 5px;
-}
-.ds-nowrap-td {
- overflow: hidden !important;
- white-space: nowrap !important;
-}
-
-.ds-repl-bind-group-label {
- width: 85px;
- margin-bottom: 25px !important;
-}
-
-.ds-config-label {
- margin-top: 10px;
- width: 200px !important;
- margin-bottom: 10px !important;
-}
-
-.ds-config-label-xlrg {
- margin-top: 10px;
- width: 180px !important;
- margin-bottom: 10px !important;
-}
-
-.ds-config-label-lrg {
- margin-top: 10px;
- width: 160px !important;
- margin-bottom: 10px !important;
-}
-
-.ds-config-label-med {
- margin-top: 10px;
- width: 100px !important;
- margin-bottom: 10px !important;
-}
-
-.ds-config-label-sm {
- margin-top: 10px;
- margin-right: 10px;
- margin-bottom: 10px !important;
-}
-
-.ds-config-sub-label {
- margin-top: 10px;
- width: 200px !important;
- margin-bottom: 10px !important;
- padding-left: 20px;
+ padding: 5px;
}
-.ds-label-lrg {
- width: 200px;
+.ds-config-label {
+ margin-top: 10px;
+ width: 225px !important;
margin-bottom: 10px !important;
}
-.ds-label-med {
- width: 180px;
+.ds-config-label-xlrg {
+ margin-top: 10px;
+ width: 180px !important;
margin-bottom: 10px !important;
}
-.ds-monitor-label {
- width: 155px;
+.ds-config-label-lrg {
margin-top: 10px;
+ width: 160px !important;
margin-bottom: 10px !important;
}
-.ds-monitor-label-med {
- width: 175px;
+.ds-config-label-med {
margin-top: 10px;
+ width: 125px !important;
margin-bottom: 10px !important;
}
-.ds-label-sm {
- width: 130px !important;
+.ds-config-sub-label {
+ margin-top: 10px;
+ width: 225px !important;
margin-bottom: 10px !important;
+ padding-left: 20px;
}
-.ds-label-xsm {
- width: 100px !important;
+.ds-label-sm {
+ width: 175px !important;
margin-bottom: 10px !important;
}
.ds-cleanallruv-label {
- width: 110px !important;
+ width: 150px !important;
margin-bottom: 10px !important;
}
-.ds-cache-label {
- width: 210px !important;
- margin-left: 20px !important;
-}
-
-.ds-repl-config-label {
- width: 210px !important;
- margin-top: 20px;
- margin-bottom: 20px;
-}
-
.ds-expire-label {
margin-top: 7px;
margin-bottom: 7px;
width: 285px !important;
}
-.ds-pw-list-label {
- margin-top: 7px !important;
- margin-bottom: 7px !important;
- width: 200px !important;
-}
-
-.ds-passwd-label {
- width: 300px !important;
-}
-
.ds-minage-label {
padding: 10px;
}
-.ds-history-label {
- padding: 10px;
- margin-left: 50px;
-}
-
-.ds-lockout-label {
- padding: 10px;
- margin-left: 20px;
-}
-
-.ds-server-label {
- padding-top: 20px;
- padding-bottom: 10px;
- padding-right: 60px !important;
-}
-
.ds-config-diskmon-label {
width: 210px !important;
padding-left: 30px;
margin-top: 10px;
}
-.ds-config-ndn-label {
- padding-left: 40px;
- padding-right: 10px;
-}
-
.ds-config-indent-sm-label {
margin-top: 10px;
margin-bottom: 10px;
- width: 175px;
+ width: 195px;
margin-left: 40px !important;
padding-right: 10px !important;
}
-.ds-config-indent-med-label {
- margin-left: 80px !important;
- padding-right: 10px !important;
-}
-
-.ds-config-indent-large-label {
- margin-top: 7px;
- margin-bottom: 7px;
- margin-left: 120px !important;
- padding-right: 10px !important;
- width: 175px;
-}
-
.ds-expired-div {
padding-left: 30px !important;
}
@@ -999,38 +468,6 @@ td {
margin-left: 30px !important;
}
-.ds-loglevel-list {
- width: 400px;
- margin-top: 10px;
- margin-left: 20px;
- min-height: 400px !important;
-}
-
-.ds-accordion-spacing {
- margin-bottom: 10px !important;
- padding-bottom: 10px !important;
-}
-.ds-button-wide {
- margin: 10px;
- height: 40px;
- width: 210px;
-}
-
-.ds-adj-btn {
- padding: 0 !important;
- margin-left: 10px !important;
- width: 100px !important;
-}
-
-.ds-task-panel {
- background-color: white;
- border: 1px groove black;
- border-style: groove;
- margin: 10px;
- width: 235px;
- line-height: 0;
-}
-
.ds-modal-row {
margin-left: 20px;
margin-right: 0px !important;
@@ -1049,18 +486,6 @@ td {
width: 85px;
}
-.ds-chaining-form-list {
- width: 100%;
-}
-
-.ds-vlv-sort-list {
- width: 100%;
-}
-
-.ds-index-form-list {
- width: 100%;
-}
-
.ds-oc-form-list {
width: 232px !important;
max-height: 350px !important;
@@ -1110,6 +535,7 @@ textarea {
line-height: 1;
font-family: monospace !important;
overflow-y: scroll;
+ font-size: 12px;
}
.ds-conflict {
@@ -1134,18 +560,6 @@ option {
color: #181818;
}
-.jstree-wholerow-hovered {
- background: linear-gradient(to right, #0a676f 0%, #e5e5e5 100%) !important;
- opacity: 0.05;
-}
-
-.jstree-wholerow-clicked {
- background-color: #0a676f !important;
- background: linear-gradient(to right, #0a676f 0%, #e5e5e5 100%) !important;
- opacity: 0.15 !important;
- transition: opacity 0.25s ease-in-out !important;
-}
-
.ds-server-action {
padding: 0 !important;
margin-left: 10px;
@@ -1163,6 +577,7 @@ option {
.ds-nav {
padding: 0 !important;
+ background-color: #f3f3f3;
}
.ds-nav-tab a {
@@ -1178,7 +593,18 @@ option {
float: left;
}
-.ds-footer{
+.ds-inline-btn {
+ margin-top: -3px;
+ margin-left: 10px;
+ display: inline-block;
+}
+
+.spinner-sm {
+ height: 18px !important;
+ Width: 18px !important;
+}
+
+.ds-footer {
background-color: #f5f5f5 !important;
margin-left: -25px;
padding: 10px;
@@ -1189,19 +615,6 @@ option {
border-top: 1px solid #e2e2e2 !important;
}
-.modal-footer {
- background-color: #f5f5f5 !important;
- padding: 10px;
- bottom: 0;
- width: 100%;
- height: 50px;
- border-top: .5px solid #e2e2e2 !important;
-}
-
-.modal-header {
- border-bottom: .5px solid #e2e2e2 !important;
-}
-
.ds-nav-bar {
position: fixed;
top: 0;
@@ -1232,7 +645,7 @@ option {
border: 0;
position: relative;
overflow: hidden;
- width: 700px;
+ width: 100%;
text-align: left;
font: inherit;
}
@@ -1243,7 +656,7 @@ option {
height: 1px;
background: #228bc0;
position: absolute;
- width: 700px;
+ width: 100%;
top: 50% !important;
margin-left: 10px;
text-align: left;
@@ -1268,12 +681,9 @@ option {
}
.ds-margin-top {
- margin-top: 10px;
+ margin-top: 10px !important;
}
-.ds-margin-top-sm {
- margin-top: 9px;
-}
.ds-margin-top-med {
margin-top: 15px !important;
}
@@ -1286,14 +696,17 @@ option {
margin-top: 35px !important;
}
+.ds-margin-top-xxlg {
+ margin-top: 45px !important;
+}
+
.ds-modal-spinner {
margin-top: 10px;
text-align: center;
}
-.ds-accordion-header {
- margin-bottom: 15px;
- margin-top: 0px;
+.modal {
+ overflow-y:auto;
}
/* wizard accordions are narrower */
@@ -1337,31 +750,21 @@ option {
margin-left: 15px !important;
}
-.ds-right-indent {
- margin-right: 15px !important;
+.ds-left-indent-md {
+ margin-left: 30px !important;
}
-.ds-gap {
- margin-bottom: 40px !important;
+.ds-right-indent {
+ margin-right: 15px !important;
}
.ds-tree-content {
margin-left: 25px;
- margin-top: 25px;
width: 100%;
}
-.ds-select {
- height: 30px;
- outline: 0 !important;
- padding: 0px !important;
- padding-left: 5px;
- padding-right: 10px;
- text-align: left;
-}
-
-.ds-center {
- text-align: center;
+.ds-inst-indent {
+ margin-left: 240px;
}
.ds-left-margin {
@@ -1372,10 +775,6 @@ option {
margin-left: 5px !important;
}
-.ds-table-btn {
- height: 30px;
-}
-
.ds-td {
padding-left: 10px !important;
padding-top: 8px !important;
@@ -1401,65 +800,36 @@ option {
text-align: center;
}
-.ds-table-btn {
- width: 110px;
- text-align: center;
-}
-
.ds-page-content {
margin-top: 30px !important;
}
-.ds-checkbox-group {
- margin-top: 10px !important;
-}
-
-.ds-alert-header {
- background-color: #f2dede;
-}
-
-.ds-sec-dropdown {
- width: 80px;
-}
-
-.ds-agmt-dropdown {
- width: 120px;
-}
-
-.ds-first {
- margin-top: 10px;
-}
-
-.ds-save-btn {
- margin-top: 20px !important;
-}
-
.ds-spacing-sm {
margin-right: 10px;
}
-.ds-spacing-med {
- margin-right: 20px;
-}
-
.navbar-default {
margin-bottom: 0px;
}
-.ds-center {
- text-align: center;
-}
-
.ds-modal-error {
color: red;
}
+.ds-clear-text {
+ opacity: 0;
+}
+
+.ds-margin-left-sm {
+ margin-left: 30px !important;
+}
+
.ds-margin-left {
margin-left: 40px !important;
}
.ds-margin-left-piechart {
- margin-left: 130px !important;
+ margin-left: 140px !important;
}
.ds-nested-modal {
@@ -1484,10 +854,6 @@ option {
margin-top: -2px;
}
-.ds-suffix-cfg-div {
- min-width: 500px;
-}
-
.ds-loading-spinner {
position: fixed;
top: 25%;
@@ -1495,21 +861,22 @@ option {
transform: translate(-25%, -50%);
}
-.ds-loader {
- font-size: 15px;
-}
-
-.ds-popup {
- min-width: 350px !important;
+.ds-loading-spinner-tree {
+ position: fixed;
+ top: 40%;
+ left: 40%;
+ transform: translate(-40%, -40%);
}
-.ds-disable-repl-btn {
- margin-left: 15px !important;
- max-height: 25px !important;
+.ds-loading {
+ position: fixed;
+ top: 25%;
+ left: 35%;
+ transform: translate(-25%, -35%);
}
-.ds-repl-form {
- max-width: 350px !important;
+.ds-popup {
+ min-width: 350px !important;
}
.ds-input-auto {
@@ -1526,21 +893,16 @@ option {
bottom: 10px;
}
-.ds-plugin-button {
- margin-right: 5px;
- margin-left: 5px;
-}
-
-.ds-raise-button {
- margin-top: -5px !important;
-}
-
.ds-plugin-spinner {
margin-top: 20px;
margin-bottom: 10px;
margin-left: 80px;
}
+.ds-h4 {
+ font-size: 18px !important;
+}
+
@media screen and (max-width: 1300px) {
.ds-plugin-spinner {
margin-left: 0px;
@@ -1560,6 +922,10 @@ option {
border-radius: 3px;
}
+.nav-pills > li.active > a {
+ color: #fff !important;
+}
+
.ds-plugin-tab-header {
position: relative;
top: -16px;
@@ -1572,10 +938,6 @@ option {
}
}
-.control-label {
- text-align: left !important;
-}
-
.rbt-token {
background-color: #ededed;
color: #363636;
@@ -1601,24 +963,20 @@ option {
border: 1px solid #d1d1d1;
}
-.ds-vlv-label {
- width: 50px;
-}
-
.list-view-pf-view {
margin-top: 10px;
}
.ds-fadein {
- visibility: visible;
- opacity: 1;
- transition: opacity 0.5s linear;
+ visibility: visible;
+ opacity: 1;
+ transition: opacity 0.5s linear;
}
.ds-fadeout {
- visibility: hidden;
- opacity: 0;
- transition: visibility 0s 0s, opacity 1s linear;
+ visibility: hidden;
+ opacity: 0;
+ transition: visibility 0s 0s, opacity 1s linear;
}
.ds-col-append {
@@ -1630,12 +988,12 @@ option {
.ds-word-wrap {
word-wrap: break-word;
- padding-top: 3px;
}
.ds-suffix-header {
- font-size: 16px;
+ font-size: 18px;
margin-bottom: 15px;
+ padding-top: 5px;
}
.ds-header {
@@ -1690,173 +1048,25 @@ input {
width: 100%;
}
-/* Dual List CSS */
-.dual-list-pf-arrows {
- display: inline-block;
- margin: auto;
- position: relative;
- bottom: 170px;
- font-size: 23px;
- color: #bbb;
-}
-@media only screen and (max-width: 600px) {
- .dual-list-pf-arrows {
- display: block;
- position: inherit;
- margin: 5px 0;
- padding-left: 79px;
- }
-}
-
-.dual-list-pf-arrows span {
- display: block;
- margin: 25px;
- cursor: pointer;
- transition: color 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
- transform: rotate(-90deg);
-}
-@media only screen and (max-width: 600px) {
- .dual-list-pf-arrows span {
- display: inline;
- margin: 0 20px 0 0;
- }
-}
-
-.dual-list-pf-arrows span:hover {
- color: #8b8d8f;
-}
-
-.dual-list-pf-body {
- height: 375px;
- width: 320px;
- overflow-y: scroll;
- overflow-x: auto;
- display: inline-grid;
- align-content: flex-start;
-}
-
-.dual-list-pf-body::-webkit-scrollbar {
- width: 12px;
- height: 12px;
- background: #fafafa;
-}
-
-.dual-list-pf-body::-webkit-scrollbar-thumb {
- background: #d1d1d1;
- border-radius: 6px;
- border: 3px solid transparent;
- background-clip: content-box;
-}
-
-.dual-list-pf-body::-webkit-scrollbar-thumb:hover {
- background: #bbb;
- border-radius: 6px;
- border: 3px solid transparent;
- background-clip: content-box;
-}
-
-.dual-list-pf-filter {
- margin-left: 20px;
-}
-
-.dual-list-pf-filter input {
- background-color: #f5f5f5;
- border: 1px solid #ededed;
- width: 145px;
- padding: 0 22px 0 5px;
- margin-top: 3px;
- margin-bottom: 3px;
-}
-
-.dual-list-pf-filter .search-icon {
- position: relative;
- right: 20px;
- bottom: 1px;
- color: #bbb;
-}
-
-.dual-list-pf-filter ::-webkit-input-placeholder {
- font-style: italic;
-}
-
-.dual-list-pf-footer {
- padding: 10px;
- border-top: 1px solid #d1d1d1;
-}
-
-.dual-list-pf-heading {
- border-bottom: 1px solid #d1d1d1;
-}
-
-.dual-list-pf-item {
- padding: 5px 0;
- margin-bottom: 0;
- font-weight: 400;
- transition: background 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94), color 0.3s
ease-out;
- cursor: pointer;
- white-space: nowrap;
-}
-
-.dual-list-pf-item input[type='checkbox'] {
- position: relative;
- left: 10px;
- vertical-align: top;
- cursor: pointer;
-}
-
-.dual-list-pf-item.selected {
- background-color: #0088ce;
- color: white;
-}
-
-.dual-list-pf-item.disabled {
- cursor: not-allowed;
- background: #f5f5f5;
- color: #8b8d8f;
-}
-
-.dual-list-pf-item.disabled input[type='checkbox'] {
- cursor: not-allowed;
-}
-
-.dual-list-pf-item.child {
- padding-left: 22px;
-}
-
-.dual-list-pf-item:hover:not(.selected):not(.disabled) {
- background-color: #bee1f4;
- color: inherit;
-}
-
-.dual-list-pf-item-label {
- margin-left: 20px;
-}
-
-.dual-list-pf-main-checkbox {
- position: relative;
- left: 10px;
- vertical-align: text-top;
- cursor: pointer;
+th {
+ text-align: center;
}
-.dual-list-pf-no-items {
- margin-top: 30px;
+td {
text-align: center;
}
-.dual-list-pf-selector {
- display: inline-block;
- border: 1px solid #d1d1d1;
- user-select: none;
+hr {
+ margin-top: 20px;
+ margin-bottom: 20px;
}
-.dual-list-pf-sort-icon {
- cursor: pointer;
+h3 {
+ margin-top: 20px;
+ margin-bottom: 20px;
}
-.dropdown-kebab-pf.btn-group {
- margin-left: 10px;
- float: right;
- margin-right: 10px;
+h4 {
+ margin-top: 15px;
+ margin-bottom: 15px;
}
-/* End of dual list */
diff --git a/src/cockpit/389-console/src/database.jsx
b/src/cockpit/389-console/src/database.jsx
index e5adf79..b1c3953 100644
--- a/src/cockpit/389-console/src/database.jsx
+++ b/src/cockpit/389-console/src/database.jsx
@@ -13,9 +13,13 @@ import {
Modal,
Icon,
Form,
+ Row,
+ Col,
+ ControlLabel,
Button,
noop,
TreeView,
+ Radio,
Spinner
} from "patternfly-react";
import PropTypes from "prop-types";
@@ -41,7 +45,11 @@ export class Database extends React.Component {
showSuffixModal: false,
createSuffix: "",
createBeName: "",
- createRootNode: false,
+ createSuffixEntry: false,
+ createSampleEntries: false,
+ noSuffixInit: true,
+ disableTree: false,
+
// DB config
globalDBConfig: {},
configUpdated: 0,
@@ -67,6 +75,7 @@ export class Database extends React.Component {
this.removeNotification = this.removeNotification.bind(this);
this.addNotification = this.addNotification.bind(this);
this.handleChange = this.handleChange.bind(this);
+ this.handleRadioChange = this.handleRadioChange.bind(this);
this.loadGlobalConfig = this.loadGlobalConfig.bind(this);
this.loadLDIFs = this.loadLDIFs.bind(this);
this.loadBackups = this.loadBackups.bind(this);
@@ -75,7 +84,6 @@ export class Database extends React.Component {
// Suffix
this.showSuffixModal = this.showSuffixModal.bind(this);
this.closeSuffixModal = this.closeSuffixModal.bind(this);
- this.handleChange = this.handleChange.bind(this);
this.createSuffix = this.createSuffix.bind(this);
this.loadSuffix = this.loadSuffix.bind(this);
this.loadSuffixConfig = this.loadSuffixConfig.bind(this);
@@ -94,6 +102,7 @@ export class Database extends React.Component {
// Other
this.loadSuffixTree = this.loadSuffixTree.bind(this);
+ this.enableTree = this.enableTree.bind(this);
}
componentWillMount () {
@@ -441,6 +450,13 @@ export class Database extends React.Component {
}
selectNode(selectedNode) {
+ if (selectedNode.selected) {
+ return;
+ }
+ this.setState({
+ disableTree: true // Disable the tree to allow node to be fully loaded
+ });
+
if (selectedNode.id == "dbconfig" ||
selectedNode.id == "chaining-config" ||
selectedNode.id == "backups") {
@@ -541,10 +557,32 @@ export class Database extends React.Component {
showSuffixModal () {
this.setState({
showSuffixModal: true,
+ createSuffixEntry: false,
+ createSampleEntries: false,
+ noSuffixInit: true,
errObj: {},
});
}
+ handleRadioChange(e) {
+ // Handle the create suffix init option radio button group
+ let noInit = false;
+ let addSuffix = false;
+ let addSample = false;
+ if (e.target.id == "noSuffixInit") {
+ noInit = true;
+ } else if (e.target.id == "createSuffixEntry") {
+ addSuffix = true;
+ } else { // createSampleEntries
+ addSample = true;
+ }
+ this.setState({
+ noSuffixInit: noInit,
+ createSuffixEntry: addSuffix,
+ createSampleEntries: addSample
+ });
+ }
+
handleChange(e) {
const value = e.target.type === 'checkbox' ? e.target.checked :
e.target.value;
let valueErr = false;
@@ -570,7 +608,7 @@ export class Database extends React.Component {
let errors = false;
let missingArgs = {
createSuffix: false,
- createBeName: false
+ createBeName: false,
};
if (this.state.createSuffix == "") {
@@ -601,9 +639,12 @@ export class Database extends React.Component {
"dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-"
+ this.props.serverId + ".socket",
"backend", "create", "--be-name",
this.state.createBeName, '--suffix', this.state.createSuffix,
];
- if (this.state.createSampleEntries == true) {
+ if (this.state.createSampleEntries) {
cmd.push('--create-entries');
}
+ if (this.state.createSuffixEntry) {
+ cmd.push('--create-suffix');
+ }
log_cmd("createSuffix", "Create a new backend", cmd);
cockpit
@@ -616,6 +657,7 @@ export class Database extends React.Component {
);
// Refresh tree
this.loadSuffixTree(false);
+ this.loadSuffixList();
})
.fail(err => {
let errMsg = JSON.parse(err);
@@ -942,8 +984,38 @@ export class Database extends React.Component {
suffixLoading: false
});
});
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.addNotification(
+ "error",
+ `Error attribute encryption for ${suffix}
- ${errMsg.desc}`
+ );
+ this.setState({
+ suffixLoading: false
+ });
});
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.addNotification(
+ "error",
+ `Error loading VLV indexes for ${suffix} -
${errMsg.desc}`
+ );
+ this.setState({
+ suffixLoading: false
+ });
});
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.addNotification(
+ "error",
+ `Error loading config for ${suffix} - ${errMsg.desc}`
+ );
+ this.setState({
+ suffixLoading: false
+ });
});
}
@@ -1013,9 +1085,20 @@ export class Database extends React.Component {
});
}
+ enableTree () {
+ this.setState({
+ disableTree: false
+ });
+ }
+
render() {
const { nodes } = this.state;
let db_element = "";
+ let disabled = "tree-view-container";
+ if (this.state.disableTree) {
+ disabled = "tree-view-container ds-disabled";
+ }
+
if (this.state.loaded) {
if (this.state.node_name == DB_CONFIG || this.state.node_name ==
"") {
db_element =
@@ -1024,6 +1107,7 @@ export class Database extends React.Component {
addNotification={this.addNotification}
reload={this.loadGlobalConfig}
data={this.state.globalDBConfig}
+ enableTree={this.enableTree}
key={this.state.configUpdated}
/>;
} else if (this.state.node_name == CHAINING_CONFIG) {
@@ -1033,6 +1117,7 @@ export class Database extends React.Component {
addNotification={this.addNotification}
reload={this.loadChainingConfig}
data={this.state.chainingConfig}
+ enableTree={this.enableTree}
key={this.state.chainingUpdated}
/>;
} else if (this.state.node_name == BACKUP_CONFIG) {
@@ -1043,6 +1128,7 @@ export class Database extends React.Component {
backups={this.state.BackupRows}
suffixes={this.state.suffixList}
ldifs={this.state.LDIFRows}
+ enableTree={this.enableTree}
reload={this.loadBackups}
/>;
} else if (this.state.node_name != "") {
@@ -1050,10 +1136,9 @@ export class Database extends React.Component {
if (this.state.dbtype == "suffix" || this.state.dbtype ==
"subsuffix") {
if (this.state.suffixLoading) {
db_element =
- <div className="ds-loading-spinner
ds-center">
- <p />
+ <div className="ds-margin-top ds-loading-spinner
ds-center">
<h4>Loading suffix configuration for
<b>{this.state.node_text} ...</b></h4>
- <Spinner loading size="md" />
+ <Spinner className="ds-margin-top-lg"
loading size="md" />
</div>;
} else {
db_element =
@@ -1073,6 +1158,7 @@ export class Database extends React.Component {
dbtype={this.state.dbtype}
data={this.state[this.state.node_text]}
attrs={this.state.attributes}
+ enableTree={this.enableTree}
key={this.state.node_text}
/>;
}
@@ -1080,10 +1166,9 @@ export class Database extends React.Component {
// Chaining
if (this.state.chainingLoading) {
db_element =
- <div className="ds-loading-spinner
ds-center">
- <p />
+ <div className="ds-margin-top ds-loading-spinner
ds-center">
<h4>Loading chaining configuration for
<b>{this.state.node_text} ...</b></h4>
- <Spinner loading size="md" />
+ <Spinner className="ds-margin-top-lg"
loading size="md" />
</div>;
} else {
db_element =
@@ -1094,6 +1179,7 @@ export class Database extends React.Component {
loadSuffixTree={this.loadSuffixTree}
addNotification={this.addNotification}
data={this.state[this.state.node_text]}
+ enableTree={this.enableTree}
reload={this.loadChainingLink}
/>;
}
@@ -1110,7 +1196,7 @@ export class Database extends React.Component {
<div className="ds-container">
<div>
<div className="ds-tree">
- <div className="tree-view-container"
id="db-tree"
+ <div className={disabled} id="db-tree"
style={treeViewContainerStyles}>
<TreeView
nodes={nodes}
@@ -1133,7 +1219,11 @@ export class Database extends React.Component {
showModal={this.state.showSuffixModal}
closeHandler={this.closeSuffixModal}
handleChange={this.handleChange}
+ handleRadioChange={this.handleRadioChange}
saveHandler={this.createSuffix}
+ noInit={this.state.noSuffixInit}
+ addSuffix={this.state.createSuffixEntry}
+ addSample={this.state.createSampleEntries}
error={this.state.errObj}
/>
</div>
@@ -1147,7 +1237,11 @@ class CreateSuffixModal extends React.Component {
showModal,
closeHandler,
handleChange,
+ handleRadioChange,
saveHandler,
+ noInit,
+ addSuffix,
+ addSample,
error
} = this.props;
@@ -1169,20 +1263,39 @@ class CreateSuffixModal extends React.Component {
</Modal.Header>
<Modal.Body>
<Form horizontal autoComplete="off">
- <div className="ds-inline">
- <div>
- <label htmlFor="createSuffix"
className="ds-config-label" title="Database Suffix DN
(nsslapd-suffix)">
- Suffix DN</label><input
onChange={handleChange} className={error.createSuffix ? "ds-input-bad" :
"ds-input"} type="text" id="createSuffix"
size="40" />
- </div>
- <div>
- <label htmlFor="createBeName"
className="ds-config-label" title="Database backend name
(nsslapd-backend)">
- Backend Name</label><input
onChange={handleChange} className={error.createBeName ? "ds-input-bad" :
"ds-input"} type="text" id="createBeName"
size="40" />
- </div>
- <div>
- <p />
- <input type="checkbox"
className="ds-config-checkbox" id="createSampleEntries"
onChange={handleChange} /><label
- htmlFor="createSampleEntries"
className="ds-label" title="Create the datbase with sample
entries"> Create Sample Entries</label>
- </div>
+ <Row title="Database suffix, like
'dc=example,dc=com'. The suffix must be a valid LDAP Distiguished Name
(DN)">
+ <Col sm={3}>
+ <ControlLabel>Suffix DN</ControlLabel>
+ </Col>
+ <Col sm={5}>
+ <input onChange={handleChange}
className={error.createSuffix ? "ds-input-bad" : "ds-input"}
type="text" id="createSuffix" size="40" />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The
name for the backend database, like 'userroot'. The name can be a combination of
alphanumeric characters, dashes (-), and underscores (_). No other characters are allowed,
and the name must be unique across all backends.">
+ <Col sm={3}>
+ <ControlLabel>Database
Name</ControlLabel>
+ </Col>
+ <Col sm={5}>
+ <input onChange={handleChange}
className={error.createBeName ? "ds-input-bad" : "ds-input"}
type="text" id="createBeName" size="40" />
+ </Col>
+ </Row>
+ <hr />
+ <div>
+ <Row className="ds-indent">
+ <Radio name="radioGroup"
id="noSuffixInit" onChange={handleRadioChange} checked={noInit} inline>
+ Do Not Initialize Database
+ </Radio>
+ </Row>
+ <Row className="ds-indent">
+ <Radio name="radioGroup"
id="createSuffixEntry" onChange={handleRadioChange} checked={addSuffix}
inline>
+ Create The Top Suffix Entry
+ </Radio>
+ </Row>
+ <Row className="ds-indent">
+ <Radio name="radioGroup"
id="createSampleEntries" onChange={handleRadioChange} checked={addSample}
inline>
+ Add Sample Entries
+ </Radio>
+ </Row>
</div>
</Form>
</Modal.Body>
@@ -1221,7 +1334,11 @@ CreateSuffixModal.propTypes = {
showModal: PropTypes.bool,
closeHandler: PropTypes.func,
handleChange: PropTypes.func,
+ handleRadioChange: PropTypes.func,
saveHandler: PropTypes.func,
+ noInit: PropTypes.bool,
+ addSuffix: PropTypes.bool,
+ addSample: PropTypes.bool,
error: PropTypes.object,
};
@@ -1229,6 +1346,10 @@ CreateSuffixModal.defaultProps = {
showModal: false,
closeHandler: noop,
handleChange: noop,
+ handleRadioChange: noop,
saveHandler: noop,
+ noInit: true,
+ addSuffix: false,
+ addSample: false,
error: {},
};
diff --git a/src/cockpit/389-console/src/ds.js b/src/cockpit/389-console/src/ds.js
index e023fdc..7b4db77 100644
--- a/src/cockpit/389-console/src/ds.js
+++ b/src/cockpit/389-console/src/ds.js
@@ -10,7 +10,7 @@ var dn_regex = new RegExp( "^([A-Za-z]+=.*)" );
var server_page_loaded = 0;
var security_page_loaded = 1;
var db_page_loaded = 1;
-var repl_page_loaded = 0;
+var repl_page_loaded = 1;
var plugin_page_loaded = 1;
var schema_page_loaded = 0;
var monitor_page_loaded = 1;
@@ -77,7 +77,19 @@ function valid_dn (dn){
function valid_num (val){
// Validate value is a number
- return !isNaN(val);
+ let result = !isNaN(val);
+ return result;
+}
+
+function valid_port (val){
+ // Validate value is a number and between 1 and 65535
+ let result = !isNaN(val);
+ if (result) {
+ if (val < 1 || val > 65535) {
+ result = false;
+ }
+ }
+ return result;
}
function tableize (val) {
@@ -249,7 +261,7 @@ function get_insts() {
}
if (server_id != "None") {
- $("#ds-banner").html("Managing Instance <select class=\"btn
btn-default dropdown ds-dropdown-server\"
id=\"select-server\"></select>");
+ $("#ds-banner").html("Managing Instance <select class=\"btn
btn-default dropdown\" id=\"select-server\"></select>");
}
// Populate the server instance drop down
@@ -340,38 +352,19 @@ function popup_success(msg) {
// This is called when any Save button is clicked on the main page. We call
// all the save functions for all the pages here. This is not used for modal forms
function save_all () {
- save_config(); // Server Config Page
+ save_config(); // Server Config Page
}
-function load_repl_suffix_dropdowns() {
- // Update replication drop downs (agmts mainly)
- var repl_dropdowns = ['select-repl-agmt-suffix',
'select-repl-winsync-suffix',
- 'cleanallruv-suffix',
'monitor-repl-backend-list'];
- var repl_cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id +
'.socket','replication', 'list'];
- log_cmd('load_repl_suffix_dropdowns', 'get replicated suffix list',
repl_cmd);
- cockpit.spawn(repl_cmd, { superuser: true, "err": "message",
"environ": [ENV]}).done(function(data) {
- // Update dropdowns
- for (var idx in repl_dropdowns) {
- $("#" + repl_dropdowns[idx]).find('option').remove();
- }
- var obj = JSON.parse(data);
- for (var idx in obj['items']) {
- for (var list in repl_dropdowns){
- $("#" + repl_dropdowns[list]).append('<option value="'
+ obj['items'][idx] + '" selected="selected">' +
obj['items'][idx] +'</option>');
- }
- }
- if (obj['items'].length == 0){
- // Disable create agmt buttons
- $("#create-agmt").prop("disabled", true);
- $("#winsync-create-agmt").prop("disabled", true);
- $("#create-cleanallruv-btn").prop("disabled", true);
- } else {
- // Enable repl agmt buttons
- $("#create-agmt").prop("disabled", false);
- $("#winsync-create-agmt").prop("disabled", false);
- $("#create-cleanallruv-btn").prop("disabled", false);
+var progress = 10;
+
+function update_progress () {
+ progress += 10;
+ if (progress > 100) {
+ progress = 100;
}
- });
+ $("#ds-progress-label").text(progress + "%");
+ $("#ds-progress-bar").attr("aria-valuenow", progress);
+ $("#ds-progress-bar").css("width", progress + "%");
}
var loading_cfg = 0;
@@ -382,6 +375,8 @@ function load_config (refresh){
return;
}
loading_cfg = 1;
+ progress = 10;
+ update_progress();
// Load the configuration for all the pages.
var dropdowns = ['local-pwp-suffix', 'select-repl-cfg-suffix'];
@@ -415,20 +410,11 @@ function load_config (refresh){
get_and_set_config();
get_and_set_sasl();
get_and_set_localpwp();
+ update_progress();
// Schema page
get_and_set_schema_tables();
-
- // Replication page
- get_and_set_repl_config();
- get_and_set_repl_agmts();
- get_and_set_repl_winsync_agmts();
- get_and_set_cleanallruv();
-
- // Security page
- // Database page
- // Plugin page
- // Monitoring page
+ update_progress();
// Initialize the tabs
$(".ds-tab-list").css( 'color', '#777');
@@ -486,4 +472,8 @@ $(window.document).ready(function() {
$(".all-pages").hide();
$("#security-content").show();
});
+ $("#replication-tab").on("click", function() {
+ $(".all-pages").hide();
+ $("#replication-content").show();
+ });
});
diff --git a/src/cockpit/389-console/src/index.es6
b/src/cockpit/389-console/src/index.es6
index 71e9c5e..6797dd5 100644
--- a/src/cockpit/389-console/src/index.es6
+++ b/src/cockpit/389-console/src/index.es6
@@ -4,6 +4,7 @@ import { Plugins } from "./plugins.jsx";
import { Database } from "./database.jsx";
import { Monitor } from "./monitor.jsx";
import { Security } from "./security.jsx";
+import { Replication } from "./replication.jsx";
var serverIdElem;
@@ -31,6 +32,12 @@ function renderReactDOM(clear) {
document.getElementById("database")
);
+ // Replication tab
+ ReactDOM.render(
+ <Replication serverId={serverIdElem} key={tabKey} />,
+ document.getElementById("replication")
+ );
+
// Monitor tab
ReactDOM.render(
<Monitor serverId={serverIdElem} key={tabKey} />,
diff --git a/src/cockpit/389-console/src/index.html
b/src/cockpit/389-console/src/index.html
index c93f754..6a0479c 100644
--- a/src/cockpit/389-console/src/index.html
+++ b/src/cockpit/389-console/src/index.html
@@ -12,7 +12,6 @@
<script src="static/moment.min.js"></script>
<script src="static/dataTables.datetime-moment.js"></script>
<script src="static/jquery.timepicker.min.js"></script>
- <script src="static/jstree.min.js"></script>
<script src="static/bootstrap.min.js"></script>
<script src="static/bootpopup.min.js"></script>
<script src="static/patternfly.min.js"></script>
@@ -21,7 +20,6 @@
<script src="ds.js"></script>
<script src="schema.js"></script>
<script src="servers.js"></script>
- <script src="replication.js"></script>
<link href="static/bootstrap.min.css" rel="stylesheet">
<link href="static/jquery.dataTables.min.css" type="text/css"
rel="stylesheet">
<link href="static/jquery.timepicker.min.css" type="text/css"
rel="stylesheet">
@@ -34,9 +32,19 @@
<body>
<div id="reload-page" hidden></div>
- <div id="loading-page" class="ds-center ds-loading-spinner"
hidden>
- <h3 id="loading-msg">Loading Directory Server
Configuration...</h3>
- <p><span class="spinner spinner-lg
spinner-inline"></span></p>
+ <div id="loading-page" class="ds-center ds-loading" hidden>
+ <h4 id="loading-msg">Loading Directory Server
Configuration...</h4>
+ <p class="ds-margin-top-lg"><span class="spinner spinner-lg
spinner-inline"></span></p>
+
+ <div class="progress ds-margin-top-lg">
+ <div class="progress-bar" role="progressbar"
id="ds-progress-bar"
+ aria-valuenow="20" aria-valuemin="0"
+ aria-valuemax="100" style="width: 20%;"
+ >
+ <span id="ds-progress-label">20%</span>
+ </div>
+ </div>
+
</div>
<div id="everything" hidden>
<div class="ds-nav-bar">
@@ -50,7 +58,7 @@
<!-- Server Config navtab -->
<li class="dropdown ds-nav-tab">
- <a href="#0" class="dropdown-toggle ds-tab-list"
data-toggle="dropdown" id="server-tab">
+ <a href="#0" class="ds-tab-list"
data-toggle="dropdown" id="server-tab">
Server Settings
<b class="caret"></b>
</a>
@@ -93,22 +101,15 @@
</li>
<!-- Replication navtab -->
- <li class="dropdown ds-nav-tab">
- <a href="#0" class="dropdown-toggle ds-tab-list"
data-toggle="dropdown" id="replication-tab">
- Replication
- <b class="caret"></b>
- </a>
- <ul class="dropdown-menu ds-nav-item">
- <li><a href="#0" class="ds-nav-choice"
id="repl-config-btn"
parent-id="replication-tab">Configuration</a></li>
- <li><a href="#0" class="ds-nav-choice"
id="repl-agmts-btn"
parent-id="replication-tab">Agreements</a></li>
- <li><a href="#0" class="ds-nav-choice"
id="repl-winsync-btn" parent-id="replication-tab">Winsync
Agreements</a></li>
- <li><a href="#0" class="ds-nav-choice"
id="repl-tasks-btn" parent-id="replication-tab">Replication
Tasks</a></li>
- </ul>
+ <li class="ds-nav-tab">
+ <a href="#0" class="ds-tab-list ds-tab-standalone"
id="replication-tab">
+ Replication
+ </a>
</li>
<!-- Schema navtab -->
<li class="dropdown ds-nav-tab">
- <a href="#0" class="dropdown-toggle ds-tab-list"
data-toggle="dropdown" id="schema-tab">
+ <a href="#0" class="ds-tab-list"
data-toggle="dropdown" id="schema-tab">
Schema
<b class="caret"></b>
</a>
@@ -242,8 +243,8 @@
</div>
<div class="modal-body">
<form class="form-horizontal">
- <label for="reload-dir" class="ds-config-label"
title="The name of the database link.">Schema File
Directory</label><input
- class="ds-input" type="text" placeholder="Leave
empty to use default schema location" id="reload-dir">
+ <label for="reload-dir" class="ds-config-label"
title="The name of the database link.">Schema File
Directory:</label><input
+ class="ds-input-auto" size="40" type="text"
placeholder="Leave empty to use default schema location"
id="reload-dir">
</form>
<div id="reload-spinner" class="ds-center" hidden>
<p></p>
@@ -369,53 +370,63 @@
<button type="button" class="close"
data-dismiss="modal" aria-hidden="true"
aria-label="Close">
<span class="pficon pficon-close"></span>
</button>
- <h4 class="modal-title"
id="create-inst-header">Create New Server Instance</h4>
+ <h4 class="modal-title ds-center"
id="create-inst-header">Create New Server Instance</h4>
</div>
<div class="modal-body">
<form class="form-horizontal">
<p class="ds-modal-error"></p>
<div class="ds-inline">
<div>
- <label for="create-inst-serverid"
class="ds-config-label" title="The instance name, this is what gets
appended to \"slapi-\"">
- Instance Name</label><input class="ds-input
ds-inst-input" type="text" id="create-inst-serverid"
placeholder="Your_Instance_Name" required />
+ <label for="create-inst-serverid"
class="ds-config-label ds-input-right" title="The instance name, this is
what gets appended to 'slapi-'. The instance name can only contain letters,
numbers, and: # % : - _">
+ Instance Name</label><input class="ds-input
ds-inst-input ds-left-margin" size="40" type="text"
id="create-inst-serverid" placeholder="Your_Instance_Name" required
/>
</div>
<div>
- <label for="create-inst-port"
class="ds-config-label" title="The server port number">
- Port</label><input class="ds-input ds-inst-input"
type="text" value="389" id="create-inst-port" required
/>
+ <label for="create-inst-port" class="ds-config-label
ds-input-right" title="The server port number">
+ Port</label><input class="ds-input ds-inst-input
ds-left-margin" size="40" type="text" value="389"
id="create-inst-port" required />
</div>
<div>
- <label for="create-inst-secureport"
class="ds-config-label" title="The secure port number for TLS
connections">
- Secure Port</label><input class="ds-input
ds-inst-input" type="text" value="636"
id="create-inst-secureport" required />
+ <label for="create-inst-secureport"
class="ds-config-label ds-input-right" title="The secure port number for
TLS connections">
+ Secure Port</label><input class="ds-input ds-inst-input
ds-left-margin" size="40" type="text" value="636"
id="create-inst-secureport" required />
+ </div>
+ <div class="ds-inst-indent">
+ <input type="checkbox"
class="ds-config-checkbox" id="create-inst-tls" checked><label
+ for="create-inst-tls" class="ds-label"
title="Create a self-signed certificate database">Create Self-Signed TLS
Certificate DB</label>
</div>
<div>
- <label for="create-inst-rootdn"
class="ds-config-label" title="The DN for the unrestricted user">
- Directory Manager DN</label><input class="ds-input
ds-inst-input" autocomplete="username" value="cn=Directory
Manager" type="text" id="create-inst-rootdn" required />
+ <label for="create-inst-rootdn"
class="ds-config-label ds-input-right" title="The DN for the unrestricted
user">
+ Directory Manager DN</label><input class="ds-input
ds-inst-input ds-left-margin" size="40" autocomplete="username"
value="cn=Directory Manager" type="text"
id="create-inst-rootdn" required />
</div>
<div>
- <label for="rootdn-pw" class="ds-config-label"
title="Directory Manager password.">Directory Manager
Password</label><input
- class="ds-input ds-inst-input" type="password"
autocomplete="new-password" placeholder="Enter password"
id="rootdn-pw" name="name" required>
+ <label for="rootdn-pw" class="ds-config-label
ds-input-right" title="Directory Manager password.">Directory Manager
Password</label><input
+ class="ds-input ds-inst-input ds-left-margin"
size="40" type="password" autocomplete="new-password"
placeholder="Enter password" id="rootdn-pw" name="name"
required>
</div>
<div>
- <label for="rootdn-pw-confirm"
class="ds-config-label" title="Confirm password">Confirm
Password</label><input
- class="ds-input ds-inst-input" type="password"
autocomplete="new-password" placeholder="Confirm password"
id="rootdn-pw-confirm" name="name" required>
+ <label for="rootdn-pw-confirm" class="ds-config-label
ds-input-right" title="Confirm password">Confirm
Password</label><input
+ class="ds-input ds-inst-input ds-left-margin"
size="40" type="password" autocomplete="new-password"
placeholder="Confirm password" id="rootdn-pw-confirm"
name="name" required>
</div>
<hr>
+ <h5 class="ds-center">Optional Database
Settings</h5>
<div>
- <label for="backend-name"
class="ds-config-label" title="The backend name, like
'userroot'">Backend Name (optional)</label><input
- class="ds-input ds-inst-input" type="text"
id="backend-name">
+ <label for="backend-suffix" class="ds-config-label
ds-input-right" title="Database suffix, like 'dc=example,dc=com'. The
suffix must be a valid LDAP Distiguished Name (DN)">Database
Suffix</label><input
+ class="ds-input ds-inst-input ds-left-margin"
size="40" placeholder="e.g. dc=example,dc=com" type="text"
id="backend-suffix">
</div>
<div>
- <label for="backend-suffix"
class="ds-config-label" title="Database suffix, like
'dc=example,dc=com'">Backend Suffix (optional)</label><input
- class="ds-input ds-inst-input" type="text"
id="backend-suffix">
+ <label for="backend-name" class="ds-config-label
ds-input-right" title="The name for the backend database, like
'userroot'. The name can be a combination of alphanumeric characters, dashes (-),
and underscores (_). No other characters are allowed, and the name must be unique across
all backends.">Database Name</label><input
+ class="ds-input ds-inst-input ds-left-margin"
placeholder="e.g. userRoot" size="40" type="text"
id="backend-name">
</div>
- <div>
- <label for="create-sample-entries"
class="ds-config-label" title="Create sample entries in the
suffix">Create Sample Entries </label><input
- type="checkbox" class="ds-input
ds-config-checkbox" id="create-sample-entries">
- </div>
- <hr>
- <div>
- <input type="checkbox"
class="ds-config-checkbox" id="create-inst-tls" checked><label
- for="create-inst-tls" class="ds-label"
title="Create a self-signed certificate database">Create Self Signed
Certificate DB</label>
+ <div class="ds-inst-indent ds-margin-top">
+ <div>
+ <input type="radio" name="ds-radio-group"
id="no-init" checked /><label class="ds-left-margin"
for="no-init"
+ title="Do not initialize the backend database">
Do Not Initialize Database</label>
+ </div>
+ <div>
+ <input type="radio" name="ds-radio-group"
id="create-suffix-entry" /><label class="ds-left-margin"
for="create-suffix-entry"
+ title="Create the suffix entry with a basic READ
ACI"> Create Suffix Entry</label>
+ </div>
+ <div>
+ <input type="radio" name="ds-radio-group"
id="create-sample-entries" /><label class="ds-left-margin"
for="create-sample-entries"
+ title="Create sample entries under the suffix">
Create Suffix Entry And Add Sample Entries</label>
+ </div>
</div>
<div id="create-inst-spinner" class="ds-center"
hidden>
<hr>
@@ -446,10 +457,8 @@
</div>
<div class="modal-body">
<form class="form-horizontal">
- <div class="ds-inline">
- <label for="backup-name" title="Enter a directory name
for the backup">
- Backup Name</label><input class="ds-input
ds-left-margin" type="text" id="backup-name"/>
- </div>
+ <label for="backup-name" title="Enter a name for the
backup subdirectory located under the server's backup directory
(nsslapd-bakdir)">
+ Name For The Backup</label><input
class="ds-input-auto" type="text" id="backup-name"/>
</form>
<div id="backup-spinner" class="ds-center" hidden>
<p></p>
@@ -484,6 +493,12 @@
</div>
<div id="server-content" class="all-pages" hidden>
+ <div id="server-settings"></div>
+ <div id="server-tuning"></div>
+ <div id="server-sasl"></div>
+ <div id="server-pwp"></div>
+ <div id="server-ldapi"></div>
+ <div id="server-logs"></div>
</div>
<div id="security-content" class="all-pages" hidden>
@@ -495,6 +510,7 @@
</div>
<div id="replication-content" class="all-pages" hidden>
+ <div id="replication"></div>
</div>
<div id="schema-content" class="all-pages" hidden>
diff --git a/src/cockpit/389-console/src/lib/customCollapse.jsx
b/src/cockpit/389-console/src/lib/customCollapse.jsx
index b992982..a9dc471 100644
--- a/src/cockpit/389-console/src/lib/customCollapse.jsx
+++ b/src/cockpit/389-console/src/lib/customCollapse.jsx
@@ -30,7 +30,7 @@ class CustomCollapse extends React.Component {
/>{" "}
{open ? textOpened : textClosed}
</Button>
- <div className="ds-accordion-panel">{open &&
children}</div>
+ <div className="ds-margin-top">{open &&
children}</div>
</div>
);
}
diff --git a/src/cockpit/389-console/src/lib/database/attrEncryption.jsx
b/src/cockpit/389-console/src/lib/database/attrEncryption.jsx
index 9bc86bc..f34f6f8 100644
--- a/src/cockpit/389-console/src/lib/database/attrEncryption.jsx
+++ b/src/cockpit/389-console/src/lib/database/attrEncryption.jsx
@@ -129,8 +129,7 @@ export class AttrEncryption extends React.Component {
rows={this.props.rows}
loadModalHandler={this.showConfirmAttrDelete}
/>
- <p />
- <Row>
+ <Row className="ds-margin-top">
<Col sm={6}>
<Typeahead
id="attrEncrypt"
diff --git a/src/cockpit/389-console/src/lib/database/backups.jsx
b/src/cockpit/389-console/src/lib/database/backups.jsx
index 065e8a1..6204673 100644
--- a/src/cockpit/389-console/src/lib/database/backups.jsx
+++ b/src/cockpit/389-console/src/lib/database/backups.jsx
@@ -8,6 +8,7 @@ import {
TabContent,
TabPane,
TabContainer,
+ Checkbox,
Col,
Button,
Spinner,
@@ -19,7 +20,7 @@ import {
Row,
noop
} from "patternfly-react";
-import { log_cmd } from "../tools.jsx";
+import { log_cmd, bad_file_name } from "../tools.jsx";
import PropTypes from "prop-types";
import "../../css/ds.css";
@@ -30,7 +31,8 @@ export class Backups extends React.Component {
activeKey: 1,
showConfirmBackupDelete: false,
showConfirmBackup: false,
- showConfirmRestore: false,
+ showConfirmRestoreReplace: false,
+ showConfirmLDIFReplace: false,
showRestoreSpinningModal: false,
showDelBackupSpinningModal: false,
showBackupModal: false,
@@ -40,10 +42,12 @@ export class Backups extends React.Component {
// LDIF
showConfirmLDIFDelete: false,
showConfirmLDIFImport: false,
+ showConfirmRestore: false,
showLDIFSpinningModal: false,
showLDIFDeleteSpinningModal: false,
showExportModal: false,
exportSpinner: false,
+ includeReplData: false,
ldifName: "",
ldifSuffix: "",
errObj: {}
@@ -68,6 +72,8 @@ export class Backups extends React.Component {
this.closeRestoreSpinningModal = this.closeRestoreSpinningModal.bind(this);
this.showDelBackupSpinningModal = this.showDelBackupSpinningModal.bind(this);
this.closeDelBackupSpinningModal = this.closeDelBackupSpinningModal.bind(this);
+ this.validateBackup = this.validateBackup.bind(this);
+ this.closeConfirmRestoreReplace = this.closeConfirmRestoreReplace.bind(this);
// LDIFS
this.importLDIF = this.importLDIF.bind(this);
this.deleteLDIF = this.deleteLDIF.bind(this);
@@ -80,6 +86,12 @@ export class Backups extends React.Component {
this.doExport = this.doExport.bind(this);
this.showExportModal = this.showExportModal.bind(this);
this.closeExportModal = this.closeExportModal.bind(this);
+ this.validateLDIF = this.validateLDIF.bind(this);
+ this.closeConfirmLDIFReplace = this.closeConfirmLDIFReplace.bind(this);
+ }
+
+ componentDidMount() {
+ this.props.enableTree();
}
showExportModal () {
@@ -87,7 +99,8 @@ export class Backups extends React.Component {
showExportModal: true,
exportSpinner: false,
ldifName: "",
- ldifSuffix: this.props.suffixes[0]
+ ldifSuffix: this.props.suffixes[0],
+ includeReplData: false,
});
}
@@ -97,6 +110,12 @@ export class Backups extends React.Component {
});
}
+ closeConfirmLDIFReplace () {
+ this.setState({
+ showConfirmLDIFReplace: false
+ });
+ }
+
showLDIFSpinningModal () {
this.setState({
showLDIFSpinningModal: true
@@ -233,10 +252,16 @@ export class Backups extends React.Component {
});
}
+ closeConfirmRestoreReplace () {
+ this.setState({
+ showConfirmRestoreReplace: false,
+ });
+ }
+
importLDIF() {
this.showLDIFSpinningModal();
- const cmd = [
+ let cmd = [
"dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-"
+ this.props.serverId + ".socket",
"backend", "import", this.state.ldifSuffix,
this.state.ldifName, "--encrypted"
];
@@ -263,7 +288,7 @@ export class Backups extends React.Component {
deleteLDIF (e) {
this.showLDIFDeleteSpinningModal();
- const cmd = [
+ let cmd = [
"dsctl", this.props.serverId, "ldifs",
"--delete", this.state.ldifName
];
log_cmd("deleteLDIF", "Deleting LDIF", cmd);
@@ -288,20 +313,38 @@ export class Backups extends React.Component {
});
}
- doBackup () {
- this.setState({
- backupSpinning: true
- });
+ validateBackup() {
+ for (let i = 0; i < this.props.backups.length; i++) {
+ if (this.state.backupName == this.props.backups[i]['name']) {
+ this.setState({
+ showConfirmRestoreReplace: true
+ });
+ return;
+ }
+ }
+ this.doBackup();
+ }
+ doBackup () {
let cmd = [
"dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-"
+ this.props.serverId + ".socket",
"backup", "create"
];
-
if (this.state.backupName != "") {
+ if (bad_file_name(this.state.backupName)) {
+ this.props.addNotification(
+ "warning",
+ `Backup name should not be a path. All backups are stored in the
server's backup directory`
+ );
+ return;
+ }
cmd.push(this.state.backupName);
}
+ this.setState({
+ backupSpinning: true
+ });
+
log_cmd("doBackup", "Add backup task", cmd);
cockpit
.spawn(cmd, { superuser: true, err: "message" })
@@ -325,8 +368,15 @@ export class Backups extends React.Component {
}
restoreBackup () {
- this.showRestoreSpinningModal();
+ if (this.props.suffixes.length == 0) {
+ this.props.addNotification(
+ "error",
+ `There are no databases defined to restore`
+ );
+ return;
+ }
+ this.showRestoreSpinningModal();
const cmd = [
"dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-"
+ this.props.serverId + ".socket",
"backup", "restore", this.state.backupName
@@ -398,13 +448,43 @@ export class Backups extends React.Component {
});
}
+ validateLDIF() {
+ let ldifname = this.state.ldifName;
+ if (!ldifname.endsWith(".ldif")) {
+ // dsconf/dsctl adds ".ldif" if not set, so that's what we need
to check
+ ldifname = ldifname + ".ldif";
+ }
+ for (let i = 0; i < this.props.ldifs.length; i++) {
+ if (ldifname == this.props.ldifs[i]['name']) {
+ this.setState({
+ showConfirmLDIFReplace: true
+ });
+ return;
+ }
+ }
+ this.doExport();
+ }
+
doExport() {
- let missingArgs = {ldifLocation: false};
- if (this.state.ldifLocation == "") {
+ let missingArgs = {ldifName: false};
+ if (this.state.ldifName == "") {
this.props.addNotification(
"warning",
`LDIF name is empty`
);
+ missingArgs.ldifName = true;
+ this.setState({
+ errObj: missingArgs
+ });
+ return;
+ }
+
+ // Must not be a path
+ if (bad_file_name(this.state.ldifName)) {
+ this.props.addNotification(
+ "warning",
+ `LDIF name should not be a path. All export files are stored in the
server's LDIF directory`
+ );
missingArgs.ldifLocation = true;
this.setState({
errObj: missingArgs
@@ -418,6 +498,10 @@ export class Backups extends React.Component {
"backend", "export", this.state.ldifSuffix,
"--encrypted", "--ldif=" + this.state.ldifName
];
+ if (this.state.includeReplData) {
+ export_cmd.push("--replication");
+ }
+
this.setState({
exportSpinner: true,
});
@@ -472,13 +556,22 @@ export class Backups extends React.Component {
confirmDelete={this.showConfirmBackupDelete}
/>
</div>
- <p />
- <Button
- bsStyle="primary"
- onClick={this.showBackupModal}
-
-
Create Backup
- </Button>
+ <div className="ds-inline">
+ <Button
+ bsStyle="primary"
+ onClick={this.showBackupModal}
+ className="ds-margin-top"
+
+
Create Backup
+ </Button>
+ <Button
+ bsStyle="default"
+ onClick={this.props.reload}
+ className="ds-left-margin
ds-margin-top"
+
+
Refresh Backups
+ </Button>
+ </div>
</TabPane>
<TabPane eventKey={2}>
@@ -489,13 +582,22 @@ export class Backups extends React.Component {
confirmDelete={this.showConfirmLDIFDelete}
/>
</div>
- <p />
- <Button
- bsStyle="primary"
- onClick={this.showExportModal}
-
-
Create LDIF Export
- </Button>
+ <div className="ds-inline">
+ <Button
+ bsStyle="primary"
+ onClick={this.showExportModal}
+ className="ds-margin-top"
+
+
Create LDIF Export
+ </Button>
+ <Button
+ bsStyle="default"
+ onClick={this.props.reload}
+ className="ds-left-margin
ds-margin-top"
+
+
Refresh LDIFs
+ </Button>
+ </div>
</TabPane>
</TabContent>
</div>
@@ -505,7 +607,7 @@ export class Backups extends React.Component {
showModal={this.state.showExportModal}
closeHandler={this.closeExportModal}
handleChange={this.handleChange}
- saveHandler={this.doExport}
+ saveHandler={this.validateLDIF}
spinning={this.state.exportSpinner}
error={this.state.errObj}
suffixes={this.props.suffixes}
@@ -514,7 +616,7 @@ export class Backups extends React.Component {
showModal={this.state.showBackupModal}
closeHandler={this.closeBackupModal}
handleChange={this.handleChange}
- saveHandler={this.doBackup}
+ saveHandler={this.validateBackup}
spinning={this.state.backupSpinning}
error={this.state.errObj}
/>
@@ -570,7 +672,20 @@ export class Backups extends React.Component {
msg="Are you sure you want to delete this backup?"
msgContent={this.state.backupName}
/>
-
+ <ConfirmPopup
+ showModal={this.state.showConfirmRestoreReplace}
+ closeHandler={this.closeConfirmRestoreReplace}
+ actionFunc={this.doBackup}
+ msg="Replace Existing Backup"
+ msgContent="A backup already eixsts with the same name, do you
want to replace it?"
+ />
+ <ConfirmPopup
+ showModal={this.state.showConfirmLDIFReplace}
+ closeHandler={this.closeConfirmLDIFReplace}
+ actionFunc={this.doExport}
+ msg="Replace Existing LDIF File"
+ msgContent="A LDIF file already eixsts with the same name, do
you want to replace it?"
+ />
</div>
);
}
@@ -591,8 +706,8 @@ class ExportModal extends React.Component {
if (spinning) {
spinner =
<Row>
- <div className="ds-modal-spinner">
- <Spinner loading inline size="lg" />Exporting
database... <font size="1">(You can safely close this
window)</font>
+ <div className="ds-margin-top ds-modal-spinner">
+ <Spinner loading inline size="md" />Exporting
database... <font size="2">(You can safely close this
window)</font>
</div>
</Row>;
}
@@ -627,8 +742,7 @@ class ExportModal extends React.Component {
</select>
</Col>
</Row>
- <p />
- <Row title="Name of exported LDIF file, if left blank
the data and time will be used as the file name">
+ <Row className="ds-margin-top" title="Name
of exported LDIF file, if left blank the data and time will be used as the file
name">
<Col sm={3}>
<ControlLabel>LDIF File
Name</ControlLabel>
</Col>
@@ -641,7 +755,17 @@ class ExportModal extends React.Component {
/>
</Col>
</Row>
- <p />
+ <Row className="ds-margin-top-xlg">
+ <Col sm={12} className="ds-margin-left">
+ <Checkbox
+ id="includeReplData"
+ onChange={handleChange}
+ title="Include the replication metadata
needed to restore or initialize another replica."
+
+
Include Replication Data
+ </Checkbox>
+ </Col>
+ </Row>
{spinner}
</Form>
</Modal.Body>
@@ -680,8 +804,8 @@ export class BackupModal extends React.Component {
if (spinning) {
spinner =
<Row>
- <div className="ds-modal-spinner">
- <Spinner loading inline size="lg" />Backing up
databases... <font size="1">(You can safely close this
window)</font>
+ <div className="ds-margin-top ds-modal-spinner">
+ <Spinner loading inline size="md" />Backing up
databases... <font size="2">(You can safely close this
window)</font>
</div>
</Row>;
}
@@ -717,7 +841,6 @@ export class BackupModal extends React.Component {
/>
</Col>
</Row>
- <p />
{spinner}
</Form>
</Modal.Body>
@@ -733,7 +856,7 @@ export class BackupModal extends React.Component {
bsStyle="primary"
onClick={saveHandler}
- Do Backup
+ Create Backup
</Button>
</Modal.Footer>
</div>
@@ -769,9 +892,8 @@ class RestoreModal extends React.Component {
<Modal.Body>
<Form horizontal autoComplete="off">
<div className="ds-modal-spinner">
- <Spinner loading inline size="lg" />
Restoring backup <b>{msg}</b> ...
- <p />
- <p><font size="1"> (You can safely
close this window)</font></p>
+ <Spinner loading inline size="md" />
Restoring backup <b>{msg}</b> ...
+ <p className="ds-margin-top"><font
size="2"> (You can safely close this window)</font></p>
</div>
</Form>
</Modal.Body>
@@ -817,9 +939,8 @@ class DeleteBackupModal extends React.Component {
<Modal.Body>
<Form horizontal autoComplete="off">
<div className="ds-modal-spinner">
- <Spinner loading inline size="lg" />
Deleting backup <b>{msg}</b> ...
- <p />
- <p><font size="1"> (You can safely
close this window)</font></p>
+ <Spinner loading inline size="md" />
Deleting backup <b>{msg}</b> ...
+ <p className="ds-margin-top"><font
size="2"> (You can safely close this window)</font></p>
</div>
</Form>
</Modal.Body>
@@ -865,9 +986,8 @@ class ImportingModal extends React.Component {
<Modal.Body>
<Form horizontal autoComplete="off">
<div className="ds-modal-spinner">
- <Spinner loading inline size="lg" />
Importing LDIF <b>{msg}</b> ...
- <p />
- <p><font size="1"> (You can safely
close this window)</font></p>
+ <Spinner loading inline size="md" />
Importing LDIF <b>{msg}</b> ...
+ <p className="ds-margin-top"><font
size="2"> (You can safely close this window)</font></p>
</div>
</Form>
</Modal.Body>
@@ -913,9 +1033,8 @@ class DeletingLDIFModal extends React.Component {
<Modal.Body>
<Form horizontal autoComplete="off">
<div className="ds-modal-spinner">
- <Spinner loading inline size="lg" />
Deleting LDIF file <b>{msg}</b> ...
- <p />
- <p><font size="1"> (You can safely
close this window)</font></p>
+ <Spinner loading inline size="md" />
Deleting LDIF file <b>{msg}</b> ...
+ <p className="ds-margin-top"><font
size="2"> (You can safely close this window)</font></p>
</div>
</Form>
</Modal.Body>
@@ -982,11 +1101,13 @@ ImportingModal.propTypes = {
Backups.propTypes = {
backups: PropTypes.array,
ldifs: PropTypes.array,
- reload: PropTypes.func
+ reload: PropTypes.func,
+ enableTree: PropTypes.func,
};
Backups.defaultProps = {
backups: [],
ldifs: [],
- reload: noop
+ reload: noop,
+ enableTree: noop,
};
diff --git a/src/cockpit/389-console/src/lib/database/chaining.jsx
b/src/cockpit/389-console/src/lib/database/chaining.jsx
index c169e7b..749d414 100644
--- a/src/cockpit/389-console/src/lib/database/chaining.jsx
+++ b/src/cockpit/389-console/src/lib/database/chaining.jsx
@@ -1,6 +1,6 @@
import cockpit from "cockpit";
import React from "react";
-import { ConfirmPopup } from "../notifications.jsx";
+import { ConfirmPopup, DoubleConfirmModal } from "../notifications.jsx";
import CustomCollapse from "../customCollapse.jsx";
import { log_cmd } from "../tools.jsx";
import {
@@ -11,7 +11,6 @@ import {
Row,
Col,
ControlLabel,
- Checkbox,
FormControl,
noop
} from "patternfly-react";
@@ -98,6 +97,10 @@ export class ChainingDatabaseConfig extends React.Component {
this.closeConfirmCompDelete = this.closeConfirmCompDelete.bind(this);
}
+ componentDidMount() {
+ this.props.enableTree();
+ }
+
handleChange(e) {
// Generic
const value = e.target.type === 'checkbox' ? e.target.checked :
e.target.value;
@@ -454,7 +457,7 @@ export class ChainingDatabaseConfig extends React.Component {
);
return (
- <div className="container-fluid"
id="db-global-page">
+ <div id="chaining-page">
<h3 className="ds-config-header">Database Chaining
Settings</h3>
<hr />
<div className="ds-container">
@@ -492,66 +495,143 @@ export class ChainingDatabaseConfig extends React.Component {
</div>
</div>
</div>
-
- <h4 className="ds-sub-header"><br />Default
Database Link Creation Settings</h4>
+ <h4 className="ds-margin-top ds-sub-header
ds-center">Default Database Link Creation Settings</h4>
<hr />
- <div className="ds-container">
- <div className="ds-split">
- <label htmlFor="defSizeLimit"
className="ds-config-label" title="The size limit of entries returned over
a database link (nsslapd-sizelimit).">
- Size Limit</label><input
className="ds-input" type="text" id="defSizeLimit"
onChange={this.handleChange} value={this.state.defSizeLimit} size="15" />
- <label htmlFor="defTimeLimit"
className="ds-config-label" title="The time limit of an operation over a
database link (nsslapd-timelimit).">
- Time Limit</label><input
className="ds-input" type="text" id="defTimeLimit"
onChange={this.handleChange} value={this.state.defTimeLimit} size="15" />
- <label htmlFor="defBindConnLimit"
className="ds-config-label" title="The maximum number of TCP connections
the database link establishes with the remote server.
(nsbindconnectionslimit).">
- Max TCP Connections</label><input
className="ds-input" id="defBindConnLimit" type="text"
onChange={this.handleChange} value={this.state.defBindConnLimit} size="15"
/>
- <label htmlFor="defOpConnLimit"
className="ds-config-label" title="The maximum number of connections
allowed over the database link. (nsoperationconnectionslimit).">
- Max LDAP Connections</label><input
className="ds-input" id="defOpConnLimit" type="text"
onChange={this.handleChange} value={this.state.defOpConnLimit} size="15" />
- <label htmlFor="defConcurLimit"
className="ds-config-label" title="The maximum number of concurrent bind
operations per TCP connection. (nsconcurrentbindlimit).">
- Max Binds Per Connection</label><input
className="ds-input" id="defConcurLimit" type="text"
onChange={this.handleChange} value={this.state.defConcurLimit} size="15" />
- <label htmlFor="defBindTimeout"
className="ds-config-label" title="The amount of time before the bind
attempt times out. (nsbindtimeout).">
- Bind Timeout</label><input
className="ds-input" id="defBindTimeout" type="text"
onChange={this.handleChange} value={this.state.defBindTimeout} size="15" />
- <label htmlFor="defBindRetryLimit"
className="ds-config-label" title="The number of times the database link
tries to bind with the remote server after a connection failure.
(nsbindretrylimit).">
- Bind Retry Limit</label><input
className="ds-input" id="defBindRetryLimit" type="text"
onChange={this.handleChange} value={this.state.defBindRetryLimit} size="15"
/>
- </div>
- <div className="ds-divider" />
- <div className="ds-split ds-inline">
- <div>
- <label htmlFor="defConcurOpLimit"
className="ds-config-label" title="The maximum number of operations per
connections. (nsconcurrentoperationslimit).">
- Max Operations Per Connection</label><input
className="ds-input" id="defConcurOpLimit" type="text"
onChange={this.handleChange} value={this.state.defConcurOpLimit} size="15"
/>
- </div>
- <div>
- <label htmlFor="defConnLife"
className="ds-config-label" title="The life of a database link connection
to the remote server. 0 is unlimited (nsconnectionlife).">
- Connection Lifetime (in seconds)</label><input
className="ds-input" id="defConnLife" type="text"
onChange={this.handleChange} value={this.state.defConnLife} size="15" />
- </div>
- <div>
- <label htmlFor="defSearchCheck"
className="ds-config-label" title="The number of seconds that pass before
the server checks for abandoned operations. (nsabandonedsearchcheckinterval).">
- Abandoned Op Check Interval</label><input
className="ds-input" id="defSearchCheck" type="text"
onChange={this.handleChange} value={this.state.defSearchCheck} size="15" />
- </div>
- <div>
- <label htmlFor="defHopLimit"
className="ds-config-label" title="The maximum number of times a request
can be forwarded from one database link to another. (nshoplimit).">
- Database Link Hop Limit</label><input
className="ds-input" type="text" onChange={this.handleChange}
value={this.state.defHopLimit} id="defHopLimit" size="15" />
- </div>
- <div>
- <p />
- <input type="checkbox"
onChange={this.handleChange} checked={this.state.defCheckAci}
className="ds-config-checkbox" id="defCheckAci" /><label
- htmlFor="defCheckAci"
className="ds-label" title="Sets whether ACIs are evaluated on the database
link as well as the remote data server (nschecklocalaci)."> Check Local
ACIs</label>
- </div>
- <div>
- <input type="checkbox"
onChange={this.handleChange} checked={this.state.defRefOnScoped}
className="ds-config-checkbox" id="defRefOnScoped" /><label
- htmlFor="defRefOnScoped"
className="ds-label" title="Sets whether referrals are returned by scoped
searches (meaning 'one-level' or 'subtree' scoped searches).
(nsreferralonscopedsearch)."> Send Referral On Scoped Search</label>
- </div>
- <div>
- <input type="checkbox"
onChange={this.handleChange} checked={this.state.defProxy}
className="ds-config-checkbox" id="defProxy" /><label
- htmlFor="defProxy"
className="ds-label" title="Sets whether proxied authentication is allowed
(nsproxiedauthorization)."> Allow Proxied Authentication</label>
- </div>
- <div>
- <input type="checkbox"
onChange={this.handleChange} checked={this.state.defUseStartTLS}
className="ds-config-checkbox" id="defUseStartTLS" /><label
- htmlFor="defUseStartTLS"
className="ds-label" title="Sets whether to use Start TLS to initiate a
secure, encrypted connection over an insecure port. (nsusestarttls)."> Use
StartTLS</label>
- </div>
- </div>
- </div>
- <div className="ds-save-btn">
- <button className="btn btn-primary save-button"
onClick={this.save_chaining_config}>Save Default Settings</button>
- </div>
+ <Form horizontal>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4} title="The size
limit of entries returned over a database link (nsslapd-sizelimit).">
+ Size Limit
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" id="defSizeLimit" onChange={this.handleChange}
defaultValue={this.state.defSizeLimit} />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4} title="The
maximum number of operations per connections. (nsconcurrentoperationslimit).">
+ Max Operations Per Conn
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" id="defConcurOpLimit" onChange={this.handleChange}
defaultValue={this.state.defConcurOpLimit} />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4} title="The time
limit of an operation over a database link (nsslapd-timelimit).">
+ Time Limit
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" id="defTimeLimit" onChange={this.handleChange}
defaultValue={this.state.defTimeLimit} />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4} title="The
maximum number of operations per connections. (nsconcurrentoperationslimit).">
+ Connection Lifetime
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" id="defConnLife" onChange={this.handleChange}
defaultValue={this.state.defConnLife} />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4} title="The
maximum number of TCP connections the database link establishes with the remote server.
(nsbindconnectionslimit).">
+ Max TCP Connections
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" id="defBindConnLimit" onChange={this.handleChange}
defaultValue={this.state.defBindConnLimit} />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4} title="The
number of seconds that pass before the server checks for abandoned operations.
(nsabandonedsearchcheckinterval).">
+ Abandoned Op Check Interval
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" id="defSearchCheck" onChange={this.handleChange}
defaultValue={this.state.defSearchCheck} />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4} title="The
maximum number of connections allowed over the database link.
(nsoperationconnectionslimit).">
+ Max LDAP Connections
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" id="defOpConnLimit" onChange={this.handleChange}
defaultValue={this.state.defOpConnLimit} />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4} title="The
number of seconds that pass before the server checks for abandoned operations.
(nsabandonedsearchcheckinterval).">
+ Abandoned Op Check Interval
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" id="defSearchCheck" onChange={this.handleChange}
defaultValue={this.state.defSearchCheck} />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4} title="The
maximum number of connections allowed over the database link.
(nsoperationconnectionslimit).">
+ Max Binds Per Connection
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" id="defOpConnLimit" onChange={this.handleChange}
defaultValue={this.state.defOpConnLimit} />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4} title="The
maximum number of times a request can be forwarded from one database link to another.
(nshoplimit).">
+ Database Link Hop Limit
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" id="defHopLimit" onChange={this.handleChange}
defaultValue={this.state.defHopLimit} />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4} title="The
amount of time before the bind attempt times out. (nsbindtimeout).">
+ Bind Timeout
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" id="defBindTimeout" onChange={this.handleChange}
defaultValue={this.state.defBindTimeout} />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4} title="The
number of times the database link tries to bind with the remote server after a connection
failure. (nsbindretrylimit).">
+ Bind Retry Limit
+ </Col>
+ <Col sm={8}>
+ <input className="ds-input-auto"
type="text" id="defBindRetryLimit" onChange={this.handleChange}
defaultValue={this.state.defBindRetryLimit} />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4} title="Sets
whether ACIs are evaluated on the database link as well as the remote data server
(nschecklocalaci).">
+ Check Local ACIs
+ </Col>
+ <Col sm={8}>
+ <input type="checkbox"
onChange={this.handleChange} defaultChecked={this.state.defCheckAci}
className="ds-config-checkbox" id="nsusdefCheckAciestarttls" />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4} title="Sets
whether referrals are returned by scoped searches (meaning 'one-level' or
'subtree' scoped searches). (nsreferralonscopedsearch).">
+ Send Referral On Scoped Search
+ </Col>
+ <Col sm={8}>
+ <input type="checkbox"
onChange={this.handleChange} defaultChecked={this.state.defRefOnScoped}
className="ds-config-checkbox" id="defRefOnScoped" />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4} title="Sets
whether ACIs are evaluated on the database link as well as the remote data server
(nschecklocalaci).">
+ Allow Proxied Authentication
+ </Col>
+ <Col sm={8}>
+ <input type="checkbox"
onChange={this.handleChange} defaultChecked={this.state.defProxy}
className="ds-config-checkbox" id="defProxy" />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4} title="Sets
whether referrals are returned by scoped searches (meaning 'one-level' or
'subtree' scoped searches). (nsreferralonscopedsearch).">
+ Use StartTLS
+ </Col>
+ <Col sm={8}>
+ <input type="checkbox"
onChange={this.handleChange} defaultChecked={this.state.defUseStartTLS}
className="ds-config-checkbox" id="defUseStartTLS" />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top-lg">
+ <Col sm={5}>
+ <button className="btn btn-primary save-button"
onClick={this.save_chaining_config}>Save Default Settings</button>
+ </Col>
+ </Row>
+ </Form>
<ChainControlsModal
showModal={this.state.showOidModal}
@@ -598,6 +678,8 @@ export class ChainingConfig extends React.Component {
errObj: {},
showDeleteConfirm: false,
linkPwdMatch: true,
+ modalSpinning: false,
+ modalChecked: false,
// Settings
nsfarmserverurl: this.props.data.nsfarmserverurl,
nsmultiplexorbinddn: this.props.data.nsmultiplexorbinddn,
@@ -655,15 +737,23 @@ export class ChainingConfig extends React.Component {
this.closeDeleteConfirm = this.closeDeleteConfirm.bind(this);
}
+ componentDidMount() {
+ this.props.enableTree();
+ }
+
showDeleteConfirm () {
this.setState({
- showDeleteConfirm: true
+ showDeleteConfirm: true,
+ modalSpinning: false,
+ modalChecked: false,
});
}
closeDeleteConfirm () {
this.setState({
- showDeleteConfirm: false
+ showDeleteConfirm: false,
+ modalSpinning: false,
+ modalChecked: false,
});
}
@@ -874,91 +964,28 @@ export class ChainingConfig extends React.Component {
render () {
const error = this.state.errObj;
- let useStartTLSCheckBox;
- let checkLocalAci;
- let referralOnScope;
- let proxiedAuth;
-
- // Check local aci's checkbox
- if (this.state.nschecklocalaci) {
- checkLocalAci =
- <Checkbox id="nschecklocalaci" onChange={this.handleChange}
key={this.state.nschecklocalaci} defaultChecked
- title="Sets whether ACIs are evaluated on the database link as
well as the remote data server (nschecklocalaci).">
- Check Local ACIs
- </Checkbox>;
- } else {
- checkLocalAci =
- <Checkbox id="nschecklocalaci" onChange={this.handleChange}
key={this.state.nschecklocalaci}
- title="Sets whether ACIs are evaluated on the database link as
well as the remote data server (nschecklocalaci).">
- Check Local ACIs
- </Checkbox>;
- }
- // Referral on scoped search checkbox
- if (this.state.nsreferralonscopedsearch) {
- referralOnScope =
- <Checkbox id="nsreferralonscopedsearch"
onChange={this.handleChange} defaultChecked
- title="Sets whether referrals are returned by scoped searches
(meaning 'one-level' or 'subtree' scoped searches).
(nsreferralonscopedsearch).">
- Send Referral On Scoped Search
- </Checkbox>;
- } else {
- referralOnScope =
- <Checkbox id="nsreferralonscopedsearch"
onChange={this.handleChange}
- title="Sets whether referrals are returned by scoped searches
(meaning 'one-level' or 'subtree' scoped searches).
(nsreferralonscopedsearch).">
- Send Referral On Scoped Search
- </Checkbox>;
- }
- // Allow proxied auth checkbox
- if (this.state.nsproxiedauthorization) {
- proxiedAuth =
- <Checkbox id="nsproxiedauthorization"
onChange={this.handleChange} defaultChecked
- title="Allow proxied authentication to the remote server.
(nsproxiedauthorization).">
- Allow Proxied Authentication
- </Checkbox>;
- } else {
- proxiedAuth =
- <Checkbox id="nsproxiedauthorization"
onChange={this.handleChange}
- title="Allow proxied authentication to the remote server.
(nsproxiedauthorization).">
- Allow Proxied Authentication
- </Checkbox>;
- }
- // use startTLS checkbox
- if (this.state.nsusestarttls) {
- useStartTLSCheckBox =
- <Checkbox id="nsusestarttls" onChange={this.handleChange}
title="Use StartTLS for connection to remote server. (nsusestarttls)"
- defaultChecked
-
- Use StartTLS for remote connection
- </Checkbox>;
- } else {
- useStartTLSCheckBox =
- <Checkbox id="nsusestarttls" onChange={this.handleChange}
title="Use StartTLS for connection to remote server. (nsusestarttls)">
- Use StartTLS for remote connection
- </Checkbox>;
- }
return (
- <div className="container-fluid">
+ <div>
<Row>
- <Col sm={8} className="ds-word-wrap">
- <ControlLabel
className="ds-suffix-header"><Icon type="fa"
name="link" /> {this.props.suffix}
(<i>{this.props.bename}</i>)</ControlLabel>
+ <Col sm={10} className="ds-word-wrap">
+ <ControlLabel
className="ds-suffix-header"><Icon type="fa"
name="link" /> <b>{this.props.suffix}</b>
(<i>{this.props.bename}</i>)</ControlLabel>
</Col>
<Col sm={2}>
<Button
- bsStyle="primary"
+ bsStyle="danger"
onClick={this.showDeleteConfirm}
Delete Link
</Button>
</Col>
</Row>
- <h4>Database Link Configuration</h4>
- <hr />
- <Form horizontal autoComplete="off">
+ <Form horizontal autoComplete="off"
className="ds-margin-top-xlg">
<Row title="The LDAP URL for the remote server. Add
additional failure server URLs by separating them with a space.
(nsfarmserverurl)">
- <Col sm={3}>
- <ControlLabel>Remote Server LDAP
URL(s)</ControlLabel>
+ <Col componentClass={ControlLabel} sm={4}>
+ Remote Server LDAP URL
</Col>
- <Col sm={7}>
+ <Col sm={8}>
<FormControl
type="text"
id="nsfarmserverurl"
@@ -968,12 +995,11 @@ export class ChainingConfig extends React.Component {
/>
</Col>
</Row>
- <p />
- <Row title="The distinguished name (DN) of the entry to
authenticate to the remote server. (nsmultiplexorbinddn)">
- <Col sm={3}>
- <ControlLabel>Remote Server Bind
DN</ControlLabel>
+ <Row className="ds-margin-top" title="The
distinguished name (DN) of the entry to authenticate to the remote server.
(nsmultiplexorbinddn)">
+ <Col componentClass={ControlLabel} sm={4}>
+ Remote Server Bind DN
</Col>
- <Col sm={7}>
+ <Col sm={8}>
<FormControl
type="text"
id="nsmultiplexorbinddn"
@@ -983,12 +1009,11 @@ export class ChainingConfig extends React.Component {
/>
</Col>
</Row>
- <p />
- <Row title="The password for the authenticating entry.
(nsmultiplexorcredentials)">
- <Col sm={3}>
- <ControlLabel>Bind DN Password</ControlLabel>
+ <Row className="ds-margin-top" title="The password
for the authenticating entry. (nsmultiplexorcredentials)">
+ <Col componentClass={ControlLabel} sm={4}>
+ Bind DN Password
</Col>
- <Col sm={7}>
+ <Col sm={8}>
<FormControl
type="password"
id="nsmultiplexorcredentials"
@@ -998,12 +1023,11 @@ export class ChainingConfig extends React.Component {
/>
</Col>
</Row>
- <p />
- <Row title="Confirm the password for the authenticating
entry. (nsmultiplexorcredentials)">
- <Col sm={3}>
- <ControlLabel>Confirm Password</ControlLabel>
+ <Row className="ds-margin-top" title="Confirm the
password for the authenticating entry. (nsmultiplexorcredentials)">
+ <Col componentClass={ControlLabel} sm={4}>
+ Confirm Password
</Col>
- <Col sm={7}>
+ <Col sm={8}>
<FormControl
type="password"
id="nsmultiplexorcredentials_confirm"
@@ -1013,12 +1037,11 @@ export class ChainingConfig extends React.Component {
/>
</Col>
</Row>
- <p />
- <Row title="THe authentication mechanism. Simple (user name
and password), SASL/DIGEST-MD5, or SASL>GSSAPI. (nsbindmechanism)">
- <Col sm={3}>
- <ControlLabel>Bind Mechanism</ControlLabel>
+ <Row className="ds-margin-top" title="The
authentication mechanism. Simple (user name and password), SASL/DIGEST-MD5, or
SASL>GSSAPI. (nsbindmechanism)">
+ <Col componentClass={ControlLabel} sm={4}>
+ Bind Mechanism
</Col>
- <Col sm={7}>
+ <Col sm={8}>
<select value={this.state.nsbindmechanism}
className="btn btn-default dropdown
ds-dblink-dropdown"
onChange={this.handleChange}
@@ -1030,93 +1053,214 @@ export class ChainingConfig extends React.Component {
</select>
</Col>
</Row>
- <p />
- <Row>
- <Col sm={9}>
- {useStartTLSCheckBox}
+ <Row className="ds-margin-top" title="Use StartTLS
for connections to the remote server. (nsusestarttls)">
+ <Col componentClass={ControlLabel} sm={4}>
+ Use StartTLS
+ </Col>
+ <Col sm={8}>
+ <input type="checkbox"
onChange={this.props.handleChange} defaultChecked={this.state.nsusestarttls}
className="ds-config-checkbox" id="nsusestarttls" />
</Col>
</Row>
</Form>
- <p />
- <CustomCollapse>
- <div className="ds-accordion-panel">
- <div className="ds-margin-left">
- <div className="ds-container">
- <div className="ds-inline">
- <div>
- <label htmlFor="sizelimit"
className="ds-config-label" title="The size limit of entries returned over
a database link (nsslapd-sizelimit).">
- Size Limit</label><input
onChange={this.handleChange} defaultValue={this.state.sizelimit}
className="ds-input" type="text" id="sizelimit"
size="15" />
- </div>
- <div>
- <label htmlFor="timelimit"
className="ds-config-label" title="The time limit of an operation over a
database link (nsslapd-timelimit).">
- Time Limit</label><input
onChange={this.handleChange} defaultValue={this.state.timelimit}
className="ds-input" type="text" id="timelimit"
size="15" />
- </div>
- <div>
- <label htmlFor="bindconnlimit"
className="ds-config-label" title="The maximum number of TCP connections
the database link establishes with the remote server.
(nsbindconnectionslimit).">
- Max TCP Connections</label><input
onChange={this.handleChange} defaultValue={this.state.bindconnlimit}
className="ds-input" type="text" id="bindconnlimit"
size="15" />
- </div>
- <div>
- <label htmlFor="opconnlimit"
className="ds-config-label" title="The maximum number of connections
allowed over the database link. (nsoperationconnectionslimit).">
- Max LDAP Connections</label><input
onChange={this.handleChange} defaultValue={this.state.opconnlimit}
className="ds-input" type="text" id="opconnlimit"
size="15" />
- </div>
- <div>
- <label htmlFor="concurrbindlimit"
className="ds-config-label" title="The maximum number of concurrent bind
operations per TCP connection. (nsconcurrentbindlimit).">
- Max Binds Per
Connection</label><input onChange={this.handleChange}
defaultValue={this.state.concurrbindlimit} className="ds-input"
type="text" id="concurrbindlimit" size="15" />
- </div>
- <div>
- <label htmlFor="bindtimeout"
className="ds-config-label" title="The amount of time before the bind
attempt times out. (nsbindtimeout).">
- Bind Timeout</label><input
onChange={this.handleChange} defaultValue={this.state.bindtimeout}
className="ds-input" type="text" id="bindtimeout"
size="15" />
- </div>
- </div>
- <div className="ds-divider" />
- <div className="ds-inline">
- <div>
- <label htmlFor="bindretrylimit"
className="ds-config-label" title="The number of times the database link
tries to bind with the remote server after a connection failure.
(nsbindretrylimit).">
- Bind Retry Limit</label><input
onChange={this.handleChange} defaultValue={this.state.bindretrylimit}
className="ds-input" type="text" id="bindretrylimit"
size="15" />
- </div>
- <div>
- <label htmlFor="concurroplimit"
className="ds-config-label" title="The maximum number of operations per
connections. (nsconcurrentoperationslimit).">
- Max Operations Per
Connection</label><input onChange={this.handleChange}
defaultValue={this.state.concurroplimit} className="ds-input"
type="text" id="concurroplimit" size="15" />
- </div>
- <div>
- <label htmlFor="connlifetime"
className="ds-config-label" title="The life of a database link connection
to the remote server. 0 is unlimited (nsconnectionlife).">
- Connection Lifetime (in
seconds)</label><input onChange={this.handleChange}
defaultValue={this.state.connlifetime} className="ds-input"
type="text" id="connlifetime" size="15" />
- </div>
- <div>
- <label htmlFor="searchcheckinterval"
className="ds-config-label" title="The number of seconds that pass before
the server checks for abandoned operations. (nsabandonedsearchcheckinterval).">
- Abandoned Op Check
Interval</label><input onChange={this.handleChange}
defaultValue={this.state.searchcheckinterval} className="ds-input"
type="text" id="searchcheckinterval" size="15" />
- </div>
- <div>
- <label htmlFor="hoplimit"
className="ds-config-label" title="The maximum number of times a request
can be forwarded from one database link to another. (nshoplimit).">
- Database Link Hop
Limit</label><input onChange={this.handleChange}
defaultValue={this.state.hoplimit} className="ds-input" type="text"
id="hoplimit" size="15" />
- </div>
- </div>
- </div>
- <p />
- <div>
- {proxiedAuth}
- </div>
- <div>
- {checkLocalAci}
- </div>
- <div>
- {referralOnScope}
- </div>
- </div>
- </div>
+ <CustomCollapse className="ds-margin-top">
+ <Form horizontal className="ds-margin-top
ds-margin-left">
+ <Row className="ds-margin-top" title="The size
limit of entries returned over a database link (nsslapd-sizelimit).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Size Limit
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="sizelimit"
+ className="ds-input-auto"
+ onChange={this.handleChange}
+ defaultValue={this.state.sizelimit}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The time
limit of an operation over a database link (nsslapd-timelimit).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Time Limit
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="timelimit"
+ className="ds-input-auto"
+ onChange={this.handleChange}
+ defaultValue={this.state.timelimit}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The
maximum number of TCP connections the database link establishes with the remote server.
(nsbindconnectionslimit).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Max TCP Connections
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="bindconnlimit"
+ className="ds-input-auto"
+ onChange={this.handleChange}
+ defaultValue={this.state.bindconnlimit}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The
maximum number of connections allowed over the database link.
(nsoperationconnectionslimit).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Max LDAP Connections
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="opconnlimit"
+ className="ds-input-auto"
+ onChange={this.handleChange}
+ defaultValue={this.state.opconnlimit}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The
maximum number of concurrent bind operations per TCP connection.
(nsconcurrentbindlimit).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Max Binds Per Connection
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="concurrbindlimit"
+ className="ds-input-auto"
+ onChange={this.handleChange}
+ defaultValue={this.state.concurrbindlimit}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The
amount of time before the bind attempt times out. (nsbindtimeout).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Bind Timeout
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="bindtimeout"
+ className="ds-input-auto"
+ onChange={this.handleChange}
+ defaultValue={this.state.bindtimeout}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The
number of times the database link tries to bind with the remote server after a connection
failure. (nsbindretrylimit).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Bind Retry Limit
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="bindretrylimit"
+ className="ds-input-auto"
+ onChange={this.handleChange}
+ defaultValue={this.state.bindretrylimit}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The
maximum number of operations per connections. (nsconcurrentoperationslimit).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Max Operations Per Connection
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="concurroplimit"
+ className="ds-input-auto"
+ onChange={this.handleChange}
+ defaultValue={this.state.concurroplimit}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The life
of a database link connection to the remote server in seconds. 0 is unlimited
(nsconnectionlife).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Connection Lifetime
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="connlifetime"
+ className="ds-input-auto"
+ onChange={this.handleChange}
+ defaultValue={this.state.connlifetime}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The
number of seconds that pass before the server checks for abandoned operations.
(nsabandonedsearchcheckinterval).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Abandoned Op Check Interval
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="searchcheckinterval"
+ className="ds-input-auto"
+ onChange={this.handleChange}
+ defaultValue={this.state.searchcheckinterval}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The
maximum number of times a request can be forwarded from one database link to another.
(nshoplimit).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Hop Limit
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="hoplimit"
+ className="ds-input-auto"
+ onChange={this.handleChange}
+ defaultValue={this.state.hoplimit}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="Allow
proxied authentication to the remote server. (nsproxiedauthorization).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Allow Proxied Authentication
+ </Col>
+ <Col sm={8}>
+ <input type="checkbox"
onChange={this.props.handleChange} defaultChecked={this.state.nsproxiedauthorization}
className="ds-config-checkbox" id="nsproxiedauthorization" />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="Sets
whether ACIs are evaluated on the database link as well as the remote data server
(nschecklocalaci).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Check Local ACIs
+ </Col>
+ <Col sm={8}>
+ <input type="checkbox"
onChange={this.props.handleChange} defaultChecked={this.state.nschecklocalaci}
className="ds-config-checkbox" id="nschecklocalaci" />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="Sets
whether referrals are returned by scoped searches (meaning 'one-level' or
'subtree' scoped searches). (nsreferralonscopedsearch).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Send Referral On Scoped Search
+ </Col>
+ <Col sm={8}>
+ <input type="checkbox"
onChange={this.props.handleChange} defaultChecked={this.state.nsreferralonscopedsearch}
className="ds-config-checkbox" id="nsreferralonscopedsearch" />
+ </Col>
+ </Row>
+ </Form>
<hr />
</CustomCollapse>
- <div className="ds-save-btn">
+ <div className="ds-margin-top-lg">
<button onClick={this.saveLink} className="btn
btn-primary">Save Configuration</button>
</div>
- <ConfirmPopup
+ <DoubleConfirmModal
showModal={this.state.showDeleteConfirm}
closeHandler={this.closeDeleteConfirm}
- actionFunc={this.deleteLink}
- actionParam={this.props.suffix}
- msg="Are you really sure you want to delete this database
link?"
- msgContent={this.props.suffix}
+ handleChange={this.handleChange}
+ actionHandler={this.deleteLink}
+ spinning={this.state.modalSpinning}
+ item={this.props.suffix}
+ checked={this.state.modalChecked}
+ mTitle="Delete Database Link"
+ mMsg="Are you really sure you want to delete this database
link?"
+ mSpinningMsg="Deleting Database Linkt ..."
+ mBtnName="Delete Database Link"
/>
</div>
);
@@ -1161,7 +1305,7 @@ export class ChainControlsModal extends React.Component {
<Form horizontal autoComplete="off">
<label className="ds-config-label"
htmlFor="avail-chaining-oid-list" title="A list of LDAP control OIDs to be
forwarded through chaining">Available LDAP Controls</label>
<div>
- <select id="avail-chaining-oid-list"
onChange={handleChange} className="ds-chaining-form-list" size="10"
multiple>
+ <select id="avail-chaining-oid-list"
onChange={handleChange} className="ds-width-auto" size="10"
multiple>
{oids}
</select>
</div>
@@ -1222,7 +1366,7 @@ export class ChainCompsModal extends React.Component {
<Form horizontal autoComplete="off">
<label className="ds-config-label"
htmlFor="avail-chaining-comp-list" title="A list of LDAP control OIDs to be
forwarded through chaining">Available Components</label>
<div>
- <select id="avail-chaining-comp-list"
onChange={handleChange} className="ds-chaining-form-list" size="10"
multiple>
+ <select id="avail-chaining-comp-list"
onChange={handleChange} className="ds-width-auto" size="10"
multiple>
{comps}
</select>
</div>
@@ -1288,6 +1432,7 @@ ChainingDatabaseConfig.propTypes = {
addNotification: PropTypes.func,
reload: PropTypes.func,
data: PropTypes.object,
+ enableTree: PropTypes.func,
};
ChainingDatabaseConfig.defaultProps = {
@@ -1295,6 +1440,7 @@ ChainingDatabaseConfig.defaultProps = {
addNotification: noop,
reload: noop,
data: {},
+ enableTree: PropTypes.noop,
};
ChainingConfig.propTypes = {
@@ -1305,6 +1451,7 @@ ChainingConfig.propTypes = {
addNotification: PropTypes.func,
data: PropTypes.object,
reload: PropTypes.func,
+ enableTree: PropTypes.func,
};
ChainingConfig.defaultProps = {
@@ -1315,4 +1462,5 @@ ChainingConfig.defaultProps = {
addNotification: noop,
data: {},
reload: noop,
+ enableTree: noop,
};
diff --git a/src/cockpit/389-console/src/lib/database/databaseConfig.jsx
b/src/cockpit/389-console/src/lib/database/databaseConfig.jsx
index edb0557..5adac79 100644
--- a/src/cockpit/389-console/src/lib/database/databaseConfig.jsx
+++ b/src/cockpit/389-console/src/lib/database/databaseConfig.jsx
@@ -3,6 +3,10 @@ import React from "react";
import CustomCollapse from "../customCollapse.jsx";
import { log_cmd } from "../tools.jsx";
import {
+ Row,
+ Col,
+ ControlLabel,
+ Form,
Spinner,
noop
} from "patternfly-react";
@@ -55,6 +59,10 @@ export class GlobalDatabaseConfig extends React.Component {
this.save_db_config = this.save_db_config.bind(this);
}
+ componentDidMount() {
+ this.props.enableTree();
+ }
+
select_auto_cache (e) {
this.setState({
db_cache_auto: !this.state.db_cache_auto
@@ -210,8 +218,8 @@ export class GlobalDatabaseConfig extends React.Component {
db_cache_form = <div id="auto-cache-form"
className="ds-margin-left">
<div>
<label htmlFor="autosize"
className="ds-config-label-xlrg"
- title="Enable database and entry cache auto-tuning using a
percentage of the systems current resources (nsslapd-cache-autosize).">
- System Memory Percentage</label><input
className="ds-input" type="text"
+ title="Enable database and entry cache auto-tuning using a
percentage of the system's current resources (nsslapd-cache-autosize).">
+ Memory Percentage</label><input
className="ds-input" type="text"
id="autosizee" size="10"
onChange={this.handleChange}
value={this.state.autosize} />
</div>
@@ -228,7 +236,7 @@ export class GlobalDatabaseConfig extends React.Component {
db_cache_form = <div id="manual-cache-form"
className="ds-margin-left">
<label htmlFor="dbcachesize"
className="ds-config-label-xlrg"
title="Specifies the database index cache size in bytes
(nsslapd-dbcachesize).">
- Database Cache Size (bytes)</label><input
className="ds-input" type="text"
+ Database Cache Size</label><input
className="ds-input" type="text"
id="dbcachesize" size="10"
onChange={this.handleChange} value={this.state.dbcachesize} />
</div>;
db_auto_checked = false;
@@ -246,8 +254,8 @@ export class GlobalDatabaseConfig extends React.Component {
} else {
import_cache_form = <div id="manual-import-cache-form"
className="ds-margin-left">
<label htmlFor="importcachesize"
className="ds-config-label-xlrg"
- title="The size of the database cache used in the bulk import
process. (nsslapd-import-cachesize).">
- Import Cache Size (bytes)</label><input
className="ds-input" type="text" id="importcachesize"
+ title="The size of the database cache in bytes used in the bulk
import process. (nsslapd-import-cachesize).">
+ Import Cache Size</label><input
className="ds-input" type="text" id="importcachesize"
size="10" onChange={this.handleChange}
value={this.state.importcachesize} />
</div>;
import_auto_checked = false;
@@ -256,58 +264,96 @@ export class GlobalDatabaseConfig extends React.Component {
let spinner = "";
if (this.state.loading) {
spinner =
- <div className="ds-loading-spinner ds-center">
- <p />
+ <div className="ds-loading-spinner ds-margin-top
ds-center">
<h4>Loading global database configuration ...</h4>
- <Spinner loading size="md" />
+ <Spinner className="ds-margin-top" loading
size="md" />
</div>;
}
return (
- <div className="container-fluid"
id="db-global-page">
+ <div id="db-global-page">
{spinner}
<div className={this.state.loading ? 'ds-fadeout' :
'ds-fadein'}>
<h3 className="ds-config-header">Global Database
Configuration</h3>
<hr />
- <div className="ds-container">
- <div className="ds-inline">
- <div>
- <label htmlFor="nsslapd-lookthrough-limit"
className="ds-config-label"
- title="The maximum number of entries that the
Directory Server will check when examining candidate entries in response to a search
request (nsslapd-lookthrough-limit).">
- Database Look Though Limit</label><input
id="looklimit" value={this.state.looklimit}
- onChange={this.handleChange}
className="ds-input" type="text" size="15" />
- </div>
- <div>
- <label htmlFor="nsslapd-idlistscanlimit"
className="ds-config-label"
- title="The number of entry IDs that are searched
during a search operation (nsslapd-idlistscanlimit).">
- ID List Scan Limit</label><input
id="idscanlimit" value={this.state.idscanlimit}
- onChange={this.handleChange}
className="ds-input" type="text"size="15" />
- </div>
- </div>
- <div className="ds-divider" />
- <div className="ds-inline">
- <div>
- <label
htmlFor="nsslapd-pagedlookthroughlimit" className="ds-config-label"
- title="The maximum number of entries that the
Directory Server will check when examining candidate entries for a search which uses the
simple paged results control (nsslapd-pagedlookthroughlimit).">
- Paged Search Look Through
Limit</label><input id="pagelooklimit"
value={this.state.pagelooklimit}
- onChange={this.handleChange}
className="ds-input" type="text" size="15" />
- </div>
- <div>
- <label
htmlFor="nsslapd-pagedidlistscanlimit" className="ds-config-label"
- title="The number of entry IDs that are
searched, specifically, for a search operation using the simple paged results control
(nsslapd-pagedidlistscanlimit).">
- Paged Search ID List Scan
Limit</label><input id="pagescanlimit"
- value={this.state.pagescanlimit}
onChange={this.handleChange}
- className="ds-input" type="text"
size="15" />
- </div>
- <div>
- <label
htmlFor="nsslapd-rangelookthroughlimit" className="ds-config-label"
- title="The maximum number of entries that the
Directory Server will check when examining candidate entries in response to a range search
request (nsslapd-rangelookthroughlimit).">
- Range Search Look Through
Limit</label><input id="rangelooklimit"
- value={this.state.rangelooklimit}
onChange={this.handleChange}
- className="ds-input" type="text"
size="15" />
- </div>
- </div>
- </div>
+ <Form horizontal>
+ <Row
+ title="The maximum number of entries that the Directory
Server will check when examining candidate entries in response to a search request
(nsslapd-lookthrough-limit)."
+ className="ds-margin-top"
+
+ <Col
componentClass={ControlLabel} sm={5}>
+ Database Look Though Limit
+ </Col>
+ <Col sm={4}>
+ <input
+ id="looklimit"
+ value={this.state.looklimit}
+ onChange={this.handleChange}
className="ds-input-auto" type="text"
+ />
+ </Col>
+ </Row>
+ <Row
+ title="The number of entry IDs that are searched during
a search operation (nsslapd-idlistscanlimit)."
+ className="ds-margin-top"
+
+ <Col
componentClass={ControlLabel} sm={5}>
+ ID List Scan Limit
+ </Col>
+ <Col sm={4}>
+ <input
+ id="idscanlimit"
+ value={this.state.idscanlimit}
+ onChange={this.handleChange}
className="ds-input-auto" type="text"
+ />
+ </Col>
+ </Row>
+ <Row
+ title="The maximum number of entries that the Directory
Server will check when examining candidate entries for a search which uses the simple
paged results control (nsslapd-pagedlookthroughlimit)."
+ className="ds-margin-top"
+
+ <Col
componentClass={ControlLabel} sm={5}>
+ Paged Search Look Through Limit
+ </Col>
+ <Col sm={4}>
+ <input
+ id="pagelooklimit"
+ value={this.state.pagelooklimit}
+ onChange={this.handleChange}
className="ds-input-auto" type="text"
+ />
+ </Col>
+ </Row>
+ <Row
+ title="The number of entry IDs that are searched,
specifically, for a search operation using the simple paged results control
(nsslapd-pagedidlistscanlimit)."
+ className="ds-margin-top"
+
+ <Col
componentClass={ControlLabel} sm={5}>
+ Paged Search ID List Scan Limit
+ </Col>
+ <Col sm={4}>
+ <input
+ id="pagescanlimit"
+ value={this.state.pagescanlimit}
+ onChange={this.handleChange}
className="ds-input-auto" type="text"
+ />
+ </Col>
+ </Row>
+ <Row
+ title="The maximum number of entries that the Directory
Server will check when examining candidate entries in response to a range search request
(nsslapd-rangelookthroughlimit)."
+ className="ds-margin-top"
+
+ <Col
componentClass={ControlLabel} sm={5}>
+ Range Search Look Through Limit
+ </Col>
+ <Col sm={4}>
+ <input
+ id="rangelooklimit"
+ value={this.state.rangelooklimit}
+ onChange={this.handleChange}
className="ds-input-auto" type="text"
+ />
+ </Col>
+ </Row>
+ </Form>
+
<div className="ds-container">
<div>
<h4 className="ds-sub-header">Database Cache
Settings</h4>
@@ -333,46 +379,54 @@ export class GlobalDatabaseConfig extends React.Component {
</div>
</div>
<CustomCollapse>
- <div className="ds-accordion-panel">
- <div className="ds-container">
- <div className="ds-inline">
- <div>
- <label
htmlFor="nsslapd-db-logdirectory" className="ds-config-label"
- title="Database Transaction Log Location
(nsslapd-db-logdirectory).">
- Transaction Logs
Directory</label><input id="txnlogdir" value={this.state.txnlogdir}
- onChange={this.handleChange}
className="ds-input" type="text" size="25" />
- </div>
- <div>
- <label
htmlFor="nsslapd-db-home-directory" className="ds-config-label"
- title="Location for database memory mapped
files. You must specify a subdirectory of a tempfs type filesystem
(nsslapd-db-home-directory).">
- Database Home
Directory</label><input id="dbhomedir" value={this.state.dbhomedir}
- onChange={this.handleChange}
className="ds-input" type="text" size="25" />
- </div>
- <div>
- <label htmlFor="nsslapd-db-locks"
className="ds-config-label"
- title="The number of database locks
(nsslapd-db-locks).">
- Database Locks</label><input
id="dblocks" value={this.state.dblocks}
- onChange={this.handleChange}
className="ds-input" type="text" size="25" />
- </div>
- <div>
- <label
htmlFor="nsslapd-db-checkpoint-interval" className="ds-config-label"
- title="Amount of time in seconds after
which the Directory Server sends a checkpoint entry to the database transaction log
(nsslapd-db-checkpoint-interval).">
- Database Checkpoint
Interval</label><input id="chxpoint" value={this.state.chxpoint}
- onChange={this.handleChange}
className="ds-input" type="text" size="25" />
- </div>
- <div>
- <label
htmlFor="nsslapd-db-compactdb-interval" className="ds-config-label"
- title="The interval in seconds when the
database is compacted (nsslapd-db-compactdb-interval).">
- Database Compact
Interval</label><input id="compactinterval"
value={this.state.compactinterval}
-
onChange={this.handleChange}className="ds-input" type="text"
size="25" />
- </div>
- </div>
+ <div className="ds-margin-top">
+ <div className="ds-margin-left">
+ <Form horizontal>
+ <Row className="ds-margin-top"
title="Database Transaction Log Location (nsslapd-db-logdirectory).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Transaction Logs Directory
+ </Col>
+ <Col sm={8}>
+ <input id="txnlogdir"
value={this.state.txnlogdir} onChange={this.handleChange}
className="ds-input-auto" type="text" />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="Location for database memory mapped files. You must specify a subdirectory of
a tempfs type filesystem (nsslapd-db-home-directory).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Database Home Directory
+ </Col>
+ <Col sm={8}>
+ <input id="dbhomedir"
value={this.state.dbhomedir} onChange={this.handleChange}
className="ds-input-auto" type="text" />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="The number of database locks (nsslapd-db-locks).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Database Locks
+ </Col>
+ <Col sm={8}>
+ <input id="dblocks"
value={this.state.dblocks} onChange={this.handleChange}
className="ds-input-auto" type="text" />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="Amount of time in seconds after which the Directory Server sends a checkpoint
entry to the database transaction log (nsslapd-db-checkpoint-interval).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Database Checkpoint Interval
+ </Col>
+ <Col sm={8}>
+ <input id="chxpoint"
value={this.state.chxpoint} onChange={this.handleChange}
className="ds-input-auto" type="text" />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="The interval in seconds when the database is compacted
(nsslapd-db-compactdb-interval).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Database Compact Interval
+ </Col>
+ <Col sm={8}>
+ <input id="compactinterval"
value={this.state.compactinterval} onChange={this.handleChange}
className="ds-input-auto" type="text" />
+ </Col>
+ </Row>
+ </Form>
</div>
- <p />
</div>
</CustomCollapse>
- <p />
- <div className="ds-save-btn">
+ <div className="ds-margin-top-lg">
<button className="btn btn-primary save-button"
onClick={this.save_db_config}>Save
Configuration</button>
</div>
@@ -389,6 +443,7 @@ GlobalDatabaseConfig.propTypes = {
addNotification: PropTypes.func,
data: PropTypes.object,
reload: PropTypes.func,
+ enableTree: PropTypes.func,
};
GlobalDatabaseConfig.defaultProps = {
@@ -396,4 +451,5 @@ GlobalDatabaseConfig.defaultProps = {
addNotification: noop,
data: {},
reload: noop,
+ enableTree: noop,
};
diff --git a/src/cockpit/389-console/src/lib/database/databaseModal.jsx
b/src/cockpit/389-console/src/lib/database/databaseModal.jsx
index 01db9bb..881b0b4 100644
--- a/src/cockpit/389-console/src/lib/database/databaseModal.jsx
+++ b/src/cockpit/389-console/src/lib/database/databaseModal.jsx
@@ -2,8 +2,10 @@ import React from "react";
import {
Modal,
Row,
+ Checkbox,
Col,
ControlLabel,
+ Radio,
Icon,
Button,
Form,
@@ -44,7 +46,7 @@ class CreateLinkModal extends React.Component {
</Modal.Title>
</Modal.Header>
<Modal.Body>
- <Form horizontal autoComplete="off">
+ <Form horizontal>
<div>
<label htmlFor="createLinkSuffix"
className="ds-config-label" title="The RDN of the link suffix">
Link Sub-Suffix</label><input
className={error.createLinkSuffix ? "ds-input-bad ds-input-right" :
"ds-input ds-input-right"} onChange={handleChange} type="text"
id="createLinkSuffix" size="12" /><b><font
color="blue"> ,{suffix}</font></b>
@@ -110,8 +112,12 @@ class CreateSubSuffixModal extends React.Component {
showModal,
closeHandler,
handleChange,
+ handleRadioChange,
saveHandler,
suffix,
+ noInit,
+ addSuffix,
+ addSample,
error
} = this.props;
@@ -133,7 +139,7 @@ class CreateSubSuffixModal extends React.Component {
</Modal.Header>
<Modal.Body>
<Form horizontal autoComplete="off">
- <Row title="Database Suffix DN
(nsslapd-suffix)">
+ <Row title="Database suffix, like
'dc=example,dc=com'. The suffix must be a valid LDAP Distiguished Name
(DN)">
<Col sm={3}>
<ControlLabel>Sub-Suffix
DN</ControlLabel>
</Col>
@@ -149,10 +155,9 @@ class CreateSubSuffixModal extends React.Component {
<ControlLabel><b><font
color="blue">,{suffix}</font></b></ControlLabel>
</Col>
</Row>
- <p />
- <Row title="Database backend name
(nsslapd-backend)">
+ <Row className="ds-margin-top" title="The
name for the backend database, like 'userroot'. The name can be a combination of
alphanumeric characters, dashes (-), and underscores (_). No other characters are allowed,
and the name must be unique across all backends.">
<Col sm={3}>
- <ControlLabel>Backend
Name</ControlLabel>
+ <ControlLabel>Database
Name</ControlLabel>
</Col>
<Col sm={9}>
<FormControl
@@ -163,6 +168,24 @@ class CreateSubSuffixModal extends React.Component {
/>
</Col>
</Row>
+ <hr />
+ <div>
+ <Row className="ds-indent">
+ <Radio name="radioGroup"
id="noSuffixInit" onChange={handleRadioChange} checked={noInit} inline>
+ Do Not Initialize Database
+ </Radio>
+ </Row>
+ <Row className="ds-indent">
+ <Radio name="radioGroup"
id="createSuffixEntry" onChange={handleRadioChange} checked={addSuffix}
inline>
+ Create The Top Sub-Suffix Entry
+ </Radio>
+ </Row>
+ <Row className="ds-indent">
+ <Radio name="radioGroup"
id="createSampleEntries" onChange={handleRadioChange} checked={addSample}
inline>
+ Add Sample Entries
+ </Radio>
+ </Row>
+ </div>
</Form>
</Modal.Body>
<Modal.Footer>
@@ -200,7 +223,7 @@ class ExportModal extends React.Component {
if (spinning) {
spinner =
<Row>
- <div className="ds-modal-spinner">
+ <div className="ds-margin-top-lg ds-modal-spinner">
<Spinner loading inline size="lg" />Exporting
database... <font size="1">(You can safely close this
window)</font>
</div>
</Row>;
@@ -237,7 +260,17 @@ class ExportModal extends React.Component {
/>
</Col>
</Row>
- <p />
+ <Row className="ds-margin-top-xlg">
+ <Col sm={12} className="ds-margin-left">
+ <Checkbox
+ id="includeReplData"
+ onChange={handleChange}
+ title="Include the replication metadata
needed to restore or initialize another replica."
+
+
Include Replication Data
+ </Checkbox>
+ </Col>
+ </Row>
{spinner}
</Form>
</Modal.Body>
@@ -279,8 +312,7 @@ class ImportModal extends React.Component {
if (spinning) {
spinner =
<Row>
- <div className="ds-modal-spinner">
- <p />
+ <div className="ds-margin-top-lg ds-modal-spinner">
<Spinner loading inline size="lg" />Importing
LDIF file... <font size="1">(You can safely close this
window)</font>
</div>
</Row>;
@@ -308,7 +340,6 @@ class ImportModal extends React.Component {
</Modal.Title>
</Modal.Header>
<Modal.Body>
- <p />
<LDIFTable
rows={suffixRows}
confirmImport={this.props.showConfirmImport}
@@ -335,7 +366,6 @@ class ImportModal extends React.Component {
</Button>
</Col>
</Row>
- <p />
{spinner}
</Form>
</Modal.Body>
diff --git a/src/cockpit/389-console/src/lib/database/databaseTables.jsx
b/src/cockpit/389-console/src/lib/database/databaseTables.jsx
index 5d90f02..b8715e8 100644
--- a/src/cockpit/389-console/src/lib/database/databaseTables.jsx
+++ b/src/cockpit/389-console/src/lib/database/databaseTables.jsx
@@ -60,6 +60,7 @@ class ReferralTable extends React.Component {
return [
<td key={rowData.name[0]}>
<Button
+ bsStyle="primary"
onClick={() => {
this.props.loadModalHandler(rowData);
}}
@@ -228,7 +229,8 @@ class IndexTable extends React.Component {
return [
<td key={rowData.name[0]}>
<DropdownButton id={rowData.name[0]}
- bsStyle="default"
title="Actions">
+ className="ds-action-button"
+ bsStyle="primary"
title="Actions">
<MenuItem eventKey="1"
onClick={() => {
this.props.editIndex(rowData);
}}
@@ -331,6 +333,7 @@ class EncryptedAttrTable extends React.Component {
return [
<td key={rowData.name[0]}>
<Button
+ bsStyle="primary"
onClick={() => {
this.props.loadModalHandler(rowData);
}}
@@ -680,6 +683,7 @@ class LDIFManageTable extends React.Component {
return [
<td key={rowData.name[0]}>
<DropdownButton id={rowData.name[0]}
+ className="ds-action-button"
bsStyle="primary"
title="Actions">
<MenuItem eventKey="1"
onClick={() => {
this.props.confirmImport(rowData);
@@ -855,6 +859,7 @@ class BackupTable extends React.Component {
return [
<td key={rowData.name[0]}>
<DropdownButton id={rowData.name[0]}
+ className="ds-action-button"
bsStyle="primary"
title="Actions">
<MenuItem eventKey="1"
onClick={() => {
this.props.confirmRestore(rowData);
diff --git a/src/cockpit/389-console/src/lib/database/indexes.jsx
b/src/cockpit/389-console/src/lib/database/indexes.jsx
index f8e157d..d33db25 100644
--- a/src/cockpit/389-console/src/lib/database/indexes.jsx
+++ b/src/cockpit/389-console/src/lib/database/indexes.jsx
@@ -631,7 +631,7 @@ class AddIndexModal extends React.Component {
</Modal.Header>
<Modal.Body>
<Form horizontal autoComplete="off">
- <label className="ds-config-label"
htmlFor="ds-index-form-list" title="Select an attribute to
index">Select An Attribute</label>
+ <label className="ds-config-label"
htmlFor="indexAttributeName" title="Select an attribute to
index">Select An Attribute</label>
<Typeahead
id="indexAttributeName"
onChange={values => {
@@ -665,7 +665,6 @@ class AddIndexModal extends React.Component {
</Col>
</Row>
</div>
- <p />
<Row className="ds-margin-top-lg">
<Col sm={12} title="List of matching rules
separated by a 'space'">
<p><b>Matching Rules</b></p>
@@ -836,7 +835,6 @@ class EditIndexModal extends React.Component {
</Col>
</Row>
</div>
- <p />
<Row className="ds-margin-top-lg">
<Col sm={12}>
<p><b>Matching Rules</b></p>
diff --git a/src/cockpit/389-console/src/lib/database/referrals.jsx
b/src/cockpit/389-console/src/lib/database/referrals.jsx
index 22f9c50..13f4c15 100644
--- a/src/cockpit/389-console/src/lib/database/referrals.jsx
+++ b/src/cockpit/389-console/src/lib/database/referrals.jsx
@@ -12,7 +12,7 @@ import {
Form,
noop
} from "patternfly-react";
-import { log_cmd } from "../tools.jsx";
+import { log_cmd, valid_port } from "../tools.jsx";
import PropTypes from "prop-types";
import "../../css/ds.css";
@@ -122,6 +122,13 @@ export class SuffixReferrals extends React.Component {
);
missingArgs.refPort = true;
errors = true;
+ } else if (!valid_port(this.state.refPort)) {
+ this.props.addNotification(
+ "error",
+ `Invalid port number, please use a number between 1 and 65535`
+ );
+ missingArgs.refPort = true;
+ errors = true;
}
if (errors) {
this.setState({
@@ -288,8 +295,7 @@ class AddReferralModal extends React.Component {
</select>
</Col>
</Row>
- <p />
- <Row>
+ <Row className="ds-margin-top">
<Col sm={3}>
<ControlLabel>Host Name</ControlLabel>
</Col>
@@ -297,8 +303,7 @@ class AddReferralModal extends React.Component {
<input className={error.refHost ?
"ds-input-auto-bad" : "ds-input-auto"} type="text"
onChange={handleChange} id="refHost" />
</Col>
</Row>
- <p />
- <Row>
+ <Row className="ds-margin-top">
<Col sm={3}>
<ControlLabel>Port Number</ControlLabel>
</Col>
@@ -306,8 +311,7 @@ class AddReferralModal extends React.Component {
<input className={error.refPort ?
"ds-input-auto-bad" : "ds-input-auto"} type="text"
onChange={handleChange} id="refPort" />
</Col>
</Row>
- <p />
- <Row>
+ <Row className="ds-margin-top">
<Col sm={3}>
<ControlLabel>Suffix</ControlLabel>
</Col>
@@ -315,8 +319,7 @@ class AddReferralModal extends React.Component {
<input className="ds-input-auto"
onChange={handleChange} type="text" id="refSuffix" />
</Col>
</Row>
- <p />
- <Row title="Comma separated list of attributes to
return">
+ <Row className="ds-margin-top" title="Comma
separated list of attributes to return">
<Col sm={3}>
<ControlLabel>Attributes</ControlLabel>
</Col>
@@ -324,8 +327,7 @@ class AddReferralModal extends React.Component {
<input className="ds-input-auto"
onChange={handleChange} type="text" id="refAttrs" />
</Col>
</Row>
- <p />
- <Row>
+ <Row className="ds-margin-top">
<Col sm={3}>
<ControlLabel>Filter</ControlLabel>
</Col>
@@ -333,13 +335,12 @@ class AddReferralModal extends React.Component {
<input onChange={handleChange}
className="ds-input-auto" type="text" id="refFilter" />
</Col>
</Row>
- <p />
- <Row>
+ <Row className="ds-margin-top">
<Col sm={3}>
<ControlLabel>Scope</ControlLabel>
</Col>
<Col sm={9}>
- <select className="btn btn-default
dropdown" onChange={handleChange} defaultValue=""
name="refScope">
+ <select className="btn btn-default
dropdown" onChange={handleChange} defaultValue=""
id="refScope">
<option />
<option>sub</option>
<option>one</option>
diff --git a/src/cockpit/389-console/src/lib/database/suffix.jsx
b/src/cockpit/389-console/src/lib/database/suffix.jsx
index 3f3bc82..9be54a8 100644
--- a/src/cockpit/389-console/src/lib/database/suffix.jsx
+++ b/src/cockpit/389-console/src/lib/database/suffix.jsx
@@ -1,12 +1,12 @@
import cockpit from "cockpit";
import React from "react";
-import { ConfirmPopup } from "../notifications.jsx";
+import { DoubleConfirmModal } from "../notifications.jsx";
import { AttrEncryption } from "./attrEncryption.jsx";
import { SuffixConfig } from "./suffixConfig.jsx";
import { SuffixReferrals } from "./referrals.jsx";
import { SuffixIndexes } from "./indexes.jsx";
import { VLVIndexes } from "./vlvIndexes.jsx";
-import { log_cmd } from "../tools.jsx";
+import { log_cmd, bad_file_name } from "../tools.jsx";
import {
ImportModal,
ExportModal,
@@ -28,6 +28,15 @@ import {
TabContainer,
noop
} from "patternfly-react";
+
+// PR React 4 example
+// import {
+// Dropdown,
+// DropdownToggle,
+// DropdownItem,
+// DropdownSeparator,
+// } from "@patternfly/react-core";
+
import PropTypes from "prop-types";
import "../../css/ds.css";
@@ -64,12 +73,19 @@ export class Suffix extends React.Component {
importSpinner: false,
showConfirmLDIFImport: false,
deleleLDIFName: "",
+ modalChecked: false,
+ modalSpinning: false,
+ includeReplData: false,
// Reindex all
showReindexConfirm: false,
// Create Sub Suffix
showSubSuffixModal: false,
subSuffixValue: "",
subSuffixBeName: "",
+ createSuffixEntry: false,
+ noSuffixInit: true,
+ createSampleEntries: false,
+
// Create Link
showLinkModal: false,
createLinkSuffix: "",
@@ -91,6 +107,7 @@ export class Suffix extends React.Component {
this.showImportModal = this.showImportModal.bind(this);
this.closeImportModal = this.closeImportModal.bind(this);
this.handleChange = this.handleChange.bind(this);
+ this.handleRadioChange = this.handleRadioChange.bind(this);
this.doImport = this.doImport.bind(this);
this.importLDIF = this.importLDIF.bind(this);
this.showConfirmLDIFImport = this.showConfirmLDIFImport.bind(this);
@@ -114,12 +131,15 @@ export class Suffix extends React.Component {
this.handleLinkChange = this.handleLinkChange.bind(this);
// Suffix config
this.saveSuffixConfig = this.saveSuffixConfig.bind(this);
- // Attr Encrypt Modal
this.showDeleteConfirm = this.showDeleteConfirm.bind(this);
this.closeDeleteConfirm = this.closeDeleteConfirm.bind(this);
this.doDelete = this.doDelete.bind(this);
}
+ componentDidMount() {
+ this.props.enableTree();
+ }
+
handleNavSelect(key) {
this.setState({ activeKey: key });
}
@@ -162,6 +182,8 @@ export class Suffix extends React.Component {
this.setState({
showConfirmLDIFImport: true,
importLDIFName: item.name,
+ modalChecked: false,
+ modalSpinning: false,
});
}
@@ -169,6 +191,8 @@ export class Suffix extends React.Component {
// call importLDIF
this.setState({
showConfirmLDIFImport: false,
+ modalChecked: false,
+ modalSpinning: false,
});
}
@@ -226,6 +250,7 @@ export class Suffix extends React.Component {
attrEncryption: false,
showExportModal: true,
exportSpinner: false,
+ includeReplData: false,
errObj: {},
});
}
@@ -251,7 +276,20 @@ export class Suffix extends React.Component {
return;
}
- // Do import
+ // Must not be a path
+ if (bad_file_name(this.state.ldifLocation)) {
+ this.props.addNotification(
+ "warning",
+ `LDIF name should not be a path. All export files are stored in the
server's LDIF directory`
+ );
+ missingArgs.ldifLocation = true;
+ this.setState({
+ errObj: missingArgs
+ });
+ return;
+ }
+
+ // Do Export
let export_cmd = [
"dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-"
+ this.props.serverId + ".socket",
"backend", "export", this.props.suffix,
"--ldif=" + this.state.ldifLocation
@@ -261,6 +299,10 @@ export class Suffix extends React.Component {
export_cmd.push("--encrypted");
}
+ if (this.state.includeReplData) {
+ export_cmd.push("--replication");
+ }
+
this.setState({
exportSpinner: true,
});
@@ -280,7 +322,7 @@ export class Suffix extends React.Component {
})
.fail(err => {
let errMsg = JSON.parse(err);
- this.loadLDIFs();
+ this.props.reloadLDIFs();
this.props.addNotification(
"error",
`Error exporting database - ${errMsg.desc}`
@@ -296,13 +338,17 @@ export class Suffix extends React.Component {
//
showReindexConfirm() {
this.setState({
- showReindexConfirm: true
+ showReindexConfirm: true,
+ modalChecked: false,
+ modalSpinning: false,
});
}
closeReindexConfirm() {
this.setState({
- showReindexConfirm: false
+ showReindexConfirm: false,
+ modalChecked: false,
+ modalSpinning: false,
});
}
@@ -389,13 +435,20 @@ export class Suffix extends React.Component {
}
// Create a new suffix
- const cmd = [
+ let cmd = [
"dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-"
+ this.props.serverId + ".socket",
"backend", "create", "--be-name",
this.state.subSuffixBeName,
"--suffix=" + this.state.subSuffixValue + "," +
this.props.suffix,
"--parent-suffix=" + this.props.suffix
];
+ if (this.state.createSampleEntries) {
+ cmd.push('--create-entries');
+ }
+ if (this.state.createSuffixEntry) {
+ cmd.push('--create-suffix');
+ }
+
log_cmd("createSubSuffix", "Create a sub suffix", cmd);
cockpit
.spawn(cmd, { superuser: true, err: "message" })
@@ -517,7 +570,7 @@ export class Suffix extends React.Component {
this.state.createLinkName
];
if (this.state.createUseStartTLS) {
- cmd.push("--use-starttls");
+ cmd.push("--use-starttls=on");
}
log_cmd("createLink", "Create database link", cmd);
cockpit
@@ -566,18 +619,41 @@ export class Suffix extends React.Component {
}, this.checkPasswords);
}
+ handleRadioChange(e) {
+ // Handle the create suffix init option radio button group
+ let noInit = false;
+ let addSuffix = false;
+ let addSample = false;
+ if (e.target.id == "noSuffixInit") {
+ noInit = true;
+ } else if (e.target.id == "createSuffixEntry") {
+ addSuffix = true;
+ } else { // createSampleEntries
+ addSample = true;
+ }
+ this.setState({
+ noSuffixInit: noInit,
+ createSuffixEntry: addSuffix,
+ createSampleEntries: addSample
+ });
+ }
+
//
// Delete suffix
//
showDeleteConfirm(item) {
this.setState({
- showDeleteConfirm: true
+ showDeleteConfirm: true,
+ modalSpinning: false,
+ modalChecked: false
});
}
closeDeleteConfirm() {
this.setState({
- showDeleteConfirm: false
+ showDeleteConfirm: false,
+ modalChecked: false,
+ modalSpinning: false,
});
}
@@ -680,20 +756,15 @@ export class Suffix extends React.Component {
suffixIcon = "leaf";
}
- const confirm_msg =
- <span>
- Are you sure you want to import:
<b>{this.state.importLDIFName}</b> ?
- </span>;
-
return (
- <div className="container-fluid" id="suffix-page">
+ <div id="suffix-page">
<Row>
<Col sm={10} className="ds-word-wrap">
- <ControlLabel
className="ds-suffix-header"><Icon type="fa" name={suffixIcon}
/> {this.props.suffix} (<i>{this.props.bename}</i>)</ControlLabel>
+ <ControlLabel
className="ds-suffix-header"><Icon type="fa" name={suffixIcon}
/> <b>{this.props.suffix}</b>
(<i>{this.props.bename}</i>)</ControlLabel>
</Col>
<Col sm={2}>
<div>
- <DropdownButton bsStyle="primary"
title="Suffix Tasks" id="mydropdown">
+ <DropdownButton className="ds-action-button"
bsStyle="primary" title="Suffix Tasks" id="mydropdown"
pullRight>
<MenuItem eventKey="1"
onClick={this.showImportModal} title="Import an LDIF file to initialize the
database">
Initialize Suffix
</MenuItem>
@@ -717,10 +788,9 @@ export class Suffix extends React.Component {
</div>
</Col>
</Row>
- <p />
<TabContainer id="basic-tabs-pf"
onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>
- <div>
+ <div className="ds-margin-top-xlg">
<Nav bsClass="nav nav-tabs nav-tabs-pf">
<NavItem eventKey={1}>
<div dangerouslySetInnerHTML={{__html:
'Settings'}} />
@@ -803,14 +873,18 @@ export class Suffix extends React.Component {
</TabContent>
</div>
</TabContainer>
-
- <ConfirmPopup
+ <DoubleConfirmModal
showModal={this.state.showDeleteConfirm}
closeHandler={this.closeDeleteConfirm}
- actionFunc={this.doDelete}
- actionParam={this.props.suffix}
- msg="Are you really sure you want to delete the database?"
- msgContent={this.props.suffix}
+ handleChange={this.handleChange}
+ actionHandler={this.doDelete}
+ spinning={this.state.modalSpinning}
+ item={this.props.suffix}
+ checked={this.state.modalChecked}
+ mTitle="Delete Replication Agreement"
+ mMsg="Are you really sure you want to delete the
database?"
+ mSpinningMsg="Deleting database ..."
+ mBtnName="Delete Database"
/>
<CreateLinkModal
showModal={this.state.showLinkModal}
@@ -825,8 +899,12 @@ export class Suffix extends React.Component {
showModal={this.state.showSubSuffixModal}
closeHandler={this.closeSubSuffixModal}
handleChange={this.handleChange}
+ handleRadioChange={this.handleRadioChange}
saveHandler={this.createSubSuffix}
suffix={this.props.suffix}
+ noInit={this.state.noSuffixInit}
+ addSuffix={this.state.createSuffixEntry}
+ addSample={this.state.createSampleEntries}
error={this.state.errObj}
/>
<ImportModal
@@ -839,15 +917,19 @@ export class Suffix extends React.Component {
rows={this.props.LDIFRows}
suffix={this.props.suffix}
/>
- <ConfirmPopup
+ <DoubleConfirmModal
showModal={this.state.showConfirmLDIFImport}
closeHandler={this.closeConfirmLDIFImport}
- actionFunc={this.importLDIF}
- actionParam={this.state.importLDIFName}
- msg={confirm_msg}
- msgContent="WARNING: This action will permanently overwrite the
current database!"
+ handleChange={this.handleChange}
+ actionHandler={this.importLDIF}
+ spinning={this.state.modalSpinning}
+ item={this.state.importLDIFName}
+ checked={this.state.modalChecked}
+ mTitle="Initialize Database From LDIF"
+ mMsg="Are you sure you want to initialize the database (it will
permanently overwrite the current database)?"
+ mSpinningMsg="Initializing Database ..."
+ mBtnName="Initialize Database"
/>
-
<ExportModal
showModal={this.state.showExportModal}
closeHandler={this.closeExportModal}
@@ -856,13 +938,18 @@ export class Suffix extends React.Component {
spinning={this.state.exportSpinner}
error={this.state.errObj}
/>
- <ConfirmPopup
+ <DoubleConfirmModal
showModal={this.state.showReindexConfirm}
closeHandler={this.closeReindexConfirm}
- actionFunc={this.doReindex}
- actionParam={this.props.suffix}
- msg="Are you sure you want to reindex all the attribute
indexes?"
- msgContent=""
+ handleChange={this.handlChange}
+ actionHandler={this.doReindex}
+ spinning={this.state.modalSpinning}
+ item={this.props.suffix}
+ checked={this.state.modalChecked}
+ mTitle="Reindex All Attributes"
+ mMsg="Are you sure you want to reindex all the attribute
indexes?"
+ mSpinningMsg="Reindexing Database ..."
+ mBtnName="Reindex"
/>
<ReindexModal
showModal={this.state.showReindexModal}
@@ -892,6 +979,7 @@ Suffix.propTypes = {
data: PropTypes.object,
attrs: PropTypes.array,
LDIFRows: PropTypes.array,
+ enableTree: PropTypes.func,
};
Suffix.defaultProps = {
@@ -909,5 +997,6 @@ Suffix.defaultProps = {
dbtype: "",
data: {},
attrs: [],
- LDIFRows: []
+ LDIFRows: [],
+ enableTree: PropTypes.noop,
};
diff --git a/src/cockpit/389-console/src/lib/database/suffixConfig.jsx
b/src/cockpit/389-console/src/lib/database/suffixConfig.jsx
index ec9cd60..e9b71b2 100644
--- a/src/cockpit/389-console/src/lib/database/suffixConfig.jsx
+++ b/src/cockpit/389-console/src/lib/database/suffixConfig.jsx
@@ -1,7 +1,13 @@
import React from "react";
import "../../css/ds.css";
import PropTypes from "prop-types";
-import { noop } from "patternfly-react";
+import {
+ noop,
+ Row,
+ Col,
+ ControlLabel,
+ Form,
+} from "patternfly-react";
export class SuffixConfig extends React.Component {
render() {
@@ -10,53 +16,83 @@ export class SuffixConfig extends React.Component {
const cacheValue = this.props.cachesize + " (auto-sized)";
const cachememValue = this.props.cachememsize + " (auto-sized)";
cacheInputs =
- <div>
- <div title="The entry cache size setting is being auto-sized
and is read-only - see Global Database Configuration">
- <label htmlFor="cachememsize"
className="ds-config-label-lrg">
- Entry Cache Size (bytes)</label><input disabled
value={cachememValue} className="ds-input" type="text"
id="cachememsize" size="45" />
- </div>
- <div title="The entry cache max entries setting is being
auto-sized and is read-only - see Global Database Configuration">
- <label htmlFor="cachesize"
className="ds-config-label-lrg">
- Entry Cache Max Entries</label><input disabled
value={cacheValue} className="ds-input" id="cachesize"
type="text" size="45" />
- </div>
- <div>
- <label htmlFor="dncachememsize"
className="ds-config-label-lrg" title="the available memory space for the
DN cache. The DN cache is similar to the entry cache for a database, only its table stores
only the entry ID and the entry DN (nsslapd-dncachememsize).">
- DN Cache Size (bytes)</label><input
onChange={this.props.handleChange} value={this.props.dncachememsize}
className="ds-input" type="text" id="dncachememsize"
size="45" />
- </div>
- </div>;
+ <Form horizontal>
+ <Row className="ds-margin-top" title="The entry
cache size in bytes setting is being auto-sized and is read-only - see Global Database
Configuration">
+ <Col componentClass={ControlLabel} sm={4}>
+ Entry Cache Size
+ </Col>
+ <Col sm={8}>
+ <input disabled value={cachememValue}
className="ds-input-auto" type="text" id="cachememsize"
/>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The entry
cache max entries setting is being auto-sized and is read-only - see Global Database
Configuration">
+ <Col componentClass={ControlLabel} sm={4}>
+ Entry Cache Max Entries
+ </Col>
+ <Col sm={8}>
+ <input disabled value={cacheValue}
className="ds-input-auto" type="text" id="cachesize" />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The available
memory space in bytes for the DN cache. The DN cache is similar to the entry cache for a
database, only its table stores only the entry ID and the entry DN
(nsslapd-dncachememsize).">
+ <Col componentClass={ControlLabel} sm={4}>
+ DN Cache Size
+ </Col>
+ <Col sm={8}>
+ <input onChange={this.props.handleChange}
value={this.props.dncachememsize} className="ds-input-auto"
type="text" id="dncachememsize" />
+ </Col>
+ </Row>
+ </Form>;
} else {
cacheInputs =
- <div>
- <div>
- <label htmlFor="cachememsize"
className="ds-config-label-lrg" title="The size for the available memory
space for the entry cache (nsslapd-cachememsize).">
- Entry Cache Size (bytes)</label><input
onChange={this.props.handleChange} value={this.props.cachememsize}
className="ds-input" type="text" id="cachememsize"
size="30" />
- </div>
- <div>
- <label htmlFor="cachesize"
className="ds-config-label-lrg" title="The number of entries to keep in the
entry cache, use'-1' for unlimited (nsslapd-cachesize).">
- Entry Cache Max Entries</label><input
onChange={this.props.handleChange} value={this.props.cachesize}
className="ds-input" type="text" id="cachesize"
size="30" />
- </div>
- <div>
- <label htmlFor="dncachememsize"
className="ds-config-label-lrg" title="the available memory space for the
DN cache. The DN cache is similar to the entry cache for a database, only its table stores
only the entry ID and the entry DN (nsslapd-dncachememsize).">
- DN Cache Size (bytes)</label><input
onChange={this.props.handleChange} value={this.props.dncachememsize}
className="ds-input" type="text" id="dncachememsize"
size="30" />
- </div>
- </div>;
+ <Form horizontal>
+ <Row className="ds-margin-top" title="The size for
the available memory space in bytes for the entry cache (nsslapd-cachememsize).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Entry Cache Size
+ </Col>
+ <Col sm={8}>
+ <input onChange={this.props.handleChange}
value={this.props.cachememsize} className="ds-input-auto" type="text"
id="cachememsize" />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The number of
entries to keep in the entry cache, use'-1' for unlimited
(nsslapd-cachesize).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Entry Cache Max Entries
+ </Col>
+ <Col sm={8}>
+ <input onChange={this.props.handleChange}
value={this.props.cachesize} className="ds-input-auto" type="text"
id="cachesize" />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="the available
memory space in bytes for the DN cache. The DN cache is similar to the entry cache for a
database, only its table stores only the entry ID and the entry DN
(nsslapd-dncachememsize).">
+ <Col componentClass={ControlLabel} sm={4}>
+ DN Cache Size
+ </Col>
+ <Col sm={8}>
+ <input onChange={this.props.handleChange}
value={this.props.dncachememsize} className="ds-input-auto"
type="text" id="dncachememsize" />
+ </Col>
+ </Row>
+ </Form>;
}
return (
<div className="ds-margin-top-lg">
- <p />
{cacheInputs}
- <p />
- <div>
- <div>
- <input type="checkbox"
onChange={this.props.handleChange} checked={this.props.readOnly}
className="ds-config-checkbox" id="readOnly" /><label
- htmlFor="readOnly" className="ds-label"
title="Put database in Read-Only mode (nsslapd-readonly)."> Database
Read-Only Mode</label>
- </div>
- <div>
- <input type="checkbox"
onChange={this.props.handleChange} checked={this.props.requireIndex}
className="ds-config-checkbox" id="requireIndex" /><label
- htmlFor="requireIndex"
className="ds-label" title="Block unindexed searches on this suffix
(nsslapd-require-index)."> Block Unindexed Searches</label>
- </div>
- </div>
- <div className="ds-save-btn">
+ <Form horizontal>
+ <Row className="ds-margin-top-lg" title="Put
database in Read-Only mode (nsslapd-readonly).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Database Read-Only Mode
+ </Col>
+ <Col sm={8}>
+ <input type="checkbox"
onChange={this.props.handleChange} checked={this.props.readOnly}
className="ds-config-checkbox" id="readOnly" />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="Block
unindexed searches on this suffix (nsslapd-require-index).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Block Unindexed Searches
+ </Col>
+ <Col sm={8}>
+ <input type="checkbox"
onChange={this.props.handleChange} checked={this.props.requireIndex}
className="ds-config-checkbox" id="requireIndex" />
+ </Col>
+ </Row>
+ </Form>
+ <div className="ds-margin-top-lg">
<button className="btn btn-primary save-button"
onClick={this.props.saveHandler}>Save Configuration</button>
</div>
</div>
diff --git a/src/cockpit/389-console/src/lib/database/vlvIndexes.jsx
b/src/cockpit/389-console/src/lib/database/vlvIndexes.jsx
index a4df2b9..582bfd2 100644
--- a/src/cockpit/389-console/src/lib/database/vlvIndexes.jsx
+++ b/src/cockpit/389-console/src/lib/database/vlvIndexes.jsx
@@ -517,9 +517,9 @@ export class VLVIndexes extends React.Component {
<Row>
<Col sm={11} key={vlvItem.dn}>
- <p key={vlvItem.dn + "-p"}><label
className="ds-vlv-label">Base</label>{vlvItem.attrs.vlvbase[0]}</p>
- <p><label
className="ds-vlv-label">Filter</label>{vlvItem.attrs.vlvfilter[0]}</p>
- <p><label
className="ds-vlv-label">Scope</label>{this.getScopeKey(vlvItem.attrs.vlvscope[0])}</p>
+ <p key={vlvItem.dn + "-p"}><label
className="ds-divider-lrg">Base</label>{vlvItem.attrs.vlvbase[0]}</p>
+ <p><label
className="ds-divider-lrg">Filter</label>{vlvItem.attrs.vlvfilter[0]}</p>
+ <p><label
className="ds-divider-lrg">Scope</label>{this.getScopeKey(vlvItem.attrs.vlvscope[0])}</p>
<hr />
{
vlvItem.sorts.map(sort => {
@@ -529,7 +529,7 @@ export class VLVIndexes extends React.Component {
} else {
indexState = <font size="1"
color="#4dac26"><b>Uses:
</b>{sort.attrs.vlvuses[0]}</font>;
}
- return (<p key={sort.dn +
sort.attrs.vlvsort[0]}><label
className="ds-vlv-label">Sort</label>{sort.attrs.vlvsort[0]}
({indexState})</p>);
+ return (<p key={sort.dn +
sort.attrs.vlvsort[0]}><label
className="ds-divider-lrg">Sort</label>{sort.attrs.vlvsort[0]}
({indexState})</p>);
})
}
</Col>
@@ -831,8 +831,7 @@ class AddVLVModal extends React.Component {
{nameInput}
</Col>
</Row>
- <p />
- <Row>
+ <Row className="ds-margin-top">
<Col sm={3}>
<ControlLabel>Search Base</ControlLabel>
</Col>
@@ -841,8 +840,7 @@ class AddVLVModal extends React.Component {
onChange={handleChange} type="text"
id="vlvBase" defaultValue={base} />
</Col>
</Row>
- <p />
- <Row>
+ <Row className="ds-margin-top">
<Col sm={3}>
<ControlLabel>Search
Filter</ControlLabel>
</Col>
@@ -851,16 +849,14 @@ class AddVLVModal extends React.Component {
onChange={handleChange} type="text"
id="vlvFilter" defaultValue={filter} />
</Col>
</Row>
- <p />
{vlvscope}
<hr />
<div>
- <p />
- <div>
+ <div className="ds-margin-top">
{sortTable}
- <p />
<Typeahead
multiple
+ className="ds-margin-top"
id="vlvsortindex"
onChange={values => {
this.handleTypeaheadChange(values);
@@ -870,8 +866,7 @@ class AddVLVModal extends React.Component {
placeholder="Start typing attribute names to
create a sort index"
ref={(typeahead) => { this.typeahead =
typeahead }}
/>
- <p />
- <button type="button"
onClick={this.updateSorts}>Add Sort Index</button>
+ <button className="ds-margin-top"
type="button" onClick={this.updateSorts}>Add Sort Index</button>
</div>
</div>
<hr />
diff --git a/src/cockpit/389-console/src/lib/monitor/accesslog.jsx
b/src/cockpit/389-console/src/lib/monitor/accesslog.jsx
index e373c08..42053f4 100644
--- a/src/cockpit/389-console/src/lib/monitor/accesslog.jsx
+++ b/src/cockpit/389-console/src/lib/monitor/accesslog.jsx
@@ -17,6 +17,10 @@ export class AccessLogMonitor extends React.Component {
textarea.scrollTop = textarea.scrollHeight;
}
+ componentDidMount() {
+ this.props.enableTree();
+ }
+
render() {
let spinner = "";
if (this.props.reloading) {
@@ -58,7 +62,7 @@ export class AccessLogMonitor extends React.Component {
</div>;
return (
- <div id="monitor-log-access-page"
className="container-fluid">
+ <div id="monitor-log-access-page">
<Row>
<Col sm={3}>
<ControlLabel className="ds-suffix-header">
@@ -73,8 +77,7 @@ export class AccessLogMonitor extends React.Component {
{spinner}
</Col>
</Row>
- <p />
- <Row>
+ <Row className="ds-margin-top-lg">
<Col sm={6}>
{selectLines}
</Col>
@@ -102,6 +105,7 @@ AccessLogMonitor.propTypes = {
reloading: PropTypes.bool,
refreshing: PropTypes.bool,
lines: PropTypes.string,
+ enableTree: PropTypes.func,
};
AccessLogMonitor.defaultProps = {
@@ -111,7 +115,8 @@ AccessLogMonitor.defaultProps = {
reload: noop,
reloading: false,
refreshing: false,
- line: "50"
+ line: "50",
+ enableTree: noop,
};
export default AccessLogMonitor;
diff --git a/src/cockpit/389-console/src/lib/monitor/auditfaillog.jsx
b/src/cockpit/389-console/src/lib/monitor/auditfaillog.jsx
index d533fcc..b2ddca9 100644
--- a/src/cockpit/389-console/src/lib/monitor/auditfaillog.jsx
+++ b/src/cockpit/389-console/src/lib/monitor/auditfaillog.jsx
@@ -17,6 +17,10 @@ export class AuditFailLogMonitor extends React.Component {
textarea.scrollTop = textarea.scrollHeight;
}
+ componentDidMount() {
+ this.props.enableTree();
+ }
+
render() {
let spinner = "";
if (this.props.reloading) {
@@ -58,9 +62,9 @@ export class AuditFailLogMonitor extends React.Component {
</div>;
return (
- <div id="monitor-log-auditfail-page"
className="container-fluid">
+ <div id="monitor-log-auditfail-page">
<Row>
- <Col sm={3}>
+ <Col sm={4}>
<ControlLabel className="ds-suffix-header">
Audit Failure Log
<Icon className="ds-left-margin ds-refresh"
@@ -69,12 +73,11 @@ export class AuditFailLogMonitor extends React.Component {
/>
</ControlLabel>
</Col>
- <Col sm={9} className="ds-float-left">
+ <Col sm={8} className="ds-float-left">
{spinner}
</Col>
</Row>
- <p />
- <Row>
+ <Row className="ds-margin-top-lg">
<Col sm={6}>
{selectLines}
</Col>
@@ -102,6 +105,7 @@ AuditFailLogMonitor.propTypes = {
reloading: PropTypes.bool,
refreshing: PropTypes.bool,
lines: PropTypes.string,
+ enableTree: PropTypes.func,
};
AuditFailLogMonitor.defaultProps = {
@@ -111,7 +115,8 @@ AuditFailLogMonitor.defaultProps = {
reload: noop,
reloading: false,
refreshing: false,
- line: "50"
+ line: "50",
+ enableTree: noop,
};
export default AuditFailLogMonitor;
diff --git a/src/cockpit/389-console/src/lib/monitor/auditlog.jsx
b/src/cockpit/389-console/src/lib/monitor/auditlog.jsx
index ae84c6b..8d1c9f3 100644
--- a/src/cockpit/389-console/src/lib/monitor/auditlog.jsx
+++ b/src/cockpit/389-console/src/lib/monitor/auditlog.jsx
@@ -17,6 +17,10 @@ export class AuditLogMonitor extends React.Component {
textarea.scrollTop = textarea.scrollHeight;
}
+ componentDidMount() {
+ this.props.enableTree();
+ }
+
render() {
let spinner = "";
if (this.props.reloading) {
@@ -58,9 +62,9 @@ export class AuditLogMonitor extends React.Component {
</div>;
return (
- <div id="monitor-log-audit-page"
className="container-fluid">
+ <div id="monitor-log-audit-page">
<Row>
- <Col sm={3}>
+ <Col sm={4}>
<ControlLabel className="ds-suffix-header">
Audit Log
<Icon className="ds-left-margin ds-refresh"
@@ -69,12 +73,11 @@ export class AuditLogMonitor extends React.Component {
/>
</ControlLabel>
</Col>
- <Col sm={9} className="ds-float-left">
+ <Col sm={8} className="ds-float-left">
{spinner}
</Col>
</Row>
- <p />
- <Row>
+ <Row className="ds-margin-top-lg">
<Col sm={6}>
{selectLines}
</Col>
@@ -102,6 +105,7 @@ AuditLogMonitor.propTypes = {
reloading: PropTypes.bool,
refreshing: PropTypes.bool,
lines: PropTypes.string,
+ enableTree: PropTypes.func,
};
AuditLogMonitor.defaultProps = {
@@ -111,7 +115,8 @@ AuditLogMonitor.defaultProps = {
reload: noop,
reloading: false,
refreshing: false,
- line: "50"
+ line: "50",
+ enableTree: noop,
};
export default AuditLogMonitor;
diff --git a/src/cockpit/389-console/src/lib/monitor/chainingMonitor.jsx
b/src/cockpit/389-console/src/lib/monitor/chainingMonitor.jsx
index 9c108ff..717e03d 100644
--- a/src/cockpit/389-console/src/lib/monitor/chainingMonitor.jsx
+++ b/src/cockpit/389-console/src/lib/monitor/chainingMonitor.jsx
@@ -6,9 +6,14 @@ import {
Col,
ControlLabel,
Icon,
+ noop
} from "patternfly-react";
export class ChainingMonitor extends React.Component {
+ componentDidMount() {
+ this.props.enableTree();
+ }
+
render() {
return (
<div id="monitor-server-page"
className="container-fluid">
@@ -23,9 +28,7 @@ export class ChainingMonitor extends React.Component {
</ControlLabel>
</Col>
</Row>
- <p />
- <hr />
- <div>
+ <div className="ds-margin-top-lg">
<Row className="ds-margin-top">
<Col sm={3}>
<ControlLabel>
@@ -165,13 +168,15 @@ export class ChainingMonitor extends React.Component {
ChainingMonitor.propTypes = {
suffix: PropTypes.string,
bename: PropTypes.string,
- data: PropTypes.object
+ data: PropTypes.object,
+ enableTree: PropTypes.func,
};
ChainingMonitor.defaultProps = {
suffix: "",
bename: "",
- data: {}
+ data: {},
+ enableTree: noop,
};
export default ChainingMonitor;
diff --git a/src/cockpit/389-console/src/lib/monitor/dbMonitor.jsx
b/src/cockpit/389-console/src/lib/monitor/dbMonitor.jsx
index f5449dc..ea5dcd8 100644
--- a/src/cockpit/389-console/src/lib/monitor/dbMonitor.jsx
+++ b/src/cockpit/389-console/src/lib/monitor/dbMonitor.jsx
@@ -12,6 +12,7 @@ import {
Row,
Col,
ControlLabel,
+ Form,
Icon,
noop,
} from "patternfly-react";
@@ -22,10 +23,15 @@ export class DatabaseMonitor extends React.Component {
super(props);
this.state = {
activeKey: 1,
+ disableTabs: false,
};
this.handleNavSelect = this.handleNavSelect.bind(this);
}
+ componentDidMount() {
+ this.props.enableTree();
+ }
+
handleNavSelect(key) {
this.setState({ activeKey: key });
}
@@ -82,7 +88,7 @@ export class DatabaseMonitor extends React.Component {
}
return (
- <div id="db-content" className="container-fluid">
+ <div id="db-content">
<Row>
<Col sm={12} className="ds-word-wrap">
<ControlLabel className="ds-suffix-header">
@@ -94,7 +100,7 @@ export class DatabaseMonitor extends React.Component {
</ControlLabel>
</Col>
</Row>
- <TabContainer id="basic-tabs-pf"
onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>
+ <TabContainer className="ds-margin-top-lg"
id="basic-tabs-pf" onSelect={this.handleNavSelect}
activeKey={this.state.activeKey}>
<div>
<Nav bsClass="nav nav-tabs nav-tabs-pf">
<NavItem eventKey={1}>
@@ -125,83 +131,70 @@ export class DatabaseMonitor extends React.Component {
<b className="ds-left-margin">DB
Cache Hit Ratio</b>
</div>
<hr />
- <div>
+ <Form horizontal>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- Database Cache Hit Ratio
- </ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Database Cache Hit Ratio
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.dbcachehitratio} size="28" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- Database Cache Tries
- </ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Database Cache Tries
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.dbcachetries} size="28" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- Database Cache Hits
- </ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Database Cache Hits
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.dbcachehits} size="28" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- Cache Pages Read
- </ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Cache Pages Read
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.dbcachepagein} size="28" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- Cache Pages Written
- </ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Cache Pages Written
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.dbcachepageout} size="28" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- Read-Only Page Evictions
- </ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Read-Only Page Evictions
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.dbcacheroevict} size="28" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- Read-Write Page Evictions
- </ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Read-Write Page Evictions
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.dbcacherwevict} size="28" readOnly />
</Col>
</Row>
- </div>
+ </Form>
</TabPane>
<TabPane eventKey={2}>
<div className="ds-margin-top-lg">
<div className="ds-container">
+ <div className="ds-divider" />
<div className="ds-left-margin">
<DonutChart
id="monitor-db-cache-ndn-hitratio-chart"
@@ -219,6 +212,8 @@ export class DatabaseMonitor extends React.Component {
/>
<b>NDN Cache Hit Ratio</b>
</div>
+ <div className="ds-divider" />
+ <div className="ds-divider" />
<div className="ds-chart-right">
<PieChart
id="monitor-db-cache-ndn-util-chart"
@@ -243,6 +238,7 @@ export class DatabaseMonitor extends React.Component {
}}
title={{type: 'pie'}}
legend={{show: true, position:
'right'}}
+ unloadBeforeLoad
/>
<b>NDN Cache Utilization</b>
<div>
@@ -251,98 +247,80 @@ export class DatabaseMonitor extends React.Component {
</div>
</div>
<hr />
- <div>
+ <Form horizontal>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- NDN Cache Hit Ratio
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ NDN Cache Hit Ratio
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.normalizeddncachehitratio} size="28" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- NDN Cache Tries
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ NDN Cache Tries
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.normalizeddncachetries} size="28" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- NDN Cache Hits
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ NDN Cache Hits
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.normalizeddncachehits} size="28" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- NDN Cache Evictions
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ NDN Cache Evictions
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.normalizeddncacheevictions} size="28" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- NDN Cache Max Size
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ NDN Cache Max Size
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.maxnormalizeddncachesize} size="28" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- NDN Current Cache Size
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ NDN Current Cache Size
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.currentnormalizeddncachesize} size="28" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- NDN Cache DN Count
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ NDN Cache DN Count
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.currentnormalizeddncachecount} size="28" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- NDN Cache Thread Size
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ NDN Cache Thread Size
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.normalizeddncachethreadsize} size="28" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- NDN Cache Thread Slots
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ NDN Cache Thread Slots
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.normalizeddncachethreadslots} size="28" readOnly />
</Col>
</Row>
- </div>
+ </Form>
</div>
</TabPane>
</TabContent>
@@ -357,12 +335,14 @@ export class DatabaseMonitor extends React.Component {
DatabaseMonitor.propTypes = {
data: PropTypes.object,
- reload: PropTypes.func
+ reload: PropTypes.func,
+ enableTree: PropTypes.func,
};
DatabaseMonitor.defaultProps = {
data: {},
- reload: noop
+ reload: noop,
+ enableTree: noop,
};
export default DatabaseMonitor;
diff --git a/src/cockpit/389-console/src/lib/monitor/errorlog.jsx
b/src/cockpit/389-console/src/lib/monitor/errorlog.jsx
index 335a6d6..b8c28fc 100644
--- a/src/cockpit/389-console/src/lib/monitor/errorlog.jsx
+++ b/src/cockpit/389-console/src/lib/monitor/errorlog.jsx
@@ -17,6 +17,10 @@ export class ErrorLogMonitor extends React.Component {
textarea.scrollTop = textarea.scrollHeight;
}
+ componentDidMount() {
+ this.props.enableTree();
+ }
+
render() {
let spinner = "";
if (this.props.reloading) {
@@ -58,7 +62,7 @@ export class ErrorLogMonitor extends React.Component {
</div>;
return (
- <div id="monitor-log-errors-page"
className="container-fluid">
+ <div id="monitor-log-errors-page">
<Row>
<Col sm={3}>
<ControlLabel className="ds-suffix-header">
@@ -73,8 +77,7 @@ export class ErrorLogMonitor extends React.Component {
{spinner}
</Col>
</Row>
- <p />
- <Row>
+ <Row className="ds-margin-top-lg">
<Col sm={5}>
{selectLines}
</Col>
@@ -122,8 +125,8 @@ ErrorLogMonitor.propTypes = {
handleSevLevel: PropTypes.func,
refreshing: PropTypes.bool,
handleRefresh: PropTypes.func,
- lines: PropTypes.string
-
+ lines: PropTypes.string,
+ enableTree: PropTypes.func,
};
ErrorLogMonitor.defaultProps = {
@@ -134,7 +137,8 @@ ErrorLogMonitor.defaultProps = {
handleSevLevel: noop,
refreshing: false,
handleRefresh: noop,
- lines: "50"
+ lines: "50",
+ enableTree: noop,
};
export default ErrorLogMonitor;
diff --git a/src/cockpit/389-console/src/lib/monitor/monitorModals.jsx
b/src/cockpit/389-console/src/lib/monitor/monitorModals.jsx
index 5593cb0..519acb7 100644
--- a/src/cockpit/389-console/src/lib/monitor/monitorModals.jsx
+++ b/src/cockpit/389-console/src/lib/monitor/monitorModals.jsx
@@ -76,8 +76,7 @@ class ReplLoginModal extends React.Component {
/>
</Col>
</Row>
- <p />
- <Row>
+ <Row className="ds-margin-top">
<Col sm={3}>
<ControlLabel>
Password
@@ -559,8 +558,7 @@ class ConflictCompareModal extends React.Component {
<Row>
<textarea
className="ds-conflict" value={conflict} readOnly />
</Row>
- <p />
- <Row>
+ <Row className="ds-margin-top">
<p>Child Entries:
<b>{conflictChildren}</b></p>
</Row>
</Col>
@@ -572,8 +570,7 @@ class ConflictCompareModal extends React.Component {
<Row>
<textarea
className="ds-conflict" value={valid} readOnly />
</Row>
- <p />
- <Row>
+ <Row className="ds-margin-top">
<p>Child Entries:
<b>{validChildren}</b></p>
</Row>
</Col>
@@ -598,8 +595,7 @@ class ConflictCompareModal extends React.Component {
<input onChange={handleConvertChange}
type="text" placeholder="Enter new RDN here" size="30"
/>
</Col>
</Row>
- <p />
- <Row>
+ <Row className="ds-margin-top">
<h4>Or, you can replace, or swap, the
<b>Valid Entry</b> (and its child entries) with the <b>Conflict
Entry</b></h4>
</Row>
<Row>
@@ -615,8 +611,7 @@ class ConflictCompareModal extends React.Component {
</Button>
</Col>
</Row>
- <p />
- <Row>
+ <Row className="ds-margin-top">
<h4>Or, you can delete the <b>Conflict
Entry</b></h4>
</Row>
<Row>
diff --git a/src/cockpit/389-console/src/lib/monitor/replMonitor.jsx
b/src/cockpit/389-console/src/lib/monitor/replMonitor.jsx
index ca2e354..ffbb5cc 100644
--- a/src/cockpit/389-console/src/lib/monitor/replMonitor.jsx
+++ b/src/cockpit/389-console/src/lib/monitor/replMonitor.jsx
@@ -105,6 +105,10 @@ export class ReplMonitor extends React.Component {
this.closeConfirmSwapConflict = this.closeConfirmSwapConflict.bind(this);
}
+ componentDidMount() {
+ this.props.enableTree();
+ }
+
convertConflict (dn) {
let cmd = ["dsconf", "-j",
"ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
"repl-conflict", "convert", dn, "--new-rdn=" +
this.state.convertRDN];
@@ -752,8 +756,8 @@ export class ReplMonitor extends React.Component {
let conflictsNavTitle = 'Conflicts <font size="1">(' +
(conflictEntries.length + glueEntries.length) + ')</font>';
return (
- <div id="monitor-suffix-page" className="container-fluid
ds-tab-table">
- <TabContainer id="basic-tabs-pf"
onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>
+ <div id="monitor-suffix-page"
className="ds-tab-table">
+ <TabContainer className="ds-margin-top-lg"
id="basic-tabs-pf" onSelect={this.handleNavSelect}
activeKey={this.state.activeKey}>
<div>
<Nav bsClass="nav nav-tabs nav-tabs-pf">
<NavItem eventKey={1}>
@@ -777,8 +781,8 @@ export class ReplMonitor extends React.Component {
pokeAgmt={this.pokeAgmt}
viewAgmt={this.showAgmtModal}
/>
- <p />
<Button
+ className="ds-margin-top"
bsStyle="primary"
onClick={this.getLagReportCreds}
title="Display report that shows the lag
time and replication status of each agreement in relationship to its replica"
@@ -907,6 +911,7 @@ ReplMonitor.propTypes = {
reloadAgmts: PropTypes.func,
reloadWinsyncAgmts: PropTypes.func,
reloadConflicts: PropTypes.func,
+ enableTree: PropTypes.func,
};
ReplMonitor.defaultProps = {
@@ -917,6 +922,7 @@ ReplMonitor.defaultProps = {
reloadAgmts: noop,
reloadWinsyncAgmts: noop,
reloadConflicts: noop,
+ enableTree: noop,
};
export default ReplMonitor;
diff --git a/src/cockpit/389-console/src/lib/monitor/serverMonitor.jsx
b/src/cockpit/389-console/src/lib/monitor/serverMonitor.jsx
index 503b57d..f87fb00 100644
--- a/src/cockpit/389-console/src/lib/monitor/serverMonitor.jsx
+++ b/src/cockpit/389-console/src/lib/monitor/serverMonitor.jsx
@@ -14,6 +14,7 @@ import {
Row,
Col,
ControlLabel,
+ Form,
Icon,
noop
} from "patternfly-react";
@@ -27,6 +28,10 @@ export class ServerMonitor extends React.Component {
this.handleNavSelect = this.handleNavSelect.bind(this);
}
+ componentDidMount() {
+ this.props.enableTree();
+ }
+
handleNavSelect(key) {
this.setState({ activeKey: key });
}
@@ -39,7 +44,7 @@ export class ServerMonitor extends React.Component {
let uptime = get_date_diff(startTime, currTime);
return (
- <div id="monitor-server-page"
className="container-fluid">
+ <div id="monitor-server-page">
<Row>
<Col sm={12} className="ds-word-wrap">
<ControlLabel className="ds-suffix-header">
@@ -52,7 +57,7 @@ export class ServerMonitor extends React.Component {
</Col>
</Row>
- <TabContainer id="server-tabs-pf"
onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>
+ <TabContainer className="ds-margin-top-lg"
id="server-tabs-pf" onSelect={this.handleNavSelect}
activeKey={this.state.activeKey}>
<div>
<Nav bsClass="nav nav-tabs nav-tabs-pf">
<NavItem eventKey={1}>
@@ -65,75 +70,123 @@ export class ServerMonitor extends React.Component {
<TabContent>
<TabPane eventKey={1}>
- <div className="ds-container
ds-margin-top-lg">
- <div className="ds-inline">
- <div>
- <label
htmlFor="monitor-serverid" className="ds-label-xsm">Server
Instance</label><input type="text"
- className="ds-input"
id="monitor-serverid" value={"slapd-" + this.props.serverId}
size="50" readOnly />
- </div>
- <div>
- <label htmlFor="monitor-version"
className="ds-label-xsm">Version</label><input
type="text"
- className="ds-input"
id="monitor-version" value={this.props.data.version} size="50"
readOnly />
- </div>
- <div>
- <label
htmlFor="monitor-server-starttime" className="ds-label-xsm">Server
Started</label><input type="text"
- className="ds-input"
id="monitor-server-starttime" value={startDate} size="50" readOnly
/>
- </div>
- <div>
- <label
htmlFor="monitor-server-uptime" className="ds-label-xsm">Server
Uptime</label><input type="text"
- className="ds-input"
id="monitor-server-uptime" value={uptime} size="50" readOnly />
- </div>
- </div>
- </div>
+ <Form horizontal
className="ds-margin-top-lg">
+ <Row>
+ <Col componentClass={ControlLabel} sm={4}>
+ Server Instance
+ </Col>
+ <Col sm={8}>
+ <input type="text"
className="ds-input-auto" id="monitor-serverid"
value={"slapd-" + this.props.serverId} readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Version
+ </Col>
+ <Col sm={8}>
+ <input type="text"
className="ds-input-auto" id="monitor-version"
value={this.props.data.version} readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Server Started
+ </Col>
+ <Col sm={8}>
+ <input type="text"
className="ds-input-auto" id="monitor-server-starttime"
value={startDate} readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Server Uptime
+ </Col>
+ <Col sm={8}>
+ <input type="text"
className="ds-input-auto" id="monitor-server-uptime" value={uptime}
readOnly />
+ </Col>
+ </Row>
+ </Form>
<hr />
- <div className="ds-container">
- <div className="ds-inline">
- <div>
- <label
htmlFor="monitor-server-threads"
className="ds-monitor-label">Threads</label><input
type="text"
- className="ds-input"
id="monitor-server-threads" value={this.props.data.threads} size="12"
readOnly />
- </div>
- <div>
- <label
htmlFor="monitor-server-totalconnections"
className="ds-monitor-label">Total Connections</label><input
type="text"
- className="ds-input"
id="monitor-server-totalconnections" value={this.props.data.totalconnections}
size="12" readOnly />
- </div>
- <div>
- <label
htmlFor="monitor-server-currentconnections"
className="ds-monitor-label">Current Conections</label><input
type="text"
- className="ds-input"
id="monitor-server-currentconnections"
value={this.props.data.currentconnections} size="12" readOnly />
- </div>
- <div>
- <label
htmlFor="monitor-server-currentconnectionsatmaxthreads"
className="ds-monitor-label">Conns At Max Threads</label><input
type="text"
- className="ds-input"
id="monitor-server-currentconnectionsatmaxthreads"
value={this.props.data.currentconnectionsatmaxthreads} size="12" readOnly />
- </div>
- <div>
- <label
htmlFor="monitor-server-maxthreadsperconnhits"
className="ds-monitor-label">Conns Hit Max Threads</label><input
type="text"
- className="ds-input"
id="monitor-server-maxthreadsperconnhits"
value={this.props.data.maxthreadsperconnhits} size="12" readOnly />
- </div>
- </div>
- <div className="ds-divider" />
- <div className="ds-inline">
- <div>
- <label
htmlFor="monitor-server-readwaiters"
className="ds-monitor-label-med">Threads Waiting To
Read</label><input type="text"
- className="ds-input"
id="monitor-server-readwaiters" value={this.props.data.readwaiters}
size="12" readOnly />
- </div>
- <div>
- <label
htmlFor="monitor-server-opsinitiated"
className="ds-monitor-label-med">Operations Started</label><input
type="text"
- className="ds-input"
id="monitor-server-opsinitiated" value={this.props.data.opsinitiated}
size="12" readOnly />
- </div>
- <div>
- <label
htmlFor="monitor-server-opscompleted"
className="ds-monitor-label-med">Operations Completed</label><input
type="text"
- className="ds-input"
id="monitor-server-opscompleted" value={this.props.data.opscompleted}
size="12" readOnly />
- </div>
- <div>
- <label
htmlFor="monitor-server-entriessent"
className="ds-monitor-label-med">Entries Returned To
Clients</label><input type="text"
- className="ds-input"
id="monitor-server-entriessent" value={this.props.data.entriessent}
size="12" readOnly />
- </div>
- <div>
- <label
htmlFor="monitor-server-bytessent"
className="ds-monitor-label-med">Bytes Sent to Clients</label><input
type="text"
- className="ds-input"
id="monitor-server-bytessent" value={this.props.data.bytessent}
size="12" readOnly />
- </div>
-
- </div>
- </div>
+ <Form horizontal>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Worker Threads
+ </Col>
+ <Col sm={8}>
+ <input type="text"
className="ds-input-auto" id="monitor-server-threads"
value={this.props.data.threads} readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Threads Waiting To Read
+ </Col>
+ <Col sm={8}>
+ <input type="text"
className="ds-input-auto" id="monitor-server-readwaiters"
value={this.props.data.readwaiters} readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Conns At Max Threads
+ </Col>
+ <Col sm={8}>
+ <input type="text"
className="ds-input-auto"
id="monitor-server-currentconnectionsatmaxthreads"
value={this.props.data.currentconnectionsatmaxthreads} readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Conns Exceeded Max Threads
+ </Col>
+ <Col sm={8}>
+ <input type="text"
className="ds-input-auto" id="monitor-server-maxthreadsperconnhits"
value={this.props.data.maxthreadsperconnhits} readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Total Connections
+ </Col>
+ <Col sm={8}>
+ <input type="text"
className="ds-input-auto" id="monitor-server-totalconnections"
value={this.props.data.totalconnections} readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Current Conections
+ </Col>
+ <Col sm={8}>
+ <input type="text"
className="ds-input-auto" id="monitor-server-currentconnections"
value={this.props.data.currentconnections} readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Operations Started
+ </Col>
+ <Col sm={8}>
+ <input type="text"
className="ds-input-auto" id="monitor-server-opsinitiated"
value={this.props.data.opsinitiated} readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Operations Completed
+ </Col>
+ <Col sm={8}>
+ <input type="text"
className="ds-input-auto" id="monitor-server-opscompleted"
value={this.props.data.opscompleted} readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Entries Returned To Clients
+ </Col>
+ <Col sm={8}>
+ <input type="text"
className="ds-input-auto" id="monitor-server-entriessent"
value={this.props.data.entriessent} readOnly />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Bytes Sent to Clients
+ </Col>
+ <Col sm={8}>
+ <input type="text"
className="ds-input-auto" id="monitor-server-bytessent"
value={this.props.data.bytessent} readOnly />
+ </Col>
+ </Row>
+ </Form>
</TabPane>
<TabPane eventKey={2}>
<ConnectionTable conns={this.props.data.connection}
/>
@@ -149,13 +202,15 @@ export class ServerMonitor extends React.Component {
ServerMonitor.propTypes = {
serverId: PropTypes.string,
data: PropTypes.object,
- reload: PropTypes.func
+ reload: PropTypes.func,
+ enableTree: PropTypes.func,
};
ServerMonitor.defaultProps = {
serverId: "",
data: {},
- reload: noop
+ reload: noop,
+ enableTree: noop,
};
export default ServerMonitor;
diff --git a/src/cockpit/389-console/src/lib/monitor/snmpMonitor.jsx
b/src/cockpit/389-console/src/lib/monitor/snmpMonitor.jsx
index fd1a4f1..1d3eb5d 100644
--- a/src/cockpit/389-console/src/lib/monitor/snmpMonitor.jsx
+++ b/src/cockpit/389-console/src/lib/monitor/snmpMonitor.jsx
@@ -4,15 +4,20 @@ import "../../css/ds.css";
import {
Row,
Col,
+ Form,
Icon,
ControlLabel,
noop
} from "patternfly-react";
export class SNMPMonitor extends React.Component {
+ componentDidMount() {
+ this.props.enableTree();
+ }
+
render() {
return (
- <div className="container-fluid"
id="db-global-page">
+ <div id="snmp-page">
<Row>
<Col sm={12} className="ds-word-wrap">
<ControlLabel className="ds-suffix-header">
@@ -24,185 +29,185 @@ export class SNMPMonitor extends React.Component {
</ControlLabel>
</Col>
</Row>
- <div className="ds-margin-top-med">
+ <Form horizontal className="ds-margin-top-lg">
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>Anonymous Binds</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Anonymous Binds
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.anonymousbinds} size="10" readOnly />
</Col>
- <Col sm={3}>
- <ControlLabel>Referrals</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Referrals
</Col>
- <Col sm={2}>
+ <Col sm={3}>
<input type="text"
value={this.props.data.referrals} size="10" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>Unauthenticated
Binds</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Unauthenticated Binds
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.anonymousbinds} size="10" readOnly />
</Col>
- <Col sm={3}>
- <ControlLabel>Returned Referrals</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Returned Referrals
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.referralsreturned} size="10" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>Simple Auth Binds</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Simple Auth Binds
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.simpleauthbinds} size="10" readOnly />
</Col>
- <Col sm={3}>
- <ControlLabel>Bind Security
Errors</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Bind Security Errors
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.bindsecurityerrors} size="10" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>Strong Auth Binds</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Strong Auth Binds
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.strongauthbinds} size="10" readOnly />
</Col>
- <Col sm={3}>
- <ControlLabel>Security Errors</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Security Errors
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.securityerrors} size="10" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>Initiated
Operations</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Initiated Operations
</Col>
<Col sm={2}>
<input type="text" value={this.props.data.inops}
size="10" readOnly />
</Col>
- <Col sm={3}>
- <ControlLabel>Errors</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Errors
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.errors} size="10" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>Compare Operations</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Compare Operations
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.compareops} size="10" readOnly />
</Col>
- <Col sm={3}>
- <ControlLabel>Current Connections</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Current Connections
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.connections} size="10" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>Add Operations</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Add Operations
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.addentryops} size="10" readOnly />
</Col>
- <Col sm={3}>
- <ControlLabel>Total Connections</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Total Connections
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.connectionseq} size="10" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>Delete Operations</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Delete Operations
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.removeentryops} size="10" readOnly />
</Col>
- <Col sm={3}>
- <ControlLabel>Conns in Max
Threads</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Conns in Max Threads
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.connectionsinmaxthreads} size="10" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>Modify Operation</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Modify Operation
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.modifyentryops} size="10" readOnly />
</Col>
- <Col sm={3}>
- <ControlLabel>Conns Hit Max
Threads</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Conns Exceeded Max Threads
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.connectionsmaxthreadscount} size="10" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>ModRDN Operations</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ ModRDN Operations
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.modifyrdnops} size="10" readOnly />
</Col>
- <Col sm={3}>
- <ControlLabel>Bytes Received</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Bytes Received
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.bytesrecv} size="10" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>Search Operations</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Search Operations
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.searchops} size="10" readOnly />
</Col>
- <Col sm={3}>
- <ControlLabel>Bytes Sent</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Bytes Sent
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.bytessent} size="10" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>One Level Searches</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ One Level Searches
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.onelevelsearchops} size="10" readOnly />
</Col>
- <Col sm={3}>
- <ControlLabel>Entries Sent</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Entries Sent
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.entriesreturned} size="10" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>Whole Tree Searches</ControlLabel>
+ <Col componentClass={ControlLabel} sm={3}>
+ Whole Tree Searches
</Col>
<Col sm={2}>
<input type="text"
value={this.props.data.wholesubtreesearchops} size="10" readOnly />
</Col>
</Row>
- </div>
+ </Form>
</div>
);
}
@@ -212,12 +217,14 @@ export class SNMPMonitor extends React.Component {
SNMPMonitor.propTypes = {
data: PropTypes.object,
- reload: PropTypes.func
+ reload: PropTypes.func,
+ enableTree: PropTypes.func,
};
SNMPMonitor.defaultProps = {
data: {},
- reload: noop
+ reload: noop,
+ enableTree: noop,
};
export default SNMPMonitor;
diff --git a/src/cockpit/389-console/src/lib/monitor/suffixMonitor.jsx
b/src/cockpit/389-console/src/lib/monitor/suffixMonitor.jsx
index f859f57..2174290 100644
--- a/src/cockpit/389-console/src/lib/monitor/suffixMonitor.jsx
+++ b/src/cockpit/389-console/src/lib/monitor/suffixMonitor.jsx
@@ -7,6 +7,7 @@ import {
ControlLabel,
Row,
Col,
+ Form,
Icon,
Nav,
NavItem,
@@ -26,6 +27,10 @@ export class SuffixMonitor extends React.Component {
this.handleNavSelect = this.handleNavSelect.bind(this);
}
+ componentDidMount() {
+ this.props.enableTree();
+ }
+
handleNavSelect(key) {
this.setState({ activeKey: key });
}
@@ -112,7 +117,7 @@ export class SuffixMonitor extends React.Component {
}
return (
- <div id="monitor-suffix-page"
className="container-fluid">
+ <div id="monitor-suffix-page">
<Row>
<Col sm={12} className="ds-word-wrap">
<ControlLabel className="ds-suffix-header">
@@ -124,8 +129,7 @@ export class SuffixMonitor extends React.Component {
</ControlLabel>
</Col>
</Row>
- <p />
- <TabContainer id="basic-tabs-pf"
onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>
+ <TabContainer className="ds-margin-top-lg"
id="basic-tabs-pf" onSelect={this.handleNavSelect}
activeKey={this.state.activeKey}>
<div>
<Nav bsClass="nav nav-tabs nav-tabs-pf">
<NavItem eventKey={1}>
@@ -159,6 +163,8 @@ export class SuffixMonitor extends React.Component {
/>
<b>Entry Cache Hit Ratio</b>
</div>
+ <div className="ds-divider" />
+ <div className="ds-divider" />
<div className="ds-chart-right"
title="How much of the allocated cache space is used (max size vs current size). If
the chart is RED then you should to increase the max cache size because the cache hit
ratio is below 90%">
<PieChart
id="monitor-entry-util-pie"
@@ -184,6 +190,7 @@ export class SuffixMonitor extends React.Component {
}}
title={{type: 'pie'}}
legend={{show: true, position:
'right'}}
+ unloadBeforeLoad
/>
<b>Entry Cache Utilization</b>
<div>
@@ -191,80 +198,65 @@ export class SuffixMonitor extends React.Component {
</div>
</div>
</div>
- <p />
<hr />
- <div>
+ <Form horizontal>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- Entry Cache Hit Ratio
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ Entry Cache Hit Ratio
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.entrycachehitratio} size="35" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- Entry Cache Tries
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ Entry Cache Tries
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.entrycachetries} size="35" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- Entry Cache Hits
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ Entry Cache Hits
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.entrycachehits} size="35" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- Entry Cache Max Size
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ Entry Cache Max Size
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.maxentrycachesize} size="35" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- Entry Cache Current Size
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ Entry Cache Current Size
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.currententrycachesize} size="35" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- Entry Cache Max Entries
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ Entry Cache Max Entries
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.maxentrycachecount} size="35" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- Entry Cache Count
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ Entry Cache Count
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.currententrycachecount} size="35" readOnly />
</Col>
</Row>
- </div>
+ </Form>
</div>
</TabPane>
@@ -289,6 +281,8 @@ export class SuffixMonitor extends React.Component {
/>
<b
className="ds-left-margin">DN Cache Hit Ratio</b>
</div>
+ <div className="ds-divider" />
+ <div className="ds-divider" />
<div className="ds-chart-right"
title="How much of the allocated cache space is used (max size vs current size). If
the chart is RED then you should to increase the max cache size because the cache hit
ratio is below 90%">
<PieChart
id="monitor-entry-util-pie"
@@ -314,6 +308,7 @@ export class SuffixMonitor extends React.Component {
}}
title={{type: 'pie'}}
legend={{show: true, position:
'right'}}
+ unloadBeforeLoad
/>
<div
className="ds-left-margin">
<b>DN Cache Utilization</b>
@@ -324,78 +319,64 @@ export class SuffixMonitor extends React.Component {
</div>
</div>
<hr />
- <div>
+ <Form horizontal>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- DN Cache Hit Ratio
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ DN Cache Hit Ratio
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.dncachehitratio} size="35" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- DN Cache Tries
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ DN Cache Tries
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.dncachetries} size="35" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- DN Cache Hits
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ DN Cache Hits
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.dncachehits} size="35" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- DN Cache Max Size
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ DN Cache Max Size
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.maxdncachesize} size="35" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- DN Cache Current Size
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ DN Cache Current Size
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.currentdncachesize} size="35" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- DN Cache Max Count
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ DN Cache Max Count
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.maxdncachecount} size="35" readOnly />
</Col>
</Row>
<Row className="ds-margin-top">
- <Col sm={3}>
- <ControlLabel>
- DN Cache Current Count
- </ControlLabel>
+ <Col componentClass={ControlLabel}
sm={3}>
+ DN Cache Current Count
</Col>
<Col sm={3}>
<input type="text"
value={this.props.data.currentdncachecount} size="35" readOnly />
</Col>
</Row>
- </div>
+ </Form>
</div>
</TabPane>
</TabContent>
@@ -411,6 +392,7 @@ SuffixMonitor.propTypes = {
data: PropTypes.object,
bename: PropTypes.string,
reload: PropTypes.func,
+ enableTree: PropTypes.func,
};
SuffixMonitor.defaultProps = {
@@ -418,6 +400,7 @@ SuffixMonitor.defaultProps = {
data: {},
bename: "",
reload: noop,
+ enableTree: noop,
};
export default SuffixMonitor;
diff --git a/src/cockpit/389-console/src/lib/notifications.jsx
b/src/cockpit/389-console/src/lib/notifications.jsx
index ad0b87f..990cdc7 100644
--- a/src/cockpit/389-console/src/lib/notifications.jsx
+++ b/src/cockpit/389-console/src/lib/notifications.jsx
@@ -1,8 +1,16 @@
import React from "react";
import PropTypes from "prop-types";
import {
+ Button,
+ Checkbox,
+ Col,
+ Form,
Icon,
MessageDialog,
+ Modal,
+ noop,
+ Row,
+ Spinner,
TimedToastNotification,
ToastNotificationList
} from "patternfly-react";
@@ -103,4 +111,119 @@ class ConfirmPopup extends React.Component {
}
}
+export class DoubleConfirmModal extends React.Component {
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ handleChange,
+ actionHandler,
+ checked,
+ spinning,
+ item,
+ mTitle,
+ mMsg,
+ mSpinningMsg,
+ mBtnName,
+ } = this.props;
+ let spinner = "";
+ let saveDisabled = true;
+
+ if (spinning) {
+ spinner =
+ <Row>
+ <div className="ds-margin-top ds-modal-spinner">
+ <Spinner loading inline size="md"
/>{mSpinningMsg}
+ </div>
+ </Row>;
+ saveDisabled = true;
+ }
+ if (checked) {
+ saveDisabled = false;
+ }
+
+ return (
+ <Modal show={showModal} onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+
+
<Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ {mTitle}
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal autoComplete="off">
+ <h4>{mMsg}</h4>
+ <h5 className="ds-center
ds-margin-top-xlg"><b>{item}</b></h5>
+ <Row className="ds-margin-top-xlg">
+ <Col sm={12} className="ds-center">
+ <Checkbox
+ id="modalChecked"
+ defaultChecked={checked}
+ onChange={handleChange}
+
+
<b>Yes</b>, I am sure.
+ </Checkbox>
+ </Col>
+ </Row>
+ {spinner}
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+
+ Cancel
+ </Button>
+ <Button
+ bsStyle="primary"
+ onClick={actionHandler}
+ disabled={saveDisabled}
+
+
{mBtnName}
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
+DoubleConfirmModal.propTypes = {
+ showModal: PropTypes.bool,
+ closeHandler: PropTypes.func,
+ handleChange: PropTypes.func,
+ actionHandler: PropTypes.func,
+ spinning: PropTypes.bool,
+ item: PropTypes.string,
+ checked: PropTypes.bool,
+ mTitle: PropTypes.string,
+ mMsg: PropTypes.string,
+ mSpinningMsg: PropTypes.string,
+ mBtnName: PropTypes.string,
+};
+
+DoubleConfirmModal.defaultProps = {
+ showModal: false,
+ closeHandler: noop,
+ handleChange: noop,
+ actionHandler: noop,
+ spinning: false,
+ item: "",
+ checked: false,
+ mTitle: "",
+ mMsg: "",
+ mSpinningMsg: "",
+ mBtnName: "",
+};
+
export { NotificationController, ConfirmPopup };
diff --git a/src/cockpit/389-console/src/lib/plugins/accountPolicy.jsx
b/src/cockpit/389-console/src/lib/plugins/accountPolicy.jsx
index 3225f5a..f2bc245 100644
--- a/src/cockpit/389-console/src/lib/plugins/accountPolicy.jsx
+++ b/src/cockpit/389-console/src/lib/plugins/accountPolicy.jsx
@@ -639,7 +639,7 @@ class AccountPolicy extends React.Component {
toggleLoadingHandler={this.props.toggleLoadingHandler}
<Row>
- <Col sm={6}>
+ <Col sm={10}>
<Form horizontal>
<FormGroup key="configArea"
controlId="configArea">
<Col
@@ -649,16 +649,15 @@ class AccountPolicy extends React.Component {
Shared Config Entry
</Col>
- <Col sm={6}>
+ <Col sm={7}>
<FormControl
type="text"
value={configArea}
onChange={this.handleFieldChange}
/>
</Col>
- <Col sm={3}>
+ <Col sm={2}>
<Button
- bsSize="large"
bsStyle="primary"
onClick={this.openModal}
diff --git
a/src/cockpit/389-console/src/lib/plugins/attributeUniqueness.jsx
b/src/cockpit/389-console/src/lib/plugins/attributeUniqueness.jsx
index 2a572ac..cc9ed22 100644
--- a/src/cockpit/389-console/src/lib/plugins/attributeUniqueness.jsx
+++ b/src/cockpit/389-console/src/lib/plugins/attributeUniqueness.jsx
@@ -680,7 +680,7 @@ class AttributeUniqueness extends React.Component {
toggleLoadingHandler={this.props.toggleLoadingHandler}
<Row>
- <Col sm={9}>
+ <Col sm={12}>
<AttrUniqConfigTable
rows={this.state.configRows}
editConfig={this.showEditConfigModal}
diff --git a/src/cockpit/389-console/src/lib/plugins/autoMembership.jsx
b/src/cockpit/389-console/src/lib/plugins/autoMembership.jsx
index b5079ea..aea83f6 100644
--- a/src/cockpit/389-console/src/lib/plugins/autoMembership.jsx
+++ b/src/cockpit/389-console/src/lib/plugins/autoMembership.jsx
@@ -1012,7 +1012,7 @@ class AutoMembership extends React.Component {
toggleLoadingHandler={this.props.toggleLoadingHandler}
<Row>
- <Col sm={9}>
+ <Col sm={12}>
<AutoMembershipDefinitionTable
rows={this.state.definitionRows}
editConfig={this.showEditDefinitionModal}
diff --git a/src/cockpit/389-console/src/lib/plugins/dna.jsx
b/src/cockpit/389-console/src/lib/plugins/dna.jsx
index 185ca1f..8e05995 100644
--- a/src/cockpit/389-console/src/lib/plugins/dna.jsx
+++ b/src/cockpit/389-console/src/lib/plugins/dna.jsx
@@ -908,7 +908,6 @@ class DNA extends React.Component {
</Col>
<Col sm={3}>
<Button
- bsSize="large"
bsStyle="primary"
onClick={this.showConfirmSharedSave}
@@ -1120,7 +1119,7 @@ class DNA
extends React.Component {
toggleLoadingHandler={this.props.toggleLoadingHandler}
<Row>
- <Col sm={9}>
+ <Col sm={12}>
<DNATable
rows={this.state.configRows}
editConfig={this.showEditConfigModal}
diff --git a/src/cockpit/389-console/src/lib/plugins/linkedAttributes.jsx
b/src/cockpit/389-console/src/lib/plugins/linkedAttributes.jsx
index a36ac21..eb6e425 100644
--- a/src/cockpit/389-console/src/lib/plugins/linkedAttributes.jsx
+++ b/src/cockpit/389-console/src/lib/plugins/linkedAttributes.jsx
@@ -461,7 +461,7 @@ class LinkedAttributes extends React.Component {
toggleLoadingHandler={this.props.toggleLoadingHandler}
<Row>
- <Col sm={9}>
+ <Col sm={12}>
<LinkedAttributesTable
rows={this.state.configRows}
editConfig={this.showEditConfigModal}
diff --git a/src/cockpit/389-console/src/lib/plugins/managedEntries.jsx
b/src/cockpit/389-console/src/lib/plugins/managedEntries.jsx
index 5433535..3c87169 100644
--- a/src/cockpit/389-console/src/lib/plugins/managedEntries.jsx
+++ b/src/cockpit/389-console/src/lib/plugins/managedEntries.jsx
@@ -672,7 +672,6 @@ class ManagedEntries extends React.Component {
</Col>
<Col sm={3}>
<Button
- bsSize="large"
bsStyle="primary"
onClick={this.openTemplateModal}
@@ -881,7 +880,7 @@ class
ManagedEntries extends React.Component {
/>
</Col>
</FormGroup>
- <Col sm={9}>
+ <Col sm={12}>
<ManagedEntriesTable
rows={configRows}
editConfig={this.showEditConfigModal}
diff --git a/src/cockpit/389-console/src/lib/plugins/memberOf.jsx
b/src/cockpit/389-console/src/lib/plugins/memberOf.jsx
index b816944..dba8fba 100644
--- a/src/cockpit/389-console/src/lib/plugins/memberOf.jsx
+++ b/src/cockpit/389-console/src/lib/plugins/memberOf.jsx
@@ -22,6 +22,7 @@ import "../../css/ds.css";
class MemberOf extends React.Component {
componentWillMount(prevProps) {
this.getObjectClasses();
+ this.getAttributes();
this.updateFields();
}
@@ -35,6 +36,7 @@ class MemberOf extends React.Component {
super(props);
this.getObjectClasses = this.getObjectClasses.bind(this);
+ this.getAttributes = this.getAttributes.bind(this);
this.updateFields = this.updateFields.bind(this);
this.handleFieldChange = this.handleFieldChange.bind(this);
this.handleCheckboxChange = this.handleCheckboxChange.bind(this);
@@ -49,6 +51,7 @@ class MemberOf extends React.Component {
this.state = {
objectClasses: [],
+ attributeTypes: [],
memberOfAttr: [],
memberOfGroupAttr: [],
@@ -135,6 +138,7 @@ class MemberOf extends React.Component {
openModal() {
this.getObjectClasses();
+ this.getAttributes();
if (!this.state.memberOfConfigEntry) {
this.setState({
configEntryModalShow: true,
@@ -502,9 +506,41 @@ class MemberOf extends React.Component {
});
}
+ getAttributes() {
+ const oc_cmd = [
+ "dsconf",
+ "-j",
+ "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId +
".socket",
+ "schema",
+ "attributetypes",
+ "list"
+ ];
+ log_cmd("getAttributes", "Get getAttributes", oc_cmd);
+ cockpit
+ .spawn(oc_cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const atContent = JSON.parse(content);
+ let attrs = [];
+ for (let content of atContent["items"]) {
+ attrs.push({
+ id: content.name,
+ label: content.name
+ });
+ }
+ this.setState({
+ attributeTypes: attrs
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification("error", `Failed to get
attributes - ${errMsg.desc}`);
+ });
+ }
+
render() {
const {
objectClasses,
+ attributeTypes,
memberOfAttr,
memberOfGroupAttr,
memberOfEntryScope,
@@ -725,13 +761,8 @@ class MemberOf extends React.Component {
});
}}
selected={configGroupAttr}
- newSelectionPrefix="Add a group
member: "
- options={[
- {
- id: "member",
- label: "member"
- }
- ]}
+ newSelectionPrefix="Add a group
member attribute: "
+ options={attributeTypes}
placeholder="Type a member group
attribute..."
/>
</Col>
@@ -870,7 +901,7 @@ class MemberOf extends React.Component {
toggleLoadingHandler={this.props.toggleLoadingHandler}
<Row>
- <Col sm={9}>
+ <Col sm={12}>
<Form horizontal>
<FormGroup
key="memberOfAttr"
@@ -884,7 +915,7 @@ class MemberOf extends React.Component {
Attribute
</Col>
- <Col sm={9}>
+ <Col sm={8}>
<Typeahead
allowNew
multiple
@@ -894,7 +925,7 @@ class MemberOf extends React.Component {
});
}}
selected={memberOfAttr}
- newSelectionPrefix="Add a member:
"
+ newSelectionPrefix="Add a member
attrbiute: "
options={[
{
id: "member",
@@ -925,7 +956,7 @@ class MemberOf extends React.Component {
Group Attribute
</Col>
- <Col sm={9}>
+ <Col sm={8}>
<Typeahead
allowNew
multiple
@@ -935,25 +966,8 @@ class MemberOf extends React.Component {
});
}}
selected={memberOfGroupAttr}
- newSelectionPrefix="Add a group member:
"
- options={[
- {
- id: "groupOfNames",
- label: "groupOfNames"
- },
- {
- id: "groupOfURLs",
- label: "groupOfURLs"
- },
- {
- id: "groupOfUniqueNames",
- label:
"groupOfUniqueNames"
- },
- {
- id: "groupOfCertificates",
- label:
"groupOfCertificates"
- }
- ]}
+ newSelectionPrefix="Add a group member
attribute: "
+ options={attributeTypes}
placeholder="Type a member group
attribute..."
/>
</Col>
@@ -962,7 +976,7 @@ class MemberOf extends React.Component {
</Col>
</Row>
<Row>
- <Col sm={9}>
+ <Col sm={12}>
<Form horizontal>
<FormGroup
key="memberOfEntryScope"
@@ -1028,7 +1042,7 @@ class MemberOf extends React.Component {
</Col>
</Row>
<Row>
- <Col sm={9}>
+ <Col sm={12}>
<Form horizontal>
<FormGroup
key="memberOfConfigEntry"
@@ -1050,7 +1064,6 @@ class MemberOf extends React.Component {
</Col>
<Col sm={3}>
<Button
- bsSize="large"
bsStyle="primary"
onClick={this.openModal}
@@ -1062,7 +1075,7 @@ class MemberOf
extends React.Component {
</Col>
</Row>
<Row>
- <Col sm={9}>
+ <Col sm={12}>
<Form horizontal>
<FormGroup controlId="memberOfAutoAddOC"
disabled={false}>
<Col
@@ -1071,7 +1084,7 @@ class MemberOf extends React.Component {
<ControlLabel>Auto Add OC</ControlLabel>
</Col>
- <Col sm={9}>
+ <Col sm={8}>
<Typeahead
allowNew
onChange={value => {
@@ -1090,9 +1103,8 @@ class MemberOf extends React.Component {
</Col>
</Row>
<Row>
- <Col sm={9}>
+ <Col sm={12}>
<Button
- bsSize="large"
bsStyle="primary"
onClick={this.toggleFixupModal}
diff --git
a/src/cockpit/389-console/src/lib/plugins/passthroughAuthentication.jsx
b/src/cockpit/389-console/src/lib/plugins/passthroughAuthentication.jsx
index 5dc1ab6..4bda5ac 100644
--- a/src/cockpit/389-console/src/lib/plugins/passthroughAuthentication.jsx
+++ b/src/cockpit/389-console/src/lib/plugins/passthroughAuthentication.jsx
@@ -1135,7 +1135,7 @@ class PassthroughAuthentication extends React.Component {
toggleLoadingHandler={this.props.toggleLoadingHandler}
<Row>
- <Col sm={9}>
+ <Col sm={12}>
<PassthroughAuthURLsTable
rows={urlRows}
editConfig={this.showEditURLModal}
@@ -1151,7 +1151,7 @@ class PassthroughAuthentication extends React.Component {
</Col>
</Row>
<Row>
- <Col sm={9}>
+ <Col sm={12}>
<PassthroughAuthConfigsTable
rows={pamConfigRows}
editConfig={this.showEditPAMConfigModal}
diff --git a/src/cockpit/389-console/src/lib/plugins/pluginBasicConfig.jsx
b/src/cockpit/389-console/src/lib/plugins/pluginBasicConfig.jsx
index fe6b962..966c60f 100644
--- a/src/cockpit/389-console/src/lib/plugins/pluginBasicConfig.jsx
+++ b/src/cockpit/389-console/src/lib/plugins/pluginBasicConfig.jsx
@@ -186,7 +186,7 @@ class PluginBasicConfig extends React.Component {
<div>
<Form inline>
<Row>
- <Col sm={6}>
+ <Col sm={6} className="ds-margin-top">
<h3>
<ControlLabel
className="ds-plugin-tab-header">
{this.props.pluginName}
@@ -195,7 +195,7 @@ class PluginBasicConfig extends React.Component {
</Col>
{this.props.removeSwitch || (
- <Col smOffset={1} sm={3}>
+ <Col smOffset={1} sm={4}
className="ds-margin-top">
<FormGroup key="switchPluginStatus"
controlId="switchPluginStatus">
<ControlLabel className="toolbar-pf-find
ds-float-left ds-right-indent">
Status
@@ -216,14 +216,16 @@ class PluginBasicConfig extends React.Component {
)}
</Row>
</Form>
- {this.props.children}
+ <div className="ds-margin-top">
+ {this.props.children}
+ </div>
<CustomCollapse>
<Row>
- <Col sm={4}>
+ <Col sm={12}>
<Form horizontal>
{Object.entries(modalFieldsCol1).map(([id, value]) =>
(
<FormGroup key={id} controlId={id}
disabled={false}>
- <Col componentClass={ControlLabel} sm={6}>
+ <Col componentClass={ControlLabel} sm={3}>
{this.props.memberOfAttr} Plugin{"
"}
{id.replace("currentPlugin",
"")}
</Col>
@@ -241,7 +243,7 @@ class PluginBasicConfig extends React.Component {
controlId="currentPluginDependsOnType"
disabled={false}
-
<Col componentClass={ControlLabel} sm={6}>
+ <Col componentClass={ControlLabel} sm={3}>
Plugin Depends On Type
</Col>
<Col sm={6}>
@@ -257,7 +259,7 @@ class PluginBasicConfig extends React.Component {
controlId="currentPluginDependsOnNamed"
disabled={false}
-
<Col componentClass={ControlLabel} sm={6}>
+ <Col componentClass={ControlLabel} sm={3}>
Plugin Depends On Named
</Col>
<Col sm={6}>
@@ -268,16 +270,12 @@ class PluginBasicConfig extends React.Component {
/>
</Col>
</FormGroup>
- </Form>
- </Col>
- <Col sm={4}>
- <Form horizontal>
{Object.entries(modalFieldsCol2).map(([id, value]) =>
(
<FormGroup key={id} controlId={id}
disabled={false}>
- <Col componentClass={ControlLabel} sm={5}>
+ <Col componentClass={ControlLabel} sm={3}>
Plugin {id.replace("currentPlugin",
"")}
</Col>
- <Col sm={7}>
+ <Col sm={6}>
<FormControl
type="text"
value={value}
@@ -290,10 +288,9 @@ class PluginBasicConfig extends React.Component {
</Col>
</Row>
</CustomCollapse>
- <Row>
- <Col smOffset={7} sm={1}>
+ <Row className="ds-margin-top-lg">
+ <Col sm={1}>
<Button
- bsSize="large"
bsStyle="primary"
onClick={() =>
this.props.savePluginHandler({
diff --git a/src/cockpit/389-console/src/lib/plugins/referentialIntegrity.jsx
b/src/cockpit/389-console/src/lib/plugins/referentialIntegrity.jsx
index d25e32e..af83515 100644
--- a/src/cockpit/389-console/src/lib/plugins/referentialIntegrity.jsx
+++ b/src/cockpit/389-console/src/lib/plugins/referentialIntegrity.jsx
@@ -627,7 +627,7 @@ class ReferentialIntegrity extends React.Component {
<FormGroup key="updateDelay"
controlId="updateDelay">
<Col
componentClass={ControlLabel}
- sm={2}
+ sm={3}
title="Sets the update interval. Special
values: 0 - The check is performed immediately, -1 - No check is performed
(referint-update-delay)"
Update Delay
@@ -643,7 +643,7 @@ class ReferentialIntegrity extends React.Component {
<FormGroup key="membershipAttr"
controlId="membershipAttr">
<Col
componentClass={ControlLabel}
- sm={2}
+ sm={3}
title="Specifies attributes to check for and
update (referint-membership-attr)"
Membership Attribute
@@ -667,7 +667,7 @@ class ReferentialIntegrity extends React.Component {
<FormGroup key="entryScope"
controlId="entryScope">
<Col
componentClass={ControlLabel}
- sm={2}
+ sm={3}
title="Defines the subtree in which the
plug-in looks for the delete or rename operations of a user entry
(nsslapd-pluginEntryScope)"
Entry Scope
@@ -683,7 +683,7 @@ class ReferentialIntegrity extends React.Component {
<FormGroup key="excludeEntryScope"
controlId="excludeEntryScope">
<Col
componentClass={ControlLabel}
- sm={2}
+ sm={3}
title="Defines the subtree in which the
plug-in ignores any operations for deleting or renaming a user
(nsslapd-pluginExcludeEntryScope)"
Exclude Entry Scope
@@ -699,7 +699,7 @@ class ReferentialIntegrity extends React.Component {
<FormGroup key="containerScope"
controlId="containerScope">
<Col
componentClass={ControlLabel}
- sm={2}
+ sm={3}
title="Specifies which branch the plug-in
searches for the groups to which the user belongs. It only updates groups that are under
the specified container branch, and leaves all other groups not updated
(nsslapd-pluginContainerScope)"
Container Scope
@@ -715,7 +715,7 @@ class ReferentialIntegrity extends React.Component {
<FormGroup key="logFile"
controlId="logFile">
<Col
componentClass={ControlLabel}
- sm={2}
+ sm={3}
title={`Specifies a path to the Referential
integrity logfile. For example: /var/log/dirsrv/slapd-${
this.props.serverId
}/referint`}
@@ -736,7 +736,7 @@ class ReferentialIntegrity extends React.Component {
<Col
componentClass={ControlLabel}
- sm={2}
+ sm={3}
title="The value to set as
nsslapd-pluginConfigArea"
Shared Config Entry
@@ -748,9 +748,8 @@ class ReferentialIntegrity extends React.Component {
onChange={this.handleFieldChange}
/>
</Col>
- <Col sm={3}>
+ <Col sm={2}>
<Button
- bsSize="large"
bsStyle="primary"
onClick={this.openModal}
diff --git
a/src/cockpit/389-console/src/lib/plugins/retroChangelog.jsx
b/src/cockpit/389-console/src/lib/plugins/retroChangelog.jsx
index 237d9aa..a7f35c7 100644
--- a/src/cockpit/389-console/src/lib/plugins/retroChangelog.jsx
+++ b/src/cockpit/389-console/src/lib/plugins/retroChangelog.jsx
@@ -171,7 +171,7 @@ class RetroChangelog extends React.Component {
<FormGroup key="attribute"
controlId="attribute">
<Col
componentClass={ControlLabel}
- sm={2}
+ sm={3}
title="Specifies another Directory Server
attribute which must be included in the retro changelog entries (nsslapd-attribute)"
Attribute
@@ -194,7 +194,7 @@ class RetroChangelog extends React.Component {
<FormGroup key="directory"
controlId="directory">
<Col
componentClass={ControlLabel}
- sm={2}
+ sm={3}
title="Specifies the name of the directory
in which the changelog database is created the first time the plug-in is run"
Directory
@@ -210,7 +210,7 @@ class RetroChangelog extends React.Component {
<FormGroup key="maxAge"
controlId="maxAge">
<Col
componentClass={ControlLabel}
- sm={2}
+ sm={3}
title="This attribute specifies the maximum
age of any entry in the changelog (nsslapd-changelogmaxage)"
Max Age
@@ -226,7 +226,7 @@ class RetroChangelog extends React.Component {
<FormGroup key="excludeSuffix"
controlId="excludeSuffix">
<Col
componentClass={ControlLabel}
- sm={2}
+ sm={3}
title="This attribute specifies the suffix
which will be excluded from the scope of the plugin (nsslapd-exclude-suffix)"
Exclude Suffix
diff --git a/src/cockpit/389-console/src/lib/plugins/rootDNAccessControl.jsx
b/src/cockpit/389-console/src/lib/plugins/rootDNAccessControl.jsx
index bdd8483..9064adf 100644
--- a/src/cockpit/389-console/src/lib/plugins/rootDNAccessControl.jsx
+++ b/src/cockpit/389-console/src/lib/plugins/rootDNAccessControl.jsx
@@ -177,7 +177,7 @@ class RootDNAccessControl extends React.Component {
<FormGroup key="allowHost"
controlId="allowHost">
<Col
componentClass={ControlLabel}
- sm={2}
+ sm={3}
title="Sets what hosts, by fully-qualified
domain name, the root user is allowed to use to access the Directory Server. Any hosts not
listed are implicitly denied (rootdn-allow-host)"
Allow Host
@@ -201,7 +201,7 @@ class RootDNAccessControl extends React.Component {
<FormGroup key="denyHost"
controlId="denyHost">
<Col
componentClass={ControlLabel}
- sm={2}
+ sm={3}
title="Sets what hosts, by fully-qualified
domain name, the root user is not allowed to use to access the Directory Server Any hosts
not listed are implicitly allowed (rootdn-deny-host). If an host address is listed in both
the rootdn-allow-host and rootdn-deny-host attributes, it is denied access."
Deny Host
@@ -225,7 +225,7 @@ class RootDNAccessControl extends React.Component {
<FormGroup key="allowIP"
controlId="allowIP">
<Col
componentClass={ControlLabel}
- sm={2}
+ sm={3}
title="Sets what IP addresses, either IPv4
or IPv6, for machines the root user is allowed to use to access the Directory Server Any
IP addresses not listed are implicitly denied (rootdn-allow-ip)"
Allow IP address
@@ -249,7 +249,7 @@ class RootDNAccessControl extends React.Component {
<FormGroup key="denyIP"
controlId="denyIP">
<Col
componentClass={ControlLabel}
- sm={2}
+ sm={3}
title="Sets what IP addresses, either IPv4
or IPv6, for machines the root user is not allowed to use to access the Directory Server.
Any IP addresses not listed are implicitly allowed (rootdn-deny-ip) If an IP address is
listed in both the rootdn-allow-ip and rootdn-deny-ip attributes, it is denied
access."
Deny IP address
@@ -273,7 +273,7 @@ class RootDNAccessControl extends React.Component {
<FormGroup key="openTime"
controlId="openTime">
<Col
componentClass={ControlLabel}
- sm={2}
+ sm={3}
title="Sets part of a time period or range
when the root user is allowed to access the Directory Server. This sets when the
time-based access begins (rootdn-open-time)"
Open Time
@@ -289,7 +289,7 @@ class RootDNAccessControl extends React.Component {
<FormGroup key="closeTime"
controlId="closeTime">
<Col
componentClass={ControlLabel}
- sm={2}
+ sm={3}
title="Sets part of a time period or range
when the root user is allowed to access the Directory Server. This sets when the
time-based access ends (rootdn-close-time)"
Close Time
@@ -305,7 +305,7 @@ class RootDNAccessControl extends React.Component {
<FormGroup key="daysAllowed"
controlId="daysAllowed">
<Col
componentClass={ControlLabel}
- sm={2}
+ sm={3}
title="Gives a comma-separated list of what
days the root user is allowed to use to access the Directory Server. Any days listed are
implicitly denied (rootdn-days-allowed)"
Days Allowed
diff --git a/src/cockpit/389-console/src/lib/plugins/usn.jsx
b/src/cockpit/389-console/src/lib/plugins/usn.jsx
index 08d7beb..e587755 100644
--- a/src/cockpit/389-console/src/lib/plugins/usn.jsx
+++ b/src/cockpit/389-console/src/lib/plugins/usn.jsx
@@ -318,7 +318,6 @@ class USN extends React.Component {
<Col sm={9}>
<Button
className="ds-margin-top"
- bsSize="large"
bsStyle="primary"
onClick={this.toggleCleanupModal}
diff --git
a/src/cockpit/389-console/src/lib/plugins/winsync.jsx
b/src/cockpit/389-console/src/lib/plugins/winsync.jsx
index 39be7a9..0e2ff13 100644
--- a/src/cockpit/389-console/src/lib/plugins/winsync.jsx
+++ b/src/cockpit/389-console/src/lib/plugins/winsync.jsx
@@ -1,7 +1,21 @@
+import cockpit from "cockpit";
import React from "react";
-import { Row, Col, Form, noop, FormGroup, Checkbox, ControlLabel } from
"patternfly-react";
+import {
+ Row,
+ Col,
+ Icon,
+ Modal,
+ noop,
+ Form,
+ FormControl,
+ FormGroup,
+ Checkbox,
+ Button,
+ ControlLabel
+} from "patternfly-react";
import PropTypes from "prop-types";
import PluginBasicConfig from "./pluginBasicConfig.jsx";
+import { log_cmd } from "../tools.jsx";
import "../../css/ds.css";
class WinSync extends React.Component {
@@ -19,23 +33,93 @@ class WinSync extends React.Component {
super(props);
this.handleCheckboxChange = this.handleCheckboxChange.bind(this);
+ this.handleFieldChange = this.handleFieldChange.bind(this);
this.updateFields = this.updateFields.bind(this);
+ this.runFixup = this.runFixup.bind(this);
+ this.toggleFixupModal = this.toggleFixupModal.bind(this);
this.state = {
posixWinsyncCreateMemberOfTask: false,
posixWinsyncLowerCaseUID: false,
posixWinsyncMapMemberUID: false,
posixWinsyncMapNestedGrouping: false,
- posixWinsyncMsSFUSchema: false
+ posixWinsyncMsSFUSchema: false,
+
+ fixupModalShow: false,
+ fixupDN: "",
+ fixupFilter: ""
};
}
+ toggleFixupModal() {
+ this.setState(prevState => ({
+ fixupModalShow: !prevState.fixupModalShow,
+ fixupDN: "",
+ fixupFilter: ""
+ }));
+ }
+
+ runFixup() {
+ if (!this.state.fixupDN) {
+ this.props.addNotification("warning", "Fixup DN is
required.");
+ } else {
+ let cmd = [
+ "dsconf",
+ "-j",
+ "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId +
".socket",
+ "plugin",
+ "posix-winsync",
+ "fixup",
+ this.state.fixupDN
+ ];
+
+ if (this.state.fixupFilter) {
+ cmd = [...cmd, "--filter", this.state.fixupFilter];
+ }
+
+ this.props.toggleLoadingHandler();
+ log_cmd("runFixup", "Run Member UID task", cmd);
+ cockpit
+ .spawn(cmd, {
+ superuser: true,
+ err: "message"
+ })
+ .done(content => {
+ this.props.addNotification(
+ "success",
+ `Fixup task for ${this.state.fixupDN} was successfull`
+ );
+ this.props.toggleLoadingHandler();
+ this.setState({
+ fixupModalShow: false
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Fixup task for ${this.state.fixupDN} has failed
${errMsg.desc}`
+ );
+ this.props.toggleLoadingHandler();
+ this.setState({
+ fixupModalShow: false
+ });
+ });
+ }
+ }
+
handleCheckboxChange(e) {
this.setState({
[e.target.id]: e.target.checked
});
}
+ handleFieldChange(e) {
+ this.setState({
+ [e.target.id]: e.target.value
+ });
+ }
+
updateFields() {
if (this.props.rows.length > 0) {
const pluginRow = this.props.rows.find(row => row.cn[0] === "Posix
Winsync API");
@@ -71,7 +155,10 @@ class WinSync extends React.Component {
posixWinsyncLowerCaseUID,
posixWinsyncMapMemberUID,
posixWinsyncMapNestedGrouping,
- posixWinsyncMsSFUSchema
+ posixWinsyncMsSFUSchema,
+ fixupModalShow,
+ fixupDN,
+ fixupFilter
} = this.state;
let specificPluginCMD = [
@@ -94,6 +181,69 @@ class WinSync extends React.Component {
];
return (
<div>
+ <Modal show={fixupModalShow} onHide={this.toggleFixupModal}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={this.toggleFixupModal}
+ aria-hidden="true"
+ aria-label="Close"
+
+ <Icon type="pf"
name="close" />
+ </button>
+ <Modal.Title>MemberOf Task</Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Row>
+ <Col sm={12}>
+ <Form horizontal>
+ <FormGroup controlId="fixupDN"
key="fixupDN">
+ <Col sm={3}>
+ <ControlLabel title="Base DN that
contains entries to fix up">
+ Base DN
+ </ControlLabel>
+ </Col>
+ <Col sm={9}>
+ <FormControl
+ type="text"
+ value={fixupDN}
+ onChange={this.handleFieldChange}
+ />
+ </Col>
+ </FormGroup>
+ <FormGroup controlId="fixupFilter"
key="fixupFilter">
+ <Col sm={3}>
+ <ControlLabel title="Filter for
entries to fix up. If omitted, all entries with objectclass inetuser/inetadmin/nsmemberof
under the specified base will have their memberOf attribute regenerated.">
+ Filter DN
+ </ControlLabel>
+ </Col>
+ <Col sm={9}>
+ <FormControl
+ type="text"
+ value={fixupFilter}
+ onChange={this.handleFieldChange}
+ />
+ </Col>
+ </FormGroup>
+ </Form>
+ </Col>
+ </Row>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={this.toggleFixupModal}
+
+ Cancel
+ </Button>
+ <Button bsStyle="primary"
onClick={this.runFixup}>
+ Run
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
<PluginBasicConfig
rows={this.props.rows}
serverId={this.props.serverId}
@@ -107,7 +257,7 @@ class WinSync extends React.Component {
toggleLoadingHandler={this.props.toggleLoadingHandler}
<Row>
- <Col sm={9}>
+ <Col sm={12}>
<Form horizontal>
<FormGroup
key="posixWinsyncCreateMemberOfTask"
@@ -115,7 +265,7 @@ class WinSync extends React.Component {
<Col
componentClass={ControlLabel}
- sm={3}
+ sm={6}
title="Sets whether to run the memberOf
fix-up task immediately after a sync run in order to update group memberships for synced
users"
Create MemberOf Task
@@ -134,7 +284,7 @@ class WinSync extends React.Component {
<Col
componentClass={ControlLabel}
- sm={3}
+ sm={6}
title="Sets whether to store (and, if
necessary, convert) the UID value in the memberUID attribute in lower case"
Lower Case UID
@@ -153,7 +303,7 @@ class WinSync extends React.Component {
<Col
componentClass={ControlLabel}
- sm={3}
+ sm={6}
title="Sets whether to map the memberUID
attribute in an Active Directory group to the uniqueMember attribute in a Directory Server
group"
Map Member UID
@@ -173,7 +323,7 @@ class WinSync extends React.Component {
<Col
title="Manages if nested groups are updated
when memberUID attributes in an Active Directory POSIX group change"
componentClass={ControlLabel}
- sm={3}
+ sm={6}
Map Nested Grouping
</Col>
@@ -191,7 +341,7 @@ class WinSync extends React.Component {
<Col
componentClass={ControlLabel}
- sm={3}
+ sm={6}
title="Sets whether to the older Microsoft
System Services for Unix 3.0 (msSFU30) schema when syncing Posix attributes from Active
Directory"
Microsoft System Services for Unix 3.0 (msSFU30) schema
@@ -207,6 +357,17 @@ class WinSync extends React.Component {
</Form>
</Col>
</Row>
+ <Row>
+ <Col sm={12}>
+ <Button
+ bsStyle="primary"
+ onClick={this.toggleFixupModal}
+ title="Corrects mismatched member and uniquemember
values"
+
+ Run MemberOf Task
+ </Button>
+ </Col>
+ </Row>
</PluginBasicConfig>
</div>
);
diff --git a/src/cockpit/389-console/src/lib/replication/replAgmts.jsx
b/src/cockpit/389-console/src/lib/replication/replAgmts.jsx
new file mode 100644
index 0000000..e3c4781
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/replication/replAgmts.jsx
@@ -0,0 +1,1150 @@
+import cockpit from "cockpit";
+import React from "react";
+import { ConfirmPopup, DoubleConfirmModal } from "../notifications.jsx";
+import { ReplAgmtTable } from "./replTables.jsx";
+import { ReplAgmtModal } from "./replModals.jsx";
+import { log_cmd, valid_dn, valid_port } from "../tools.jsx";
+import PropTypes from "prop-types";
+import {
+ Button,
+ noop,
+} from "patternfly-react";
+
+export class ReplAgmts extends React.Component {
+ _mounted = false;
+ constructor(props) {
+ super(props);
+ this.state = {
+ showCreateAgmtModal: false,
+ showEditAgmtModal: false,
+ showConfirmDeleteAgmt: false,
+ showConfirmInitAgmt: false,
+ showConfirmEnableAgmt: false,
+ showConfirmDisableAgmt: false,
+ errObj: {},
+ modalMsg: "",
+ modalScheduleMsg: "",
+ savingAgmt: false,
+ mounted: false,
+ // Create agreement
+ agmtName: "",
+ agmtHost: "",
+ agmtPort: "",
+ agmtProtocol: "LDAP",
+ agmtBindMethod: "SIMPLE",
+ agmtBindDN: "",
+ agmtBindPW: "",
+ agmtBindPWConfirm: "",
+ agmtStripAttrs: [],
+ agmtFracAttrs: [],
+ agmtFracInitAttrs: [],
+ agmtSync: true,
+ agmtSyncMon: true,
+ agmtSyncTue: true,
+ agmtSyncWed: true,
+ agmtSyncThu: true,
+ agmtSyncFri: true,
+ agmtSyncSat: true,
+ agmtSyncSun: true,
+ agmtStartTime: "0",
+ agmtEndTime: "0",
+ agmtInit: "noinit",
+ agmtSaveOK: false,
+ modalChecked: false,
+ modalSpinning: false,
+ // Init agmt
+ agmtInitCounter: 0,
+ agmtInitIntervals: [],
+ };
+ this.showCreateAgmtModal = this.showCreateAgmtModal.bind(this);
+ this.closeCreateAgmtModal = this.closeCreateAgmtModal.bind(this);
+ this.closeEditAgmtModal = this.closeEditAgmtModal.bind(this);
+ this.handleChange = this.handleChange.bind(this);
+ this.handleModalChange = this.handleModalChange.bind(this);
+ this.handleTAFracInitAttrChange = this.handleTAFracInitAttrChange.bind(this);
+ this.handleTAFracAttrChange = this.handleTAFracAttrChange.bind(this);
+ this.handleTAStripAttrChange = this.handleTAStripAttrChange.bind(this);
+ this.handleTAFracInitAttrChangeEdit =
this.handleTAFracInitAttrChangeEdit.bind(this);
+ this.handleTAFracAttrChangeEdit = this.handleTAFracAttrChangeEdit.bind(this);
+ this.handleTAStripAttrChangeEdit = this.handleTAStripAttrChangeEdit.bind(this);
+ this.createAgmt = this.createAgmt.bind(this);
+ this.showEditAgmt = this.showEditAgmt.bind(this);
+ this.saveAgmt = this.saveAgmt.bind(this);
+ this.pokeAgmt = this.pokeAgmt.bind(this);
+ this.initAgmt = this.initAgmt.bind(this);
+ this.enableAgmt = this.enableAgmt.bind(this);
+ this.disableAgmt = this.disableAgmt.bind(this);
+ this.deleteAgmt = this.deleteAgmt.bind(this);
+ this.showConfirmDeleteAgmt = this.showConfirmDeleteAgmt.bind(this);
+ this.closeConfirmDeleteAgmt = this.closeConfirmDeleteAgmt.bind(this);
+ this.showConfirmInitAgmt = this.showConfirmInitAgmt.bind(this);
+ this.closeConfirmInitAgmt = this.closeConfirmInitAgmt.bind(this);
+ this.confirmToggle = this.confirmToggle.bind(this);
+ this.closeConfirmEnableAgmt = this.closeConfirmEnableAgmt.bind(this);
+ this.closeConfirmDisableAgmt = this.closeConfirmDisableAgmt.bind(this);
+ this.watchAgmtInit = this.watchAgmtInit.bind(this);
+ }
+
+ componentDidMount () {
+ this._mounted = true;
+ }
+
+ componentWillUnmount () {
+ this._mounted = false;
+ }
+
+ listEqual(old_values, new_values) {
+ if (old_values.length != new_values.length) {
+ return false;
+ }
+ for (let i = old_values.length; i--;) {
+ if (old_values[i] != new_values[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ handleModalChange (e) {
+ this.setState({
+ [e.target.id]: e.target.checked,
+ });
+ }
+
+ handleChange (e) {
+ let value = e.target.type === 'checkbox' ? e.target.checked :
e.target.value;
+ let time_val = "";
+ let valueErr = false;
+ let errObj = this.state.errObj;
+ let all_good = true;
+ let modal_msg = "";
+ let modal_schedule_msg = "";
+ let edit = false;
+ if (value == "") {
+ valueErr = true;
+ }
+ errObj[e.target.id] = valueErr;
+ if (e.target.name == "agmt-modal-edit") {
+ let orig_attr = "_" + e.target.id;
+ let attr = e.target.id;
+ all_good = false;
+ if ((attr != 'agmtHost' && this.state.agmtHost !=
this.state._agmtHost) ||
+ (attr != 'agmtPort' && this.state.agmtPort !=
this.state._agmtPort) ||
+ (attr != 'agmtBindDN' && this.state.agmtBindDN !=
this.state._agmtBindDN) ||
+ (attr != 'agmtBindMethod' && this.state.agmtBindMethod !=
this.state._agmtBindMethod) ||
+ (attr != 'agmtProtocol' && this.state.agmtProtocol !=
this.state._agmtProtocol) ||
+ (attr != 'agmtSync' && this.state.agmtSync !=
this.state._agmtSync) ||
+ (attr != 'agmtStripAttrs' &&
!this.listEqual(this.state.agmtStripAttrs, this.state._agmtStripAttrs)) ||
+ (attr != 'agmtFracAttrs' &&
!this.listEqual(this.state.agmtFracAttrs, this.state._agmtFracAttrs)) ||
+ (attr != 'agmtFracInitAttrs' &&
!this.listEqual(this.state.agmtFracInitAttrs, this.state._agmtFracInitAttrs))) {
+ all_good = true;
+ }
+ if (!this.state._agmtSync) {
+ if ((attr != 'agmtSyncMon' && this.state.agmtSyncMon !=
this.state._agmtSyncMon) ||
+ (attr != 'agmtSyncTue' && this.state.agmtSyncTue !=
this.state._agmtSyncTue) ||
+ (attr != 'agmtSyncWed' && this.state.agmtSyncWed !=
this.state._agmtSyncWed) ||
+ (attr != 'agmtSyncThu' && this.state.agmtSyncThu !=
this.state._agmtSyncThu) ||
+ (attr != 'agmtSyncFri' && this.state.agmtSyncFri !=
this.state._agmtSyncFri) ||
+ (attr != 'agmtSyncSat' && this.state.agmtSyncSat !=
this.state._agmtSyncSat) ||
+ (attr != 'agmtSyncSun' && this.state.agmtSyncSun !=
this.state._agmtSyncSun)) {
+ all_good = true;
+ }
+ }
+ if (attr != 'agmtStripAttrs' &&
+ attr != 'agmtFracAttrs' &&
+ attr != 'agmtFracInitAttrs' &&
+ value != this.state[orig_attr]) {
+ all_good = true;
+ } else if ((attr == 'agmtStripAttrs' &&
!this.listEqual(value, this.state._agmtStripAttrs)) ||
+ (attr == 'agmtFracAttrs' && !this.listEqual(value,
this.state._agmtFracAttrs)) ||
+ (attr == 'agmtFracInitAttrs' &&
!this.listEqual(value, this.state._agmtFracInitAttrs))) {
+ all_good = true;
+ }
+ }
+
+ if (e.target.type == "time") {
+ // Strip out the colon from the time
+ time_val = value.replace(':', '');
+ }
+
+ if (e.target.name.startsWith("agmt-modal")) {
+ // Validate modal settings "live"
+ if (e.target.id == 'agmtName') {
+ if (value == "") {
+ all_good = false;
+ }
+ } else if (this.state.agmtName == "") {
+ all_good = false;
+ }
+ if (e.target.id == 'agmtHost') {
+ if (value == "") {
+ all_good = false;
+ }
+ } else if (this.state.agmtHost == "") {
+ all_good = false;
+ } else if (edit && value == this.state._agmtHost) {
+ all_good = false;
+ }
+ if (e.target.id == 'agmtPort') {
+ if (value == "") {
+ all_good = false;
+ } else if (!valid_port(value)) {
+ all_good = false;
+ errObj['agmtPort'] = true;
+ modal_msg = "Invalid Consumer Port number";
+ }
+ } else if (this.state.agmtPort == "") {
+ all_good = false;
+ }
+ if (e.target.id == 'agmtBindDN') {
+ if (value == "") {
+ all_good = false;
+ }
+ if (!valid_dn(value)) {
+ errObj['agmtBindDN'] = true;
+ all_good = false;
+ modal_msg = "Invalid DN for Bind DN";
+ }
+ } else if (this.state.agmtBindDN == "") {
+ all_good = false;
+ } else if (!valid_dn(this.state.agmtBindDN)) {
+ modal_msg = "Invalid DN for Bind DN";
+ errObj['agmtBindDN'] = true;
+ all_good = false;
+ }
+ if (e.target.id == 'agmtBindPW') {
+ if (value == "") {
+ all_good = false;
+ } else if (value != this.state.agmtBindPWConfirm) {
+ modal_msg = "Passwords Do Not Match";
+ errObj['agmtBindPW'] = true;
+ errObj['agmtBindPWConfirm'] = true;
+ all_good = false;
+ } else {
+ errObj['agmtBindPW'] = false;
+ errObj['agmtBindPWConfirm'] = false;
+ }
+ } else if (this.state.agmtBindPW == "") {
+ all_good = false;
+ }
+ if (e.target.id == 'agmtBindPWConfirm') {
+ if (value == "") {
+ all_good = false;
+ } else if (value != this.state.agmtBindPW) {
+ modal_msg = "Passwords Do Not Match";
+ errObj['agmtBindPW'] = true;
+ errObj['agmtBindPWConfirm'] = true;
+ all_good = false;
+ } else {
+ errObj['agmtBindPW'] = false;
+ errObj['agmtBindPWConfirm'] = false;
+ }
+ } else if (this.state.agmtBindPWConfirm == "") {
+ all_good = false;
+ }
+ if (e.target.id == 'agmtSync') {
+ if (!value) {
+ if (this.state.agmtStartTime >= this.state.agmtEndTime) {
+ modal_schedule_msg = "Schedule start time is greater than or
equal to the end time";
+ errObj['agmtStartTime'] = true;
+ all_good = false;
+ }
+ }
+ } else if (!this.state.agmtSync) {
+ // Check the days first
+ let have_days = false;
+ let days = ["agmtSyncSun", "agmtSyncMon",
"agmtSyncTue", "agmtSyncWed",
+ "agmtSyncThu", "agmtSyncFri",
"agmtSyncSat"];
+ for (let day of days) {
+ if ((e.target.id != day && this.state[day]) || (e.target.id
== day && value)) {
+ have_days = true;
+ break;
+ }
+ }
+ if (!have_days) {
+ modal_schedule_msg = "You must select at least one day for
replication";
+ all_good = false;
+ } else if (e.target.id == 'agmtStartTime') {
+ if (time_val == "") {
+ all_good = false;
+ errObj['agmtStartTime'] = true;
+ modal_schedule_msg = "Schedule start time must be
set";
+ } else if (time_val >=
this.state.agmtEndTime.replace(":", "")) {
+ errObj['agmtStartTime'] = true;
+ all_good = false;
+ modal_schedule_msg = "Schedule start time is greater than or
equal to the end time";
+ } else {
+ // All good, reset form
+ modal_schedule_msg = "";
+ errObj['agmtStartTime'] = false;
+ errObj['agmtEndTime'] = false;
+ }
+ } else if (e.target.id == 'agmtEndTime') {
+ if (time_val == "") {
+ errObj['agmtEndTime'] = true;
+ all_good = false;
+ } else if (this.state.agmtStartTime.replace(":",
"") >= time_val) {
+ modal_schedule_msg = "Schedule start time is greater than or
equal to the end time";
+ errObj['agmtStartTime'] = true;
+ all_good = false;
+ } else {
+ // All good, reset form
+ modal_schedule_msg = "";
+ errObj['agmtStartTime'] = false;
+ errObj['agmtEndTime'] = false;
+ }
+ } else if (this.state.agmtStartTime >= this.state.agmtEndTime) {
+ modal_schedule_msg = "Schedule start time is greater than or
equal to the end time";
+ errObj['agmtStartTime'] = true;
+ all_good = false;
+ }
+ }
+ // End of agmt modal live validation
+ }
+ this.setState({
+ [e.target.id]: value,
+ errObj: errObj,
+ agmtSaveOK: all_good,
+ modalMsg: modal_msg,
+ modalScheduleMsg: modal_schedule_msg,
+ });
+ }
+
+ handleTAStripAttrChangeEdit (values) {
+ // TypeAhead handling
+ let e = {
+ target: {
+ name: 'agmt-modal-edit',
+ id: 'agmtStripAttrs',
+ value: values,
+ type: 'input',
+ }
+ };
+ this.handleChange(e);
+ }
+
+ handleTAFracAttrChangeEdit (values) {
+ // TypeAhead handling
+ let e = {
+ target: {
+ name: 'agmt-modal-edit',
+ id: 'agmtFracAttrs',
+ value: values,
+ type: 'input',
+ }
+ };
+ this.handleChange(e);
+ }
+
+ handleTAFracInitAttrChangeEdit (values) {
+ // TypeAhead handling
+ let e = {
+ target: {
+ name: 'agmt-modal-edit',
+ id: 'agmtFracInitAttrs',
+ value: values,
+ type: 'input',
+ }
+ };
+ this.handleChange(e);
+ }
+
+ handleTAStripAttrChange (values) {
+ // TypeAhead handling
+ let e = {
+ target: {
+ name: 'agmt-modal',
+ id: 'agmtStripAttrs',
+ value: values,
+ type: 'input',
+ }
+ };
+ this.handleChange(e);
+ }
+
+ handleTAFracAttrChange (values) {
+ // TypeAhead handling
+ let e = {
+ target: {
+ name: 'agmt-modal',
+ id: 'agmtFracAttrs',
+ value: values,
+ type: 'input',
+ }
+ };
+ this.handleChange(e);
+ }
+
+ handleTAFracInitAttrChange (values) {
+ // TypeAhead handling
+ let e = {
+ target: {
+ name: 'agmt-modal',
+ id: 'agmtFracInitAttrs',
+ value: values,
+ type: 'input',
+ }
+ };
+ this.handleChange(e);
+ }
+
+ showConfirmDeleteAgmt (agmtName) {
+ this.setState({
+ agmtName: agmtName,
+ showConfirmDeleteAgmt: true,
+ modalChecked: false,
+ modalSpinning: false,
+ });
+ }
+
+ closeConfirmDeleteAgmt () {
+ this.setState({
+ showConfirmDeleteAgmt: false,
+ modalSpinning: false,
+ modalChecked: false,
+ });
+ }
+
+ showConfirmInitAgmt (agmtName) {
+ this.setState({
+ agmtName: agmtName,
+ showConfirmInitAgmt: true,
+ modalChecked: false,
+ modalSpinning: false,
+ });
+ }
+
+ closeConfirmInitAgmt () {
+ this.setState({
+ showConfirmInitAgmt: false,
+ modalSpinning: false,
+ modalChecked: false,
+ });
+ }
+
+ showCreateAgmtModal () {
+ this.setState({
+ showCreateAgmtModal: true,
+ agmtName: "",
+ agmtHost: "",
+ agmtPort: "",
+ agmtProtocol: "LDAP",
+ agmtBindMethod: "SIMPLE",
+ agmtBindDN: "",
+ agmtBindPW: "",
+ agmtBindPWConfirm: "",
+ agmtStripAttrs: [],
+ agmtFracAttrs: [],
+ agmtFracInitAttrs: [],
+ agmtSync: true,
+ agmtSyncMon: true,
+ agmtSyncTue: true,
+ agmtSyncWed: true,
+ agmtSyncThu: true,
+ agmtSyncFri: true,
+ agmtSyncSat: true,
+ agmtSyncSun: true,
+ agmtStartTime: "00:00",
+ agmtEndTime: "23:59",
+ agmtInit: "noinit",
+ agmtSaveOK: false,
+ modalScheduleMsg: "",
+ errObj: {
+ // Marks all the fields as required
+ agmtName: true,
+ agmtHost: true,
+ agmtPort: true,
+ agmtBindDN: true,
+ agmtBindPW: true,
+ agmtBindPWConfirm: true,
+ agmtStartTime: false,
+ agmtEndTime: false,
+ }
+ });
+ }
+
+ closeCreateAgmtModal () {
+ this.setState({
+ showCreateAgmtModal: false
+ });
+ }
+
+ closeEditAgmtModal () {
+ this.setState({
+ showEditAgmtModal: false
+ });
+ }
+
+ showEditAgmt (agmtName) {
+ // Search for the agmt to get all the details
+ let cmd = [
+ 'dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' +
this.props.serverId + '.socket',
+ 'repl-agmt', 'get', agmtName, '--suffix=' +
this.props.suffix,
+ ];
+
+ log_cmd('showEditAgmt', 'Edit replication agreement', cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const config = JSON.parse(content);
+ let agmtName = "";
+ let agmtHost = "";
+ let agmtPort = "";
+ let agmtProtocol = "";
+ let agmtBindMethod = "";
+ let agmtBindDN = "";
+ let agmtBindPW = "";
+ let agmtBindPWConfirm = "";
+ let agmtStripAttrs = [];
+ let agmtFracAttrs = [];
+ let agmtFracInitAttrs = [];
+ let agmtSync = true;
+ let agmtSyncMon = false;
+ let agmtSyncTue = false;
+ let agmtSyncWed = false;
+ let agmtSyncThu = false;
+ let agmtSyncFri = false;
+ let agmtSyncSat = false;
+ let agmtSyncSun = false;
+ let agmtStartTime = "";
+ let agmtEndTime = "";
+ for (let attr in config['attrs']) {
+ let val = config['attrs'][attr][0];
+ if (attr == "cn") {
+ agmtName = val;
+ }
+ if (attr == "nsds5replicahost") {
+ agmtHost = val;
+ }
+ if (attr == "nsds5replicaport") {
+ agmtPort = val;
+ }
+ if (attr == "nsds5replicatransportinfo") {
+ agmtProtocol = val;
+ }
+ if (attr == "nsds5replicabindmethod") {
+ agmtBindMethod = val.toUpperCase();
+ }
+ if (attr == "nsds5replicabinddn") {
+ agmtBindDN = val;
+ }
+ if (attr == "nsds5replicacredentials") {
+ agmtBindPW = val;
+ agmtBindPWConfirm = val;
+ }
+ if (attr == "nsds5replicatedattributelist") {
+ let attrs = val.replace("(objectclass=*) $
EXCLUDE", "").trim();
+ agmtFracAttrs = attrs.split(' ');
+ }
+ if (attr == "nsds5replicatedattributelisttotal") {
+ let attrs = val.replace("(objectclass=*) $
EXCLUDE", "").trim();
+ agmtFracInitAttrs = attrs.split(' ');
+ }
+ if (attr == "nsds5replicastripattrs") {
+ agmtStripAttrs = val.split(' ');
+ }
+ if (attr == "nsds5replicaupdateschedule") {
+ agmtSync = false;
+ // Parse schedule
+ let parts = val.split(' ');
+ let times = parts[0].split('-');
+ let days = parts[1];
+
+ // Do the times
+ agmtStartTime = times[0].substring(0, 2) + ":" +
times[0].substring(2, 4);
+ agmtEndTime = times[1].substring(0, 2) + ":" +
times[1].substring(2, 4);
+
+ // Do the days
+ if (days.includes("0")) {
+ agmtSyncSun = true;
+ }
+ if (days.includes("1")) {
+ agmtSyncMon = true;
+ }
+ if (days.includes("2")) {
+ agmtSyncTue = true;
+ }
+ if (days.includes("3")) {
+ agmtSyncWed = true;
+ }
+ if (days.includes("4")) {
+ agmtSyncThu = true;
+ }
+ if (days.includes("5")) {
+ agmtSyncFri = true;
+ }
+ if (days.includes("6")) {
+ agmtSyncSat = true;
+ }
+ }
+ }
+ if (this._mounted) {
+ this.setState({
+ showEditAgmtModal: true,
+ agmtName: agmtName,
+ agmtHost: agmtHost,
+ agmtPort: agmtPort,
+ agmtProtocol: agmtProtocol,
+ agmtBindMethod: agmtBindMethod,
+ agmtBindDN: agmtBindDN,
+ agmtBindPW: agmtBindPW,
+ agmtBindPWConfirm: agmtBindPWConfirm,
+ agmtStripAttrs: agmtStripAttrs,
+ agmtFracAttrs: agmtFracAttrs,
+ agmtFracInitAttrs: agmtFracInitAttrs,
+ agmtSync: agmtSync,
+ agmtSyncMon: agmtSyncMon,
+ agmtSyncTue: agmtSyncTue,
+ agmtSyncWed: agmtSyncWed,
+ agmtSyncThu: agmtSyncThu,
+ agmtSyncFri: agmtSyncFri,
+ agmtSyncSat: agmtSyncSat,
+ agmtSyncSun: agmtSyncSun,
+ agmtStartTime: agmtStartTime,
+ agmtEndTime: agmtEndTime,
+ agmtSaveOK: false,
+ // Record original values before editing
+ _agmtName: agmtName,
+ _agmtHost: agmtHost,
+ _agmtPort: agmtPort,
+ _agmtProtocol: agmtProtocol,
+ _agmtBindMethod: agmtBindMethod,
+ _agmtBindDN: agmtBindDN,
+ _agmtBindPW: agmtBindPW,
+ _agmtBindPWConfirm: agmtBindPWConfirm,
+ _agmtStripAttrs: agmtStripAttrs,
+ _agmtFracAttrs: agmtFracAttrs,
+ _agmtFracInitAttrs: agmtFracInitAttrs,
+ _agmtSync: agmtSync,
+ _agmtSyncMon: agmtSyncMon,
+ _agmtSyncTue: agmtSyncTue,
+ _agmtSyncWed: agmtSyncWed,
+ _agmtSyncThu: agmtSyncThu,
+ _agmtSyncFri: agmtSyncFri,
+ _agmtSyncSat: agmtSyncSat,
+ _agmtSyncSun: agmtSyncSun,
+ _agmtStartTime: agmtStartTime,
+ _agmtEndTime: agmtEndTime,
+ });
+ }
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to get agreement information for: "${agmtName}"
- ${errMsg.desc}`
+ );
+ });
+ }
+
+ saveAgmt () {
+ let cmd = [
+ 'dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' +
this.props.serverId + '.socket',
+ 'repl-agmt', 'set', this.state.agmtName, '--suffix='
+ this.props.suffix,
+ ];
+
+ // Handle Schedule
+ if (!this.state.agmtSync) {
+ let agmt_days = "";
+ if (this.state.agmtSyncSun) {
+ agmt_days += "0";
+ }
+ if (this.state.agmtSyncMon) {
+ agmt_days += "1";
+ }
+ if (this.state.agmtSyncTue) {
+ agmt_days += "2";
+ }
+ if (this.state.agmtSyncWed) {
+ agmt_days += "3";
+ }
+ if (this.state.agmtSyncThu) {
+ agmt_days += "4";
+ }
+ if (this.state.agmtSyncFri) {
+ agmt_days += "5";
+ }
+ if (this.state.agmtSyncSat) {
+ agmt_days += "6";
+ }
+ cmd.push('--schedule=' +
this.state.agmtStartTime.replace(':', '') + "-" +
this.state.agmtEndTime.replace(':', '') + " " + agmt_days);
+ } else if (this.state.agmtSync != this.state._agmtSync &&
this.state.agmtSync) {
+ // We disabled custom scheduleRow
+ cmd.push('--schedule=');
+ }
+ if (this.state.agmtBindMethod != this.state._agmtBindMethod) {
+ cmd.push('--bind-method=' + this.state.agmtBindMethod);
+ }
+ if (this.state.agmtProtocol != this.state._agmtProtocol) {
+ cmd.push('--conn-protocol=' + this.state.agmtProtocol);
+ }
+ if (this.state.agmtBindPW != this.state._agmtBindPW) {
+ cmd.push('--bind-passwd=' + this.state.agmtBindPW);
+ }
+ if (this.state.agmtBindDN != this.state._agmtBindDN) {
+ cmd.push('--bind-passwd=' + this.state.agmtBindDN);
+ }
+ if (this.state.agmtFracAttrs != this.state._agmtFracAttrs) {
+ cmd.push('--frac-list=' + this.state.agmtFracAttrs.join('
'));
+ }
+ if (this.state.agmtFracInitAttrs != this.state._agmtFracInitAttrs) {
+ cmd.push('--frac-list-total=' +
this.state.agmtFracInitAttrs.join(' '));
+ }
+ if (this.state.agmtStripAttrs != this.state._agmtStripAttrs) {
+ cmd.push('--strip-list=' + this.state.agmtStripAttrs.join('
'));
+ }
+ if (this.state.agmtHost != this.state._agmtHost) {
+ cmd.push('--host=' + this.state.agmtHost);
+ }
+ if (this.state.agmtPort != this.state._agmtPort) {
+ cmd.push('--port=' + this.state.agmtPort);
+ }
+
+ this.setState({
+ savingAgmt: true
+ });
+ log_cmd('saveAgmt', 'update replication agreement', cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ this.props.reload(this.props.suffix);
+ if (this._mounted) {
+ this.setState({
+ savingAgmt: false,
+ showEditAgmtModal: false,
+ });
+ }
+ this.props.addNotification(
+ 'success',
+ 'Successfully updated replication agreement'
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to update replication agreement - ${errMsg.desc}`
+ );
+ this.setState({
+ savingAgmt: false
+ });
+ });
+ }
+
+ pokeAgmt (agmtName) {
+ let cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'repl-agmt', 'poke', agmtName, '--suffix=' +
this.props.suffix];
+ log_cmd('pokeAgmt', 'send updates now', cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, "err": "message" })
+ .done(content => {
+ this.props.reload(this.props.suffix);
+ this.props.addNotification(
+ 'success',
+ 'Successfully poked replication agreement'
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ 'error',
+ `Failed to poke replication agreement - ${errMsg.desc}`
+ );
+ });
+ }
+
+ initAgmt (agmtName) {
+ let init_cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'repl-agmt', 'init', '--suffix=' + this.props.suffix,
this.state.agmtName];
+ log_cmd('initAgmt', 'Initialize agreement', init_cmd);
+ cockpit
+ .spawn(init_cmd, { superuser: true, "err": "message"
})
+ .done(content => {
+ var agmtIntervalCount = this.state.agmtInitCounter + 1;
+ var intervals = this.state.agmtInitIntervals;
+ this.props.reload(this.props.suffix);
+ intervals[agmtIntervalCount] = setInterval(this.watchAgmtInit, 2000,
this.state.agmtName, agmtIntervalCount);
+ if (this._mounted) {
+ this.setState({
+ agmtInitCounter: agmtIntervalCount,
+ agmtInitIntervals: intervals,
+ showConfirmInitAgmt: false
+ });
+ }
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ 'error',
+ `Failed to initialize replication agreement - ${errMsg.desc}`
+ );
+ this.setState({
+ showConfirmInitAgmt: false
+ });
+ });
+ }
+
+ confirmToggle (agmtName, state) {
+ if (state == 'Enabled') {
+ this.setState({
+ agmtName: agmtName,
+ showConfirmDisableAgmt: true
+ });
+ } else {
+ this.setState({
+ agmtName: agmtName,
+ showConfirmEnableAgmt: true
+ });
+ }
+ }
+
+ closeConfirmEnableAgmt () {
+ this.setState({
+ showConfirmEnableAgmt: false
+ });
+ }
+
+ closeConfirmDisableAgmt () {
+ this.setState({
+ showConfirmDisableAgmt: false
+ });
+ }
+
+ enableAgmt (agmtName) {
+ // Enable/disable agmt
+ let cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'repl-agmt', 'enable', agmtName, '--suffix=' +
this.props.suffix];
+ log_cmd('enableAgmt', 'enable agmt', cmd);
+ cockpit
+ .spawn(cmd, {superuser: true, "err": "message"})
+ .done(content => {
+ this.props.reload(this.props.suffix);
+ this.props.addNotification(
+ 'success',
+ 'Successfully enabled replication agreement');
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to enabled replication agreement - ${errMsg.desc}`
+ );
+ });
+ }
+
+ disableAgmt (agmtName) {
+ // Enable/disable agmt
+ let cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'repl-agmt', 'disable', agmtName, '--suffix=' +
this.props.suffix];
+ log_cmd('disableAgmt', 'Disable agmt', cmd);
+ cockpit
+ .spawn(cmd, {superuser: true, "err": "message"})
+ .done(content => {
+ this.props.reload(this.props.suffix);
+ this.props.addNotification(
+ 'success',
+ 'Successfully disabled replication agreement');
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to disable replication agreement - ${errMsg.desc}`
+ );
+ });
+ }
+
+ deleteAgmt () {
+ this.setState({
+ deleteSpinning: true
+ });
+ let cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'repl-agmt', 'delete', '--suffix=' +
this.props.suffix, this.state.agmtName];
+ log_cmd('deleteAgmt', 'Delete agmt', cmd);
+ cockpit
+ .spawn(cmd, {superuser: true, "err": "message"})
+ .done(content => {
+ this.props.reload(this.props.suffix);
+ this.props.addNotification(
+ 'success',
+ 'Successfully deleted replication agreement');
+ this.setState({
+ showDeleteConfirm: false,
+ deleteSpinning: false
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to delete replication agreement - ${errMsg.desc}`
+ );
+ this.setState({
+ showDeleteConfirm: false,
+ deleteSpinning: false
+ });
+ });
+ }
+
+ createAgmt () {
+ let cmd = [
+ 'dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' +
this.props.serverId + '.socket',
+ 'repl-agmt', 'create', this.state.agmtName,
'--suffix=' + this.props.suffix,
+ '--host=' + this.state.agmtHost, '--port=' +
this.state.agmtPort,
+ '--bind-method=' + this.state.agmtBindMethod,
'--conn-protocol=' + this.state.agmtProtocol,
+ '--bind-dn=' + this.state.agmtBindDN, '--bind-passwd=' +
this.state.agmtBindPW
+ ];
+
+ // Handle Schedule
+ if (!this.state.agmtSync) {
+ let agmt_days = "";
+ if (this.state.agmtSyncSun) {
+ agmt_days += "0";
+ }
+ if (this.state.agmtSyncMon) {
+ agmt_days += "1";
+ }
+ if (this.state.agmtSyncTue) {
+ agmt_days += "2";
+ }
+ if (this.state.agmtSyncWed) {
+ agmt_days += "3";
+ }
+ if (this.state.agmtSyncThu) {
+ agmt_days += "4";
+ }
+ if (this.state.agmtSyncFri) {
+ agmt_days += "5";
+ }
+ if (this.state.agmtSyncSat) {
+ agmt_days += "6";
+ }
+ cmd.push('--schedule=' +
this.state.agmtStartTime.replace(':', '') + "-" +
this.state.agmtEndTime.replace(':', '') + " " + agmt_days);
+ }
+
+ // Handle fractional and stripped attributes
+ if (this.state.agmtFracAttrs.length > 0) {
+ cmd.push('--frac-list=' + this.state.agmtFracAttrs.join('
'));
+ }
+ if (this.state.agmtFracInitAttrs.length > 0) {
+ cmd.push('--frac-list-total=' + this.state.agmtFracAttrs.join('
'));
+ }
+ if (this.state.agmtStripAttrs.length > 0) {
+ cmd.push('--strip-list=' + this.state.agmtStripAttrs.join('
'));
+ }
+
+ this.setState({
+ savingAgmt: true
+ });
+ log_cmd('createAgmt', 'Create replication agreement', cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ this.props.reload(this.props.suffix);
+ if (this._mounted) {
+ this.setState({
+ savingAgmt: false,
+ showCreateAgmtModal: false,
+ });
+ }
+ this.props.addNotification(
+ 'success',
+ 'Successfully created replication agreement'
+ );
+ if (this.state.agmtInit == 'online-init') {
+ this.initAgmt(this.state.agmtName);
+ }
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to create replication agreement - ${errMsg.desc}`
+ );
+ this.setState({
+ savingAgmt: false
+ });
+ });
+ }
+
+ watchAgmtInit(agmtName, idx) {
+ // Watch the init, then clear the interval index
+ let status_cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'repl-agmt', 'init-status', '--suffix=' +
this.props.suffix, agmtName];
+ log_cmd('watchAgmtInit', 'Get initialization status for agmt',
status_cmd);
+ cockpit
+ .spawn(status_cmd, {superuser: true, "err":
"message"})
+ .done(data => {
+ let init_status = JSON.parse(data);
+ if (init_status.startsWith('Agreement successfully
initialized') ||
+ init_status.startsWith('Agreement initialization
failed')) {
+ // Either way we're done, stop watching the status
+ clearInterval(this.state.agmtInitIntervals[idx]);
+ }
+ this.props.reload(this.props.suffix);
+ });
+ }
+
+ render() {
+ return (
+ <div className="ds-margin-right">
+ <ReplAgmtTable
+ rows={this.props.rows}
+ edit={this.showEditAgmt}
+ poke={this.pokeAgmt}
+ init={this.showConfirmInitAgmt}
+ enable={this.confirmToggle}
+ delete={this.showConfirmDeleteAgmt}
+ />
+ <div className="ds-margin-top ds-container ds-inline">
+ <Button
+ bsStyle="primary"
+ onClick={this.showCreateAgmtModal}
+
+ Create
Agreement
+ </Button>
+ <Button
+ className="ds-left-margin"
+ bsStyle="default"
+ onClick={() => {
+ this.props.reload(this.props.suffix);
+ }}
+
+ Refresh
Agreements
+ </Button>
+ </div>
+ <ReplAgmtModal
+ showModal={this.state.showCreateAgmtModal}
+ closeHandler={this.closeCreateAgmtModal}
+ handleChange={this.handleChange}
+ handleStripChange={this.handleTAStripAttrChange}
+ handleFracChange={this.handleTAFracAttrChange}
+ handleFracInitChange={this.handleTAFracInitAttrChange}
+ saveHandler={this.createAgmt}
+ spinning={this.state.savingAgmt}
+ agmtName={this.state.agmtName}
+ agmtHost={this.state.agmtHost}
+ agmtPort={this.state.agmtPort}
+ agmtBindDN={this.state.agmtBindDN}
+ agmtBindPW={this.state.agmtBindPW}
+ agmtBindPWConfirm={this.state.agmtBindPWConfirm}
+ agmtProtocol={this.state.agmtProtocol}
+ agmtBindMethod={this.state.agmtBindMethod}
+ agmtStripAttrs={this.state.agmtStripAttrs}
+ agmtFracAttrs={this.state.agmtFracAttrs}
+ agmtFracInitAttrs={this.state.agmtFracInitAttrs}
+ agmtSync={this.state.agmtSync}
+ agmtSyncMon={this.state.agmtSyncMon}
+ agmtSyncTue={this.state.agmtSyncTue}
+ agmtSyncWed={this.state.agmtSyncWed}
+ agmtSyncThu={this.state.agmtSyncThu}
+ agmtSyncFri={this.state.agmtSyncFri}
+ agmtSyncSat={this.state.agmtSyncSat}
+ agmtSyncSun={this.state.agmtSyncSun}
+ agmtStartTime={this.state.agmtStartTime}
+ agmtEndTime={this.state.agmtEndTime}
+ availAttrs={this.props.attrs}
+ error={this.state.errObj}
+ errorMsg={this.state.modalMsg}
+ errorScheduleMsg={this.state.modalScheduleMsg}
+ saveOK={this.state.agmtSaveOK}
+ />
+ <ReplAgmtModal
+ showModal={this.state.showEditAgmtModal}
+ closeHandler={this.closeEditAgmtModal}
+ handleChange={this.handleChange}
+ handleStripChange={this.handleTAStripAttrChangeEdit}
+ handleFracChange={this.handleTAFracAttrChangeEdit}
+ handleFracInitChange={this.handleTAFracInitAttrChangeEdit}
+ saveHandler={this.saveAgmt}
+ spinning={this.state.savingAgmt}
+ agmtName={this.state.agmtName}
+ agmtHost={this.state.agmtHost}
+ agmtPort={this.state.agmtPort}
+ agmtBindDN={this.state.agmtBindDN}
+ agmtBindPW={this.state.agmtBindPW}
+ agmtBindPWConfirm={this.state.agmtBindPWConfirm}
+ agmtProtocol={this.state.agmtProtocol}
+ agmtBindMethod={this.state.agmtBindMethod}
+ agmtStripAttrs={this.state.agmtStripAttrs}
+ agmtFracAttrs={this.state.agmtFracAttrs}
+ agmtFracInitAttrs={this.state.agmtFracInitAttrs}
+ agmtSync={this.state.agmtSync}
+ agmtSyncMon={this.state.agmtSyncMon}
+ agmtSyncTue={this.state.agmtSyncTue}
+ agmtSyncWed={this.state.agmtSyncWed}
+ agmtSyncThu={this.state.agmtSyncThu}
+ agmtSyncFri={this.state.agmtSyncFri}
+ agmtSyncSat={this.state.agmtSyncSat}
+ agmtSyncSun={this.state.agmtSyncSun}
+ agmtStartTime={this.state.agmtStartTime}
+ agmtEndTime={this.state.agmtEndTime}
+ availAttrs={this.props.attrs}
+ error={this.state.errObj}
+ errorMsg={this.state.modalMsg}
+ errorScheduleMsg={this.state.modalScheduleMsg}
+ saveOK={this.state.agmtSaveOK}
+ edit
+ />
+ <DoubleConfirmModal
+ showModal={this.state.showConfirmDeleteAgmt}
+ closeHandler={this.closeConfirmDeleteAgmt}
+ handleChange={this.handleModalChange}
+ actionHandler={this.deleteAgmt}
+ spinning={this.state.modalSpinning}
+ item={this.state.agmtName}
+ checked={this.state.modalChecked}
+ mTitle="Delete Replication Agreement"
+ mMsg="Are you sure you want to delete this replication
agreement?"
+ mSpinningMsg="Deleting Replication Agreement ..."
+ mBtnName="Delete Agreement"
+ />
+ <DoubleConfirmModal
+ showModal={this.state.showConfirmInitAgmt}
+ closeHandler={this.closeConfirmInitAgmt}
+ handleChange={this.handleModalChange}
+ actionHandler={this.initAgmt}
+ spinning={this.state.modalSpinning}
+ item={this.state.agmtName}
+ checked={this.state.modalChecked}
+ mTitle="Initialize Replication Agreement"
+ mMsg="Are you sure you want to initialize this replication
agreement?"
+ mSpinningMsg="Initializing Replication Agreement ..."
+ mBtnName="Initialize Agreement"
+ />
+ <ConfirmPopup
+ showModal={this.state.showConfirmEnableAgmt}
+ closeHandler={this.closeConfirmEnableAgmt}
+ actionFunc={this.enableAgmt}
+ actionParam={this.state.agmtName}
+ msg="Are you sure you want to enable this replication
agreement?"
+ msgContent={this.state.agmtName}
+ />
+ <ConfirmPopup
+ showModal={this.state.showConfirmDisableAgmt}
+ closeHandler={this.closeConfirmDisableAgmt}
+ actionFunc={this.disableAgmt}
+ actionParam={this.state.agmtName}
+ msg="Are you sure you want to disable this replication
agreement?"
+ msgContent={this.state.agmtName}
+ />
+ </div>
+ );
+ }
+}
+
+ReplAgmts.propTypes = {
+ suffix: PropTypes.string,
+ serverId: PropTypes.string,
+ rows: PropTypes.array,
+ addNotification: PropTypes.func,
+ attrs: PropTypes.array,
+};
+
+ReplAgmts.defaultProps = {
+ serverId: "",
+ suffix: "",
+ rows: [],
+ addNotification: noop,
+ attrs: [],
+};
diff --git a/src/cockpit/389-console/src/lib/replication/replChangelog.jsx
b/src/cockpit/389-console/src/lib/replication/replChangelog.jsx
new file mode 100644
index 0000000..1386393
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/replication/replChangelog.jsx
@@ -0,0 +1,395 @@
+import cockpit from "cockpit";
+import React from "react";
+import "../../css/ds.css";
+import PropTypes from "prop-types";
+import { ConfirmPopup } from "../notifications.jsx";
+import { log_cmd } from "../tools.jsx";
+import {
+ noop,
+ Row,
+ Button,
+ Col,
+ ControlLabel,
+ Checkbox,
+ Form,
+ Icon,
+ Spinner,
+} from "patternfly-react";
+
+export class Changelog extends React.Component {
+ constructor (props) {
+ super(props);
+ this.state = {
+ loading: false,
+ errObj: {},
+ showConfirmDelete: false,
+ // Changelog settings
+ clDir: this.props.clDir,
+ clMaxEntries: this.props.clMaxEntries,
+ clMaxAge: this.props.clMaxAge,
+ clCompactInt: this.props.clCompactInt,
+ clTrimInt: this.props.clTrimInt,
+ clEncrypt: this.props.clEncrypt,
+ // Preserve original settings
+ _clDir: this.props.clDir,
+ _clMaxEntries: this.props.clMaxEntries,
+ _clMaxAge: this.props.clMaxAge,
+ _clCompactInt: this.props.clCompactInt,
+ _clTrimInt: this.props.clTrimInt,
+ _clEncrypt: this.props.clEncrypt,
+ };
+
+ this.handleChange = this.handleChange.bind(this);
+ this.saveSettings = this.saveSettings.bind(this);
+ this.createChangelog = this.createChangelog.bind(this);
+ this.confirmChangelogDelete = this.confirmChangelogDelete.bind(this);
+ this.closeConfirmDelete = this.closeConfirmDelete.bind(this);
+ this.deleteChangelog = this.deleteChangelog.bind(this);
+ }
+
+ componentDidMount() {
+ this.props.enableTree();
+ }
+
+ createChangelog () {
+ this.setState({
+ saving: true
+ });
+ let cmd = [
+ 'dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' +
this.props.serverId + '.socket',
+ 'replication', 'create-changelog'
+ ];
+ cockpit
+ .spawn(cmd, {superuser: true, "err": "message"})
+ .done(content => {
+ this.props.reload();
+ this.props.addNotification(
+ "success",
+ "Successfully created replication changelog"
+ );
+ this.setState({
+ saving: false
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.reload();
+ this.setState({
+ saving: false
+ });
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.props.addNotification(
+ "error",
+ `Error creating changelog - ${msg}`
+ );
+ });
+ }
+
+ confirmChangelogDelete () {
+ this.setState({
+ showConfirmDelete: true
+ });
+ }
+
+ closeConfirmDelete () {
+ this.setState({
+ showConfirmDelete: false
+ });
+ }
+
+ deleteChangelog () {
+ this.setState({
+ saving: true
+ });
+ let cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'replication', 'delete-changelog'
+ ];
+ cockpit
+ .spawn(cmd, {superuser: true, "err": "message"})
+ .done(content => {
+ this.props.reload();
+ this.props.addNotification(
+ "success",
+ "Successfully deleted replication changelog"
+ );
+ this.setState({
+ saving: false
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.reload();
+ this.setState({
+ saving: false
+ });
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.props.addNotification(
+ "error",
+ `Error deleting changelog - ${msg}`
+ );
+ });
+ }
+
+ saveSettings () {
+ let cmd = [
+ 'dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' +
this.props.serverId + '.socket',
+ 'replication', 'set-changelog'
+ ];
+ if (this.state.clDir != this.state._clDir) {
+ if (this.state.clDir == "") {
+ // Changelog directory can not be empty
+ let errObj = this.state.errObj;
+ errObj["clDir"] = true;
+ this.setState({
+ errObj: errObj
+ });
+ return;
+ }
+ cmd.push("--cl-dir =" + this.state.clDir);
+ }
+ if (this.state.clMaxEntries != this.state._clMaxEntries) {
+ cmd.push("--max-entries=" + this.state.clMaxEntries);
+ }
+ if (this.state.clMaxAge != this.state._clMaxAge) {
+ cmd.push("--max-age=" + this.state.clMaxAge);
+ }
+ if (this.state.clCompactInt != this.state._clCompactInt) {
+ cmd.push("--compact-interval=" + this.state.clCompactInt);
+ }
+ if (this.state.clTrimInt != this.state._clTrimInt) {
+ cmd.push("--trim-interval=" + this.state.clTrimInt);
+ }
+ if (this.state.clEncrypt != this.state._clEncrypt) {
+ // TODO - Not implemented in dsconf yet
+ // cmd.push("--encrypt=" + this.state.clEncrypt);
+ }
+ if (cmd.length > 5) {
+ this.setState({
+ // Start the spinner
+ saving: true
+ });
+ log_cmd("saveSettings", "Applying replication changelog
changes", cmd);
+ let msg = "Successfully updated changelog configuration.";
+ cockpit
+ .spawn(cmd, {superuser: true, "err": "message"})
+ .done(content => {
+ this.props.reload();
+ this.props.addNotification(
+ "success",
+ msg
+ );
+ this.setState({
+ saving: false
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.reload();
+ this.setState({
+ saving: false
+ });
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.props.addNotification(
+ "error",
+ `Error updating changelog configuration - ${msg}`
+ );
+ });
+ }
+ }
+
+ handleChange(e) {
+ const value = e.target.type === 'checkbox' ? e.target.checked :
e.target.value;
+ let valueErr = false;
+ let errObj = this.state.errObj;
+ if (e.target.id == "clDir" && value == "") {
+ valueErr = true;
+ }
+ errObj[e.target.id] = valueErr;
+ this.setState({
+ [e.target.id]: value,
+ errObj: errObj
+ });
+ }
+
+ render() {
+ let clPage;
+ if (this.state._clDir == "") {
+ // No changelog, only show clDir and Create button
+ clPage =
+ <div>
+ <Row>
+ <Col sm={12} className="ds-word-wrap">
+ <ControlLabel className="ds-suffix-header">
+ Replication Changelog
+ <Icon className="ds-left-margin ds-refresh"
+ type="fa" name="refresh"
title="Refresh changelog settings"
+ onClick={this.props.reload}
+ />
+ </ControlLabel>
+ </Col>
+ </Row>
+ <hr />
+ <div className="ds-margin-top-med ds-center">
+ <p>There is no Replication Changelog</p>
+ <Row className="ds-margin-top-lg" title="Create
the replication changelog">
+ <Button
+ bsStyle="primary"
+ onClick={this.createChangelog}
+
+ Create Changelog
+ </Button>
+ </Row>
+ </div>
+ </div>;
+ } else if (this.state.saving) {
+ clPage =
+ <div className="ds-margin-top ds-loading-spinner
ds-center">
+ <h4>Saving changelog configuration ...</h4>
+ <Spinner className="ds-margin-top-lg" loading
size="md" />
+ </div>;
+ } else if (this.props.loading) {
+ clPage =
+ <div className="ds-loading-spinner ds-center">
+ <h4>Loading changelog configuration ...</h4>
+ <Spinner className="ds-margin-top-lg" loading
size="md" />
+ </div>;
+ } else {
+ clPage =
+ <div>
+ <Row>
+ <Col sm={5} className="ds-word-wrap">
+ <h4>
+ Replication Changelog
+ <Icon className="ds-left-margin ds-refresh"
+ type="fa" name="refresh"
title="Refresh changelog settings"
+ onClick={this.props.reload}
+ />
+ </h4>
+ </Col>
+ <Col sm={7}>
+ <Button
+ className="ds-float-right"
+ bsStyle="danger"
+ onClick={this.confirmChangelogDelete}
+
+ Delete Changelog
+ </Button>
+ </Col>
+ </Row>
+ <Form horizontal>
+ <hr />
+ <Row className="ds-margin-top" title="The
filesystem location of the replication changelog database">
+ <Col componentClass={ControlLabel} sm={4}>
+ Changelog Directory
+ </Col>
+ <Col sm={8}>
+ <input value={this.state.clDir} id="clDir"
onChange={this.handleChange} className={this.state.errObj.clDir ?
"ds-input-auto-bad" : "ds-input-auto"} />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="Changelog
trimming parameter. Set the maximum number of changelog entries allowed in the database
(nsslapd-changelogmaxentries).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Changelog Maximum Entries
+ </Col>
+ <Col sm={8}>
+ <input value={this.state.clMaxEntries}
id="clMaxEntries" onChange={this.handleChange}
className="ds-input-auto" />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="Changelog
trimming parameter. This set the maximum age of a changelog entry. It is recommended to
use the same value as the Replication Purge Delay. (nsslapd-changelogmaxage).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Changelog Maximum Age
+ </Col>
+ <Col sm={8}>
+ <input value={this.state.clMaxAge}
id="clMaxAge" onChange={this.handleChange} className="ds-input-auto"
/>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The
changelog trimming interval. Set how often the changelog checks if there are entries that
can be purged from the changelog based on the trimming parameters
(nsslapd-changelogtrim-interval).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Changelog Trimming Interval
+ </Col>
+ <Col sm={8}>
+ <input value={this.state.clTrimInt}
id="clTrimInt" onChange={this.handleChange} className="ds-input-auto"
/>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The
changelog compaction interval. Set how often the changelog will compact itself, meaning
remove empty/trimmed database slots. The default is 30 days.
(nsslapd-changelogcompactdb-interval).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Changelog Compaction Interval
+ </Col>
+ <Col sm={8}>
+ <input value={this.state.clCompactInt}
id="clCompactInt" onChange={this.handleChange}
className="ds-input-auto" />
+ </Col>
+ </Row>
+ <Row hidden className="ds-margin-top"
title="TLS must first be enabled in the server for change encryption to work. Please
consult Administration Guide for more details.">
+ <Col componentClass={ControlLabel} sm={4}>
+ Changelog Encryption
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="clEncrypt"
+ checked={this.state.clEncrypt}
+ onChange={this.handleChange}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top-lg">
+ <Col sm={2}>
+ <Button
+ bsStyle="primary"
+ onClick={this.saveSettings}
+
+
Save
+ </Button>
+ </Col>
+ </Row>
+ </Form>
+ </div>;
+ }
+
+ return (
+ <div>
+ {clPage}
+ <ConfirmPopup
+ showModal={this.state.showConfirmDelete}
+ closeHandler={this.closeConfirmDelete}
+ actionFunc={this.deleteChangelog}
+ msg="Are you sure you want to delete the changelog?"
+ msgContent="This will invalidate all replication agreements, and
they will need to be reinitialized."
+ />
+ </div>
+ );
+ }
+}
+
+Changelog.propTypes = {
+ serverId: PropTypes.string,
+ clDir: PropTypes.string,
+ clMaxEntries: PropTypes.string,
+ clMaxAge: PropTypes.string,
+ clCompactInt: PropTypes.string,
+ clTrimInt: PropTypes.string,
+ clEncrypt: PropTypes.bool,
+ addNotification: PropTypes.func,
+ reload: PropTypes.func,
+ enableTree: PropTypes.func,
+};
+
+Changelog.defaultProps = {
+ serverId: "",
+ clDir: "",
+ clMaxEntries: "",
+ clMaxAge: "",
+ clCompactInt: "",
+ clTrimInt: "",
+ clEncrypt: false,
+ addNotification: noop,
+ reload: noop,
+ enableTree: noop,
+};
diff --git a/src/cockpit/389-console/src/lib/replication/replConfig.jsx
b/src/cockpit/389-console/src/lib/replication/replConfig.jsx
new file mode 100644
index 0000000..2bec513
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/replication/replConfig.jsx
@@ -0,0 +1,640 @@
+import cockpit from "cockpit";
+import React from "react";
+import { log_cmd, valid_dn } from "../tools.jsx";
+import { ConfirmPopup } from "../notifications.jsx";
+import CustomCollapse from "../customCollapse.jsx";
+import { ManagerTable } from "./replTables.jsx";
+import { AddManagerModal, ChangeReplRoleModal } from "./replModals.jsx";
+import {
+ Button,
+ Row,
+ Checkbox,
+ Col,
+ ControlLabel,
+ Form,
+ FormControl,
+ Spinner,
+ // noop,
+} from "patternfly-react";
+// import PropTypes from "prop-types";
+import "../../css/ds.css";
+
+export class ReplConfig extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ saving: false,
+ showConfirmManagerDelete: false,
+ showAddManagerModal: false,
+ showPromoteDemoteModal: false,
+ addManagerSpinning: false,
+ roleChangeSpinning: false,
+ manager: "cn=replication manager,cn=config",
+ manager_passwd: "",
+ manager_passwd_confirm: "",
+ newRole: this.props.role == "Master" ? "Hub" :
"Master",
+ newRID: "1",
+ modalChecked: false,
+ errObj: {},
+ // Config Settings
+ nsds5replicabinddn: this.props.data['nsds5replicabinddn'],
+ nsds5replicabinddngroup: this.props.data['nsds5replicabinddngroup'],
+ nsds5replicabinddngroupcheckinterval:
this.props.data['nsds5replicabinddngroupcheckinterval'],
+ nsds5replicareleasetimeout:
this.props.data['nsds5replicareleasetimeout'],
+ nsds5replicapurgedelay: this.props.data['nsds5replicapurgedelay'],
+ nsds5replicatombstonepurgeinterval:
this.props.data['nsds5replicatombstonepurgeinterval'],
+ nsds5replicaprecisetombstonepurging:
this.props.data['nsds5replicaprecisetombstonepurging'],
+ nsds5replicaprotocoltimeout:
this.props.data['nsds5replicaprotocoltimeout'],
+ nsds5replicabackoffmin: this.props.data['nsds5replicabackoffmin'],
+ nsds5replicabackoffmax: this.props.data['nsds5replicabackoffmax'],
+ // Original settings
+ _nsds5replicabinddn: this.props.data['nsds5replicabinddn'],
+ _nsds5replicabinddngroup:
this.props.data['nsds5replicabinddngroup'],
+ _nsds5replicabinddngroupcheckinterval:
this.props.data['nsds5replicabinddngroupcheckinterval'],
+ _nsds5replicareleasetimeout:
this.props.data['nsds5replicareleasetimeout'],
+ _nsds5replicapurgedelay: this.props.data['nsds5replicapurgedelay'],
+ _nsds5replicatombstonepurgeinterval:
this.props.data['nsds5replicatombstonepurgeinterval'],
+ _nsds5replicaprecisetombstonepurging:
this.props.data['nsds5replicaprecisetombstonepurging'],
+ _nsds5replicaprotocoltimeout:
this.props.data['nsds5replicaprotocoltimeout'],
+ _nsds5replicabackoffmin: this.props.data['nsds5replicabackoffmin'],
+ _nsds5replicabackoffmax: this.props.data['nsds5replicabackoffmax'],
+
+ };
+
+ this.confirmManagerDelete = this.confirmManagerDelete.bind(this);
+ this.closeConfirmManagerDelete = this.closeConfirmManagerDelete.bind(this);
+ this.deleteManager = this.deleteManager.bind(this);
+ this.showAddManager = this.showAddManager.bind(this);
+ this.closeAddManagerModal = this.closeAddManagerModal.bind(this);
+ this.addManager = this.addManager.bind(this);
+ this.handleChange = this.handleChange.bind(this);
+ this.handleManagerChange = this.handleManagerChange.bind(this);
+ this.showPromoteDemoteModal = this.showPromoteDemoteModal.bind(this);
+ this.closePromoteDemoteModal = this.closePromoteDemoteModal.bind(this);
+ this.doRoleChange = this.doRoleChange.bind(this);
+ this.saveConfig = this.saveConfig.bind(this);
+ }
+
+ doRoleChange (changeType) {
+ let action = "demote";
+ if (changeType == "Promoting") {
+ action = "promote";
+ }
+ let cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
'replication', action,
+ '--suffix=' + this.props.suffix, "--newrole=" +
this.state.newRole];
+ if (this.state.newRole == "Master") {
+ let ridNum = parseInt(this.state.newRID, 10);
+ if (ridNum < 1 || ridNum >= 65535) {
+ this.props.addNotification(
+ "error",
+ "A Master replica requires a unique numerical identifier.
Please enter an ID between 1 and 65534"
+ );
+ return;
+ }
+ cmd.push("--replica-id=" + this.state.newRID);
+ }
+ this.setState({
+ roleChangeSpinning: true
+ });
+ log_cmd('doRoleChange', 'change replica role', cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ this.props.reload();
+ this.props.addNotification(
+ "success",
+ `Successfully ${action}d replica to a ${this.state.newRole}`
+ );
+ this.setState({
+ roleChangeSpinning: false,
+ showPromoteDemoteModal: false
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.reload();
+ this.props.addNotification(
+ "error",
+ `Failed to ${action} replica - ${errMsg.desc}`
+ );
+ this.setState({
+ roleChangeSpinning: false,
+ showPromoteDemoteModal: false
+ });
+ });
+ }
+
+ closePromoteDemoteModal () {
+ this.setState({
+ showPromoteDemoteModal: false
+ });
+ }
+
+ showPromoteDemoteModal () {
+ this.setState({
+ showPromoteDemoteModal: true,
+ modalChecked: false,
+ });
+ }
+
+ closeAddManagerModal () {
+ this.setState({
+ showAddManagerModal: false
+ });
+ }
+
+ showAddManager () {
+ this.setState({
+ showAddManagerModal: true,
+ manager: "cn=replication manager,cn=config",
+ manager_passwd: "",
+ manager_passwd_confirm: "",
+ errObj: {
+ manager_passwd: true,
+ manager_passwd_confirm: true,
+ }
+ });
+ }
+
+ addManager () {
+ // Validate DN
+ if (!valid_dn(this.state.manager)) {
+ this.props.addNotification(
+ "error",
+ `Invalid DN for the Replication Manager: ${this.state.manager}`
+ );
+ return;
+ }
+
+ if (this.state.manager_passwd == "" ||
this.state.manager_passwd_confirm == "") {
+ this.props.addNotification(
+ "error", "You must provide a password for the Replication
Manager"
+ );
+ return;
+ }
+ if (this.state.manager_passwd != this.state.manager_passwd_confirm) {
+ this.props.addNotification(
+ "error", "Passwords do not match"
+ );
+ return;
+ }
+
+ this.setState({
+ addManagerSpinning: true
+ });
+
+ let cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-"
+ this.props.serverId + ".socket",
+ "replication", "create-manager", "--suffix=" +
this.props.suffix, "--name=" + this.state.manager,
+ "--passwd=" + this.state.manager_passwd
+ ];
+
+ log_cmd("addManager", "Adding Replication Manager", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ this.props.reloadConfig(this.props.suffix);
+ this.props.addNotification(
+ "success",
+ `Successfully added Replication Manager`
+ );
+ this.setState({
+ addManagerSpinning: false,
+ showAddManagerModal: false
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.reloadConfig(this.props.suffix);
+ this.props.addNotification(
+ "error",
+ `Failure adding Replication Manager - ${errMsg.desc}`
+ );
+ this.setState({
+ addManagerSpinning: false,
+ showAddManagerModal: false
+ });
+ });
+ }
+
+ handleChange(e) {
+ let value = e.target.type === 'checkbox' ? e.target.checked :
e.target.value;
+ let valueErr = false;
+ let errObj = this.state.errObj;
+ if (value == "") {
+ valueErr = true;
+ }
+ errObj[e.target.id] = valueErr;
+ this.setState({
+ [e.target.id]: value,
+ errObj: errObj
+ });
+ }
+
+ handleManagerChange(e) {
+ let value = e.target.value;
+ let attr = e.target.id;
+ let valueErr = false;
+ let errObj = this.state.errObj;
+ if (value == "") {
+ valueErr = true;
+ }
+ // Handle password chnages
+ if (attr == "manager_passwd") {
+ if (value != this.state.manager_passwd_confirm) {
+ // No match
+ valueErr = true;
+ } else {
+ errObj[attr] = false;
+ errObj['manager_passwd_confirm'] = false;
+ }
+ } else if (attr == "manager_passwd_confirm") {
+ if (value != this.state.manager_passwd) {
+ // No match
+ valueErr = true;
+ } else {
+ errObj[attr] = false;
+ errObj['manager_passwd'] = false;
+ }
+ }
+
+ errObj[attr] = valueErr;
+ this.setState({
+ [attr]: value,
+ errObj: errObj
+ });
+ }
+
+ confirmManagerDelete (item) {
+ this.setState({
+ showConfirmManagerDelete: true,
+ manager: item.name,
+ });
+ }
+
+ closeConfirmManagerDelete () {
+ this.setState({
+ showConfirmManagerDelete: false,
+ manager: "",
+ });
+ }
+
+ deleteManager (dn) {
+ let cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-"
+ this.props.serverId + ".socket",
+ "replication", "delete-manager", "--suffix=" +
this.props.suffix, "--name=" + dn
+ ];
+ log_cmd("deleteManager", "Deleting Replication Manager",
cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ this.props.reloadConfig(this.props.suffix);
+ this.props.addNotification(
+ "success",
+ `Successfully removed Replication Manager`
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.reloadConfig(this.props.suffix);
+ this.props.addNotification(
+ "error",
+ `Failure removing Replication Manager - ${errMsg.desc}`
+ );
+ });
+ }
+
+ saveConfig () {
+ let cmd = [
+ 'dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' +
this.props.serverId + '.socket',
+ 'replication', 'set', '--suffix=' +
this.props.suffix
+ ];
+
+ if (this.state.nsds5replicabackoffmax != this.state._nsds5replicabackoffmax) {
+ cmd.push("--repl-backoff-max=" +
this.state.nsds5replicabackoffmax);
+ }
+ if (this.state.nsds5replicabackoffmin != this.state._nsds5replicabackoffmin) {
+ cmd.push("--repl-backoff-min=" +
this.state.nsds5replicabackoffmin);
+ }
+
+ if (this.state.nsds5replicaprotocoltimeout !=
this.state._nsds5replicaprotocoltimeout) {
+ cmd.push("--repl-protocol-timeout=" +
this.state.nsds5replicaprotocoltimeout);
+ }
+ if (this.state.nsds5replicaprecisetombstonepurging !=
this.state._nsds5replicaprecisetombstonepurging) {
+ if (this.state.nsds5replicaprecisetombstonepurging) {
+ cmd.push("--repl-fast-tombstone-purging=on");
+ } else {
+ cmd.push("--repl-fast-tombstone-purging=off");
+ }
+ }
+ if (this.state.nsds5replicatombstonepurgeinterval !=
this.state._nsds5replicatombstonepurgeinterval) {
+ cmd.push("--repl-tombstone-purge-interval=" +
this.state.nsds5replicatombstonepurgeinterval);
+ }
+ if (this.state.nsds5replicabinddngroup != this.state._nsds5replicabinddngroup) {
+ cmd.push("--repl-bind-group=" +
this.state.nsds5replicabinddngroup);
+ }
+ if (this.state.nsds5replicabinddngroupcheckinterval !=
this.state._nsds5replicabinddngroupcheckinterval) {
+ cmd.push("--repl-bind-group-interval=" +
this.state.nsds5replicabinddngroupcheckinterval);
+ }
+ if (this.state.nsds5replicareleasetimeout !=
this.state._nsds5replicareleasetimeout) {
+ cmd.push("--repl-release-timeout=" +
this.state.nsds5replicareleasetimeout);
+ }
+ if (this.state.nsds5replicapurgedelay != this.state._nsds5replicapurgedelay) {
+ cmd.push("--repl-purge-delay=" +
this.state.nsds5replicapurgedelay);
+ }
+ if (cmd.length > 6) {
+ this.setState({
+ // Start the spinner
+ saving: true
+ });
+ log_cmd("saveConfig", "Applying replication changes",
cmd);
+ let msg = "Successfully updated replication configuration.";
+ cockpit
+ .spawn(cmd, {superuser: true, "err": "message"})
+ .done(content => {
+ this.props.reloadConfig(this.props.suffix);
+ this.props.addNotification(
+ "success",
+ msg
+ );
+ this.setState({
+ saving: false
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.reloadConfig(this.props.suffix);
+ this.setState({
+ saving: false
+ });
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.props.addNotification(
+ "error",
+ `Error updating replication configuration - ${msg}`
+ );
+ });
+ }
+ }
+
+ render() {
+ let content = "";
+ let roleButton = "";
+ let manager_rows = [];
+ for (let row of this.props.data.nsds5replicabinddn) {
+ manager_rows.push({'name': row});
+ }
+
+ if (this.props.role == "Master") {
+ roleButton =
+ <Button
+ bsStyle="primary"
+ onClick={this.showPromoteDemoteModal}
+ title="Demote this Master replica to a Hub or Consumer"
+ className="ds-inline-btn"
+
+ Demote
+ </Button>;
+ } else if (this.props.role == "Hub") {
+ roleButton =
+ <Button
+ bsStyle="primary"
+ onClick={this.showPromoteDemoteModal}
+ title="Promote or Demote this Hub replica to a Master or
Consumer"
+ className="ds-inline-btn"
+
+ Promote/Demote
+ </Button>;
+ } else {
+ // Consumer
+ roleButton =
+ <Button
+ bsStyle="primary"
+ onClick={this.showPromoteDemoteModal}
+ title="Promte this Consumer replica to a Master or Hub"
+ className="ds-inline-btn"
+
+ Promote
+ </Button>;
+ }
+
+ if (this.state.saving) {
+ content =
+ <div className="ds-margin-top-xxlg ds-loading-spinner-tree
ds-center">
+ <h4>Saving replication configuration ...</h4>
+ <Spinner loading size="md" />
+ </div>;
+ } else {
+ content =
+ <div className="ds-margin-top-xxlg ds-left-margin">
+ <Form horizontal>
+ <Row className="ds-margin-top-xlg">
+ <Col sm={2}>
+ <ControlLabel>
+ Replica Role
+ </ControlLabel>
+ </Col>
+ <Col sm={6}>
+ <input type="text"
defaultValue={this.props.role} size="10" disabled />{roleButton}
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={2}>
+ <ControlLabel>
+ Replica ID
+ </ControlLabel>
+ </Col>
+ <Col sm={4}>
+ <input type="text"
defaultValue={this.props.data.nsds5replicaid} size="10" disabled />
+ </Col>
+ </Row>
+ <hr />
+ <Row className="ds-margin-top">
+ <Col sm={9}>
+ <ManagerTable
+ rows={manager_rows}
+ confirmDelete={this.confirmManagerDelete}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <Button
+ bsStyle="primary"
+ onClick={this.showAddManager}
+
+
Add Replication Manager
+ </Button>
+ </Col>
+ </Row>
+ <CustomCollapse>
+ <div className="ds-margin-top">
+ <div className="ds-margin-left">
+ <Row className="ds-margin-top-lg"
title="The DN of the replication manager">
+ <Col componentClass={ControlLabel} sm={4}>
+ Bind DN Group
+ </Col>
+ <Col sm={6}>
+ <FormControl
+ id="nsds5replicabinddngroup"
+ type="text"
+
defaultValue={this.state.nsds5replicabinddngroup}
+ onChange={this.handleChange}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="The interval to check for any changes in the group memebrship specified in the
Bind DN Group and automatically rebuilds the list for the replication managers
accordingly. (nsds5replicabinddngroupcheckinterval).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Bind DN Group Check Interval
+ </Col>
+ <Col sm={6}>
+ <FormControl
+
id="nsds5replicabinddngroupcheckinterval"
+ type="text"
+
defaultValue={this.state.nsds5replicabinddngroupcheckinterval}
+ onChange={this.handleChange}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="This controls the maximum age of deleted entries (tombstone entries), and
entry state information. (nsds5replicapurgedelay).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Purge Delay
+ </Col>
+ <Col sm={6}>
+ <FormControl
+ id="nsds5replicapurgedelay"
+ type="text"
+
defaultValue={this.state.nsds5replicapurgedelay}
+ onChange={this.handleChange}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="This attribute specifies the time interval in seconds between purge operation
cycles. (nsds5replicatombstonepurgeinterval).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Tombstone Purge Interval
+ </Col>
+ <Col sm={6}>
+ <FormControl
+
id="nsds5replicatombstonepurgeinterval"
+ type="text"
+
defaultValue={this.state.nsds5replicatombstonepurgeinterval}
+ onChange={this.handleChange}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="A time limit (in seconds) that tells a replication session to yield if other
replicas are trying to acquire this one (nsds5replicareleasetimeout).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Replica Release Timeout
+ </Col>
+ <Col sm={6}>
+ <FormControl
+
id="nsds5replicareleasetimeout"
+ type="text"
+
defaultValue={this.state.nsds5replicareleasetimeout}
+ onChange={this.handleChange}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="A timeout on how long to wait before stopping a replication session when the
server is being stopped, replication is being disabled, or when removing a replication
agreement. (nsds5replicaprotocoltimeout).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Replication Timeout
+ </Col>
+ <Col sm={6}>
+ <FormControl
+
id="nsds5replicaprotocoltimeout"
+ type="text"
+
defaultValue={this.state.nsds5replicaprotocoltimeout}
+ onChange={this.handleChange}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="This is the minimum amount of time in seconds that a replication will go into
a backoff state (nsds5replicabackoffmin).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Back Off Minimum
+ </Col>
+ <Col sm={6}>
+ <FormControl
+ id="nsds5replicabackoffmin"
+ type="text"
+
defaultValue={this.state.nsds5replicabackoffmin}
+ onChange={this.handleChange}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="This is the maximum amount of time in seconds that a replication will go into
a backoff state (nsds5replicabackoffmax).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Back Off Maximum
+ </Col>
+ <Col sm={6}>
+ <FormControl
+ id="nsds5replicabackoffmax"
+ type="text"
+
defaultValue={this.state.nsds5replicabackoffmax}
+ onChange={this.handleChange}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="Enables faster tombstone purging
(nsds5replicaprecisetombstonepurging)">
+ <Col componentClass={ControlLabel} sm={4}>
+ Fast Tombstone Purging
+ </Col>
+ <Col sm={6}>
+ <Checkbox
+
id="nsds5replicaprecisetombstonepurging"
+
defaultChecked={this.props.data.nsds5replicaprecisetombstonepurging}
+ onChange={this.handleChange}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top-lg">
+ <Col componentClass={ControlLabel} sm={4}>
+ <Button
+ bsStyle="primary"
+ onClick={this.saveConfig}
+
+
Save Configuration
+ </Button>
+ </Col>
+ </Row>
+ </div>
+ </div>
+ </CustomCollapse>
+ </Form>
+ <ConfirmPopup
+ showModal={this.state.showConfirmManagerDelete}
+ closeHandler={this.closeConfirmManagerDelete}
+ actionFunc={this.deleteManager}
+ actionParam={this.state.manager}
+ msg="Are you sure you want to remove this Replication
Manager?"
+ msgContent={this.state.manager}
+ />
+ <AddManagerModal
+ showModal={this.state.showAddManagerModal}
+ closeHandler={this.closeAddManagerModal}
+ handleChange={this.handleManagerChange}
+ saveHandler={this.addManager}
+ spinning={this.state.addManagerSpinning}
+ error={this.state.errObj}
+ />
+ <ChangeReplRoleModal
+ showModal={this.state.showPromoteDemoteModal}
+ closeHandler={this.closePromoteDemoteModal}
+ handleChange={this.handleChange}
+ saveHandler={this.doRoleChange}
+ spinning={this.state.roleChangeSpinning}
+ role={this.props.role}
+ newRole={this.state.newRole}
+ checked={this.state.modalChecked}
+ />
+ </div>;
+ }
+
+ return (
+ <div>
+ {content}
+ </div>
+ );
+ }
+}
diff --git a/src/cockpit/389-console/src/lib/replication/replModals.jsx
b/src/cockpit/389-console/src/lib/replication/replModals.jsx
new file mode 100644
index 0000000..0e7a0d9
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/replication/replModals.jsx
@@ -0,0 +1,1684 @@
+import React from "react";
+import {
+ Button,
+ Row,
+ Checkbox,
+ Col,
+ ControlLabel,
+ Form,
+ FormControl,
+ Icon,
+ Modal,
+ noop,
+ Spinner,
+} from "patternfly-react";
+import PropTypes from "prop-types";
+import CustomCollapse from "../customCollapse.jsx";
+import { Typeahead } from "react-bootstrap-typeahead";
+import "../../css/ds.css";
+
+export class WinsyncAgmtModal extends React.Component {
+ handleNavSelect(key) {
+ this.setState({ activeKey: key });
+ }
+
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ saveHandler,
+ handleChange,
+ handleFracChange,
+ spinning,
+ agmtName,
+ agmtHost,
+ agmtPort,
+ agmtProtocol,
+ agmtBindDN,
+ agmtBindPW,
+ agmtBindPWConfirm,
+ agmtFracAttrs,
+ agmtSync,
+ agmtSyncMon,
+ agmtSyncTue,
+ agmtSyncWed,
+ agmtSyncThu,
+ agmtSyncFri,
+ agmtSyncSat,
+ agmtSyncSun,
+ agmtStartTime,
+ agmtEndTime,
+ agmtSyncGroups,
+ agmtSyncUsers,
+ agmtWinDomain,
+ agmtWinSubtree,
+ agmtDSSubtree,
+ agmtOneWaySync, // "both", "toWindows",
"fromWindows"
+ agmtSyncInterval,
+ availAttrs,
+ error,
+ errorMsg,
+ errorScheduleMsg,
+ } = this.props;
+ let spinner = "";
+ let saveDisabled = !this.props.saveOK;
+ let title = "Create";
+ let initRow = "";
+ let errMsgClass = "ds-center ds-modal-error";
+ let errMsg = errorMsg;
+ let name = "agmt-modal";
+
+ if (this.props.edit) {
+ title = "Edit";
+ name = "agmt-modal-edit";
+ } else {
+ initRow =
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Consumer Initialization
+ </Col>
+ <Col sm={8}>
+ <select className="btn btn-default dropdown"
id="agmtInit" name={name} onChange={handleChange}>
+ <option value="noinit">Do Not
Initialize</option>
+ <option value="online-init">Do Online
Initialization</option>
+ </select>
+ </Col>
+ </Row>;
+ }
+
+ if (errMsg == "") {
+ // To keep the modal nice and stable during input validation
+ // We need text that is invisible to keep the modal input from
+ // jumping around
+ errMsgClass = "ds-center ds-clear-text";
+ errMsg = "No errors";
+ }
+
+ if (spinning) {
+ spinner =
+ <Row>
+ <div className="ds-margin-top ds-modal-spinner">
+ <Spinner loading inline size="md" />Creating
winsync agreement ...
+ </div>
+ </Row>;
+ }
+
+ let scheduleRow =
+ <div className="ds-left-indent-md">
+ <Row className="ds-margin-top-lg">
+ <Col sm={12}>
+ <i>Custom Synchronization Schedule</i>
+ </Col>
+ </Row>
+ <hr />
+ <div className="ds-indent">
+ <Row>
+ <Col sm={3}>
+ <Checkbox
+ id="agmtSyncMon"
+ onChange={handleChange}
+ name={name}
+ title="Monday"
+ defaultChecked={agmtSyncMon}
+
+ Mon
+ </Checkbox>
+ </Col>
+ <Col sm={3}>
+ <Checkbox
+ id="agmtSyncWed"
+ onChange={handleChange}
+ title="Wednesday"
+ name={name}
+ defaultChecked={agmtSyncWed}
+
+ Wed
+ </Checkbox>
+ </Col>
+ <Col sm={3}>
+ <Checkbox
+ id="agmtSyncFri"
+ onChange={handleChange}
+ title="Friday"
+ name={name}
+ defaultChecked={agmtSyncFri}
+
+ Fri
+ </Checkbox>
+ </Col>
+ <Col sm={3}>
+ <Checkbox
+ id="agmtSyncSun"
+ onChange={handleChange}
+ title="Sunday"
+ name={name}
+ defaultChecked={agmtSyncSun}
+
+ Sun
+ </Checkbox>
+ </Col>
+ </Row>
+ <Row>
+ <Col sm={3}>
+ <Checkbox
+ id="agmtSyncTue"
+ onChange={handleChange}
+ title="Tuesday"
+ name={name}
+ defaultChecked={agmtSyncTue}
+
+ Tue
+ </Checkbox>
+ </Col>
+ <Col sm={3}>
+ <Checkbox
+ id="agmtSyncThu"
+ onChange={handleChange}
+ title="Thursday"
+ name={name}
+ defaultChecked={agmtSyncThu}
+
+ Thu
+ </Checkbox>
+ </Col>
+ <Col sm={3}>
+ <Checkbox
+ id="agmtSyncSat"
+ onChange={handleChange}
+ title="Saturday"
+ name={name}
+ defaultChecked={agmtSyncSat}
+
+ Sat
+ </Checkbox>
+ </Col>
+ </Row>
+ </div>
+ <Row className="ds-margin-top">
+ <Col sm={10}>
+ <p
className="ds-modal-error">{errorScheduleMsg}</p>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="Time to start
initiating replication sessions">
+ <Col componentClass={ControlLabel} sm={4}>
+ Replication Start Time
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ id="agmtStartTime"
+ type="time"
+ name={name}
+ className={error.agmtStartTime ? "ds-input-bad" :
""}
+ onChange={handleChange}
+ defaultValue={agmtStartTime}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="Time to
initiating replication sessions">
+ <Col componentClass={ControlLabel} sm={4}>
+ Replication End Time
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ id="agmtEndTime"
+ type="time"
+ name={name}
+ className={error.agmtEndTime ? "ds-input-bad" :
""}
+ onChange={handleChange}
+ defaultValue={agmtEndTime}
+ />
+ </Col>
+ </Row>
+ </div>;
+
+ if (agmtSync) {
+ scheduleRow = "";
+ }
+ return (
+ <Modal show={showModal} onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+
+
<Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ {title} Winsync Agreement
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal autoComplete="off">
+ <Row className="ds-margin-top">
+ <Col sm={10}>
+ <p className={errMsgClass}>{errMsg}</p>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Agreement Name
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ id="agmtName"
+ type="text"
+ name={name}
+ className={error.agmtName ?
"ds-input-bad" : ""}
+ onChange={handleChange}
+ defaultValue={agmtName}
+ disabled={this.props.edit}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Windows AD Host
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ id="agmtHost"
+ type="text"
+ name={name}
+ className={error.agmtHost ?
"ds-input-bad" : ""}
+ onChange={handleChange}
+ defaultValue={agmtHost}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Windows AD Port
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ id="agmtPort"
+ type="text"
+ name={name}
+ className={error.agmtPort ?
"ds-input-bad" : ""}
+ onChange={handleChange}
+ defaultValue={agmtPort}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Bind DN
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ id="agmtBindDN"
+ type="text"
+ name={name}
+ className={error.agmtBindDN ?
"ds-input-bad" : ""}
+ onChange={handleChange}
+ autoComplete="false"
+ defaultValue={agmtBindDN}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Bind Password
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ id="agmtBindPW"
+ type="password"
+ name={name}
+ className={error.agmtBindPW ?
"ds-input-bad" : ""}
+ onChange={handleChange}
+ autoComplete="new-password"
+ defaultValue={agmtBindPW}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Confirm Password
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ id="agmtBindPWConfirm"
+ type="password"
+ name={name}
+ className={error.agmtBindPWConfirm ?
"ds-input-bad" : ""}
+ onChange={handleChange}
+ autoComplete="new-password"
+ defaultValue={agmtBindPWConfirm}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Windows Domain Name
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="agmtWinDomain"
+ name={name}
+ className={error.agmtWinDomain ?
"ds-input-bad" : ""}
+ onChange={handleChange}
+ defaultValue={agmtWinDomain}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The
Active Directory subtree to synchronize">
+ <Col componentClass={ControlLabel} sm={4}>
+ Windows Subtree
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="agmtWinSubtree"
+ name={name}
+ className={error.agmtWinSubtree ?
"ds-input-bad" : ""}
+ onChange={handleChange}
+ defaultValue={agmtWinSubtree}
+ placeholder="e.g.
cn=Users,dc=domain,dc=com"
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="Directory Server subtree to synchronize">
+ <Col componentClass={ControlLabel} sm={4}>
+ DS Subtree
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="agmtDSSubtree"
+ name={name}
+ className={error.agmtDSSubtree ?
"ds-input-bad" : ""}
+ onChange={handleChange}
+ defaultValue={agmtDSSubtree}
+ placeholder="e.g.
ou=People,dc=domain,dc=com"
+ />
+ </Col>
+ </Row>
+ {initRow}
+ <CustomCollapse
+ className="ds-margin-top"
+ textOpened="Hide Advanced Settings"
+ textClosed="Show Advanced Setting"
+
+ <div
className="ds-margin-left">
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Connection Protocol
+ </Col>
+ <Col sm={8}>
+ <select className="btn btn-default
dropdown" id="agmtProtocol" defaultValue={agmtProtocol} name={name}
onChange={handleChange}>
+ <option>LDAPS</option>
+ <option title="Currently not
recommended">StartTLS</option>
+ </select>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Synchronization Direction
+ </Col>
+ <Col sm={8}>
+ <select className="btn btn-default
dropdown" defaultValue={agmtOneWaySync} id="agmtOneWaySync" name={name}
onChange={handleChange}>
+ <option title="Synchronization in
both directions (default behavior).">both</option>
+ <option title="Only synchronize
Directory Server updates to Windows.">toWindows</option>
+ <option title="Only synchronize
Windows updates to Directory Server.">fromWindows</option>
+ </select>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="The interval to check for updates on Windows. Default is 300
seconds">
+ <Col componentClass={ControlLabel} sm={4}>
+ Synchronization Interval
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="agmtSyncInterval"
+ name={name}
+ className={error.agmtSyncInterval ?
"ds-input-bad" : ""}
+ onChange={handleChange}
+ defaultValue={agmtSyncInterval}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="Attribute to exclude from replication">
+ <Col componentClass={ControlLabel} sm={4}>
+ Exclude Attributes
+ </Col>
+ <Col sm={8}>
+ <Typeahead
+ multiple
+ onChange={handleFracChange}
+ selected={agmtFracAttrs}
+ options={availAttrs}
+ name={name}
+ newSelectionPrefix="Add a attribute:
"
+ placeholder="Start typing an
attribute..."
+ id="agmtFracAttrs"
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top-med">
+ <Col>
+ <Checkbox
+ id="agmtSyncGroups"
+ onChange={handleChange}
+ name={name}
+ defaultChecked={agmtSyncGroups}
+
+
Synchronize New Windows Groups
+ </Checkbox>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col>
+ <Checkbox
+ id="agmtSyncUsers"
+ onChange={handleChange}
+ name={name}
+ defaultChecked={agmtSyncUsers}
+
+
Synchronize New Windows Users
+ </Checkbox>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col>
+ <Checkbox
+ id="agmtSync"
+ defaultChecked={agmtSync}
+ onChange={handleChange}
+ name={name}
+ title="Always keep replication in
synchronization, or use a specific schedule by unchecking the box."
+
+
Keep Replication In Constant Synchronization
+ </Checkbox>
+ </Col>
+ </Row>
+ {scheduleRow}
+ </div>
+ </CustomCollapse>
+ {spinner}
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+
+ Cancel
+ </Button>
+ <Button
+ bsStyle="primary"
+ onClick={saveHandler}
+ disabled={saveDisabled}
+
+ Save
Agreement
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
+export class ReplAgmtModal extends React.Component {
+ handleNavSelect(key) {
+ this.setState({ activeKey: key });
+ }
+
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ saveHandler,
+ handleChange,
+ handleStripChange,
+ handleFracChange,
+ handleFracInitChange,
+ spinning,
+ agmtName,
+ agmtHost,
+ agmtPort,
+ agmtProtocol,
+ agmtBindMethod,
+ agmtBindDN,
+ agmtBindPW,
+ agmtBindPWConfirm,
+ agmtStripAttrs,
+ agmtFracAttrs,
+ agmtFracInitAttrs,
+ agmtSync,
+ agmtSyncMon,
+ agmtSyncTue,
+ agmtSyncWed,
+ agmtSyncThu,
+ agmtSyncFri,
+ agmtSyncSat,
+ agmtSyncSun,
+ agmtStartTime,
+ agmtEndTime,
+ availAttrs,
+ error,
+ errorMsg,
+ errorScheduleMsg,
+ } = this.props;
+ let spinner = "";
+ let saveDisabled = !this.props.saveOK;
+ let title = "Create";
+ let initRow = "";
+ let errMsgClass = "ds-center ds-modal-error";
+ let errMsg = errorMsg;
+ let name = "agmt-modal";
+
+ if (this.props.edit) {
+ title = "Edit";
+ name = "agmt-modal-edit";
+ } else {
+ initRow =
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Consumer Initialization
+ </Col>
+ <Col sm={8}>
+ <select className="btn btn-default dropdown"
id="agmtInit" name={name} onChange={handleChange}>
+ <option value="noinit">Do Not
Initialize</option>
+ <option value="online-init">Do Online
Initialization</option>
+ </select>
+ </Col>
+ </Row>;
+ }
+
+ if (errMsg == "") {
+ // To keep the modal nice and stable during input validation
+ // We need text that is invisible to keep the modal input from
+ // jumping around
+ errMsgClass = "ds-center ds-clear-text";
+ errMsg = "No errors";
+ }
+
+ if (spinning) {
+ spinner =
+ <Row>
+ <div className="ds-margin-top ds-modal-spinner">
+ <Spinner loading inline size="md" />Creating
replication agreement ...
+ </div>
+ </Row>;
+ }
+
+ let scheduleRow =
+ <div className="ds-left-indent-md">
+ <Row className="ds-margin-top-lg">
+ <Col sm={12}>
+ <i>Custom Synchronization Schedule</i>
+ </Col>
+ </Row>
+ <hr />
+ <div className="ds-indent">
+ <Row>
+ <Col sm={3}>
+ <Checkbox
+ id="agmtSyncMon"
+ onChange={handleChange}
+ name={name}
+ title="Monday"
+ defaultChecked={agmtSyncMon}
+
+ Mon
+ </Checkbox>
+ </Col>
+ <Col sm={3}>
+ <Checkbox
+ id="agmtSyncWed"
+ onChange={handleChange}
+ title="Wednesday"
+ name={name}
+ defaultChecked={agmtSyncWed}
+
+ Wed
+ </Checkbox>
+ </Col>
+ <Col sm={3}>
+ <Checkbox
+ id="agmtSyncFri"
+ onChange={handleChange}
+ title="Friday"
+ name={name}
+ defaultChecked={agmtSyncFri}
+
+ Fri
+ </Checkbox>
+ </Col>
+ <Col sm={3}>
+ <Checkbox
+ id="agmtSyncSun"
+ onChange={handleChange}
+ title="Sunday"
+ name={name}
+ defaultChecked={agmtSyncSun}
+
+ Sun
+ </Checkbox>
+ </Col>
+ </Row>
+ <Row>
+ <Col sm={3}>
+ <Checkbox
+ id="agmtSyncTue"
+ onChange={handleChange}
+ title="Tuesday"
+ name={name}
+ defaultChecked={agmtSyncTue}
+
+ Tue
+ </Checkbox>
+ </Col>
+ <Col sm={3}>
+ <Checkbox
+ id="agmtSyncThu"
+ onChange={handleChange}
+ title="Thursday"
+ name={name}
+ defaultChecked={agmtSyncThu}
+
+ Thu
+ </Checkbox>
+ </Col>
+ <Col sm={3}>
+ <Checkbox
+ id="agmtSyncSat"
+ onChange={handleChange}
+ title="Saturday"
+ name={name}
+ defaultChecked={agmtSyncSat}
+
+ Sat
+ </Checkbox>
+ </Col>
+ </Row>
+ </div>
+ <Row className="ds-margin-top">
+ <Col sm={10}>
+ <p
className="ds-modal-error">{errorScheduleMsg}</p>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="Time to start
initiating replication sessions">
+ <Col componentClass={ControlLabel} sm={4}>
+ Replication Start Time
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ id="agmtStartTime"
+ type="time"
+ name={name}
+ className={error.agmtStartTime ? "ds-input-bad" :
""}
+ onChange={handleChange}
+ defaultValue={agmtStartTime}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="Time to
initiating replication sessions">
+ <Col componentClass={ControlLabel} sm={4}>
+ Replication End Time
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ id="agmtEndTime"
+ type="time"
+ name={name}
+ className={error.agmtEndTime ? "ds-input-bad" :
""}
+ onChange={handleChange}
+ defaultValue={agmtEndTime}
+ /> </Col>
+ </Row>
+ </div>;
+
+ if (agmtSync) {
+ scheduleRow = "";
+ }
+ return (
+ <Modal show={showModal} onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+
+
<Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ {title} Replication Agreement
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal autoComplete="off">
+ <Row className="ds-margin-top">
+ <Col sm={10}>
+ <p className={errMsgClass}>{errMsg}</p>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Agreement Name
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ id="agmtName"
+ type="text"
+ name={name}
+ className={error.agmtName ?
"ds-input-bad" : ""}
+ onChange={handleChange}
+ defaultValue={agmtName}
+ disabled={this.props.edit}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Consumer Host
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ id="agmtHost"
+ type="text"
+ name={name}
+ className={error.agmtHost ?
"ds-input-bad" : ""}
+ onChange={handleChange}
+ defaultValue={agmtHost}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Consumer Port
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ id="agmtPort"
+ type="text"
+ name={name}
+ className={error.agmtPort ?
"ds-input-bad" : ""}
+ onChange={handleChange}
+ defaultValue={agmtPort}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Bind DN
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ id="agmtBindDN"
+ type="text"
+ name={name}
+ className={error.agmtBindDN ?
"ds-input-bad" : ""}
+ onChange={handleChange}
+ autoComplete="false"
+ defaultValue={agmtBindDN}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Bind Password
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ id="agmtBindPW"
+ type="password"
+ name={name}
+ className={error.agmtBindPW ?
"ds-input-bad" : ""}
+ onChange={handleChange}
+ autoComplete="new-password"
+ defaultValue={agmtBindPW}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Confirm Password
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ id="agmtBindPWConfirm"
+ type="password"
+ name={name}
+ className={error.agmtBindPWConfirm ?
"ds-input-bad" : ""}
+ onChange={handleChange}
+ autoComplete="new-password"
+ defaultValue={agmtBindPWConfirm}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Connection Protocol
+ </Col>
+ <Col sm={8}>
+ <select className="btn btn-default
dropdown" id="agmtProtocol" defaultValue={agmtProtocol} name={name}
onChange={handleChange}>
+ <option>LDAP</option>
+ <option>LDAPS</option>
+ <option title="Currently not
recommended">StartTLS</option>
+ </select>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={4}>
+ Authentication Method
+ </Col>
+ <Col sm={8}>
+ <select className="btn btn-default
dropdown" defaultValue={agmtBindMethod} id="agmtBindMethod" name={name}
onChange={handleChange}>
+ <option title="Use bind DN and
password">SIMPLE</option>
+ <option title="Use SSL Client
Certificate">SSLCLIENTAUTH</option>
+ <option title="Use SASL
Digest-MD5">SASL/DIGEST-MD5</option>
+ <option title="Use SASL
GSSAPI">SASL/GSSAPI</option>
+ </select>
+ </Col>
+ </Row>
+ {initRow}
+ <CustomCollapse
+ className="ds-margin-top"
+ textOpened="Hide Advanced Settings"
+ textClosed="Show Advanced Settings"
+
+ <div
className="ds-margin-left">
+ <Row className="ds-margin-top"
title="Attribute to exclude from replication">
+ <Col componentClass={ControlLabel} sm={4}>
+ Exclude Attributes
+ </Col>
+ <Col sm={8}>
+ <Typeahead
+ multiple
+ onChange={handleFracChange}
+ selected={agmtFracAttrs}
+ options={availAttrs}
+ name={name}
+ newSelectionPrefix="Add a attribute:
"
+ placeholder="Start typing an
attribute..."
+ id="agmtFracAttrs"
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="Attribute to exclude from replica Initializations">
+ <Col componentClass={ControlLabel} sm={4}>
+ Exclude Init Attributes
+ </Col>
+ <Col sm={8}>
+ <Typeahead
+ multiple
+ onChange={handleFracInitChange}
+ selected={agmtFracInitAttrs}
+ options={availAttrs}
+ name={name}
+ newSelectionPrefix="Add a attribute:
"
+ placeholder="Start typing an
attribute..."
+ id="agmtFracInitAttrs"
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="Attributes to strip from a replication update">
+ <Col componentClass={ControlLabel} sm={4}>
+ Strip Attributes
+ </Col>
+ <Col sm={8}>
+ <Typeahead
+ multiple
+ onChange={handleStripChange}
+ selected={agmtStripAttrs}
+ options={availAttrs}
+ name={name}
+ newSelectionPrefix="Add a attribute:
"
+ placeholder="Start typing an
attribute..."
+ id="agmtStripAttrs"
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top-med">
+ <Col sm={8}>
+ <Checkbox
+ id="agmtSync"
+ defaultChecked={agmtSync}
+ onChange={handleChange}
+ name={name}
+ title="Always keep replication in
synchronization, or use a specific schedule by unchecking the box."
+
+
Keep Replication In Constant Synchronization
+ </Checkbox>
+ </Col>
+ </Row>
+ {scheduleRow}
+ </div>
+ </CustomCollapse>
+ {spinner}
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+
+ Cancel
+ </Button>
+ <Button
+ bsStyle="primary"
+ onClick={saveHandler}
+ disabled={saveDisabled}
+
+ Save
Agreement
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
+export class ChangeReplRoleModal extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ showConfirmPromote: false,
+ showConfirmDemote: false,
+ };
+ }
+
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ handleChange,
+ saveHandler,
+ role,
+ spinning,
+ checked,
+ } = this.props;
+ let spinner = "";
+ let changeType = "";
+ let roleOptions = [];
+ let ridRow = "";
+ let newRole = this.props.newRole;
+ let saveDisabled = !checked;
+
+ // Set the change type
+ if (role == "Master") {
+ changeType = "Demoting";
+ roleOptions = ["Hub", "Consumer"];
+ } else if (role == "Consumer") {
+ changeType = "Promoting";
+ roleOptions = ["Master", "Hub"];
+ } else {
+ // Hub
+ if (newRole == "Master") {
+ changeType = "Promoting";
+ } else {
+ changeType = "Demoting";
+ }
+ roleOptions = ["Master", "Consumer"];
+ }
+ if (newRole == "Master") {
+ ridRow =
+ <Row className="ds-margin-top-lg" title="Master Replica
Identifier. This must be unique across all the Master replicas in your
environment">
+ <Col componentClass={ControlLabel} sm={2}>
+ Replica ID
+ </Col>
+ <Col sm={4}>
+ <input id="newRID" type="number"
min="1" max="65534"
+ onChange={handleChange} defaultValue="1"
size="10"
+ />
+ </Col>
+ </Row>;
+ }
+
+ let selectOptions = roleOptions.map((role) =>
+ <option key={role} value={role}>{role}</option>
+ );
+
+ if (spinning) {
+ spinner =
+ <Row>
+ <div className="ds-margin-top ds-modal-spinner">
+ <Spinner loading inline size="md" />{changeType}
replica ...
+ </div>
+ </Row>;
+ saveDisabled = true;
+ }
+
+ return (
+ <Modal show={showModal} onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+
+
<Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ Change Replica Role
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal autoComplete="off">
+ <h4>Please choose the new replication role you would
like for this suffix</h4>
+ <Row className="ds-margin-top-lg">
+ <Col componentClass={ControlLabel} sm={2}>
+ New Role
+ </Col>
+ <Col sm={4}>
+ <select id="newRole"
onChange={handleChange}>
+ {selectOptions}
+ </select>
+ </Col>
+ </Row>
+ {ridRow}
+ <Row className="ds-margin-top-xlg">
+ <Col sm={12} className="ds-center">
+ <Checkbox
+ id="modalChecked"
+ defaultChecked={checked}
+ onChange={handleChange}
+
+
<b>Yes</b>, I am sure.
+ </Checkbox>
+ </Col>
+ </Row>
+ {spinner}
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+
+ Cancel
+ </Button>
+ <Button
+ bsStyle="primary"
+ onClick={() => {
+ saveHandler(changeType);
+ }}
+ disabled={saveDisabled}
+
+ Change
Role
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
+export class AddManagerModal extends React.Component {
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ handleChange,
+ saveHandler,
+ spinning,
+ error
+ } = this.props;
+ let spinner = "";
+ if (spinning) {
+ spinner =
+ <Row>
+ <div className="ds-margin-top ds-modal-spinner">
+ <Spinner loading inline size="md" />Adding
Replication Manager...
+ </div>
+ </Row>;
+ }
+
+ return (
+ <Modal show={showModal} onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+
+
<Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ Add Replication Manager
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal autoComplete="off">
+ <p>Create a Replication Manager entry, and add it to
the replication configuration for this suffix. If the entry already exists it will be
overwritten with the new credentials.</p>
+ <Row className="ds-margin-top" title="The
DN of the replication manager">
+ <Col componentClass={ControlLabel} sm={4}>
+ Replication Manager DN
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="manager"
+ defaultValue="cn=replication
manager,cn=config"
+ className={error.manager ?
"ds-input-auto-bad" : "ds-input-auto"}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="Replication Manager password">
+ <Col componentClass={ControlLabel} sm={4}>
+ Password
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="password"
+ id="manager_passwd"
+ className={error.manager_passwd ?
"ds-input-auto-bad" : "ds-input-auto"}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="Replication Manager password">
+ <Col componentClass={ControlLabel} sm={4}>
+ Confirm Password
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="password"
+ id="manager_passwd_confirm"
+ className={error.manager_passwd_confirm ?
"ds-input-auto-bad" : "ds-input-auto"}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ {spinner}
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+
+ Cancel
+ </Button>
+ <Button
+ bsStyle="primary"
+ onClick={saveHandler}
+
+ Add
Replication Manager
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
+export class EnableReplModal extends React.Component {
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ handleChange,
+ saveHandler,
+ spinning,
+ role,
+ error
+ } = this.props;
+ let spinner = "";
+ if (spinning) {
+ spinner =
+ <Row>
+ <div className="ds-margin-top ds-modal-spinner">
+ <Spinner loading inline size="md" />Enabling
Replication ...
+ </div>
+ </Row>;
+ }
+
+ let replicaIDRow = "";
+ if (role == "Master") {
+ replicaIDRow =
+ <Row className="ds-margin-top">
+ <Col sm={3} componentClass={ControlLabel}>
+ Replica ID
+ </Col>
+ <Col sm={9}>
+ <input id="enableRID" type="number"
min="1" max="65534"
+ onChange={handleChange} defaultValue="1"
size="10"
+ />
+ </Col>
+ </Row>;
+ }
+
+ return (
+ <Modal show={showModal} onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+
+
<Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ Enable Replication
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal autoComplete="off">
+ <p>
+ Choose the replication role for this suffix. If it
+ is a Master replica then you must pick a unique ID
+ to identify it among the other Master replicas in your
+ environment. You can optionally define the
authentication
+ information for this replicated suffix, either a Bind DN
and
+ Password, or a Group DN. The replication changelog will
+ also automatically be created if it does not exist.
+ </p>
+ <hr />
+ <Row className="ds-margin-top-lg">
+ <Col sm={3} componentClass={ControlLabel}>
+ Replication Role
+ </Col>
+ <Col sm={9}>
+ <select className="btn btn-default
dropdown" id="enableRole" defaultValue="Master"
onChange={handleChange}>
+ <option>Master</option>
+ <option>Hub</option>
+ <option>Consumer</option>
+ </select>
+ </Col>
+ </Row>
+ {replicaIDRow}
+ <hr />
+ <Row className="ds-margin-top" title="The
DN of the replication manager. If you supply a password the entry will be created in the
server (it will also overwrite the entry is it already exists).">
+ <Col componentClass={ControlLabel} sm={4}>
+ Replication Manager DN
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="enableBindDN"
+ defaultValue="cn=replication
manager,cn=config"
+ className={error.enableBindDN ?
"ds-input-auto-bad" : "ds-input-auto"}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="Replication Manager password">
+ <Col componentClass={ControlLabel} sm={4}>
+ Password
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="password"
+ id="enableBindPW"
+ className={error.enableBindPW ?
"ds-input-auto-bad" : "ds-input-auto"}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top"
title="Confirm the Replication Manager password">
+ <Col componentClass={ControlLabel} sm={4}>
+ Confirm Password
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="password"
+ id="enableBindPWConfirm"
+ className={error.enableBindPWConfirm ?
"ds-input-auto-bad" : "ds-input-auto"}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The
DN of a group that contains users that can perform replication updates">
+ <Col componentClass={ControlLabel} sm={4}>
+ Bind Group DN
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="enableBindGroupDN"
+ className={error.enableBindGroupDN ?
"ds-input-auto-bad" : "ds-input-auto"}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ {spinner}
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+
+ Cancel
+ </Button>
+ <Button
+ bsStyle="primary"
+ onClick={saveHandler}
+
+ Enable
Replication
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
+export class ExportModal extends React.Component {
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ handleChange,
+ saveHandler,
+ spinning,
+ saveOK
+ } = this.props;
+ let spinner = "";
+ if (spinning) {
+ spinner =
+ <Row>
+ <div className="ds-margin-top ds-modal-spinner">
+ <Spinner loading inline size="lg" />Exporting
database... <font size="1">(You can safely close this
window)</font>
+ </div>
+ </Row>;
+ }
+
+ return (
+ <Modal show={showModal} onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+
+
<Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ Create Replication Initialization LDIF File
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal autoComplete="off">
+ <Row>
+ <Col sm={11}
className="ds-left-indent-md">
+ <p>Enter the name of the LDIF file, do not use
a path as the file will only be written to the server's LDIF directory</p>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top-lg"
title="Name of the exported LDIF file">
+ <Col sm={3}>
+ <b>LDIF Name</b>
+ </Col>
+ <Col sm={9}>
+ <FormControl
+ type="text"
+ id="ldifLocation"
+ className={saveOK ? "" :
"ds-input-bad"}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ {spinner}
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+
+ Cancel
+ </Button>
+ <Button
+ bsStyle="primary"
+ onClick={saveHandler}
+ disabled={!saveOK}
+
+ Export
Replica
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
+EnableReplModal.propTypes = {
+ showModal: PropTypes.bool,
+ closeHandler: PropTypes.func,
+ handleChange: PropTypes.func,
+ saveHandler: PropTypes.func,
+ spinning: PropTypes.bool,
+ error: PropTypes.object,
+};
+
+EnableReplModal.defaultProps = {
+ showModal: false,
+ closeHandler: noop,
+ handleChange: noop,
+ saveHandler: noop,
+ spinning: false,
+ error: {},
+};
+
+AddManagerModal.propTypes = {
+ showModal: PropTypes.bool,
+ closeHandler: PropTypes.func,
+ handleChange: PropTypes.func,
+ saveHandler: PropTypes.func,
+ spinning: PropTypes.bool,
+ error: PropTypes.object,
+};
+
+AddManagerModal.defaultProps = {
+ showModal: false,
+ closeHandler: noop,
+ handleChange: noop,
+ saveHandler: noop,
+ spinning: false,
+ error: {},
+};
+
+ChangeReplRoleModal.propTypes = {
+ showModal: PropTypes.bool,
+ closeHandler: PropTypes.func,
+ handleChange: PropTypes.func,
+ saveHandler: PropTypes.func,
+ spinning: PropTypes.bool,
+ role: PropTypes.string,
+ newRole: PropTypes.string,
+};
+
+ChangeReplRoleModal.defaultProps = {
+ showModal: false,
+ closeHandler: noop,
+ handleChange: noop,
+ saveHandler: noop,
+ spinning: false,
+ role: "",
+ newRole: "",
+};
+
+ReplAgmtModal.propTypes = {
+ showModal: PropTypes.bool,
+ closeHandler: PropTypes.func,
+ handleChange: PropTypes.func,
+ handleStripChange: PropTypes.func,
+ handleFracChange: PropTypes.func,
+ handleFracInitChange: PropTypes.func,
+ saveHandler: PropTypes.func,
+ spinning: PropTypes.bool,
+ availAttrs: PropTypes.array,
+ agmtName: PropTypes.string,
+ agmtHost: PropTypes.string,
+ agmtPort: PropTypes.string,
+ agmtProtocol: PropTypes.string,
+ agmtBindMethod: PropTypes.string,
+ agmtBindDN: PropTypes.string,
+ agmtBindPW: PropTypes.string,
+ agmtBindPWConfirm: PropTypes.string,
+ agmtStripAttrs: PropTypes.array,
+ agmtFracAttrs: PropTypes.array,
+ agmtFracInitAttrs: PropTypes.array,
+ agmtSync: PropTypes.bool,
+ agmtSyncMon: PropTypes.bool,
+ agmtSyncTue: PropTypes.bool,
+ agmtSyncWed: PropTypes.bool,
+ agmtSyncThu: PropTypes.bool,
+ agmtSyncFri: PropTypes.bool,
+ agmtSyncSat: PropTypes.bool,
+ agmtSyncSun: PropTypes.bool,
+ agmtStartTime: PropTypes.string,
+ agmtEndTime: PropTypes.string,
+ saveOK: PropTypes.bool,
+ error: PropTypes.object,
+ errorMsg: PropTypes.string,
+ edit: PropTypes.bool,
+};
+
+ReplAgmtModal.defaultProps = {
+ showModal: false,
+ closeHandler: noop,
+ handleChange: noop,
+ handleStripChange: noop,
+ handleFracChange: noop,
+ handleFracInitChange: noop,
+ saveHandler: noop,
+ spinning: false,
+ availAttrs: [],
+ agmtName: "",
+ agmtHost: "",
+ agmtPort: "",
+ agmtProtocol: "LDAP",
+ agmtBindMethod: "SIMPLE",
+ agmtBindDN: "",
+ agmtBindPW: "",
+ agmtBindPWConfirm: "",
+ agmtStripAttrs: [],
+ agmtFracAttrs: [],
+ agmtFracInitAttrs: [],
+ agmtSync: true,
+ agmtSyncMon: false,
+ agmtSyncTue: false,
+ agmtSyncWed: false,
+ agmtSyncThu: false,
+ agmtSyncFri: false,
+ agmtSyncSat: false,
+ agmtSyncSun: false,
+ agmtStartTime: "00:00",
+ agmtEndTime: "23:59",
+ saveOK: false,
+ error: {},
+ errorMsg: "",
+ edit: false,
+};
+
+WinsyncAgmtModal.propTypes = {
+ showModal: PropTypes.bool,
+ closeHandler: PropTypes.func,
+ handleChange: PropTypes.func,
+ handleFracChange: PropTypes.func,
+ saveHandler: PropTypes.func,
+ spinning: PropTypes.bool,
+ availAttrs: PropTypes.array,
+ agmtName: PropTypes.string,
+ agmtHost: PropTypes.string,
+ agmtPort: PropTypes.string,
+ agmtProtocol: PropTypes.string,
+ agmtBindDN: PropTypes.string,
+ agmtBindPW: PropTypes.string,
+ agmtBindPWConfirm: PropTypes.string,
+ agmtFracAttrs: PropTypes.array,
+ agmtSync: PropTypes.bool,
+ agmtSyncMon: PropTypes.bool,
+ agmtSyncTue: PropTypes.bool,
+ agmtSyncWed: PropTypes.bool,
+ agmtSyncThu: PropTypes.bool,
+ agmtSyncFri: PropTypes.bool,
+ agmtSyncSat: PropTypes.bool,
+ agmtSyncSun: PropTypes.bool,
+ agmtStartTime: PropTypes.string,
+ agmtEndTime: PropTypes.string,
+ agmtSyncGroups: PropTypes.bool,
+ agmtSyncUsers: PropTypes.bool,
+ agmtWinDomain: PropTypes.string,
+ agmtWinSubtree: PropTypes.string,
+ agmtDSSubtree: PropTypes.string,
+ agmtOneWaySync: PropTypes.string,
+ agmtSyncInterval: PropTypes.string,
+ saveOK: PropTypes.bool,
+ error: PropTypes.object,
+ errorMsg: PropTypes.string,
+ edit: PropTypes.bool,
+};
+
+WinsyncAgmtModal.defaultProps = {
+ showModal: false,
+ closeHandler: noop,
+ handleChange: noop,
+ handleFracChange: noop,
+ saveHandler: noop,
+ spinning: false,
+ availAttrs: [],
+ agmtName: "",
+ agmtHost: "",
+ agmtPort: "",
+ agmtProtocol: "LDAPS",
+ agmtBindDN: "",
+ agmtBindPW: "",
+ agmtBindPWConfirm: "",
+ agmtFracAttrs: [],
+ agmtSync: true,
+ agmtSyncMon: false,
+ agmtSyncTue: false,
+ agmtSyncWed: false,
+ agmtSyncThu: false,
+ agmtSyncFri: false,
+ agmtSyncSat: false,
+ agmtSyncSun: false,
+ agmtStartTime: "00:00",
+ agmtEndTime: "23:59",
+ agmtSyncGroups: false,
+ agmtSyncUsers: false,
+ agmtWinDomain: "",
+ agmtWinSubtree: "",
+ agmtDSSubtree: "",
+ agmtOneWaySync: "both", // "both", "toWindows",
"fromWindows"
+ agmtSyncInterval: "",
+ saveOK: false,
+ error: {},
+ errorMsg: "",
+ edit: false,
+};
+
+ExportModal.propTypes = {
+ showModal: PropTypes.bool,
+ closeHandler: PropTypes.func,
+ handleChange: PropTypes.func,
+ saveHandler: PropTypes.func,
+ saveOK: PropTypes.bool,
+ spinning: PropTypes.bool
+};
+
+ExportModal.defaultProps = {
+ showModal: false,
+ closeHandler: noop,
+ handleChange: noop,
+ saveHandler: noop,
+ saveOK: false,
+ spinning: false
+};
diff --git a/src/cockpit/389-console/src/lib/replication/replSuffix.jsx
b/src/cockpit/389-console/src/lib/replication/replSuffix.jsx
new file mode 100644
index 0000000..0978c0c
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/replication/replSuffix.jsx
@@ -0,0 +1,456 @@
+import React from "react";
+import cockpit from "cockpit";
+import { ReplConfig } from "./replConfig.jsx";
+import { WinsyncAgmts } from "./winsyncAgmts.jsx";
+import { ReplAgmts } from "./replAgmts.jsx";
+import { ReplRUV } from "./replTasks.jsx";
+import { DoubleConfirmModal } from "../notifications.jsx";
+import { EnableReplModal } from "./replModals.jsx";
+import {
+ Button,
+ Col,
+ ControlLabel,
+ Icon,
+ Nav,
+ NavItem,
+ noop,
+ Row,
+ Spinner,
+ TabContainer,
+ TabContent,
+ TabPane,
+} from "patternfly-react";
+import PropTypes from "prop-types";
+import { log_cmd, valid_dn } from "../tools.jsx";
+import "../../css/ds.css";
+
+export class ReplSuffix extends React.Component {
+ constructor (props) {
+ super(props);
+ this.state = {
+ loading: false,
+ activeKey: 1,
+ showDisableConfirm: false,
+ replicationEnabled: false,
+ errObj: {},
+ replEnabled: this.props.replicated,
+ // Enable replication settings
+ showEnableReplModal: false,
+ enableRole: "Master",
+ enableRID: "1",
+ enableBindDN: "cn=replication manager,cn=config",
+ enableBindPW: "",
+ enableBindPWConfirm: "",
+ enableBindGroupDN: "",
+ // Disable replication
+ showDisableReplModal: false,
+ disableChecked: false,
+ disableSpinning: false,
+ modalChecked: false,
+ modalSpinning: false,
+ };
+
+ // General bindings
+ this.handleReplChange = this.handleReplChange.bind(this);
+ this.handleEnableChange = this.handleEnableChange.bind(this);
+ this.handleChange = this.handleChange.bind(this);
+ this.handleNavSelect = this.handleNavSelect.bind(this);
+ this.disableReplication = this.disableReplication.bind(this);
+ this.enableReplication = this.enableReplication.bind(this);
+ this.closeEnableReplModal = this.closeEnableReplModal.bind(this);
+ this.closeDisableReplModal = this.closeDisableReplModal.bind(this);
+ }
+
+ componentDidMount() {
+ this.props.enableTree();
+ }
+
+ handleNavSelect(key) {
+ this.setState({ activeKey: key });
+ }
+
+ handleReplChange() {
+ if (this.props.replicated) {
+ // Disable Replication
+ this.setState({
+ showDisableReplModal: true,
+ modalChecked: false,
+ modalSpinning: false
+ });
+ } else {
+ // Enable replication
+ this.setState({
+ showEnableReplModal: true
+ });
+ }
+ }
+
+ handleChange (e) {
+ let value = e.target.type === 'checkbox' ? e.target.checked :
e.target.value;
+ let valueErr = false;
+ let errObj = this.state.errObj;
+ if (value == "") {
+ valueErr = true;
+ }
+ errObj[e.target.id] = valueErr;
+ this.setState({
+ [e.target.id]: value,
+ errObj: errObj
+ });
+ }
+
+ handleEnableChange (e) {
+ let value = e.target.value;
+ let attr = e.target.id;
+ let valueErr = false;
+ let errObj = this.state.errObj;
+
+ if (attr == "enableBindDN" && value != "" &&
!valid_dn(value)) {
+ valueErr = true;
+ }
+ if (attr == "enableBindGroupDN" && value != ""
&& !valid_dn(value)) {
+ valueErr = true;
+ }
+ if (attr == "enableBindPW") {
+ if (value != this.state.enableBindPWConfirm) {
+ valueErr = true;
+ } else {
+ errObj.enableBindPW = false;
+ errObj.enableBindPWConfirm = false;
+ }
+ }
+ if (attr == "enableBindPWConfirm") {
+ if (value != this.state.enableBindPW) {
+ valueErr = true;
+ } else {
+ errObj.enableBindPW = false;
+ errObj.enableBindPWConfirm = false;
+ }
+ }
+ errObj[attr] = valueErr;
+ this.setState({
+ [attr]: value,
+ errObj: errObj
+ });
+ }
+
+ closeEnableReplModal () {
+ this.setState({
+ showEnableReplModal: false,
+ });
+ }
+
+ enableReplication () {
+ // First, Validate
+ if (this.state.enableBindDN != "" &&
!valid_dn(this.state.enableBindDN)) {
+ this.props.addNotification(
+ "error",
+ `The Bind DN is not a valid DN (Distinguished Name)
${this.state.enableBindDN}`
+ );
+ return;
+ }
+ if (this.state.enableBindGroupDN != "" &&
!valid_dn(this.state.enableBindGroupDN)) {
+ this.props.addNotification(
+ "error",
+ `The Group DN is not a valid DN (Distinguished Name)`
+ );
+ return;
+ }
+ if (this.state.enableBindPW != this.state.enableBindPWConfirm) {
+ this.props.addNotification(
+ "error",
+ `The Bind DN passwords do not match`
+ );
+ return;
+ }
+ if (this.state.enableRID != "" && (this.state.enableRID < 1
|| this.state.enableRID > 65534)) {
+ this.props.addNotification(
+ "error",
+ `The Replica ID is not in the valid range of 1 - 65534`
+ );
+ return;
+ }
+
+ // Now enable replication
+ let cmd = [
+ 'dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' +
this.props.serverId + '.socket',
+ 'replication', 'enable', '--suffix=' +
this.props.suffix,
+ '--role=' + this.state.enableRole
+ ];
+ if (this.state.enableBindDN != "") {
+ cmd.push('--bind-dn=' + this.state.enableBindDN);
+ }
+ if (this.state.enableBindPW != "") {
+ cmd.push('--bind-passwd=' + this.state.enableBindPW);
+ }
+ if (this.state.enableBindGroupDN != "") {
+ cmd.push('--bind-group-dn=' + this.state.enableBindGroupDN);
+ }
+ if (this.state.enableRole == "Master") {
+ cmd.push('--replica-id=' + this.state.enableRID);
+ }
+
+ this.props.disableTree();
+ log_cmd('enableReplication', 'Enable replication', cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ this.props.reload(1);
+ this.props.addNotification(
+ "success",
+ `Successfully enabled replication for
"${this.props.suffix}"`
+ );
+ })
+ .fail(err => {
+ this.props.reload(1);
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to enable replication for
"${this.props.suffix}" - ${errMsg.desc}`
+ );
+ });
+ }
+
+ closeDisableReplModal () {
+ this.setState({
+ showDisableReplModal: false
+ });
+ }
+
+ disableReplication () {
+ this.props.disableTree();
+ let cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
'replication', 'disable', '--suffix=' + this.props.suffix];
+ log_cmd('disableReplication', 'Disable replication', cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ this.props.reload(1);
+ this.props.addNotification(
+ "success",
+ `Successfully disabled replication for
"${this.props.suffix}"`
+ );
+ })
+ .fail(err => {
+ this.props.reload(1);
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to disable replication for
"${this.props.suffix}" - ${errMsg.desc}`
+ );
+ });
+ }
+
+ //
+ // Render the component
+ //
+ render () {
+ let spinning = "";
+ let spintext = "";
+ let suffixIcon = "tree";
+ if (this.props.replicated) {
+ suffixIcon = "clone";
+ } else {
+ if (this.props.repl == "subsuffix") {
+ suffixIcon = "leaf";
+ }
+ }
+ if (this.props.spinning) {
+ spinning =
+ <Spinner className="ds-margin-top ds-margin-left
ds-inline-spinner" loading inline size="sm" />;
+ spintext =
+ <font
size="2"><i>Refreshing</i></font>;
+ }
+ let suffixClass = "ds-margin-top-xlg";
+ if (this.props.disabled) {
+ suffixClass = "ds-margin-top-xlg ds-disabled";
+ }
+ let replAgmtNavTitle = 'Replication Agreements <font
size="1">(' + this.props.agmtRows.length + ')</font>';
+ let winsyncNavTitle = 'Winsync Agreements <font
size="1">(' + this.props.winsyncRows.length + ')</font>';
+
+ let enabledContent =
+ <div className={suffixClass}>
+ <TabContainer id="basic-tabs-pf"
onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>
+ <div>
+ <Nav bsClass="nav nav-tabs nav-tabs-pf">
+ <NavItem eventKey={1}>
+ <div dangerouslySetInnerHTML={{__html:
'Configuration'}} />
+ </NavItem>
+ <NavItem eventKey={2}>
+ <div dangerouslySetInnerHTML={{__html:
replAgmtNavTitle}} />
+ </NavItem>
+ <NavItem eventKey={3}>
+ <div dangerouslySetInnerHTML={{__html:
winsyncNavTitle}} />
+ </NavItem>
+ <NavItem eventKey={4}>
+ <div dangerouslySetInnerHTML={{__html: "RUV's
& Tasks"}} />
+ </NavItem>
+ </Nav>
+ <TabContent>
+ <TabPane eventKey={1}>
+ <ReplConfig
+ suffix={this.props.suffix}
+ role={this.props.role}
+ data={this.props.data}
+ serverId={this.props.serverId}
+ addNotification={this.props.addNotification}
+ reload={this.props.reload}
+ reloadConfig={this.props.reloadConfig}
+ />
+ </TabPane>
+ <TabPane eventKey={2}>
+ <ReplAgmts
+ suffix={this.props.suffix}
+ serverId={this.props.serverId}
+ rows={this.props.agmtRows}
+ addNotification={this.props.addNotification}
+ reload={this.props.reloadAgmts}
+ attrs={this.props.attrs}
+ disableTable={this.props.disableAgmtTable}
+ key={this.props.agmtRows}
+ />
+ </TabPane>
+ <TabPane eventKey={3}>
+ <WinsyncAgmts
+ suffix={this.props.suffix}
+ serverId={this.props.serverId}
+ rows={this.props.winsyncRows}
+ addNotification={this.props.addNotification}
+ reload={this.props.reloadWinsyncAgmts}
+ attrs={this.props.attrs}
+ disableTable={this.props.disableWSAgmtTable}
+ key={this.props.winsyncRows}
+ />
+ </TabPane>
+ <TabPane eventKey={4}>
+ <ReplRUV
+ suffix={this.props.suffix}
+ serverId={this.props.serverId}
+ rows={this.props.ruvRows}
+ addNotification={this.props.addNotification}
+ reload={this.props.reloadRUV}
+ localRID={this.props.data.nsds5replicaid}
+ key={this.props.ruvRows}
+ />
+ </TabPane>
+ </TabContent>
+ </div>
+ </TabContainer>
+ </div>;
+
+ let replActionButton = "";
+ if (this.props.replicated) {
+ replActionButton =
+ <Button
+ bsStyle="danger"
+ onClick={this.handleReplChange}
+ title="Disable replication, and remove all replication
agreements."
+
+ Disable
+ </Button>;
+ } else {
+ enabledContent =
+ <div className="ds-center ds-margin-top-xlg">
+ <h4>
+ Replication is not enabled for this suffix
+ </h4>
+ <Button
+ bsStyle="primary"
+ onClick={this.handleReplChange}
+ className="ds-margin-top-lg"
+
+ Enable
Replication
+ </Button>
+ </div>;
+ }
+
+ return (
+ <div id="suffix-page">
+ <Row>
+ <Col sm={8} className="ds-word-wrap">
+ <ControlLabel
className="ds-suffix-header"><Icon type="fa" name={suffixIcon}
/>
+ {" " + this.props.suffix}
+ <Icon className="ds-left-margin ds-refresh"
+ type="fa" name="refresh"
title="Refresh replication settings for this suffix"
+ onClick={() => {
+ this.props.reload(false);
+ }}
+ />
+ {spinning} {spintext}
+ </ControlLabel>
+ </Col>
+ <Col sm={4}>
+ <Row>
+ <Col className="ds-no-padding ds-container"
componentClass={ControlLabel} sm={12}>
+ {replActionButton}
+ </Col>
+ </Row>
+ </Col>
+ </Row>
+ <p />
+ {enabledContent}
+ <EnableReplModal
+ showModal={this.state.showEnableReplModal}
+ closeHandler={this.closeEnableReplModal}
+ handleChange={this.handleEnableChange}
+ saveHandler={this.enableReplication}
+ spinning={this.state.addManagerSpinning}
+ role={this.state.enableRole}
+ error={this.state.errObj}
+ />
+ <DoubleConfirmModal
+ showModal={this.state.showDisableReplModal}
+ closeHandler={this.closeDisableReplModal}
+ handleChange={this.handleChange}
+ actionHandler={this.disableReplication}
+ spinning={this.state.modalSpinning}
+ item={this.props.suffix}
+ checked={this.state.modalChecked}
+ mTitle="Disable Replication"
+ mMsg="Are you sure you want to disable replication for this
suffix?"
+ mSpinningMsg="Disabling Replication ..."
+ mBtnName="Disable Replication"
+ />
+ </div>
+ );
+ }
+}
+
+ReplSuffix.propTypes = {
+ serverId: PropTypes.string,
+ suffix: PropTypes.string,
+ role: PropTypes.string,
+ addNotification: PropTypes.func,
+ agmtRows: PropTypes.array,
+ winsyncRows: PropTypes.array,
+ ruvRows: PropTypes.array,
+ reloadAgmts: PropTypes.func,
+ reloadRUV: PropTypes.func,
+ reloadConfig: PropTypes.func,
+ reload: PropTypes.func,
+ replicated: PropTypes.bool,
+ attrs: PropTypes.array,
+ enableTree: PropTypes.func,
+ disableTree: PropTypes.func,
+ spinning: PropTypes.bool,
+ disabled: PropTypes.bool,
+};
+
+ReplSuffix.defaultProps = {
+ serverId: "",
+ suffix: "",
+ role: "",
+ addNotification: noop,
+ agmtRows: [],
+ winsyncRows: [],
+ ruvRows: [],
+ reloadAgmts: noop,
+ reloadRUV: noop,
+ reloadConfig: noop,
+ reload: noop,
+ replicated: false,
+ attrs: [],
+ enableTree: noop,
+ disableTree: noop,
+ spinning: false,
+ disabled: false,
+};
diff --git a/src/cockpit/389-console/src/lib/security/securityTables.jsx
b/src/cockpit/389-console/src/lib/replication/replTables.jsx
similarity index 57%
copy from src/cockpit/389-console/src/lib/security/securityTables.jsx
copy to src/cockpit/389-console/src/lib/replication/replTables.jsx
index 6b74a01..332d458 100644
--- a/src/cockpit/389-console/src/lib/security/securityTables.jsx
+++ b/src/cockpit/389-console/src/lib/replication/replTables.jsx
@@ -1,6 +1,6 @@
import React from "react";
import {
- // Button,
+ Button,
DropdownButton,
MenuItem,
actionHeaderCellFormatter,
@@ -8,21 +8,22 @@ import {
tableCellFormatter,
noop
} from "patternfly-react";
-import { DSTable } from "../dsTable.jsx";
+import { DSTable, DSShortTable } from "../dsTable.jsx";
import PropTypes from "prop-types";
import "../../css/ds.css";
-class CertTable extends React.Component {
+class ReplAgmtTable extends React.Component {
constructor(props) {
super(props);
-
this.state = {
- rowKey: "nickname",
+ searchField: "Agreements",
+ fieldsToSearch: ["name"],
+ rowKey: "name",
columns: [
{
- property: "nickname",
+ property: "name",
header: {
- label: "Certificate Name",
+ label: "Agreement Name",
props: {
index: 0,
rowSpan: 1,
@@ -41,9 +42,9 @@ class CertTable extends React.Component {
}
},
{
- property: "subject",
+ property: "host",
header: {
- label: "Subject DN",
+ label: "Replica Hostname",
props: {
index: 1,
rowSpan: 1,
@@ -62,9 +63,9 @@ class CertTable extends React.Component {
}
},
{
- property: "issuer",
+ property: "port",
header: {
- label: "Issued By",
+ label: "Replica Port",
props: {
index: 2,
rowSpan: 1,
@@ -83,9 +84,9 @@ class CertTable extends React.Component {
}
},
{
- property: "flags",
+ property: "state",
header: {
- label: "Trust Flags",
+ label: "State",
props: {
index: 3,
rowSpan: 1,
@@ -104,9 +105,9 @@ class CertTable extends React.Component {
}
},
{
- property: "expires",
+ property: "status",
header: {
- label: "Expiration Date",
+ label: "Last Update Status",
props: {
index: 4,
rowSpan: 1,
@@ -125,38 +126,80 @@ class CertTable extends React.Component {
}
},
{
- property: "action",
+ property: "initstatus",
header: {
- label: "",
+ label: "Last Init Status",
props: {
index: 5,
rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 5
+ },
+ }
+ },
+ {
+ property: "actions",
+ header: {
+ props: {
+ index: 6,
+ rowSpan: 1,
colSpan: 1
},
formatters: [actionHeaderCellFormatter]
},
cell: {
props: {
- index: 5
+ index: 6
},
formatters: [
(value, { rowData }) => {
return [
- <td key={rowData.nickname[0]}>
- <DropdownButton id={rowData.nickname[0]}
- bsStyle="default"
title="Actions">
+ <td key={rowData.name[0]}>
+ <DropdownButton
+ pullRight
+ id={rowData.name[0]}
+ className="ds-action-button"
+ bsStyle="primary"
title="Actions"
+
<MenuItem eventKey="1" onClick={() => {
- this.props.editCert(rowData);
+ this.props.edit(rowData.name[0]);
}}
-
Edit Trust Flags
+ Edit Agreement
</MenuItem>
- <MenuItem divider />
<MenuItem eventKey="2"
onClick={() => {
- this.props.delCert(rowData);
+ this.props.init(rowData.name[0]);
+ }}
+
+
Initialize Agreement
+ </MenuItem>
+ <MenuItem eventKey="3"
onClick={() => {
+ this.props.poke(rowData.name[0]);
}}
+ title="Awaken agreement if it is
sleeping"
-
Delete Certificate
+ Poke Agreement
+ </MenuItem>
+ <MenuItem eventKey="4"
onClick={() => {
+ this.props.enable(rowData.name[0],
rowData.state[0]);
+ }}
+
+
Disable/Enable Agreement
+ </MenuItem>
+ <MenuItem divider />
+ <MenuItem eventKey="3"
onClick={() => {
+ this.props.delete(rowData.name[0],
rowData.state[0]);
+ }}
+
+
Delete Agreement
</MenuItem>
</DropdownButton>
</td>
@@ -164,9 +207,10 @@ class CertTable extends React.Component {
}
]
}
- }
- ]
+ },
+ ],
};
+
this.getColumns = this.getColumns.bind(this);
this.getSingleColumn = this.getSingleColumn.bind(this);
}
@@ -176,7 +220,7 @@ class CertTable extends React.Component {
{
property: "msg",
header: {
- label: "Certificates",
+ label: "Agreements",
props: {
index: 0,
rowSpan: 1,
@@ -202,46 +246,38 @@ class CertTable extends React.Component {
}
render() {
- let certRows = [];
- let serverTable;
- for (let cert of this.props.certs) {
- let obj = {
- 'nickname': [cert.attrs['nickname']],
- 'subject': [cert.attrs['subject']],
- 'issuer': [cert.attrs['issuer']],
- 'expires': [cert.attrs['expires']],
- 'flags': [cert.attrs['flags']],
- };
- certRows.push(obj);
- }
-
- if (certRows.length == 0) {
- serverTable = <DSTable
- getColumns={this.getSingleColumn}
- rowKey={"msg"}
- rows={[{msg: "No Certificates"}]}
- key={"nocerts"}
- />;
+ let agmtTable;
+ if (this.props.rows.length == 0) {
+ agmtTable =
+ <DSShortTable
+ getColumns={this.getSingleColumn}
+ rowKey={"msg"}
+ rows={[{msg: "No Agreements"}]}
+ />;
} else {
- serverTable = <DSTable
- getColumns={this.getColumns}
- rowKey={this.state.rowKey}
- rows={certRows}
- key={certRows}
- disableLoadingSpinner
- />;
+ agmtTable =
+ <DSTable
+ getColumns={this.getColumns}
+ fieldsToSearch={this.state.fieldsToSearch}
+ toolBarSearchField={this.state.searchField}
+ rowKey={this.state.rowKey}
+ rows={this.props.rows}
+ disableLoadingSpinner
+ toolBarPagination={[6, 12, 24, 48, 96]}
+ toolBarPaginationPerPage={6}
+ />;
}
return (
- <div>
- {serverTable}
+ <div className="ds-margin-top-xlg">
+ {agmtTable}
</div>
+
);
}
}
-// Future -
https://pagure.io/389-ds-base/issue/50491
-class CRLTable extends React.Component {
+class ManagerTable extends React.Component {
constructor(props) {
super(props);
@@ -251,7 +287,7 @@ class CRLTable extends React.Component {
{
property: "name",
header: {
- label: "Issued By",
+ label: "Replication Manager",
props: {
index: 0,
rowSpan: 1,
@@ -270,12 +306,113 @@ class CRLTable extends React.Component {
}
},
{
- property: "effective",
+ property: "actions",
header: {
- label: "Effective Date",
+ label: "",
props: {
index: 1,
rowSpan: 1,
+ colSpan: 1
+ },
+ formatters: [actionHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 1
+ },
+ formatters: [
+ (value, { rowData }) => {
+ return [
+ <td key={rowData.name[0]}>
+ <Button
+ bsStyle="default"
+ onClick={() => {
+ this.props.confirmDelete(rowData);
+ }}
+ title="Delete replication manager"
+
+
Delete
+ </Button>
+ </td>
+ ];
+ }
+ ]
+ }
+ }
+ ]
+ };
+ this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
+ }
+
+ getSingleColumn () {
+ return [
+ {
+ property: "msg",
+ header: {
+ label: "Replication Managers",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ ];
+ }
+
+ getColumns() {
+ return this.state.columns;
+ }
+
+ render() {
+ let managerTable;
+ if (this.props.rows.length == 0) {
+ managerTable = <DSShortTable
+ getColumns={this.getSingleColumn}
+ rowKey={"msg"}
+ rows={[{msg: "No Replication Managers"}]}
+ />;
+ } else {
+ managerTable = <DSShortTable
+ getColumns={this.getColumns}
+ rowKey={this.state.rowKey}
+ rows={this.props.rows}
+ disableLoadingSpinner
+ />;
+ }
+ return (
+ <div>
+ {managerTable}
+ </div>
+ );
+ }
+}
+
+class RUVTable extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ rowKey: "rid",
+ columns: [
+ {
+ property: "rid",
+ header: {
+ label: "Replica ID",
+ props: {
+ index: 0,
+ rowSpan: 1,
colSpan: 1,
sort: true
},
@@ -285,17 +422,17 @@ class CRLTable extends React.Component {
},
cell: {
props: {
- index: 1
+ index: 0
},
formatters: [tableCellFormatter]
}
},
{
- property: "nextUpdate",
+ property: "url",
header: {
- label: "Next Updateo",
+ label: "Replica LDAP URL",
props: {
- index: 2,
+ index: 1,
rowSpan: 1,
colSpan: 1,
sort: true
@@ -306,18 +443,17 @@ class CRLTable extends React.Component {
},
cell: {
props: {
- index: 2
+ index: 1
},
formatters: [tableCellFormatter]
}
},
-
{
- property: "type",
+ property: "maxcsn",
header: {
- label: "Type",
+ label: "Max CSN",
props: {
- index: 3,
+ index: 2,
rowSpan: 1,
colSpan: 1,
sort: true
@@ -328,17 +464,17 @@ class CRLTable extends React.Component {
},
cell: {
props: {
- index: 3
+ index: 2
},
formatters: [tableCellFormatter]
}
},
{
- property: "action",
+ property: "actions",
header: {
label: "",
props: {
- index: 4,
+ index: 3,
rowSpan: 1,
colSpan: 1
},
@@ -346,27 +482,21 @@ class CRLTable extends React.Component {
},
cell: {
props: {
- index: 4
+ index: 3
},
formatters: [
(value, { rowData }) => {
return [
- <td key={rowData.name[0]}>
- <DropdownButton id={rowData.name[0]}
- bsStyle="default"
title="Actions">
- <MenuItem eventKey="1"
onClick={() => {
- this.props.editIndex(rowData);
+ <td key={rowData.rid[0]}>
+ <Button
+ bsStyle="default"
+ onClick={() => {
+
this.props.confirmDelete(rowData.rid[0]);
}}
-
-
View CRL
- </MenuItem>
- <MenuItem eventKey="2"
onClick={() => {
- this.props.reindexIndex(rowData);
- }}
-
-
Delete CRL
- </MenuItem>
- </DropdownButton>
+ title="Attempt to clean and remove this
Replica ID from this suffix"
+
+
Clean
+ </Button>
</td>
];
}
@@ -376,6 +506,7 @@ class CRLTable extends React.Component {
]
};
this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
}
getSingleColumn () {
@@ -383,7 +514,7 @@ class CRLTable extends React.Component {
{
property: "msg",
header: {
- label: "Certificate Revocation Lists",
+ label: "Remote RUV's",
props: {
index: 0,
rowSpan: 1,
@@ -409,15 +540,15 @@ class CRLTable extends React.Component {
}
render() {
- let crlTable;
+ let ruvTable;
if (this.props.rows.length == 0) {
- crlTable = <DSTable
+ ruvTable = <DSShortTable
getColumns={this.getSingleColumn}
rowKey={"msg"}
- rows={[{msg: "None"}]}
+ rows={[{msg: "No RUV's"}]}
/>;
} else {
- crlTable = <DSTable
+ ruvTable = <DSShortTable
getColumns={this.getColumns}
rowKey={this.state.rowKey}
rows={this.props.rows}
@@ -426,29 +557,52 @@ class CRLTable extends React.Component {
}
return (
<div>
- {crlTable}
+ {ruvTable}
</div>
);
}
}
-// Props and defaults
+ReplAgmtTable.propTypes = {
+ rows: PropTypes.array,
+ edit: PropTypes.func,
+ poke: PropTypes.func,
+ init: PropTypes.func,
+ enable: PropTypes.func,
+ delete: PropTypes.func,
+};
+
+ReplAgmtTable.defaultProps = {
+ rows: [],
+ edit: noop,
+ poke: noop,
+ init: noop,
+ enable: noop,
+ delete: noop,
+};
+
+ManagerTable.propTypes = {
+ rows: PropTypes.array,
+ confirmDelete: PropTypes.func
+};
+
+ManagerTable.defaultProps = {
+ rows: [],
+ confirmDelete: noop
+};
-CertTable.propTypes = {
- // serverId: PropTypes.string,
- certs: PropTypes.array,
- editCert: PropTypes.func,
- delCert: PropTypes.func,
+RUVTable.propTypes = {
+ rows: PropTypes.array,
+ confirmDelete: PropTypes.func
};
-CertTable.defaultProps = {
- // serverId: "",
- certs: [],
- editCert: noop,
- delCert: noop,
+RUVTable.defaultProps = {
+ rows: [],
+ confirmDelete: noop
};
export {
- CertTable,
- CRLTable
+ ReplAgmtTable,
+ ManagerTable,
+ RUVTable,
};
diff --git a/src/cockpit/389-console/src/lib/replication/replTasks.jsx
b/src/cockpit/389-console/src/lib/replication/replTasks.jsx
new file mode 100644
index 0000000..acdc6c8
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/replication/replTasks.jsx
@@ -0,0 +1,314 @@
+import cockpit from "cockpit";
+import React from "react";
+import { log_cmd, bad_file_name } from "../tools.jsx";
+import { RUVTable } from "./replTables.jsx";
+import { ExportModal } from "./replModals.jsx";
+import { DoubleConfirmModal } from "../notifications.jsx";
+import PropTypes from "prop-types";
+import {
+ Button,
+ Col,
+ ControlLabel,
+ Icon,
+ noop,
+ Row,
+} from "patternfly-react";
+
+export class ReplRUV extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ errObj: {},
+ rid: "",
+ localRID: "",
+ ldifLocatio: "",
+ saveOK: false,
+ showConfirmCleanRUV: false,
+ modalChecked: false,
+ modalSpinning: false,
+ };
+ this.showConfirmCleanRUV = this.showConfirmCleanRUV.bind(this);
+ this.closeConfirmCleanRUV = this.closeConfirmCleanRUV.bind(this);
+ this.showConfirmExport = this.showConfirmExport.bind(this);
+ this.closeConfirmExport = this.closeConfirmExport.bind(this);
+ this.handleLDIFChange = this.handleLDIFChange.bind(this);
+ this.handleChange = this.handleChange.bind(this);
+ this.cleanRUV = this.cleanRUV.bind(this);
+ this.doExport = this.doExport.bind(this);
+ }
+
+ showConfirmCleanRUV (rid) {
+ this.setState({
+ rid: rid,
+ showConfirmCleanRUV: true,
+ modalChecked: false,
+ modalSpinning: false,
+ });
+ }
+
+ closeConfirmCleanRUV () {
+ this.setState({
+ showConfirmCleanRUV: false,
+ modalChecked: false,
+ modalSpinning: false,
+ });
+ }
+
+ showConfirmExport () {
+ this.setState({
+ saveOK: false,
+ showConfirmExport: true,
+ });
+ }
+
+ closeConfirmExport () {
+ this.setState({
+ showConfirmExport: false,
+ });
+ }
+
+ cleanRUV () {
+ // Enable/disable agmt
+ let cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'repl-tasks', 'cleanallruv', '--replica-id=' +
this.state.rid,
+ '--force-cleaning', '--suffix=' + this.props.suffix];
+
+ log_cmd('cleanRUV', 'Clean the rid', cmd);
+ cockpit
+ .spawn(cmd, {superuser: true, "err": "message"})
+ .done(content => {
+ this.props.reload(this.props.suffix);
+ this.props.addNotification(
+ 'success',
+ 'Successfully started CleanAllRUV task');
+ this.closeConfirmCleanRUV();
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to start CleanAllRUV task - ${errMsg.desc}`
+ );
+ this.closeConfirmCleanRUV();
+ });
+ }
+
+ handleLDIFChange (e) {
+ let value = e.target.value;
+ let saveOK = true;
+ if (value == "" || bad_file_name(value)) {
+ saveOK = false;
+ }
+ this.setState({
+ [e.target.id]: value,
+ saveOK: saveOK
+ });
+ }
+
+ handleChange (e) {
+ this.setState({
+ [e.target.id]: e.target.checked,
+ });
+ }
+
+ doExport () {
+ // Do Export
+ let export_cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-"
+ this.props.serverId + ".socket",
+ "backend", "export", this.props.suffix,
"--ldif=" + this.state.ldifLocation,
+ '--replication', "--encrypted"
+ ];
+
+ this.setState({
+ exportSpinner: true,
+ });
+
+ log_cmd("doExport", "replication do online export",
export_cmd);
+ cockpit
+ .spawn(export_cmd, { superuser: true, err: "message" })
+ .done(content => {
+ this.props.addNotification(
+ "success",
+ `Database export complete`
+ );
+ this.setState({
+ showConfirmExport: false,
+ exportSpinner: false,
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Error exporting database - ${errMsg.desc}`
+ );
+ this.setState({
+ showConfirmExport: false,
+ exportSpinner: false,
+ });
+ });
+ }
+
+ render() {
+ // Strip outthe loca RUV and display it diffent then onmly allow cleaning of
remote rids
+ let remote_rows = [];
+ let localRID = "";
+ let localURL = "";
+ let localCSN = "";
+ let localRawCSN = "";
+ let localMinCSN = "";
+ let localRawMinCSN = "";
+ for (let row of this.props.rows) {
+ if (row.rid == this.props.localRID) {
+ localRID = row.rid;
+ localURL = row.url;
+ localCSN = row.maxcsn;
+ localRawCSN = row.raw_maxcsn;
+ localMinCSN = row.csn;
+ localRawMinCSN = row.raw_csn;
+ } else {
+ remote_rows.push(row);
+ }
+ }
+ let localRUV =
+ <div className="ds-left-indent-md">
+ <Row className="ds-margin-top-med">
+ <Col sm={2}>
+ <ControlLabel>
+ Replica ID
+ </ControlLabel>
+ </Col>
+ <Col sm={10}>
+ <b>{localRID}</b>
+ </Col>
+ </Row>
+ <Row>
+ <Col sm={2}>
+ <ControlLabel>
+ LDAP URL
+ </ControlLabel>
+ </Col>
+ <Col sm={10}>
+ <b>{localURL}</b>
+ </Col>
+ </Row>
+ <Row>
+ <Col sm={2}>
+ <ControlLabel>
+ Min CSN
+ </ControlLabel>
+ </Col>
+ <Col sm={10}>
+ <b>{localMinCSN}</b> ({localRawMinCSN})
+ </Col>
+ </Row>
+ <Row>
+ <Col sm={2}>
+ <ControlLabel>
+ Max CSN
+ </ControlLabel>
+ </Col>
+ <Col sm={10}>
+ <b>{localCSN}</b> ({localRawCSN})
+ </Col>
+ </Row>
+ </div>;
+
+ if (localRID == "") {
+ localRUV =
+ <div className="ds-indent">
+ <i>
+ There is no local RUV, the database might not have been
initialized yet.
+ </i>
+ </div>;
+ }
+
+ return (
+ <div className="ds-margin-top-xlg ds-indent">
+ <ControlLabel className="ds-h4">
+ Local RUV
+ <Icon className="ds-left-margin ds-refresh"
+ type="fa" name="refresh" title="Refresh
the RUV for this suffix"
+ onClick={() => {
+ this.props.reload(this.props.suffix);
+ }}
+ />
+ </ControlLabel>
+ {localRUV}
+ <hr />
+ <Row className="ds-margin-top">
+ <Col sm={12} className="ds-word-wrap">
+ <ControlLabel className="ds-h4">
+ Remote RUV's
+ <Icon className="ds-left-margin ds-refresh"
+ type="fa" name="refresh"
title="Refresh the RUV for this suffix"
+ onClick={() => {
+ this.props.reload(this.props.suffix);
+ }}
+ />
+ </ControlLabel>
+ </Col>
+ </Row>
+ <div className="ds-left-indent-md ds-margin-top-lg">
+ <RUVTable
+ rows={remote_rows}
+ confirmDelete={this.showConfirmCleanRUV}
+ />
+ </div>
+ <hr />
+ <h4>Create Initialization LDIF File</h4>
+ <div className="ds-left-indent-md ds-margin-top-lg">
+ <p><i>Export this suffix with the replication metadata
needed to initialize another replica</i></p>
+ <Button
+ bsStyle="primary"
+ onClick={this.showConfirmExport}
+ className="ds-margin-top"
+ title="See Database Tab -> Backups & LDIFs to manage
the new LDIF"
+
+ Export To
LDIF
+ </Button>
+ </div>
+ <DoubleConfirmModal
+ showModal={this.state.showConfirmCleanRUV}
+ closeHandler={this.closeConfirmCleanRUV}
+ handleChange={this.handleChange}
+ actionHandler={this.cleanRUV}
+ spinning={this.state.modalSpinning}
+ item={"Replica ID " + this.state.rid}
+ checked={this.state.modalChecked}
+ mTitle="Remove RUV Element (CleanAllRUV)"
+ mMsg="Are you sure you want to attempt to clean this Replica ID
from the suffix?"
+ mSpinningMsg="Starting cleaning task (CleanAllRUV) ..."
+ mBtnName="Remove RUV Element"
+ />
+ <ExportModal
+ showModal={this.state.showConfirmExport}
+ closeHandler={this.closeConfirmExport}
+ handleChange={this.handleLDIFChange}
+ saveHandler={this.doExport}
+ spinning={this.state.exportSpinner}
+ saveOK={this.state.saveOK}
+ />
+ </div>
+ );
+ }
+}
+
+ReplRUV.propTypes = {
+ suffix: PropTypes.string,
+ serverId: PropTypes.string,
+ rows: PropTypes.array,
+ addNotification: PropTypes.func,
+ localRID: PropTypes.string,
+ reload: PropTypes.func,
+};
+
+ReplRUV.defaultProps = {
+ serverId: "",
+ suffix: "",
+ rows: [],
+ addNotification: noop,
+ localRID: "",
+ reload: noop,
+};
diff --git a/src/cockpit/389-console/src/lib/replication/winsyncAgmts.jsx
b/src/cockpit/389-console/src/lib/replication/winsyncAgmts.jsx
new file mode 100644
index 0000000..9303720
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/replication/winsyncAgmts.jsx
@@ -0,0 +1,1210 @@
+import cockpit from "cockpit";
+import React from "react";
+import { ConfirmPopup, DoubleConfirmModal } from "../notifications.jsx";
+import { ReplAgmtTable } from "./replTables.jsx";
+import { WinsyncAgmtModal } from "./replModals.jsx";
+import { log_cmd, valid_dn, valid_port } from "../tools.jsx";
+import PropTypes from "prop-types";
+import {
+ Button,
+ // Icon,
+ noop,
+} from "patternfly-react";
+
+export class WinsyncAgmts extends React.Component {
+ _mounted = false;
+ constructor(props) {
+ super(props);
+ this.state = {
+ showCreateAgmtModal: false,
+ showEditAgmtModal: false,
+ showConfirmDeleteAgmt: false,
+ showConfirmInitAgmt: false,
+ showConfirmEnableAgmt: false,
+ showConfirmDisableAgmt: false,
+ errObj: {},
+ modalMsg: "",
+ modalScheduleMsg: "",
+ savingAgmt: false,
+ mounted: false,
+ // Create agreement
+ agmtName: "",
+ agmtHost: "",
+ agmtPort: "",
+ agmtProtocol: "LDAPS",
+ agmtBindDN: "",
+ agmtBindPW: "",
+ agmtBindPWConfirm: "",
+ agmtFracAttrs: [],
+ agmtSync: true,
+ agmtSyncMon: true,
+ agmtSyncTue: true,
+ agmtSyncWed: true,
+ agmtSyncThu: true,
+ agmtSyncFri: true,
+ agmtSyncSat: true,
+ agmtSyncSun: true,
+ agmtStartTime: "0",
+ agmtEndTime: "0",
+ agmtInit: "noinit",
+ agmtSaveOK: false,
+ modalChecked: false,
+ modalSpinning: false,
+ // Winsync specific settings
+ agmtSyncGroups: false,
+ agmtSyncUsers: true,
+ agmtWinDomain: "",
+ agmtWinSubtree: "",
+ agmtDSSubtree: "",
+ agmtOneWaySync: "both", // "both", "toWindows",
"fromWindows"
+ agmtSyncInterval: "",
+ // Init agmt
+ agmtInitCounter: 0,
+ agmtInitIntervals: [],
+ };
+ this.showCreateAgmtModal = this.showCreateAgmtModal.bind(this);
+ this.closeCreateAgmtModal = this.closeCreateAgmtModal.bind(this);
+ this.closeEditAgmtModal = this.closeEditAgmtModal.bind(this);
+ this.handleChange = this.handleChange.bind(this);
+ this.handleModalChange = this.handleModalChange.bind(this);
+ this.handleTAFracAttrChange = this.handleTAFracAttrChange.bind(this);
+ this.handleTAFracAttrChangeEdit = this.handleTAFracAttrChangeEdit.bind(this);
+ this.createAgmt = this.createAgmt.bind(this);
+ this.showEditAgmt = this.showEditAgmt.bind(this);
+ this.saveAgmt = this.saveAgmt.bind(this);
+ this.pokeAgmt = this.pokeAgmt.bind(this);
+ this.initAgmt = this.initAgmt.bind(this);
+ this.enableAgmt = this.enableAgmt.bind(this);
+ this.disableAgmt = this.disableAgmt.bind(this);
+ this.deleteAgmt = this.deleteAgmt.bind(this);
+ this.showConfirmDeleteAgmt = this.showConfirmDeleteAgmt.bind(this);
+ this.closeConfirmDeleteAgmt = this.closeConfirmDeleteAgmt.bind(this);
+ this.showConfirmInitAgmt = this.showConfirmInitAgmt.bind(this);
+ this.closeConfirmInitAgmt = this.closeConfirmInitAgmt.bind(this);
+ this.confirmToggle = this.confirmToggle.bind(this);
+ this.closeConfirmEnableAgmt = this.closeConfirmEnableAgmt.bind(this);
+ this.closeConfirmDisableAgmt = this.closeConfirmDisableAgmt.bind(this);
+ this.watchAgmtInit = this.watchAgmtInit.bind(this);
+ }
+
+ componentDidMount () {
+ this._mounted = true;
+ }
+
+ componentWillUnmount () {
+ this._mounted = false;
+ }
+
+ listEqual(old_values, new_values) {
+ if (old_values.length != new_values.length) {
+ return false;
+ }
+ for (let i = old_values.length; i--;) {
+ if (old_values[i] != new_values[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ handleModalChange (e) {
+ this.setState({
+ [e.target.id]: e.target.checked,
+ });
+ }
+
+ handleChange (e) {
+ let value = e.target.type === 'checkbox' ? e.target.checked :
e.target.value;
+ let time_val = "";
+ let valueErr = false;
+ let errObj = this.state.errObj;
+ let all_good = true;
+ let modal_msg = "";
+ let modal_schedule_msg = "";
+ let edit = false;
+ if (value == "") {
+ valueErr = true;
+ }
+ errObj[e.target.id] = valueErr;
+ if (e.target.name == "agmt-modal-edit") {
+ let orig_attr = "_" + e.target.id;
+ let attr = e.target.id;
+ all_good = false;
+ if ((attr != 'agmtHost' && this.state.agmtHost !=
this.state._agmtHost) ||
+ (attr != 'agmtPort' && this.state.agmtPort !=
this.state._agmtPort) ||
+ (attr != 'agmtBindDN' && this.state.agmtBindDN !=
this.state._agmtBindDN) ||
+ (attr != 'agmtProtocol' && this.state.agmtProtocol !=
this.state._agmtProtocol) ||
+ (attr != 'agmtSync' && this.state.agmtSync !=
this.state._agmtSync) ||
+ (attr != 'agmtSyncGroups' && this.state.agmtSyncGroups !=
this.state._agmtSyncGroups) ||
+ (attr != 'agmtSyncUsers' && this.state.agmtSyncUsers !=
this.state._agmtSyncUsers) ||
+ (attr != 'agmtWinDomain' && this.state.agmtWinDomain !=
this.state._agmtWinDomain) ||
+ (attr != 'agmtWinSubtree' && this.state.agmtWinSubtree !=
this.state._agmtWinSubtree) ||
+ (attr != 'agmtDSSubtree' && this.state.agmtDSSubtree !=
this.state._agmtDSSubtree) ||
+ (attr != 'agmtOneWaySync' && this.state.agmtOneWaySync !=
this.state._agmtOneWaySync) ||
+ (attr != 'agmtSyncInterval' &&
this.state.agmtSyncInterval != this.state._agmtSyncInterval) ||
+ (attr != 'agmtFracAttrs' &&
!this.listEqual(this.state.agmtFracAttrs, this.state._agmtFracAttrs))) {
+ all_good = true;
+ }
+ if (!this.state._agmtSync) {
+ if ((attr != 'agmtSyncMon' && this.state.agmtSyncMon !=
this.state._agmtSyncMon) ||
+ (attr != 'agmtSyncTue' && this.state.agmtSyncTue !=
this.state._agmtSyncTue) ||
+ (attr != 'agmtSyncWed' && this.state.agmtSyncWed !=
this.state._agmtSyncWed) ||
+ (attr != 'agmtSyncThu' && this.state.agmtSyncThu !=
this.state._agmtSyncThu) ||
+ (attr != 'agmtSyncFri' && this.state.agmtSyncFri !=
this.state._agmtSyncFri) ||
+ (attr != 'agmtSyncSat' && this.state.agmtSyncSat !=
this.state._agmtSyncSat) ||
+ (attr != 'agmtSyncSun' && this.state.agmtSyncSun !=
this.state._agmtSyncSun)) {
+ all_good = true;
+ }
+ }
+ if (attr != 'agmtFracAttrs' &&
+ value != this.state[orig_attr]) {
+ all_good = true;
+ } else if (attr == 'agmtFracAttrs' && !this.listEqual(value,
this.state._agmtFracAttrs)) {
+ all_good = true;
+ }
+ }
+
+ if (e.target.type == "time") {
+ // Strip out the colon from the time
+ time_val = value.replace(':', '');
+ }
+
+ if (e.target.name.startsWith("agmt-modal")) {
+ // Validate modal settings "live"
+ if (e.target.id == 'agmtName') {
+ if (value == "") {
+ all_good = false;
+ }
+ } else if (this.state.agmtName == "") {
+ all_good = false;
+ }
+ if (e.target.id == 'agmtHost') {
+ if (value == "") {
+ all_good = false;
+ }
+ } else if (this.state.agmtHost == "") {
+ all_good = false;
+ } else if (edit && value == this.state._agmtHost) {
+ all_good = false;
+ }
+ if (e.target.id == 'agmtPort') {
+ if (value == "") {
+ all_good = false;
+ } else if (!valid_port(value)) {
+ all_good = false;
+ errObj['agmtPort'] = true;
+ modal_msg = "Invalid Consumer Port number";
+ }
+ } else if (this.state.agmtPort == "") {
+ all_good = false;
+ }
+ if (e.target.id == 'agmtBindDN') {
+ if (value == "") {
+ all_good = false;
+ }
+ if (!valid_dn(value)) {
+ errObj['agmtBindDN'] = true;
+ all_good = false;
+ modal_msg = "Invalid DN for Bind DN";
+ }
+ } else if (this.state.agmtBindDN == "") {
+ all_good = false;
+ } else if (!valid_dn(this.state.agmtBindDN)) {
+ modal_msg = "Invalid DN for Bind DN";
+ errObj['agmtBindDN'] = true;
+ all_good = false;
+ }
+ if (e.target.id == 'agmtBindPW') {
+ if (value == "") {
+ all_good = false;
+ } else if (value != this.state.agmtBindPWConfirm) {
+ modal_msg = "Passwords Do Not Match";
+ errObj['agmtBindPW'] = true;
+ errObj['agmtBindPWConfirm'] = true;
+ all_good = false;
+ } else {
+ errObj['agmtBindPW'] = false;
+ errObj['agmtBindPWConfirm'] = false;
+ }
+ } else if (this.state.agmtBindPW == "") {
+ all_good = false;
+ }
+ if (e.target.id == 'agmtBindPWConfirm') {
+ if (value == "") {
+ all_good = false;
+ } else if (value != this.state.agmtBindPW) {
+ modal_msg = "Passwords Do Not Match";
+ errObj['agmtBindPW'] = true;
+ errObj['agmtBindPWConfirm'] = true;
+ all_good = false;
+ } else {
+ errObj['agmtBindPW'] = false;
+ errObj['agmtBindPWConfirm'] = false;
+ }
+ } else if (this.state.agmtBindPWConfirm == "") {
+ all_good = false;
+ }
+ if (e.target.id == 'agmtSync') {
+ if (!value) {
+ if (this.state.agmtStartTime >= this.state.agmtEndTime) {
+ modal_schedule_msg = "Schedule start time is greater than or
equal to the end time";
+ errObj['agmtStartTime'] = true;
+ all_good = false;
+ }
+ }
+ } else if (!this.state.agmtSync) {
+ // Check the days first
+ let have_days = false;
+ let days = ["agmtSyncSun", "agmtSyncMon",
"agmtSyncTue", "agmtSyncWed",
+ "agmtSyncThu", "agmtSyncFri",
"agmtSyncSat"];
+ for (let day of days) {
+ if ((e.target.id != day && this.state[day]) || (e.target.id
== day && value)) {
+ have_days = true;
+ break;
+ }
+ }
+ if (!have_days) {
+ modal_schedule_msg = "You must select at least one day for
replication";
+ all_good = false;
+ } else if (e.target.id == 'agmtStartTime') {
+ if (time_val == "") {
+ all_good = false;
+ errObj['agmtStartTime'] = true;
+ } else if (time_val >=
this.state.agmtEndTime.replace(":", "")) {
+ errObj['agmtStartTime'] = true;
+ all_good = false;
+ modal_schedule_msg = "Schedule start time is greater than or
equal to the end time";
+ } else {
+ // All good, reset form
+ modal_schedule_msg = "";
+ errObj['agmtStartTime'] = false;
+ errObj['agmtEndTime'] = false;
+ }
+ } else if (e.target.id == 'agmtEndTime') {
+ if (time_val == "") {
+ errObj['agmtEndTime'] = true;
+ all_good = false;
+ } else if (this.state.agmtStartTime.replace(":",
"") >= time_val) {
+ modal_schedule_msg = "Schedule start time is greater than or
equal to the end time";
+ errObj['agmtStartTime'] = true;
+ all_good = false;
+ } else {
+ // All good, reset form
+ modal_schedule_msg = "";
+ errObj['agmtStartTime'] = false;
+ errObj['agmtEndTime'] = false;
+ }
+ } else if (this.state.agmtStartTime >= this.state.agmtEndTime) {
+ modal_schedule_msg = "Schedule start time is greater than or
equal to the end time";
+ errObj['agmtStartTime'] = true;
+ all_good = false;
+ }
+ }
+ if (e.target.id == 'agmtSyncGroups') {
+ if (edit && value == this.state._agmtSyncGroups) {
+ all_good = false;
+ }
+ }
+ if (e.target.id == 'agmtSyncUsers') {
+ if (edit && value == this.state._agmtSyncUsers) {
+ all_good = false;
+ }
+ }
+ if (e.target.id == 'agmtWinSubtree') {
+ if (value == "") {
+ all_good = false;
+ } else if (edit && value == this.state._agmtWinSubtree) {
+ all_good = false;
+ }
+ if (!valid_dn(value)) {
+ errObj['agmtWinSubtree'] = true;
+ all_good = false;
+ modal_msg = "Invalid DN for Windows Subtree";
+ }
+ }
+ if (e.target.id == 'agmtDSSubtree') {
+ if (value == "") {
+ all_good = false;
+ } else if (edit && value == this.state._agmtDSSubtree) {
+ all_good = false;
+ }
+ if (!valid_dn(value)) {
+ errObj['agmtDSSubtree'] = true;
+ all_good = false;
+ modal_msg = "Invalid DN for Directory Server Subtree";
+ }
+ }
+ if (e.target.id == 'agmtSyncInterval') {
+ if (value != "" && isNaN(value)) {
+ errObj['agmtSyncInterval'] = true;
+ all_good = false;
+ modal_msg = "Invalid value, value must be a number";
+ } else if (edit && value == this.state._agmtSyncInterval) {
+ all_good = false;
+ }
+ }
+ if (e.target.id == 'agmtOneWaySync') {
+ if (edit && value == this.state._agmtOneWaySync) {
+ all_good = false;
+ }
+ }
+ // End of agmt modal live validation
+ }
+ this.setState({
+ [e.target.id]: value,
+ errObj: errObj,
+ agmtSaveOK: all_good,
+ modalMsg: modal_msg,
+ modalScheduleMsg: modal_schedule_msg,
+ });
+ }
+
+ handleTAFracAttrChangeEdit (values) {
+ // TypeAhead handling
+ let e = {
+ target: {
+ name: 'agmt-modal-edit',
+ id: 'agmtFracAttrs',
+ value: values,
+ type: 'input',
+ }
+ };
+ this.handleChange(e);
+ }
+
+ handleTAFracAttrChange (values) {
+ // TypeAhead handling
+ let e = {
+ target: {
+ name: 'agmt-modal',
+ id: 'agmtFracAttrs',
+ value: values,
+ type: 'input',
+ }
+ };
+ this.handleChange(e);
+ }
+
+ showConfirmDeleteAgmt (agmtName) {
+ this.setState({
+ agmtName: agmtName,
+ showConfirmDeleteAgmt: true,
+ modalSpinning: false,
+ modalChecked: false,
+ });
+ }
+
+ closeConfirmDeleteAgmt () {
+ this.setState({
+ showConfirmDeleteAgmt: false,
+ modalChecked: false,
+ });
+ }
+
+ showConfirmInitAgmt (agmtName) {
+ this.setState({
+ agmtName: agmtName,
+ showConfirmInitAgmt: true,
+ modalSpinning: false,
+ modalChecked: false,
+ });
+ }
+
+ closeConfirmInitAgmt () {
+ this.setState({
+ showConfirmInitAgmt: false,
+ modalChecked: false,
+ });
+ }
+
+ showCreateAgmtModal () {
+ this.setState({
+ showCreateAgmtModal: true,
+ agmtName: "",
+ agmtHost: "",
+ agmtPort: "",
+ agmtProtocol: "LDAP",
+ agmtBindDN: "",
+ agmtBindPW: "",
+ agmtBindPWConfirm: "",
+ agmtFracAttrs: [],
+ agmtSync: true,
+ agmtSyncMon: true,
+ agmtSyncTue: true,
+ agmtSyncWed: true,
+ agmtSyncThu: true,
+ agmtSyncFri: true,
+ agmtSyncSat: true,
+ agmtSyncSun: true,
+ agmtStartTime: "00:00",
+ agmtEndTime: "23:59",
+ agmtInit: "noinit",
+ agmtSaveOK: false,
+ agmtSyncGroups: false,
+ agmtSyncUsers: true,
+ agmtWinDomain: "",
+ agmtWinSubtree: "",
+ agmtDSSubtree: "",
+ agmtOneWaySync: "both", // "both", "toWindows",
"fromWindows"
+ agmtSyncInterval: "",
+ modalScheduleMsg: "",
+ errObj: {
+ // Marks all the fields as required
+ agmtName: true,
+ agmtHost: true,
+ agmtPort: true,
+ agmtBindDN: true,
+ agmtBindPW: true,
+ agmtBindPWConfirm: true,
+ agmtStartTime: false,
+ agmtEndTime: false,
+ agmtWinDomain: true,
+ agmtWinSubtree: true,
+ agmtDSSubtree: true,
+ }
+ });
+ }
+
+ closeCreateAgmtModal () {
+ this.setState({
+ showCreateAgmtModal: false
+ });
+ }
+
+ closeEditAgmtModal () {
+ this.setState({
+ showEditAgmtModal: false
+ });
+ }
+
+ showEditAgmt (agmtName) {
+ // Search for the agmt to get all the details
+ let cmd = [
+ 'dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' +
this.props.serverId + '.socket',
+ 'repl-winsync-agmt', 'get', agmtName, '--suffix=' +
this.props.suffix,
+ ];
+
+ log_cmd('showEditAgmt', 'Edit winsync agreement', cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const config = JSON.parse(content);
+ let agmtName = "";
+ let agmtHost = "";
+ let agmtPort = "";
+ let agmtProtocol = "";
+ let agmtBindDN = "";
+ let agmtBindPW = "";
+ let agmtBindPWConfirm = "";
+ let agmtFracAttrs = [];
+ let agmtSync = true;
+ let agmtSyncMon = false;
+ let agmtSyncTue = false;
+ let agmtSyncWed = false;
+ let agmtSyncThu = false;
+ let agmtSyncFri = false;
+ let agmtSyncSat = false;
+ let agmtSyncSun = false;
+ let agmtStartTime = "";
+ let agmtEndTime = "";
+ let agmtSyncGroups = false;
+ let agmtSyncUsers = false;
+ let agmtWinDomain = "";
+ let agmtWinSubtree = "";
+ let agmtDSSubtree = "";
+ let agmtOneWaySync = "both";
+ let agmtSyncInterval = "";
+
+ for (let attr in config['attrs']) {
+ let val = config['attrs'][attr][0];
+ if (attr == "winsyncinterval") {
+ agmtSyncInterval = val;
+ }
+ if (attr == "onewaysync") {
+ agmtOneWaySync = val;
+ }
+ if (attr == "nsds7directoryreplicasubtree") {
+ agmtDSSubtree = val;
+ }
+ if (attr == "nsds7windowsreplicasubtree") {
+ agmtWinSubtree = val;
+ }
+ if (attr == "nsds7windowsdomain") {
+ agmtWinDomain = val;
+ }
+ if (attr == "nsds7newwinusersyncenabled") {
+ if (val.toLowerCase() == "on") {
+ agmtSyncUsers = true;
+ }
+ }
+ if (attr == "nsds7newwingroupsyncenabled") {
+ if (val.toLowerCase() == "on") {
+ agmtSyncGroups = true;
+ }
+ }
+ if (attr == "cn") {
+ agmtName = val;
+ }
+ if (attr == "nsds5replicahost") {
+ agmtHost = val;
+ }
+ if (attr == "nsds5replicaport") {
+ agmtPort = val;
+ }
+ if (attr == "nsds5replicatransportinfo") {
+ agmtProtocol = val;
+ }
+ if (attr == "nsds5replicabinddn") {
+ agmtBindDN = val;
+ }
+ if (attr == "nsds5replicacredentials") {
+ agmtBindPW = val;
+ agmtBindPWConfirm = val;
+ }
+ if (attr == "nsds5replicatedattributelist") {
+ let attrs = val.replace("(objectclass=*) $
EXCLUDE", "").trim();
+ agmtFracAttrs = attrs.split(' ');
+ }
+ if (attr == "nsds5replicaupdateschedule") {
+ agmtSync = false;
+ // Parse schedule
+ let parts = val.split(' ');
+ let times = parts[0].split('-');
+ let days = parts[1];
+
+ // Do the times
+ agmtStartTime = times[0].substring(0, 2) + ":" +
times[0].substring(2, 4);
+ agmtEndTime = times[1].substring(0, 2) + ":" +
times[1].substring(2, 4);
+
+ // Do the days
+ if (days.includes("0")) {
+ agmtSyncSun = true;
+ }
+ if (days.includes("1")) {
+ agmtSyncMon = true;
+ }
+ if (days.includes("2")) {
+ agmtSyncTue = true;
+ }
+ if (days.includes("3")) {
+ agmtSyncWed = true;
+ }
+ if (days.includes("4")) {
+ agmtSyncThu = true;
+ }
+ if (days.includes("5")) {
+ agmtSyncFri = true;
+ }
+ if (days.includes("6")) {
+ agmtSyncSat = true;
+ }
+ }
+ }
+ if (this._mounted) {
+ this.setState({
+ showEditAgmtModal: true,
+ errObj: {},
+ agmtName: agmtName,
+ agmtHost: agmtHost,
+ agmtPort: agmtPort,
+ agmtProtocol: agmtProtocol,
+ agmtBindDN: agmtBindDN,
+ agmtBindPW: agmtBindPW,
+ agmtBindPWConfirm: agmtBindPWConfirm,
+ agmtFracAttrs: agmtFracAttrs,
+ agmtSync: agmtSync,
+ agmtSyncMon: agmtSyncMon,
+ agmtSyncTue: agmtSyncTue,
+ agmtSyncWed: agmtSyncWed,
+ agmtSyncThu: agmtSyncThu,
+ agmtSyncFri: agmtSyncFri,
+ agmtSyncSat: agmtSyncSat,
+ agmtSyncSun: agmtSyncSun,
+ agmtStartTime: agmtStartTime,
+ agmtEndTime: agmtEndTime,
+ agmtSyncGroups: agmtSyncGroups,
+ agmtSyncUsers: agmtSyncUsers,
+ agmtWinDomain: agmtWinDomain,
+ agmtWinSubtree: agmtWinSubtree,
+ agmtDSSubtree: agmtDSSubtree,
+ agmtOneWaySync: agmtOneWaySync,
+ agmtSyncInterval: agmtSyncInterval,
+ agmtSaveOK: false,
+ // Record original values before editing
+ _agmtName: agmtName,
+ _agmtHost: agmtHost,
+ _agmtPort: agmtPort,
+ _agmtProtocol: agmtProtocol,
+ _agmtBindDN: agmtBindDN,
+ _agmtBindPW: agmtBindPW,
+ _agmtBindPWConfirm: agmtBindPWConfirm,
+ _agmtFracAttrs: agmtFracAttrs,
+ _agmtSync: agmtSync,
+ _agmtSyncMon: agmtSyncMon,
+ _agmtSyncTue: agmtSyncTue,
+ _agmtSyncWed: agmtSyncWed,
+ _agmtSyncThu: agmtSyncThu,
+ _agmtSyncFri: agmtSyncFri,
+ _agmtSyncSat: agmtSyncSat,
+ _agmtSyncSun: agmtSyncSun,
+ _agmtStartTime: agmtStartTime,
+ _agmtEndTime: agmtEndTime,
+ _agmtSyncGroups: agmtSyncGroups,
+ _agmtSyncUsers: agmtSyncUsers,
+ _agmtWinDomain: agmtWinDomain,
+ _agmtWinSubtree: agmtWinSubtree,
+ _agmtDSSubtree: agmtDSSubtree,
+ _agmtOneWaySync: agmtOneWaySync,
+ _agmtSyncInterval: agmtSyncInterval,
+ });
+ }
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to get agreement information for: "${agmtName}"
- ${errMsg.desc}`
+ );
+ });
+ }
+
+ saveAgmt () {
+ let cmd = [
+ 'dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' +
this.props.serverId + '.socket',
+ 'repl-winsync-agmt', 'set', this.state.agmtName,
'--suffix=' + this.props.suffix,
+ ];
+
+ // Handle Schedule
+ if (!this.state.agmtSync) {
+ let agmt_days = "";
+ if (this.state.agmtSyncSun) {
+ agmt_days += "0";
+ }
+ if (this.state.agmtSyncMon) {
+ agmt_days += "1";
+ }
+ if (this.state.agmtSyncTue) {
+ agmt_days += "2";
+ }
+ if (this.state.agmtSyncWed) {
+ agmt_days += "3";
+ }
+ if (this.state.agmtSyncThu) {
+ agmt_days += "4";
+ }
+ if (this.state.agmtSyncFri) {
+ agmt_days += "5";
+ }
+ if (this.state.agmtSyncSat) {
+ agmt_days += "6";
+ }
+ cmd.push('--schedule=' +
this.state.agmtStartTime.replace(':', '') + "-" +
this.state.agmtEndTime.replace(':', '') + " " + agmt_days);
+ } else if (this.state.agmtSync != this.state._agmtSync &&
this.state.agmtSync) {
+ // We disabled custom scheduleRow
+ cmd.push('--schedule=');
+ }
+ if (this.state.agmtSyncGroups != this.state._agmtSyncGroups) {
+ let val = "off";
+ if (this.state.agmtSyncGroups) {
+ val = "on";
+ }
+ cmd.push('--sync-groups=' + val);
+ }
+ if (this.state.agmtSyncUsers != this.state._agmtSyncUsers) {
+ let val = "off";
+ if (this.state.agmtSyncUsers) {
+ val = "on";
+ }
+ cmd.push('--sync-users=' + val);
+ }
+ if (this.state.agmtWinDomain != this.state._agmtWinDomain) {
+ cmd.push('--win-domain=' + this.state.agmtWinDomain);
+ }
+ if (this.state.agmtWinSubtree != this.state._agmtWinSubtree) {
+ cmd.push('--win-subtree=' + this.state.agmtWinSubtree);
+ }
+ if (this.state.agmtDSSubtree != this.state._agmtDSSubtree) {
+ cmd.push('--ds-subtree=' + this.state.agmtDSSubtree);
+ }
+ if (this.state.agmtOneWaySync != this.state._agmtOneWaySync) {
+ cmd.push('--one-way-sync=' + this.state.agmtOneWaySync);
+ }
+ if (this.state.agmtSyncInterval != this.state._agmtSyncInterval) {
+ cmd.push('--sync-interval=' + this.state.agmtSyncInterval);
+ }
+ if (this.state.agmtProtocol != this.state._agmtProtocol) {
+ cmd.push('--conn-protocol=' + this.state.agmtProtocol);
+ }
+ if (this.state.agmtBindPW != this.state._agmtBindPW) {
+ cmd.push('--bind-passwd=' + this.state.agmtBindPW);
+ }
+ if (this.state.agmtBindDN != this.state._agmtBindDN) {
+ cmd.push('--bind-passwd=' + this.state.agmtBindDN);
+ }
+ if (this.state.agmtFracAttrs != this.state._agmtFracAttrs) {
+ cmd.push('--frac-list=' + this.state.agmtFracAttrs.join('
'));
+ }
+ if (this.state.agmtHost != this.state._agmtHost) {
+ cmd.push('--host=' + this.state.agmtHost);
+ }
+ if (this.state.agmtPort != this.state._agmtPort) {
+ cmd.push('--port=' + this.state.agmtPort);
+ }
+
+ this.setState({
+ savingAgmt: true
+ });
+ log_cmd('saveAgmt', 'update winsync agreement', cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ this.props.reload(this.props.suffix);
+ if (this._mounted) {
+ this.setState({
+ savingAgmt: false,
+ showEditAgmtModal: false,
+ });
+ }
+ this.props.addNotification(
+ 'success',
+ 'Successfully updated winsync agreement'
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to update winsync agreement - ${errMsg.desc}`
+ );
+ this.setState({
+ savingAgmt: false
+ });
+ });
+ }
+
+ pokeAgmt (agmtName) {
+ let cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'repl-winsync-agmt', 'poke', agmtName, '--suffix=' +
this.props.suffix];
+ log_cmd('pokeAgmt', 'send updates now', cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, "err": "message" })
+ .done(content => {
+ this.props.reload(this.props.suffix);
+ this.props.addNotification(
+ 'success',
+ 'Successfully poked winsync agreement'
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ 'error',
+ `Failed to poke winsync agreement - ${errMsg.desc}`
+ );
+ });
+ }
+
+ initAgmt () {
+ let init_cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'repl-winsync-agmt', 'init', '--suffix=' +
this.props.suffix, this.state.agmtName];
+ log_cmd('initAgmt', 'Initialize agreement', init_cmd);
+ cockpit
+ .spawn(init_cmd, { superuser: true, "err": "message"
})
+ .done(content => {
+ var agmtIntervalCount = this.state.agmtInitCounter + 1;
+ var intervals = this.state.agmtInitIntervals;
+ this.props.reload(this.props.suffix);
+ intervals[agmtIntervalCount] = setInterval(this.watchAgmtInit, 2000,
this.state.agmtName, agmtIntervalCount);
+ // This triggers error and does not actually work
+ if (this._mounted) {
+ this.setState({
+ agmtInitCounter: agmtIntervalCount,
+ agmtInitIntervals: intervals,
+ showConfirmInitAgmt: false
+ });
+ }
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ 'error',
+ `Failed to initialize winsync agreement - ${errMsg.desc}`
+ );
+ this.setState({
+ showConfirmInitAgmt: false
+ });
+ });
+ }
+
+ confirmToggle (agmtName, state) {
+ if (state == 'Enabled') {
+ this.setState({
+ agmtName: agmtName,
+ showConfirmDisableAgmt: true
+ });
+ } else {
+ this.setState({
+ agmtName: agmtName,
+ showConfirmEnableAgmt: true
+ });
+ }
+ }
+
+ closeConfirmEnableAgmt () {
+ this.setState({
+ showConfirmEnableAgmt: false
+ });
+ }
+
+ closeConfirmDisableAgmt () {
+ this.setState({
+ showConfirmDisableAgmt: false
+ });
+ }
+
+ enableAgmt (agmtName) {
+ // Enable/disable agmt
+ let cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'repl-winsync-agmt', 'enable', agmtName, '--suffix='
+ this.props.suffix];
+ log_cmd('enableAgmt', 'enable agmt', cmd);
+ cockpit
+ .spawn(cmd, {superuser: true, "err": "message"})
+ .done(content => {
+ this.props.reload(this.props.suffix);
+ this.props.addNotification(
+ 'success',
+ 'Successfully enabled winsync agreement');
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to enabled winsync agreement - ${errMsg.desc}`
+ );
+ });
+ }
+
+ disableAgmt (agmtName) {
+ // Enable/disable agmt
+ let cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'repl-winsync-agmt', 'disable', agmtName, '--suffix='
+ this.props.suffix];
+ log_cmd('disableAgmt', 'Disable agmt', cmd);
+ cockpit
+ .spawn(cmd, {superuser: true, "err": "message"})
+ .done(content => {
+ this.props.reload(this.props.suffix);
+ this.props.addNotification(
+ 'success',
+ 'Successfully disabled winsync agreement');
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to disable winsync agreement - ${errMsg.desc}`
+ );
+ });
+ }
+
+ deleteAgmt () {
+ this.setState({
+ deleteSpinning: true
+ });
+ let cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'repl-winsync-agmt', 'delete', '--suffix=' +
this.props.suffix, this.state.agmtName];
+ log_cmd('deleteAgmt', 'Delete agmt', cmd);
+ cockpit
+ .spawn(cmd, {superuser: true, "err": "message"})
+ .done(content => {
+ this.props.reload(this.props.suffix);
+ this.props.addNotification(
+ 'success',
+ 'Successfully deleted winsync agreement');
+ this.setState({
+ showDeleteConfirm: false,
+ deleteSpinning: false
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to delete winsync agreement - ${errMsg.desc}`
+ );
+ this.setState({
+ showDeleteConfirm: false,
+ deleteSpinning: false
+ });
+ });
+ }
+
+ createAgmt () {
+ let cmd = [
+ 'dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' +
this.props.serverId + '.socket',
+ 'repl-winsync-agmt', 'create', this.state.agmtName,
'--suffix=' + this.props.suffix,
+ '--host=' + this.state.agmtHost, '--port=' +
this.state.agmtPort,
+ '--conn-protocol=' + this.state.agmtProtocol,
+ '--bind-dn=' + this.state.agmtBindDN, '--bind-passwd=' +
this.state.agmtBindPW,
+ '--ds-subtree=' + this.state.agmtDSSubtree, '--win-subtree='
+ this.state.agmtWinSubtree,
+ '--win-domain=' + this.state.agmtWinDomain, '--one-way-sync='
+ this.state.agmtOneWaySync
+ ];
+
+ // Handle Schedule
+ if (!this.state.agmtSync) {
+ let agmt_days = "";
+ if (this.state.agmtSyncSun) {
+ agmt_days += "0";
+ }
+ if (this.state.agmtSyncMon) {
+ agmt_days += "1";
+ }
+ if (this.state.agmtSyncTue) {
+ agmt_days += "2";
+ }
+ if (this.state.agmtSyncWed) {
+ agmt_days += "3";
+ }
+ if (this.state.agmtSyncThu) {
+ agmt_days += "4";
+ }
+ if (this.state.agmtSyncFri) {
+ agmt_days += "5";
+ }
+ if (this.state.agmtSyncSat) {
+ agmt_days += "6";
+ }
+ cmd.push('--schedule=' +
this.state.agmtStartTime.replace(':', '') + "-" +
this.state.agmtEndTime.replace(':', '') + " " + agmt_days);
+ }
+
+ if (this.state.agmtFracAttrs.length > 0) {
+ cmd.push('--frac-list=' + this.state.agmtFracAttrs.join('
'));
+ }
+ if (this.state.agmtSyncGroups) {
+ cmd.push('--sync-groups=on');
+ }
+ if (this.state.agmtSyncUsers) {
+ cmd.push('--sync-users=on');
+ }
+ if (this.state.agmtSyncInterval != "") {
+ cmd.push('--sync-interval=' + this.state.agmtSyncInterval);
+ }
+
+ this.setState({
+ savingAgmt: true
+ });
+ log_cmd('createAgmt', 'Create winsync agreement', cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ this.props.reload(this.props.suffix);
+ if (this._mounted) {
+ this.setState({
+ savingAgmt: false,
+ showCreateAgmtModal: false,
+ });
+ }
+ this.props.addNotification(
+ 'success',
+ 'Successfully created winsync agreement'
+ );
+ if (this.state.agmtInit == 'online-init') {
+ this.initAgmt(this.state.agmtName);
+ }
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to create winsync agreement - ${errMsg.desc}`
+ );
+ this.setState({
+ savingAgmt: false
+ });
+ });
+ }
+
+ watchAgmtInit(agmtName, idx) {
+ // Watch the init, then clear the interval index
+ let status_cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'repl-winsync-agmt', 'init-status', '--suffix=' +
this.props.suffix, agmtName];
+ log_cmd('watchAgmtInit', 'Get initialization status for agmt',
status_cmd);
+ cockpit
+ .spawn(status_cmd, {superuser: true, "err":
"message"})
+ .done(data => {
+ let init_status = JSON.parse(data);
+ if (init_status.startsWith('Agreement successfully
initialized') ||
+ init_status.startsWith('Agreement initialization
failed')) {
+ // Either way we're done, stop watching the status
+ clearInterval(this.state.agmtInitIntervals[idx]);
+ }
+ this.props.reload(this.props.suffix);
+ });
+ }
+
+ render() {
+ return (
+ <div className="ds-margin-right">
+ <ReplAgmtTable
+ rows={this.props.rows}
+ edit={this.showEditAgmt}
+ poke={this.pokeAgmt}
+ init={this.showConfirmInitAgmt}
+ enable={this.confirmToggle}
+ delete={this.showConfirmDeleteAgmt}
+ />
+ <div className="ds-margin-top ds-container ds-inline">
+ <Button
+ bsStyle="primary"
+ onClick={this.showCreateAgmtModal}
+
+ Create
Agreement
+ </Button>
+ <Button
+ className="ds-left-margin"
+ bsStyle="default"
+ onClick={() => {
+ this.props.reload(this.props.suffix);
+ }}
+
+ Refresh
Agreements
+ </Button>
+ </div>
+ <WinsyncAgmtModal
+ showModal={this.state.showCreateAgmtModal}
+ closeHandler={this.closeCreateAgmtModal}
+ handleChange={this.handleChange}
+ handleFracChange={this.handleTAFracAttrChange}
+ saveHandler={this.createAgmt}
+ spinning={this.state.savingAgmt}
+ agmtName={this.state.agmtName}
+ agmtHost={this.state.agmtHost}
+ agmtPort={this.state.agmtPort}
+ agmtBindDN={this.state.agmtBindDN}
+ agmtBindPW={this.state.agmtBindPW}
+ agmtBindPWConfirm={this.state.agmtBindPWConfirm}
+ agmtProtocol={this.state.agmtProtocol}
+ agmtFracAttrs={this.state.agmtFracAttrs}
+ agmtSync={this.state.agmtSync}
+ agmtSyncMon={this.state.agmtSyncMon}
+ agmtSyncTue={this.state.agmtSyncTue}
+ agmtSyncWed={this.state.agmtSyncWed}
+ agmtSyncThu={this.state.agmtSyncThu}
+ agmtSyncFri={this.state.agmtSyncFri}
+ agmtSyncSat={this.state.agmtSyncSat}
+ agmtSyncSun={this.state.agmtSyncSun}
+ agmtStartTime={this.state.agmtStartTime}
+ agmtEndTime={this.state.agmtEndTime}
+ agmtSyncGroups={this.state.agmtSyncGroups}
+ agmtSyncUsers={this.state.agmtSyncUsers}
+ agmtWinDomain={this.state.agmtWinDomain}
+ agmtWinSubtree={this.state.agmtWinSubtree}
+ agmtDSSubtree={this.state.agmtDSSubtree}
+ agmtOneWaySync={this.state.agmtOneWaySync}
+ agmtSyncInterval={this.state.agmtSyncInterval}
+ availAttrs={this.props.attrs}
+ error={this.state.errObj}
+ errorMsg={this.state.modalMsg}
+ errorScheduleMsg={this.state.modalScheduleMsg}
+ saveOK={this.state.agmtSaveOK}
+ />
+ <WinsyncAgmtModal
+ showModal={this.state.showEditAgmtModal}
+ closeHandler={this.closeEditAgmtModal}
+ handleChange={this.handleChange}
+ handleFracChange={this.handleTAFracAttrChangeEdit}
+ saveHandler={this.saveAgmt}
+ spinning={this.state.savingAgmt}
+ agmtName={this.state.agmtName}
+ agmtHost={this.state.agmtHost}
+ agmtPort={this.state.agmtPort}
+ agmtBindDN={this.state.agmtBindDN}
+ agmtBindPW={this.state.agmtBindPW}
+ agmtBindPWConfirm={this.state.agmtBindPWConfirm}
+ agmtProtocol={this.state.agmtProtocol}
+ agmtStripAttrs={this.state.agmtStripAttrs}
+ agmtFracAttrs={this.state.agmtFracAttrs}
+ agmtFracInitAttrs={this.state.agmtFracInitAttrs}
+ agmtSync={this.state.agmtSync}
+ agmtSyncMon={this.state.agmtSyncMon}
+ agmtSyncTue={this.state.agmtSyncTue}
+ agmtSyncWed={this.state.agmtSyncWed}
+ agmtSyncThu={this.state.agmtSyncThu}
+ agmtSyncFri={this.state.agmtSyncFri}
+ agmtSyncSat={this.state.agmtSyncSat}
+ agmtSyncSun={this.state.agmtSyncSun}
+ agmtStartTime={this.state.agmtStartTime}
+ agmtEndTime={this.state.agmtEndTime}
+ agmtSyncGroups={this.state.agmtSyncGroups}
+ agmtSyncUsers={this.state.agmtSyncUsers}
+ agmtWinDomain={this.state.agmtWinDomain}
+ agmtWinSubtree={this.state.agmtWinSubtree}
+ agmtDSSubtree={this.state.agmtDSSubtree}
+ agmtOneWaySync={this.state.agmtOneWaySync}
+ agmtSyncInterval={this.state.agmtSyncInterval}
+ availAttrs={this.props.attrs}
+ error={this.state.errObj}
+ errorMsg={this.state.modalMsg}
+ errorScheduleMsg={this.state.modalScheduleMsg}
+ saveOK={this.state.agmtSaveOK}
+ edit
+ />
+ <DoubleConfirmModal
+ showModal={this.state.showConfirmDeleteAgmt}
+ closeHandler={this.closeConfirmDeleteAgmt}
+ handleChange={this.handleModalChange}
+ actionHandler={this.deleteAgmt}
+ spinning={this.state.modalSpinning}
+ item={this.state.agmtName}
+ checked={this.state.modalChecked}
+ mTitle="Delete Winsync Agreement"
+ mMsg="Are you sure you want to delete this winsync
agreement"
+ mSpinningMsg="Deleting Winsync Agreement ..."
+ mBtnName="Delete Agreement"
+ />
+ <DoubleConfirmModal
+ showModal={this.state.showConfirmInitAgmt}
+ closeHandler={this.closeConfirmInitAgmt}
+ handleChange={this.handleModalChange}
+ actionHandler={this.initAgmt}
+ spinning={this.state.modalSpinning}
+ item={this.state.agmtName}
+ checked={this.state.modalChecked}
+ mTitle="Initialize Winsync Agreement"
+ mMsg="Are you sure you want to initialize this winsync
agreement"
+ mSpinningMsg="Initializing Winsync Agreement ..."
+ mBtnName="Initialize Agreement"
+ />
+ <ConfirmPopup
+ showModal={this.state.showConfirmEnableAgmt}
+ closeHandler={this.closeConfirmEnableAgmt}
+ actionFunc={this.enableAgmt}
+ actionParam={this.state.agmtName}
+ msg="Are you sure you want to enable this winsync
agreement?"
+ msgContent={this.state.agmtName}
+ />
+ <ConfirmPopup
+ showModal={this.state.showConfirmDisableAgmt}
+ closeHandler={this.closeConfirmDisableAgmt}
+ actionFunc={this.disableAgmt}
+ actionParam={this.state.agmtName}
+ msg="Are you sure you want to disable this winsync
agreement?"
+ msgContent={this.state.agmtName}
+ />
+ </div>
+ );
+ }
+}
+
+WinsyncAgmts.propTypes = {
+ suffix: PropTypes.string,
+ serverId: PropTypes.string,
+ rows: PropTypes.array,
+ addNotification: PropTypes.func,
+};
+
+WinsyncAgmts.defaultProps = {
+ serverId: "",
+ suffix: "",
+ rows: [],
+ addNotification: noop,
+};
diff --git a/src/cockpit/389-console/src/lib/security/certificateManagement.jsx
b/src/cockpit/389-console/src/lib/security/certificateManagement.jsx
index d5cd927..5bf091c 100644
--- a/src/cockpit/389-console/src/lib/security/certificateManagement.jsx
+++ b/src/cockpit/389-console/src/lib/security/certificateManagement.jsx
@@ -10,7 +10,7 @@ import {
Spinner,
noop
} from "patternfly-react";
-import { ConfirmPopup } from "../../lib/notifications.jsx";
+import { DoubleConfirmModal } from "../../lib/notifications.jsx";
import {
CertTable
} from "./securityTables.jsx";
@@ -41,11 +41,13 @@ export class CertificateManagement extends React.Component {
isCACert: false,
showConfirmCAChange: false,
loading: false,
+ modalSpinning: false,
+ modalChecked: false,
};
this.handleNavSelect = this.handleNavSelect.bind(this);
this.addCACert = this.addCACert.bind(this);
- this.handleAddChange = this.handleAddChange.bind(this);
+ this.handleChange = this.handleChange.bind(this);
this.addCert = this.addCert.bind(this);
this.showAddModal = this.showAddModal.bind(this);
this.closeAddModal = this.closeAddModal.bind(this);
@@ -217,6 +219,8 @@ export class CertificateManagement extends React.Component {
this.setState({
showConfirmDelete: true,
certName: dataRow.nickname[0],
+ modalSpinning: false,
+ modalChecked: false,
});
}
@@ -295,7 +299,9 @@ export class CertificateManagement extends React.Component {
if (!SSLFlags[0].includes('C') || !SSLFlags[0].includes('T'))
{
// This could remove the CA cert properties, better warn user
this.setState({
- showConfirmCAChange: true
+ showConfirmCAChange: true,
+ modalSpinning: false,
+ modalChecked: false
});
return;
}
@@ -305,7 +311,9 @@ export class CertificateManagement extends React.Component {
closeConfirmCAChange () {
this.setState({
- showConfirmCAChange: false
+ showConfirmCAChange: false,
+ modalSpinning: false,
+ modalChecked: false,
});
}
@@ -354,8 +362,8 @@ export class CertificateManagement extends React.Component {
});
}
- handleAddChange (e) {
- const value = e.target.value;
+ handleChange (e) {
+ const value = e.target.type === 'checkbox' ? e.target.checked :
e.target.value;
let valueErr = false;
let errObj = this.state.errObj;
@@ -418,6 +426,8 @@ export class CertificateManagement extends React.Component {
closeConfirmDelete () {
this.setState({
showConfirmDelete: false,
+ modalSpinning: false,
+ modalChecked: false,
});
}
@@ -567,7 +577,7 @@ export class CertificateManagement extends React.Component {
<SecurityAddCertModal
showModal={this.state.showAddModal}
closeHandler={this.closeAddModal}
- handleChange={this.handleAddChange}
+ handleChange={this.handleChange}
saveHandler={this.addCert}
spinning={this.state.modalSpinner}
error={this.state.errObj}
@@ -575,23 +585,36 @@ export class CertificateManagement extends React.Component {
<SecurityAddCACertModal
showModal={this.state.showAddCAModal}
closeHandler={this.closeAddCAModal}
- handleChange={this.handleAddChange}
+ handleChange={this.handleChange}
saveHandler={this.addCACert}
spinning={this.state.modalSpinner}
error={this.state.errObj}
/>
- <ConfirmPopup
+ <DoubleConfirmModal
showModal={this.state.showConfirmDelete}
closeHandler={this.closeConfirmDelete}
- actionFunc={this.delCert}
- msg="Are you sure you want to delete this certificate?"
- msgContent={this.state.certName}
+ handleChange={this.handleChange}
+ actionHandler={this.delCert}
+ spinning={this.state.modalSpinning}
+ item={this.state.certName}
+ checked={this.state.modalChecked}
+ mTitle="Delete Certificate"
+ mMsg="Are you sure you want to delete this certificate?"
+ mSpinningMsg="Deleting Certificate ..."
+ mBtnName="Delete Certificate"
/>
- <ConfirmPopup
+ <DoubleConfirmModal
showModal={this.state.showConfirmCAChange}
closeHandler={this.closeConfirmCAChange}
- actionFunc={this.doEditCert}
- msg="Removing the 'C' or 'T' flags from the SSL
trust catagory could break all TLS connectivity to and from the server, are you sure you
want to proceed?"
+ handleChange={this.handleChange}
+ actionHandler={this.doEditCert}
+ spinning={this.state.modalSpinning}
+ item={this.state.certName}
+ checked={this.state.modalChecked}
+ mTitle="Warning - "
+ mMsg="Removing the 'C' or 'T' flags from the SSL
trust catagory could break all TLS connectivity to and from the server, are you sure you
want to proceed?"
+ mSpinningMsg="Editing CA Certificate ..."
+ mBtnName="Change Trust Flags"
/>
</div>
);
diff --git a/src/cockpit/389-console/src/lib/security/ciphers.jsx
b/src/cockpit/389-console/src/lib/security/ciphers.jsx
index 4714fcb..c07c0dc 100644
--- a/src/cockpit/389-console/src/lib/security/ciphers.jsx
+++ b/src/cockpit/389-console/src/lib/security/ciphers.jsx
@@ -89,7 +89,7 @@ export class Ciphers extends React.Component {
.done(() => {
this.props.addNotification(
"success",
- `Successfully set cipher preferences. You must restart the
server for these changes to take effect.`
+ `Successfully set cipher preferences. You must restart the
Directory Server for these changes to take effect.`
);
this.setState({
saving: false,
diff --git a/src/cockpit/389-console/src/lib/security/securityTables.jsx
b/src/cockpit/389-console/src/lib/security/securityTables.jsx
index 6b74a01..ddcc717 100644
--- a/src/cockpit/389-console/src/lib/security/securityTables.jsx
+++ b/src/cockpit/389-console/src/lib/security/securityTables.jsx
@@ -144,7 +144,8 @@ class CertTable extends React.Component {
return [
<td key={rowData.nickname[0]}>
<DropdownButton id={rowData.nickname[0]}
- bsStyle="default"
title="Actions">
+ className="ds-action-button"
+ bsStyle="primary"
title="Actions">
<MenuItem eventKey="1"
onClick={() => {
this.props.editCert(rowData);
}}
@@ -353,7 +354,8 @@ class CRLTable extends React.Component {
return [
<td key={rowData.name[0]}>
<DropdownButton id={rowData.name[0]}
- bsStyle="default"
title="Actions">
+ className="ds-action-button"
+ bsStyle="primary"
title="Actions">
<MenuItem eventKey="1"
onClick={() => {
this.props.editIndex(rowData);
}}
diff --git a/src/cockpit/389-console/src/lib/tools.jsx
b/src/cockpit/389-console/src/lib/tools.jsx
index b3e7573..ca402f5 100644
--- a/src/cockpit/389-console/src/lib/tools.jsx
+++ b/src/cockpit/389-console/src/lib/tools.jsx
@@ -65,7 +65,7 @@ export function get_date_string (timestamp) {
let hour = timestamp.substr(8, 2);
let minute = timestamp.substr(10, 2);
let sec = timestamp.substr(12, 2);
- let date = new Date(parseInt(year), parseInt(month), parseInt(day),
+ let date = new Date(parseInt(year), (parseInt(month) - 1), parseInt(day),
parseInt(hour), parseInt(minute), parseInt(sec));
return date.toLocaleString();
}
@@ -103,3 +103,29 @@ export function get_date_diff(start, end) {
return `${days} days, ${hours} hours, ${minutes} minutes, and ${seconds} seconds`;
}
+
+export function bad_file_name(file_name) {
+ // file_name must be a string, and not a location/directory
+ if (file_name.includes("/")) {
+ return true;
+ }
+ return false;
+}
+
+export function valid_port (val) {
+ // Validate value is a number and between 1 and 65535
+ let result = !isNaN(val);
+ if (result) {
+ if (val < 1 || val > 65535) {
+ result = false;
+ }
+ }
+ return result;
+}
+
+export function valid_dn (dn) {
+ // Validate value is a valid DN (sanity validation)
+ let dn_regex = new RegExp("^([A-Za-z]+=.*)");
+ let result = dn_regex.test(dn);
+ return result;
+}
diff --git a/src/cockpit/389-console/src/monitor.jsx
b/src/cockpit/389-console/src/monitor.jsx
index 89d47b0..d9d7807 100644
--- a/src/cockpit/389-console/src/monitor.jsx
+++ b/src/cockpit/389-console/src/monitor.jsx
@@ -39,9 +39,9 @@ export class Monitor extends React.Component {
snmpData: {},
ldbmData: {},
serverData: {},
- showLoading: false,
loadingMsg: "",
notifications: [],
+ disableTree: false,
// Suffix
suffixLoading: false,
serverLoading: false,
@@ -112,9 +112,11 @@ export class Monitor extends React.Component {
this.addNotification = this.addNotification.bind(this);
this.removeNotification = this.removeNotification.bind(this);
this.loadSuffixTree = this.loadSuffixTree.bind(this);
+ this.enableTree = this.enableTree.bind(this);
this.update_tree_nodes = this.update_tree_nodes.bind(this);
this.selectNode = this.selectNode.bind(this);
this.loadMonitorSuffix = this.loadMonitorSuffix.bind(this);
+ this.disableSuffixLoading = this.disableSuffixLoading.bind(this);
this.loadMonitorLDBM = this.loadMonitorLDBM.bind(this);
this.reloadLDBM = this.reloadLDBM.bind(this);
this.loadMonitorSNMP = this.loadMonitorSNMP.bind(this);
@@ -209,6 +211,7 @@ export class Monitor extends React.Component {
icon: "pficon-catalog",
selectable: false,
id: "log-monitor",
+ state: {"expanded": true},
nodes: [
{
text: "Access Log",
@@ -279,8 +282,11 @@ export class Monitor extends React.Component {
}
selectNode(selectedNode) {
+ if (selectedNode.selected) {
+ return;
+ }
this.setState({
- showLoading: true
+ disableTree: true, // Disable the tree to allow node to be fully loaded
});
if (selectedNode.id == "database-monitor" ||
@@ -344,12 +350,7 @@ export class Monitor extends React.Component {
});
} else {
if (selectedNode.id in this.state) {
- // This suffix is already cached, but it might be incomplete...
- if (selectedNode.type == "dblink" &&
this.state.nsaddcount === undefined) {
- this.loadMonitorChaining(selectedNode.id);
- } else if (selectedNode.type != "dblink" &&
this.state.entrycachehitratio === undefined) {
- this.loadMonitorSuffix(selectedNode.id);
- }
+ // This suffix is already cached
this.setState(prevState => {
return {
nodes: this.nodeSelector(prevState.nodes, selectedNode),
@@ -597,6 +598,12 @@ export class Monitor extends React.Component {
});
}
+ disableSuffixLoading () {
+ this.setState({
+ suffixLoading: false
+ });
+ }
+
loadMonitorSuffix(suffix) {
this.setState({
suffixLoading: true
@@ -616,8 +623,7 @@ export class Monitor extends React.Component {
...this.state[suffix],
suffixData: config.attrs,
},
- suffixLoading: false,
- });
+ }, this.disableSuffixLoading);
})
.fail(() => {
// Notification of failure (could only be server down)
@@ -1001,10 +1007,20 @@ export class Monitor extends React.Component {
), this.loadMonitorReplication);
}
+ enableTree () {
+ this.setState({
+ disableTree: false
+ });
+ }
+
render() {
const { nodes } = this.state;
let monitorPage = "";
let monitor_element = "";
+ let disabled = "tree-view-container";
+ if (this.state.disableTree) {
+ disabled = "tree-view-container ds-disabled";
+ }
if (this.state.loaded) {
if (this.state.node_name == "database-monitor" ||
this.state.node_name == "") {
@@ -1012,14 +1028,15 @@ export class Monitor extends React.Component {
monitor_element =
<div className="ds-loading-spinner ds-center">
<p />
- <h4><b>Loading database monitor information
...</b></h4>
- <Spinner loading size="md" />
+ <h4>Loading database monitor information
...</h4>
+ <Spinner className="ds-margin-top-lg" loading
size="md" />
</div>;
} else {
monitor_element =
<DatabaseMonitor
data={this.state.ldbmData}
reload={this.reloadLDBM}
+ enableTree={this.enableTree}
/>;
}
} else if (this.state.node_name == "server-monitor") {
@@ -1027,8 +1044,8 @@ export class Monitor extends React.Component {
monitor_element =
<div className="ds-loading-spinner ds-center">
<p />
- <h4><b>Loading server monitor information
...</b></h4>
- <Spinner loading size="md" />
+ <h4>Loading server monitor information ...</h4>
+ <Spinner className="ds-margin-top-lg" loading
size="md" />
</div>;
} else {
monitor_element =
@@ -1036,6 +1053,7 @@ export class Monitor extends React.Component {
data={this.state.serverData}
reload={this.reloadServer}
serverId={this.props.serverId}
+ enableTree={this.enableTree}
/>;
}
} else if (this.state.node_name == "snmp-monitor") {
@@ -1043,14 +1061,15 @@ export class Monitor extends React.Component {
monitor_element =
<div className="ds-loading-spinner ds-center">
<p />
- <h4><b>Loading SNMP monitor information
...</b></h4>
- <Spinner loading size="md" />
+ <h4>Loading SNMP monitor information ...</h4>
+ <Spinner className="ds-margin-top-lg" loading
size="md" />
</div>;
} else {
monitor_element =
<SNMPMonitor
data={this.state.snmpData}
reload={this.reloadSNMP}
+ enableTree={this.enableTree}
/>;
}
} else if (this.state.node_name == "access-log-monitor") {
@@ -1063,6 +1082,7 @@ export class Monitor extends React.Component {
refreshing={this.state.accessRefreshing}
handleRefresh={this.accessRefreshCont}
lines={this.state.accessLines}
+ enableTree={this.enableTree}
/>;
} else if (this.state.node_name == "audit-log-monitor") {
monitor_element =
@@ -1074,6 +1094,7 @@ export class Monitor extends React.Component {
refreshing={this.state.auditRefreshing}
handleRefresh={this.auditRefreshCont}
lines={this.state.auditLines}
+ enableTree={this.enableTree}
/>;
} else if (this.state.node_name == "auditfail-log-monitor") {
monitor_element =
@@ -1085,6 +1106,7 @@ export class Monitor extends React.Component {
refreshing={this.state.auditfailRefreshing}
handleRefresh={this.auditFailRefreshCont}
lines={this.state.auditfailLines}
+ enableTree={this.enableTree}
/>;
} else if (this.state.node_name == "error-log-monitor") {
monitor_element =
@@ -1097,6 +1119,7 @@ export class Monitor extends React.Component {
handleRefresh={this.errorRefreshCont}
handleSevLevel={this.handleSevChange}
lines={this.state.errorLines}
+ enableTree={this.enableTree}
/>;
} else if (this.state.node_name == "replication-monitor") {
if (this.state.replLoading) {
@@ -1104,7 +1127,7 @@ export class Monitor extends React.Component {
<div className="ds-loading-spinner ds-center">
<p />
<h4>Loading replication monitor information
...</h4>
- <Spinner loading size="md" />
+ <Spinner className="ds-margin-top-lg" loading
size="md" />
</div>;
} else {
if (this.state.replicatedSuffixes.length < 1) {
@@ -1139,6 +1162,7 @@ export class Monitor extends React.Component {
reloadAgmts={this.reloadReplAgmts}
reloadWinsyncAgmts={this.reloadReplWinsyncAgmts}
reloadConflicts={this.loadConflicts}
+ enableTree={this.enableTree}
key={this.state.replSuffix}
/>
</div>
@@ -1152,14 +1176,14 @@ export class Monitor extends React.Component {
<div className="ds-loading-spinner ds-center">
<p />
<h4>Loading suffix monitor information for
<b>{this.state.node_text} ...</b></h4>
- <Spinner loading size="md" />
+ <Spinner className="ds-margin-top-lg" loading
size="md" />
</div>;
} else if (this.state.chainingLoading) {
monitor_element =
<div className="ds-loading-spinner ds-center">
<p />
<h4>Loading chaining monitor information for
<b>{this.state.node_text} ...</b></h4>
- <Spinner loading size="md" />
+ <Spinner className="ds-margin-top-lg" loading
size="md" />
</div>;
} else {
if (this.state.node_type == "dblink") {
@@ -1169,6 +1193,7 @@ export class Monitor extends React.Component {
bename={this.state.bename}
reload={this.loadMonitorChaining}
data={this.state[this.state.node_text].chainingData}
+ enableTree={this.enableTree}
key={this.state.node_text}
/>;
} else {
@@ -1179,6 +1204,7 @@ export class Monitor extends React.Component {
bename={this.state.bename}
reload={this.loadMonitorSuffix}
data={this.state[this.state.node_text].suffixData}
+ enableTree={this.enableTree}
key={this.state.node_text}
/>;
}
@@ -1193,13 +1219,14 @@ export class Monitor extends React.Component {
<div className="ds-container">
<div>
<div className="ds-tree">
- <div className="tree-view-container"
id="db-tree"
+ <div className={disabled} id="monitor-tree"
style={treeViewContainerStyles}>
<TreeView
nodes={nodes}
highlightOnHover
highlightOnSelect
selectNode={this.selectNode}
+ key={this.state.node_text}
/>
</div>
</div>
@@ -1214,7 +1241,7 @@ export class Monitor extends React.Component {
<div className="ds-loading-spinner ds-center">
<p />
<h4>Loading monitor information ...</h4>
- <Spinner loading size="md" />
+ <Spinner className="ds-margin-top-lg" loading
size="md" />
</div>;
}
diff --git a/src/cockpit/389-console/src/plugins.jsx
b/src/cockpit/389-console/src/plugins.jsx
index 0523614..342a19c 100644
--- a/src/cockpit/389-console/src/plugins.jsx
+++ b/src/cockpit/389-console/src/plugins.jsx
@@ -493,10 +493,7 @@ export class Plugins extends React.Component {
removeNotificationAction={this.removeNotification}
/>
<Row className="clearfix">
- <Col sm={1}>
- <h2>Plugins</h2>
- </Col>
- <Col sm={10}>
+ <Col sm={12}>
<Spinner
className="ds-float-left ds-plugin-spinner"
loading={this.state.loading}
@@ -504,13 +501,12 @@ export class Plugins extends React.Component {
/>
</Col>
</Row>
- <hr />
<Tab.Container
id="left-tabs-example"
defaultActiveKey={Object.keys(selectPlugins)[0]}
<Row
className="clearfix">
- <Col sm={2}>
+ <Col sm={3}>
<Nav bsStyle="pills" stacked>
{Object.entries(selectPlugins).map(([id, item]) => (
<NavItem key={id} eventKey={id}>
@@ -519,7 +515,7 @@ export class Plugins extends React.Component {
))}
</Nav>
</Col>
- <Col sm={10}>
+ <Col sm={9}>
<Tab.Content animation={false}>
{Object.entries(selectPlugins).map(([id, item]) => (
<Tab.Pane key={id} eventKey={id}>
diff --git a/src/cockpit/389-console/src/replication.jsx
b/src/cockpit/389-console/src/replication.jsx
new file mode 100644
index 0000000..7663129
--- /dev/null
+++ b/src/cockpit/389-console/src/replication.jsx
@@ -0,0 +1,1148 @@
+import cockpit from "cockpit";
+import React from "react";
+import { NotificationController } from "./lib/notifications.jsx";
+import { log_cmd } from "./lib/tools.jsx";
+import { ReplSuffix } from "./lib/replication/replSuffix.jsx";
+import { Changelog } from "./lib/replication/replChangelog.jsx";
+import { TreeView, Spinner } from "patternfly-react";
+import PropTypes from "prop-types";
+import "./css/ds.css";
+
+const treeViewContainerStyles = {
+ width: '295px',
+};
+
+export class Replication extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ notifications: [],
+ errObj: {},
+ nodes: [],
+ node_name: "",
+ node_text: "",
+ node_type: "",
+ node_replicated: false,
+ disableTree: true,
+
+ // Changelog
+ clLoading: false,
+ clDir: "",
+ clMaxEntries: "",
+ clMaxAge: "",
+ clCompactInt: "",
+ clTrimInt: "",
+ clEncrypt: false,
+
+ // Suffix
+ suffixLoading: false,
+ attributes: [],
+ role: "",
+ rid: "0",
+ bindDNs: [],
+ bindDNGroup: "",
+ agmtRows: [],
+ winsyncRows: [],
+ ruvRows: [],
+ suffixSpinning: false,
+ disabled: false,
+ suffixKey: 0,
+
+ showDisableConfirm: false,
+ loaded: false,
+ };
+
+ // General
+ this.selectNode = this.selectNode.bind(this);
+ this.removeNotification = this.removeNotification.bind(this);
+ this.addNotification = this.addNotification.bind(this);
+ this.handleChange = this.handleChange.bind(this);
+ this.disableTree = this.disableTree.bind(this);
+ this.enableTree = this.enableTree.bind(this);
+
+ this.reloadConfig = this.reloadConfig.bind(this);
+ this.reloadAgmts = this.reloadAgmts.bind(this);
+ this.reloadWinsyncAgmts = this.reloadWinsyncAgmts.bind(this);
+ this.reloadRUV = this.reloadRUV.bind(this);
+ this.loadAttrs = this.loadAttrs.bind(this);
+ this.loadReplication = this.loadReplication.bind(this);
+ this.loadReplSuffix = this.loadReplSuffix.bind(this);
+ this.reloadChangelog = this.reloadChangelog.bind(this);
+ this.loadSuffixTree = this.loadSuffixTree.bind(this);
+ }
+
+ componentWillMount () {
+ this.loadReplication();
+ if (!this.state.loaded) {
+ this.loadAttrs();
+ }
+ }
+
+ componentDidMount() {
+ // this.loadSuffixTree(false);
+ }
+
+ componentDidUpdate(prevProps) {
+ if (this.props.serverId !== prevProps.serverId) {
+ this.loadSuffixTree(true);
+ }
+ }
+
+ loadReplication () {
+ // Load the changelog, and build the suffix tree
+ this.setState({
+ loaded: false
+ });
+ let cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
'replication', 'get-changelog'];
+ log_cmd("loadReplication", "Load the replication info",
cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const config = JSON.parse(content);
+ let clDir = "";
+ let clMaxEntries = "";
+ let clMaxAge = "";
+ let clCompactInt = "";
+ let clTrimInt = "";
+ let clEncrypt = false;
+ for (let attr in config['attrs']) {
+ let val = config['attrs'][attr][0];
+ if (attr == "nsslapd-changelogdir") {
+ clDir = val;
+ }
+ if (attr == "nsslapd-changelogmaxentries") {
+ clMaxEntries = val;
+ }
+ if (attr == "nsslapd-changelogmaxage") {
+ clMaxAge = val;
+ }
+ if (attr == "nsslapd-changelogcompactdb-interval") {
+ clCompactInt = val;
+ }
+ if (attr == "nsslapd-changelogtrim-interval") {
+ clTrimInt = val;
+ }
+ if (attr == "nsslapd-encryptionalgorithm") {
+ clEncrypt = true;
+ }
+ }
+ this.setState({
+ clDir: clDir,
+ clMaxEntries: clMaxEntries,
+ clMaxAge: clMaxAge,
+ clCompactInt: clCompactInt,
+ clTrimInt: clTrimInt,
+ clEncrypt: clEncrypt,
+ }, this.loadSuffixTree(true));
+ })
+ .fail(() => {
+ this.setState({
+ clDir: "",
+ clMaxEntries: "",
+ clMaxAge: "",
+ clCompactInt: "",
+ clTrimInt: "",
+ clEncrypt: false,
+ }, this.loadSuffixTree(true));
+ });
+ }
+
+ reloadChangelog () {
+ // Refresh the changelog
+ this.setState({
+ clLoading: true
+ });
+ let cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
'replication', 'get-changelog'];
+ log_cmd("reloadChangelog", "Reload the changelog", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const config = JSON.parse(content);
+ let clDir = "";
+ let clMaxEntries = "";
+ let clMaxAge = "";
+ let clCompactInt = "";
+ let clTrimInt = "";
+ let clEncrypt = false;
+ for (let attr in config['attrs']) {
+ let val = config['attrs'][attr][0];
+ if (attr == "nsslapd-changelogdir") {
+ clDir = val;
+ }
+ if (attr == "nsslapd-changelogmaxentries") {
+ clMaxEntries = val;
+ }
+ if (attr == "nsslapd-changelogmaxage") {
+ clMaxAge = val;
+ }
+ if (attr == "nsslapd-changelogcompactdb-interval") {
+ clCompactInt = val;
+ }
+ if (attr == "nsslapd-changelogtrim-interval") {
+ clTrimInt = val;
+ }
+ if (attr == "nsslapd-encryptionalgorithm") {
+ clEncrypt = true;
+ }
+ }
+ this.setState({
+ clDir: clDir,
+ clMaxEntries: clMaxEntries,
+ clMaxAge: clMaxAge,
+ clCompactInt: clCompactInt,
+ clTrimInt: clTrimInt,
+ clEncrypt: clEncrypt,
+ clLoading: false
+ });
+ })
+ .fail(() => {
+ this.setState({
+ clDir: "",
+ clMaxEntries: "",
+ clMaxAge: "",
+ clCompactInt: "",
+ clTrimInt: "",
+ clEncrypt: false,
+ clLoading: false
+ });
+ });
+ }
+
+ processBranch(treeBranch) {
+ if (treeBranch.length == 0) {
+ return;
+ }
+ for (let sub in treeBranch) {
+ if (!treeBranch[sub].type.endsWith("suffix")) {
+ // Not a suffix, skip it
+ treeBranch.splice(sub, 1);
+ continue;
+ } else if (treeBranch[sub].replicated) {
+ treeBranch[sub].icon = "fa fa-clone";
+ treeBranch[sub].replicated = true;
+ }
+ this.processBranch(treeBranch[sub].nodes);
+ }
+ }
+
+ loadSuffixTree(fullReset) {
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-"
+ this.props.serverId + ".socket",
+ "backend", "get-tree",
+ ];
+ log_cmd("loadSuffixTree", "Start building the suffix tree",
cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ let treeData = [];
+ if (content != "") {
+ treeData = JSON.parse(content);
+ }
+ let basicData = [
+ {
+ text: "Replication Changelog",
+ selectable: true,
+ selected: false,
+ icon: "pficon-catalog",
+ id: "changelog",
+
+ },
+ {
+ text: "Suffixes",
+ icon: "pficon-topology",
+ state: {"expanded": true},
+ selectable: false,
+ id: "repl-suffixes",
+ nodes: []
+ }
+ ];
+ let current_node = this.state.node_name;
+ let current_type = this.state.node_type;
+ let replicated = this.state.node_replicated;
+ if (fullReset && treeData.length > 0) {
+ let found = false;
+ for (let i = 0; i < treeData.length; i++) {
+ if (treeData[i].replicated) {
+ treeData[i].icon = "fa fa-clone";
+ replicated = true;
+ if (!found) {
+ // Load the first replicated suffix we find
+ treeData[i].selected = true;
+ current_node = treeData[i].id;
+ current_type = treeData[i].type;
+ this.loadReplSuffix(treeData[i].id);
+ found = true;
+ }
+ }
+ this.processBranch(treeData[i].nodes);
+ }
+ if (!found) {
+ // No replicated suffixes, load the first one
+ treeData[0].selected = true;
+ current_node = treeData[0].id;
+ current_type = treeData[0].type;
+ this.loadReplSuffix(treeData[0].id);
+ }
+ } else if (treeData.length > 0) {
+ // Reset current suffix
+ for (let suffix of treeData) {
+ this.processBranch(suffix.nodes);
+ if (suffix.id == current_node) {
+ suffix.selected = true;
+ replicated = suffix.replicated;
+ }
+ if (suffix.replicated) {
+ suffix.icon = "fa fa-clone";
+ }
+ }
+ this.loadReplSuffix(current_node);
+ }
+ basicData[1].nodes = treeData;
+ this.setState(() => ({
+ nodes: basicData,
+ node_name: current_node,
+ node_type: current_type,
+ node_replicated: replicated,
+ }), this.update_tree_nodes);
+ });
+ }
+
+ selectNode(selectedNode) {
+ if (selectedNode.selected) {
+ return;
+ }
+
+ this.setState({
+ disableTree: true // Disable the tree to allow node to be fully loaded
+ });
+
+ if (selectedNode.id == "changelog") {
+ // Nothing special to do, these configurations have already been loaded
+ this.setState(prevState => {
+ return {
+ nodes: this.nodeSelector(prevState.nodes, selectedNode),
+ node_name: selectedNode.id,
+ node_text: selectedNode.text,
+ node_type: selectedNode.id,
+ };
+ });
+ } else {
+ if (selectedNode.id in this.state) {
+ // This suffix is already cached, just use what we have...
+ this.setState(prevState => {
+ return {
+ nodes: this.nodeSelector(prevState.nodes, selectedNode),
+ node_name: selectedNode.id,
+ node_text: selectedNode.text,
+ node_type: selectedNode.type,
+ node_replicated: selectedNode.replicated,
+ disableTree: false,
+ suffixKey: new Date(),
+ };
+ });
+ } else {
+ // Suffix/subsuffix
+ this.loadReplSuffix(selectedNode.id);
+ this.setState(prevState => {
+ return {
+ nodes: this.nodeSelector(prevState.nodes, selectedNode),
+ node_name: selectedNode.id,
+ node_text: selectedNode.text,
+ node_type: selectedNode.type,
+ node_replicated: selectedNode.replicated,
+ suffixKey: new Date(),
+ };
+ });
+ }
+ }
+ }
+
+ nodeSelector(nodes, targetNode) {
+ return nodes.map(node => {
+ if (node.nodes) {
+ return {
+ ...node,
+ nodes: this.nodeSelector(node.nodes, targetNode),
+ selected: node.id === targetNode.id ? !node.selected : false
+ };
+ } else if (node.id === targetNode.id) {
+ return { ...node, selected: !node.selected };
+ } else if (node.id !== targetNode.id && node.selected) {
+ return { ...node, selected: false };
+ } else {
+ return node;
+ }
+ });
+ }
+
+ addNotification(type, message, timerdelay, persistent) {
+ this.setState(prevState => ({
+ notifications: [
+ ...prevState.notifications,
+ {
+ key: prevState.notifications.length + 1,
+ type: type,
+ persistent: persistent,
+ timerdelay: timerdelay,
+ message: message,
+ }
+ ]
+ }));
+ }
+
+ removeNotification(notificationToRemove) {
+ this.setState({
+ notifications: this.state.notifications.filter(
+ notification => notificationToRemove.key !== notification.key
+ )
+ });
+ }
+
+ update_tree_nodes() {
+ // Set title to the text value of each suffix node. We need to do this
+ // so we can read long suffixes in the UI tree div. This is the last
+ // step of loading the page, so mark it loaded!
+ let elements = document.getElementsByClassName('treeitem-row');
+ for (let el of elements) {
+ el.setAttribute('title', el.innerText);
+ }
+ this.setState({
+ loaded: true
+ });
+ }
+
+ handleChange(e) {
+ const value = e.target.type === 'checkbox' ? e.target.checked :
e.target.value;
+ let valueErr = false;
+ let errObj = this.state.errObj;
+ if (value == "") {
+ valueErr = true;
+ }
+ errObj[e.target.id] = valueErr;
+ this.setState({
+ [e.target.id]: value,
+ errObj: errObj
+ });
+ }
+
+ closeSuffixModal() {
+ this.setState({
+ showSuffixModal: false
+ });
+ }
+
+ reloadAgmts (suffix) {
+ this.setState({
+ suffixSpinning: true,
+ disabled: true,
+ });
+ let cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-"
+ this.props.serverId + ".socket",
+ "repl-agmt", "list", "--suffix", suffix
+ ];
+ log_cmd("reloadAgmts", "get repl agreements", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const obj = JSON.parse(content);
+ let rows = [];
+ for (let idx in obj['items']) {
+ let agmt_attrs = obj['items'][idx]['attrs'];
+ let state = "Enabled";
+ let update_status = "";
+ let agmt_init_status = "";
+
+ // Compute state (enabled by default)
+ if ('nsds5replicaenabled' in agmt_attrs) {
+ if
(agmt_attrs['nsds5replicaenabled'][0].toLowerCase() == 'off') {
+ state = "Disabled";
+ }
+ }
+
+ // Check for status msgs
+ if ('nsds5replicalastupdatestatus' in agmt_attrs) {
+ update_status =
agmt_attrs['nsds5replicalastupdatestatus'][0];
+ }
+ if ('nsds5replicalastinitstatus' in agmt_attrs
&&
+ agmt_attrs['nsds5replicalastinitstatus'][0] !=
"") {
+ agmt_init_status =
agmt_attrs['nsds5replicalastinitstatus'][0];
+ if (agmt_init_status == "Error (0) Total update in
progress" ||
+ agmt_init_status == "Error (0)") {
+ agmt_init_status = <td
key={agmt_attrs['cn']}><i>Initializing</i><Spinner loading
size="sm" /></td>;
+ } else if (agmt_init_status == "Error (0) Total update
succeeded") {
+ agmt_init_status = "Initialized";
+ agmt_init_status = <td
key={agmt_attrs['cn']}><i>Initialized</i></td>;
+ } else {
+ agmt_init_status = <td
key={agmt_attrs['cn']}>{agmt_init_status}</td>;
+ }
+ } else if (agmt_attrs['nsds5replicalastinitstart'][0] ==
"19700101000000Z") {
+ agmt_init_status = "Not initialized";
+ agmt_init_status = <td
key={agmt_attrs['cn']}><i>Not Initialized</i></td>;
+ } else if ('nsds5beginreplicarefresh' in agmt_attrs) {
+ agmt_init_status = <td
key={agmt_attrs['cn']}><i>Initializing</i><Spinner loading
size="sm" /></td>;
+ }
+
+ // Update table
+ rows.push({
+ 'name': agmt_attrs['cn'],
+ 'host': agmt_attrs['nsds5replicahost'],
+ 'port': agmt_attrs['nsds5replicaport'],
+ 'state': [state],
+ 'status': [update_status],
+ 'initstatus': [agmt_init_status]
+ });
+ }
+
+ // Set agmt
+ this.setState({
+ [suffix]: {
+ ...this.state[suffix],
+ agmtRows: rows,
+ },
+ suffixSpinning: false,
+ disabled: false,
+ });
+ })
+ .fail(() => {
+ this.setState({
+ suffixSpinning: false,
+ disabled: false,
+ });
+ });
+ }
+
+ reloadWinsyncAgmts (suffix) {
+ this.setState({
+ suffixSpinning: true,
+ disabled: true,
+ });
+ let cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-"
+ this.props.serverId + ".socket",
+ "repl-winsync-agmt", "list", "--suffix",
suffix
+ ];
+ log_cmd("reloadWinsyncAgmts", "Get Winsync Agreements",
cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const obj = JSON.parse(content);
+ let ws_rows = [];
+ for (var idx in obj['items']) {
+ let state = "Enabled";
+ let update_status = "";
+ let ws_agmt_init_status = "Initialized";
+ let agmt_attrs = obj['items'][idx]['attrs'];
+ // let agmt_name = agmt_attrs['cn'][0];
+
+ // Compute state (enabled by default)
+ if ('nsds5replicaenabled' in agmt_attrs) {
+ if
(agmt_attrs['nsds5replicaenabled'][0].toLowerCase() == 'off') {
+ state = "Disabled";
+ }
+ }
+
+ if ('nsds5replicalastupdatestatus' in agmt_attrs) {
+ update_status =
agmt_attrs['nsds5replicalastupdatestatus'][0];
+ }
+
+ if ('nsds5replicalastinitstatus' in agmt_attrs
&&
+ agmt_attrs['nsds5replicalastinitstatus'][0] !=
"") {
+ ws_agmt_init_status =
agmt_attrs['nsds5replicalastinitstatus'][0];
+ if (ws_agmt_init_status == "Error (0) Total update in
progress" ||
+ ws_agmt_init_status == "Error (0)") {
+ ws_agmt_init_status = <td
key={agmt_attrs['cn']}><i>Initializing</i><Spinner loading
size="sm" /></td>;
+ } else if (ws_agmt_init_status == "Error (0) Total
update succeeded") {
+ ws_agmt_init_status = <td
key={agmt_attrs['cn']}><i>Initialized</i></td>;
+ } else {
+ ws_agmt_init_status = <td
key={agmt_attrs['cn']}>{ws_agmt_init_status}</td>;
+ }
+ } else if ('nsds5replicalastinitstart' in agmt_attrs
&& agmt_attrs['nsds5replicalastinitstart'][0] ==
"19700101000000Z") {
+ ws_agmt_init_status = <td
key={agmt_attrs['cn']}><i>Not initialized</i></td>;
+ } else if ('nsds5beginreplicarefresh' in agmt_attrs) {
+ ws_agmt_init_status = <td
key={agmt_attrs['cn']}><i>Initializing</i><Spinner loading
size="sm" /></td>;
+ }
+
+ // Update table
+ ws_rows.push({
+ 'name': agmt_attrs['cn'],
+ 'host': agmt_attrs['nsds5replicahost'],
+ 'port': agmt_attrs['nsds5replicaport'],
+ 'state': [state],
+ 'status': [update_status],
+ 'initstatus': [ws_agmt_init_status]
+ });
+ }
+ // Set winsync agmts
+ this.setState({
+ [suffix]: {
+ ...this.state[suffix],
+ winsyncRows: ws_rows,
+ },
+ suffixSpinning: false,
+ disabled: false,
+ });
+ })
+ .fail(() => {
+ this.setState({
+ suffixSpinning: false,
+ disabled: false,
+ });
+ });
+ }
+
+ reloadConfig (suffix) {
+ this.setState({
+ suffixSpinning: true,
+ disabled: true,
+ });
+ let cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-"
+ this.props.serverId + ".socket",
+ "replication", "get", "--suffix", suffix
+ ];
+ log_cmd("reloadConfig", "Reload suffix repl config", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const config = JSON.parse(content);
+ let current_role = "";
+ let nsds5replicaprecisetombstonepurging = false;
+ if ('nsds5replicaprecisetombstonepurging' in
config['attrs']) {
+ if
(config['attrs']['nsds5replicaprecisetombstonepurging'][0].toLowerCase()
== "on") {
+ nsds5replicaprecisetombstonepurging = true;
+ }
+ }
+ // Set the replica role
+ if (config['attrs']['nsds5replicatype'][0] ==
"3") {
+ current_role = "Master";
+ } else {
+ if (config['attrs']['nsds5flags'][0] ==
"1") {
+ current_role = "Hub";
+ } else {
+ current_role = "Consumer";
+ }
+ }
+
+ this.setState({
+ [suffix]: {
+ role: current_role,
+ nsds5flags:
config['attrs']['nsds5flags'][0],
+ nsds5replicatype:
config['attrs']['nsds5replicatype'][0],
+ nsds5replicaid: 'nsds5replicaid' in
config['attrs'] ? config['attrs']['nsds5replicaid'][0] :
"",
+ nsds5replicabinddn: 'nsds5replicabinddn' in
config['attrs'] ? config['attrs']['nsds5replicabinddn'] :
"",
+ nsds5replicabinddngroup: 'nsds5replicabinddngroup' in
config['attrs'] ? config['attrs']['nsds5replicabinddngroup'][0] :
"",
+ nsds5replicabinddngroupcheckinterval:
'nsds5replicabinddngroupcheckinterval' in config['attrs'] ?
config['attrs']['nsds5replicabinddngroupcheckinterval'][0] :
"",
+ nsds5replicareleasetimeout:
'nsds5replicareleasetimeout' in config['attrs'] ?
config['attrs']['nsds5replicareleasetimeout'][0] : "",
+ nsds5replicapurgedelay: 'nsds5replicapurgedelay' in
config['attrs'] ? config['attrs']['nsds5replicapurgedelay'][0] :
"",
+ nsds5replicatombstonepurgeinterval:
'nsds5replicatombstonepurgeinterval' in config['attrs'] ?
config['attrs']['nsds5replicatombstonepurgeinterval'][0] : "",
+ nsds5replicaprecisetombstonepurging:
nsds5replicaprecisetombstonepurging,
+ nsds5replicaprotocoltimeout:
'nsds5replicaprotocoltimeout' in config['attrs'] ?
config['attrs']['nsds5replicaprotocoltimeout'][0] : "",
+ nsds5replicabackoffmin: 'nsds5replicabackoffmin' in
config['attrs'] ? config['attrs']['nsds5replicabackoffmin'][0] :
"",
+ nsds5replicabackoffmax: 'nsds5replicabackoffmax' in
config['attrs'] ? config['attrs']['nsds5replicabackoffmax'][0] :
"",
+ },
+ suffixSpinning: false,
+ disabled: false,
+ suffixKey: new Date(),
+ });
+ })
+ .fail(() => {
+ this.setState({
+ suffixSpinning: false,
+ disabled: false,
+ });
+ });
+ }
+
+ reloadRUV (suffix) {
+ this.setState({
+ suffixSpinning: true,
+ disabled: true,
+ });
+ // Load suffix RUV
+ let cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'replication', 'get-ruv', '--suffix=' + suffix];
+ log_cmd('reloadRUV', 'Get the suffix RUV', cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ let ruvs = JSON.parse(content);
+ let ruv_rows = [];
+ for (let idx in ruvs['items']) {
+ let ruv = ruvs['items'][idx];
+ // Update table
+ ruv_rows.push({
+ 'rid': ruv['rid'],
+ 'url': ruv['url'],
+ 'csn': ruv['csn'],
+ 'raw_csn': ruv['raw_csn'],
+ 'maxcsn': ruv['maxcsn'],
+ 'raw_maxcsn': ruv['raw_maxcsn'],
+ });
+ }
+ this.setState({
+ [suffix]: {
+ ...this.state[suffix],
+ ruvRows: ruv_rows,
+ },
+ suffixSpinning: false,
+ disabled: false
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ if (errMsg.desc != "No such object") {
+ this.addNotification(
+ "error",
+ `Error loading suffix RUV - ${errMsg.desc}`
+ );
+ }
+ this.setState({
+ suffixSpinning: false,
+ disabled: false
+ });
+ });
+ }
+
+ loadReplSuffix(suffix) {
+ // Load everything, we must nest cockpit promise so we can proper set
+ // the loading is finished.
+ // - Get Suffix config
+ // - Get Repl agmts
+ // - Get Winsync Agmts
+ // - Get RUV's
+ this.setState({
+ activeKey: 1,
+ suffixLoading: true,
+ [suffix]: {},
+ });
+
+ let cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-"
+ this.props.serverId + ".socket",
+ "replication", "get", "--suffix", suffix
+ ];
+ log_cmd("loadReplSuffix", "Load suffix repl config", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const config = JSON.parse(content);
+ let current_role = "";
+ let nsds5replicaprecisetombstonepurging = false;
+ if ('nsds5replicaprecisetombstonepurging' in
config['attrs']) {
+ if
(config['attrs']['nsds5replicaprecisetombstonepurging'][0].toLowerCase()
== "on") {
+ nsds5replicaprecisetombstonepurging = true;
+ }
+ }
+ // Set the replica role
+ if (config['attrs']['nsds5replicatype'][0] ==
"3") {
+ current_role = "Master";
+ } else {
+ if (config['attrs']['nsds5flags'][0] ==
"1") {
+ current_role = "Hub";
+ } else {
+ current_role = "Consumer";
+ }
+ }
+
+ this.setState({
+ [suffix]: {
+ role: current_role,
+ nsds5flags:
config['attrs']['nsds5flags'][0],
+ nsds5replicatype:
config['attrs']['nsds5replicatype'][0],
+ nsds5replicaid: 'nsds5replicaid' in
config['attrs'] ? config['attrs']['nsds5replicaid'][0] :
"",
+ nsds5replicabinddn: 'nsds5replicabinddn' in
config['attrs'] ? config['attrs']['nsds5replicabinddn'] :
"",
+ nsds5replicabinddngroup: 'nsds5replicabinddngroup' in
config['attrs'] ? config['attrs']['nsds5replicabinddngroup'][0] :
"",
+ nsds5replicabinddngroupcheckinterval:
'nsds5replicabinddngroupcheckinterval' in config['attrs'] ?
config['attrs']['nsds5replicabinddngroupcheckinterval'][0] :
"",
+ nsds5replicareleasetimeout:
'nsds5replicareleasetimeout' in config['attrs'] ?
config['attrs']['nsds5replicareleasetimeout'][0] : "",
+ nsds5replicapurgedelay: 'nsds5replicapurgedelay' in
config['attrs'] ? config['attrs']['nsds5replicapurgedelay'][0] :
"",
+ nsds5replicatombstonepurgeinterval:
'nsds5replicatombstonepurgeinterval' in config['attrs'] ?
config['attrs']['nsds5replicatombstonepurgeinterval'][0] : "",
+ nsds5replicaprecisetombstonepurging:
nsds5replicaprecisetombstonepurging,
+ nsds5replicaprotocoltimeout:
'nsds5replicaprotocoltimeout' in config['attrs'] ?
config['attrs']['nsds5replicaprotocoltimeout'][0] : "",
+ nsds5replicabackoffmin: 'nsds5replicabackoffmin' in
config['attrs'] ? config['attrs']['nsds5replicabackoffmin'][0] :
"",
+ nsds5replicabackoffmax: 'nsds5replicabackoffmax' in
config['attrs'] ? config['attrs']['nsds5replicabackoffmax'][0] :
"",
+ }
+ });
+
+ // Now load agmts, then the winsync agreement, and finally the RUV
+ let cmd = [
+ "dsconf", "-j",
"ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "repl-agmt", "list", "--suffix",
suffix
+ ];
+ log_cmd("loadReplSuffix", "get repl agreements",
cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const obj = JSON.parse(content);
+ let rows = [];
+ for (let idx in obj['items']) {
+ let agmt_attrs =
obj['items'][idx]['attrs'];
+ let state = "Enabled";
+ let update_status = "";
+ let agmt_init_status = "";
+
+ // Compute state (enabled by default)
+ if ('nsds5replicaenabled' in agmt_attrs) {
+ if
(agmt_attrs['nsds5replicaenabled'][0].toLowerCase() == 'off') {
+ state = "Disabled";
+ }
+ }
+
+ // Check for status msgs
+ if ('nsds5replicalastupdatestatus' in
agmt_attrs) {
+ update_status =
agmt_attrs['nsds5replicalastupdatestatus'][0];
+ }
+ if ('nsds5replicalastinitstatus' in
agmt_attrs &&
+
agmt_attrs['nsds5replicalastinitstatus'][0] != "") {
+ agmt_init_status =
agmt_attrs['nsds5replicalastinitstatus'][0];
+ if (agmt_init_status == "Error (0) Total
update in progress" ||
+ agmt_init_status == "Error (0)") {
+ agmt_init_status = <td
key={agmt_attrs['cn']}><i>Initializing</i><Spinner loading
size="sm" /></td>;
+ } else if (agmt_init_status == "Error (0)
Total update succeeded") {
+ agmt_init_status = <td
key={agmt_attrs['cn']}><i>Initialized</i></td>;
+ } else {
+ agmt_init_status = <td
key={agmt_attrs['cn']}>{agmt_init_status}</td>;
+ }
+ } else if ('nsds5replicalastinitstart' in
agmt_attrs && agmt_attrs['nsds5replicalastinitstart'][0] ==
"19700101000000Z") {
+ agmt_init_status = <td
key={agmt_attrs['cn']}><i>Not initialized</i></td>;
+ } else if ('nsds5beginreplicarefresh' in
agmt_attrs) {
+ agmt_init_status = <td
key={agmt_attrs['cn']}><i>Initializing</i><Spinner loading
size="sm" /></td>;
+ }
+
+ // Update table
+ rows.push({
+ 'name': agmt_attrs['cn'],
+ 'host':
agmt_attrs['nsds5replicahost'],
+ 'port':
agmt_attrs['nsds5replicaport'],
+ 'state': [state],
+ 'status': [update_status],
+ 'initstatus': [agmt_init_status]
+ });
+ }
+
+ // Set agmt
+ this.setState({
+ [suffix]: {
+ ...this.state[suffix],
+ agmtRows: rows,
+ }
+ });
+
+ // Load winsync agreements
+ let cmd = [
+ "dsconf", "-j",
"ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "repl-winsync-agmt", "list",
"--suffix", suffix
+ ];
+ log_cmd("loadReplSuffix", "Get Winsync
Agreements", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err:
"message" })
+ .done(content => {
+ const obj = JSON.parse(content);
+ let ws_rows = [];
+ for (var idx in obj['items']) {
+ let state = "Enabled";
+ let update_status = "";
+ let ws_agmt_init_status =
"Initialized";
+ let agmt_attrs =
obj['items'][idx]['attrs'];
+ // let agmt_name =
agmt_attrs['cn'][0];
+
+ // Compute state (enabled by default)
+ if ('nsds5replicaenabled' in
agmt_attrs) {
+ if
(agmt_attrs['nsds5replicaenabled'][0].toLowerCase() == 'off') {
+ state = "Disabled";
+ }
+ }
+
+ if
('nsds5replicalastupdatestatus' in agmt_attrs) {
+ update_status =
agmt_attrs['nsds5replicalastupdatestatus'][0];
+ }
+
+ if ('nsds5replicalastinitstatus'
in agmt_attrs &&
+
agmt_attrs['nsds5replicalastinitstatus'][0] != "") {
+ ws_agmt_init_status =
agmt_attrs['nsds5replicalastinitstatus'][0];
+ if (ws_agmt_init_status ==
"Error (0) Total update in progress" ||
+ ws_agmt_init_status ==
"Error (0)") {
+ ws_agmt_init_status = <td
key={agmt_attrs['cn']}><i>Initializing</i><Spinner loading
size="sm" /></td>;
+ } else if (ws_agmt_init_status ==
"Error (0) Total update succeeded") {
+ ws_agmt_init_status = <td
key={agmt_attrs['cn']}><i>Initialized</i></td>;
+ } else {
+ ws_agmt_init_status = <td
key={agmt_attrs['cn']}>{ws_agmt_init_status}</td>;
+ }
+ } else if
('nsds5replicalastinitstart' in agmt_attrs &&
agmt_attrs['nsds5replicalastinitstart'][0] == "19700101000000Z") {
+ ws_agmt_init_status = <td
key={agmt_attrs['cn']}><i>Not initialized</i></td>;
+ } else if
('nsds5beginreplicarefresh' in agmt_attrs) {
+ ws_agmt_init_status = <td
key={agmt_attrs['cn']}><i>Initializing</i><Spinner loading
size="sm" /></td>;
+ }
+
+ // Update table
+ ws_rows.push({
+ 'name':
agmt_attrs['cn'],
+ 'host':
agmt_attrs['nsds5replicahost'],
+ 'port':
agmt_attrs['nsds5replicaport'],
+ 'state': [state],
+ 'status': [update_status],
+ 'initstatus':
[ws_agmt_init_status]
+ });
+ }
+ // Set winsync agmts
+ this.setState({
+ [suffix]: {
+ ...this.state[suffix],
+ winsyncRows: ws_rows,
+ }
+ });
+
+ // Load suffix RUV
+ let cmd = ['dsconf', '-j',
'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'replication', 'get-ruv',
'--suffix=' + suffix];
+ log_cmd('loadReplSuffix', 'Get
the suffix RUV', cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err:
"message" })
+ .done(content => {
+ let ruvs = JSON.parse(content);
+ let ruv_rows = [];
+ for (let idx in
ruvs['items']) {
+ let ruv =
ruvs['items'][idx];
+ // Update table
+ ruv_rows.push({
+ 'rid':
ruv['rid'],
+ 'url':
ruv['url'],
+ 'csn':
ruv['csn'],
+ 'raw_csn':
ruv['raw_csn'],
+ 'maxcsn':
ruv['maxcsn'],
+ 'raw_maxcsn':
ruv['raw_maxcsn'],
+ });
+ }
+
+ this.setState({
+ [suffix]: {
+ ...this.state[suffix],
+ ruvRows: ruv_rows,
+ },
+ suffixLoading: false,
+ disableTree: false
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ if (errMsg.desc != "No such
object") {
+ this.addNotification(
+ "error",
+ `Error loading suffix RUV
- ${errMsg.desc}`
+ );
+ }
+ this.setState({
+ suffixLoading: false,
+ disableTree: false
+ });
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.addNotification(
+ "error",
+ `Error loading winsync agreements -
${errMsg.desc}`
+ );
+ this.setState({
+ suffixLoading: false,
+ disableTree: false
+ });
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.addNotification(
+ "error",
+ `Error loading replication agreements configuration -
${errMsg.desc}`
+ );
+ this.setState({
+ suffixLoading: false,
+ disableTree: false
+ });
+ });
+ })
+ .fail(() => {
+ this.setState({
+ suffixLoading: false,
+ disableTree: false,
+ node_replicated: false
+ });
+ });
+ }
+
+ loadAttrs() {
+ // Now get the schema that various tabs use
+ const attr_cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-"
+ this.props.serverId + ".socket",
+ "schema", "attributetypes", "list"
+ ];
+ log_cmd("Suffixes", "Get attrs", attr_cmd);
+ cockpit
+ .spawn(attr_cmd, { superuser: true, err: "message" })
+ .done(content => {
+ let attrContent = JSON.parse(content);
+ let attrs = [];
+ for (let content of attrContent['items']) {
+ attrs.push(content.name);
+ }
+ this.setState({
+ attributes: attrs,
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.addNotification(
+ "error",
+ `Failed to get attributes - ${errMsg.desc}`
+ );
+ });
+ }
+
+ enableTree () {
+ this.setState({
+ disableTree: false
+ });
+ }
+
+ disableTree () {
+ this.setState({
+ disableTree: true
+ });
+ }
+
+ render() {
+ const { nodes } = this.state;
+ let repl_page = "";
+ let disabled = "tree-view-container";
+ if (this.state.disableTree) {
+ disabled = "tree-view-container ds-disabled";
+ }
+ let repl_element =
+ <h4>There are currently no databases to configure for
replication</h4>;
+ if (this.state.loaded) {
+ // We have a suffix, or database link
+ if (this.state.node_type == "suffix" || this.state.node_type ==
"subsuffix") {
+ if (this.state.suffixLoading) {
+ repl_element =
+ <div className="ds-margin-top ds-loading-spinner
ds-center">
+ <h4>Loading replication configuration for
<b>{this.state.node_name} ...</b></h4>
+ <Spinner className="ds-margin-top-lg" loading
size="md" />
+ </div>;
+ } else {
+ if (this.state.node_name in this.state) {
+ repl_element =
+ <div>
+ <ReplSuffix
+ serverId={this.props.serverId}
+ suffix={this.state.node_name}
+ role={this.state[this.state.node_name].role}
+ data={this.state[this.state.node_name]}
+ addNotification={this.addNotification}
+ agmtRows={this.state[this.state.node_name].agmtRows}
+
winsyncRows={this.state[this.state.node_name].winsyncRows}
+ ruvRows={this.state[this.state.node_name].ruvRows}
+ reloadAgmts={this.reloadAgmts}
+ reloadWinsyncAgmts={this.reloadWinsyncAgmts}
+ reloadRUV={this.reloadRUV}
+ reloadConfig={this.reloadConfig}
+ reload={this.loadSuffixTree}
+ attrs={this.state.attributes}
+ replicated={this.state.node_replicated}
+ enableTree={this.enableTree}
+ disableTree={this.disableTree}
+ key={this.state.suffixKey}
+ disabled={this.state.disabled}
+ spinning={this.state.suffixSpinning}
+ />
+ </div>;
+ } else {
+ // Suffix is not replicated
+ repl_element =
+ <ReplSuffix
+ serverId={this.props.serverId}
+ suffix={this.state.node_name}
+ role=""
+ data=""
+ addNotification={this.addNotification}
+ disableWSAgmtTable={this.state.disableWSAgmtTable}
+ disableAgmtTable={this.state.disableAgmtTable}
+ reloadAgmts={this.reloadAgmts}
+ reloadWinsyncAgmts={this.reloadWinsyncAgmts}
+ reloadRUV={this.reloadRUV}
+ reloadConfig={this.reloadConfig}
+ reload={this.loadSuffixTree}
+ attrs={this.state.attributes}
+ replicated={this.state.node_replicated}
+ enableTree={this.enableTree}
+ disableTree={this.disableTree}
+ spinning={this.state.suffixSpinning}
+ disabled={this.state.disabled}
+ key={this.state.node_name}
+ />;
+ }
+ }
+ } else if (this.state.node_type == "changelog") {
+ repl_element =
+ <Changelog
+ serverId={this.props.serverId}
+ clDir={this.state.clDir}
+ clMaxEntries={this.state.clMaxEntries}
+ clMaxAge={this.state.clMaxAge}
+ clCompactInt={this.state.clCompactInt}
+ clTrimInt={this.state.clTrimInt}
+ clEncrypt={this.state.clEncrypt}
+ addNotification={this.addNotification}
+ enableTree={this.enableTree}
+ reload={this.reloadChangelog}
+ loading={this.state.clLoading}
+ key={this.state.clDir}
+ />;
+ }
+ repl_page =
+ <div className="container-fluid">
+ <NotificationController
+ notifications={this.state.notifications}
+ removeNotificationAction={this.removeNotification}
+ />
+ <div className="ds-container">
+ <div>
+ <div className="ds-tree">
+ <div className={disabled} id="repl-tree"
+ style={treeViewContainerStyles}>
+ <TreeView
+ nodes={nodes}
+ highlightOnHover
+ highlightOnSelect
+ selectNode={this.selectNode}
+ />
+ </div>
+ </div>
+ </div>
+ <div className="ds-tree-content">
+ {repl_element}
+ </div>
+ </div>
+ </div>;
+ } else {
+ repl_page =
+ <div className="ds-margin-top ds-loading-spinner
ds-center">
+ <h4>Loading Replication Information ...</h4>
+ <Spinner className="ds-margin-top-lg" loading
size="md" />
+ </div>;
+ }
+
+ return (
+ <div>
+ {repl_page}
+ </div>
+ );
+ }
+}
+
+// Property types and defaults
+
+Replication.propTypes = {
+ serverId: PropTypes.string
+};
+
+Replication.defaultProps = {
+ serverId: ""
+};
+
+export default Replication;
diff --git a/src/cockpit/389-console/src/schema.html
b/src/cockpit/389-console/src/schema.html
index 35fcdfc..36f61be 100644
--- a/src/cockpit/389-console/src/schema.html
+++ b/src/cockpit/389-console/src/schema.html
@@ -1,6 +1,6 @@
<div id="schema-content">
- <div id="objectclass-page" class="all-pages" hidden>
+ <div id="objectclass-page" class="all-pages ds-margin-left"
hidden>
<h3 class="ds-config-header">Objectclasses</h3>
<div>
<input type="checkbox" class="ds-config-checkbox"
id="oc-user-defined"><label
@@ -9,12 +9,11 @@
<table id="oc-table" class="display ds-repl-table"
cellspacing="0" width="100%">
<thead>
<tr class="ds-table-header">
- <th>Objectclass Name</th>
- <th>OID</th>
- <th>Parent</th>
- <th>Required Attributes</th>
- <th>Allowed Attributes</th>
- <th>Action</th>
+ <th class="ds-center">Objectclass Name</th>
+ <th class="ds-center">OID</th>
+ <th class="ds-center">Required Attributes</th>
+ <th class="ds-center">Allowed Attributes</th>
+ <th class="ds-center">Action</th>
</tr>
</thead>
<tbody>
@@ -22,7 +21,8 @@
</table>
<button id="add-oc-button" name="create-oc"
data-toggle="modal" data-target="#add-edit-oc-form" class="btn
btn-primary ds-button">Create Objectclass</button>
</div>
- <div id="attribute-page" class="all-pages" hidden>
+
+ <div id="attribute-page" class="all-pages ds-margin-left"
hidden>
<h3 class="ds-config-header">Attributes</h3>
<div>
<input type="checkbox" class="ds-config-checkbox"
id="attr-user-defined"><label
@@ -31,14 +31,11 @@
<table id="attr-table" class="display ds-repl-table"
cellspacing="0" width="100%">
<thead>
<tr class="ds-table-header">
- <th>Attribute Name</th>
- <th>OID</th>
- <th>Syntax</th>
- <th>Multivalued</th>
- <th title="Equality matching rules">Equality Rules</th>
- <th title="Ordering matching rules">Ordering Rules</th>
- <th title="Substring matching rules">Substring
Rules</th>
- <th>Action</th>
+ <th class="ds-center">Attribute Name</th>
+ <th class="ds-center">OID</th>
+ <th class="ds-center">Syntax</th>
+ <th class="ds-center">Multivalued</th>
+ <th class="ds-center">Action</th>
</tr>
</thead>
<tbody>
@@ -46,7 +43,8 @@
</table>
<button id="create-attr-button" data-toggle="modal"
data-target="#add-edit-attr-form" class="btn btn-primary
ds-button">Create Attribute</button>
</div>
- <div id="schema-mr" class="all-pages" hidden>
+
+ <div id="schema-mr" class="all-pages ds-margin-left" hidden>
<div class="">
<h3 class="ds-config-header">Matching Rules</h3>
<table id="schema-mr-table" class="display ds-table"
cellspacing="0" width="100%">
@@ -67,6 +65,80 @@
<!-- Modals/Popups/Wizards -->
+ <!-- View Attribute modal -->
+ <div class="modal fade" id="view-attr-form"
data-backdrop="static" tabindex="-1" role="dialog"
aria-labelledby="view-attr-header" aria-hidden="true">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close"
data-dismiss="modal" aria-hidden="true"
aria-label="Close">
+ <span class="pficon pficon-close"></span>
+ </button>
+ <h4 class="modal-title">View Attribute</h4>
+ </div>
+ <div class="modal-body">
+ <div class="ds-inline">
+ <div>
+ <label for="attr-name-view"
class="ds-config-label-lrg" title="The attribute name"><b
+ >Attribute Name</b></label><input
class="ds-input" type="text" id="attr-name-view"
size="40" readonly />
+ </div>
+ <div>
+ <label for="attr-desc-view"
class="ds-config-label-lrg" title="The attribute
description"><b
+ >Description</b></label><input
class="ds-input" type="text" id="attr-desc-view"
size="40" readonly />
+ </div>
+ <div>
+ <label for="attr-oid-view"
class="ds-config-label-lrg" title="The attribute name"><b
+ >OID</b></label><input
class="ds-input" type="text" id="attr-oid-view"
size="40" readonly />
+ </div>
+ <div>
+ <label for="attr-parent-view"
class="ds-config-label-lrg" title="The parent
attribute"><b>Parent Attribute</b></label><input
+ class="ds-input" type="text"
id="attr-parent-view" size="40" readonly />
+ </div>
+ <div>
+ <label for="attr-syntax-view"
class="ds-config-label-lrg" title="The attribute
syntax"><b>Attribute Syntax</b></label><input
+ class="ds-input" type="text"
id="attr-syntax-view" size="40" readonly />
+ </div>
+ <div>
+ <label for="attr-usage-view"
class="ds-config-label-lrg" title="The parent
attribute"><b>Attribute Usage</b></label><input
+ class="ds-input" type="text"
id="attr-usage-view" size="40" readonly />
+ </div>
+ <div>
+ <input type="checkbox"
class="ds-config-checkbox" id="attr-multivalued-view"
disabled="disabled" /><label
+ for="attr-multivalued-view"
class="ds-label"> Attribute Multi-Valued </label>
+ </div>
+ <div>
+ <input type="checkbox"
class="ds-config-checkbox" id="attr-no-user-mod-view"
disabled="disabled" /><label
+ for="attr-no-user-mod-view"
class="ds-label"> Read-only (NO-USER-MODIFICATION flag) </label>
+ </div>
+ <div>
+ <label for="attr-alias-view"
class="ds-config-label-lrg" title="The attribute alias list separated by
space"><b
+ >Attribute Aliases</b></label><input
class="ds-input" type="text" id="attr-alias-view"
size="40" readonly />
+ </div>
+ <div class="panel panel-default ds-margin-top">
+ <div
class="panel-heading"><strong>Matching
rules</strong></div>
+ <div class="panel-body">
+ <div>
+ <label for="attr-eq-mr-select-view"
class="ds-config-label-lrg"><b>Equality</b></label><input
+ class="ds-input"
type="text" id="attr-eq-mr-select-view" size="35" readonly
/>
+ </div>
+ <div>
+ <label for="attr-order-mr-select-view"
class="ds-config-label-lrg"><b>Ordering</b></label><input
+ class="ds-input" type="text"
id="attr-order-mr-select-view" size="35" readonly />
+ </div>
+ <div>
+ <label for="attr-sub-mr-select-view"
class="ds-config-label-lrg"><b>Substring</b></label><input
+ class="ds-input" type="text"
id="attr-sub-mr-select-view" size="35" readonly />
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn
btn-default" data-dismiss="modal">Close</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
<!-- Add/edit Attribute modal -->
<div class="modal fade" id="add-edit-attr-form"
data-backdrop="static" tabindex="-1" role="dialog"
aria-labelledby="add-edit-attr-header" aria-hidden="true">
<div class="modal-dialog">
@@ -81,22 +153,22 @@
<div class="ds-inline">
<p class="ds-modal-error"></p>
<div>
- <label for="attr-parent" class="ds-config-label-lrg"
title="The parent attribute"><b>Parent
Attribute</b></label><select
- class="btn btn-default dropdown ds-oc-dropdown"
id="attr-parent">
- <option value=""></option>
- </select>
- </div>
- <div>
<label for="attr-name" class="ds-config-label-lrg"
title="The attribute name"><b
Attribute Name</b></label><input
class="ds-input" type="text" id="attr-name"
size="40"/>
</div>
<div>
<label for="attr-desc" class="ds-config-label-lrg"
title="The attribute description"><b
- >Attribute Description</b></label><input
class="ds-input" type="text" id="attr-desc"
size="40"/>
+ >Description</b></label><input class="ds-input"
type="text" id="attr-desc" size="40"/>
</div>
<div>
<label for="attr-oid" class="ds-config-label-lrg"
title="The attribute name"><b
- >Attribute OID</b></label><input
class="ds-input" type="text" id="attr-oid"
size="40"/>
+ >OID</b></label><input class="ds-input"
type="text" id="attr-oid" size="40"/>
+ </div>
+ <div>
+ <label for="attr-parent" class="ds-config-label-lrg"
title="The parent attribute"><b>Parent
Attribute</b></label><select
+ class="btn btn-default dropdown ds-oc-dropdown"
id="attr-parent">
+ <option value=""></option>
+ </select>
</div>
<div>
<label for="attr-syntax" class="ds-config-label-lrg"
title="The attribute syntax"><b>Attribute
Syntax</b></label><select
@@ -121,28 +193,28 @@
<label for="attr-alias" class="ds-config-label-lrg"
title="The attribute alias list separated by space"><b
Attribute Aliases</b></label><input
class="ds-input" type="text" id="attr-alias"
size="40"/>
</div>
- <div class="panel panel-default">
- <div class="panel-heading"><strong>Matching
rules</strong></div>
- <div class="panel-body">
- <div>
- <label for="attr-eq-mr-select"
class="ds-config-label-lrg"><b>Equality</b></label><select
- class="btn btn-default dropdown ds-oc-dropdown"
id="attr-eq-mr-select">
- <option></option>
- </select>
- </div>
- <div>
- <label for="attr-order-mr-select"
class="ds-config-label-lrg"><b>Ordering</b></label><select
- class="btn btn-default dropdown ds-oc-dropdown"
id="attr-order-mr-select">
- <option></option>
- </select>
- </div>
- <div>
- <label for="attr-sub-mr-select"
class="ds-config-label-lrg"><b>Substring</b></label><select
- class="btn btn-default dropdown ds-oc-dropdown"
id="attr-sub-mr-select">
- <option></option>
- </select>
- </div>
- </div>
+ <div class="panel panel-default ds-margin-top">
+ <div class="panel-heading"><strong>Matching
rules</strong></div>
+ <div class="panel-body">
+ <div>
+ <label for="attr-eq-mr-select"
class="ds-config-label-lrg"><b>Equality</b></label><select
+ class="btn btn-default dropdown ds-oc-dropdown"
id="attr-eq-mr-select">
+ <option></option>
+ </select>
+ </div>
+ <div>
+ <label for="attr-order-mr-select"
class="ds-config-label-lrg"><b>Ordering</b></label><select
+ class="btn btn-default dropdown ds-oc-dropdown"
id="attr-order-mr-select">
+ <option></option>
+ </select>
+ </div>
+ <div>
+ <label for="attr-sub-mr-select"
class="ds-config-label-lrg"><b>Substring</b></label><select
+ class="btn btn-default dropdown ds-oc-dropdown"
id="attr-sub-mr-select">
+ <option></option>
+ </select>
+ </div>
+ </div>
</div>
</div>
<div id="save-attr-spinner" class="ds-center"
hidden>
@@ -159,6 +231,65 @@
</div>
+ <!-- View Objectclass -->
+ <div class="modal fade" id="view-objectclass-form"
aria-labelledby="view-objectclass-form" data-backdrop="static"
tabindex="-1" role="dialog" aria-hidden="true">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close"
data-dismiss="modal" aria-hidden="true"
aria-label="Close">
+ <span class="pficon pficon-close"></span>
+ </button>
+ <h4 class="modal-title">View Objectclass</h4>
+ </div>
+ <div class="modal-body">
+ <form class="form-horizontal">
+ <div class="ds-inline">
+ <div>
+ <label for="oc-name-view"
class="ds-config-label-lrg" title="The objectclass name"><b
+ >Objectclass Name</b></label><input
class="ds-input" type="text" id="oc-name-view"
size="40" readonly />
+ </div>
+ <div>
+ <label for="oc-desc-view"
class="ds-config-label-lrg" title="The objectClass
description"><b
+ >