From 5e48e9f4c60c3dd1d393b78bfd46936be74cfcc0 Mon Sep 17 00:00:00 2001 From: Mark Reynolds Date: Oct 31 2019 20:53:07 +0000 Subject: 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" relates: https://pagure.io/389-ds-base/issue/50592 Reviewed by: spichugi(Thanks!) --- diff --git a/rpm/389-ds-base.spec.in b/rpm/389-ds-base.spec.in index 22ed298..cbc128b 100644 --- a/rpm/389-ds-base.spec.in +++ b/rpm/389-ds-base.spec.in @@ -246,6 +246,8 @@ Requires: perl-NetAddr-IP Requires: perl-Mozilla-LDAP # for setup-ds.pl Requires: bind-utils +%global __provides_exclude_from %{_libdir}/%{pkgname}/perl +%global __requires_exclude perl\\((DSCreate|DSMigration|DSUpdate|DSUtil|Dialog|DialogManager|FileConn|Inf|Migration|Resource|Setup|SetupLog) %{?perl_default_filter} %endif # End use perl 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/src/css/ds.css b/src/cockpit/389-console/src/css/ds.css index f5b1e4f..fdc79e7 100644 --- a/src/cockpit/389-console/src/css/ds.css +++ b/src/cockpit/389-console/src/css/ds.css @@ -19,6 +19,10 @@ vertical-align: top !important; } +.ds-left-align { + right: 0 !important; +} + /* Main nav page index.html */ .ds-content { padding: 0; @@ -116,6 +120,10 @@ td { width: 525px; } +.ds-no-padding { + padding: 0px !important; +} + .ds-tree { font-size: 0.5; background-color: #f8f8f8; @@ -131,6 +139,11 @@ td { padding-top: 5px; } +.ds-disabled { + pointer-events: none; + opacity: 0.7; +} + .ds-split { width: 400px; } @@ -142,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; } @@ -238,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; @@ -594,6 +593,17 @@ option { float: left; } +.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; @@ -635,7 +645,7 @@ option { border: 0; position: relative; overflow: hidden; - width: 700px; + width: 100%; text-align: left; font: inherit; } @@ -646,7 +656,7 @@ option { height: 1px; background: #228bc0; position: absolute; - width: 700px; + width: 100%; top: 50% !important; margin-left: 10px; text-align: left; @@ -671,7 +681,7 @@ option { } .ds-margin-top { - margin-top: 10px; + margin-top: 10px !important; } .ds-margin-top-med { @@ -686,6 +696,10 @@ option { margin-top: 35px !important; } +.ds-margin-top-xxlg { + margin-top: 45px !important; +} + .ds-modal-spinner { margin-top: 10px; text-align: center; @@ -736,6 +750,10 @@ option { margin-left: 15px !important; } +.ds-left-indent-md { + margin-left: 30px !important; +} + .ds-right-indent { margin-right: 15px !important; } @@ -798,6 +816,10 @@ option { color: red; } +.ds-clear-text { + opacity: 0; +} + .ds-margin-left-sm { margin-left: 30px !important; } @@ -807,7 +829,7 @@ option { } .ds-margin-left-piechart { - margin-left: 130px !important; + margin-left: 140px !important; } .ds-nested-modal { @@ -839,6 +861,13 @@ option { transform: translate(-25%, -50%); } +.ds-loading-spinner-tree { + position: fixed; + top: 40%; + left: 40%; + transform: translate(-40%, -40%); +} + .ds-loading { position: fixed; top: 25%; @@ -870,6 +899,10 @@ option { margin-left: 80px; } +.ds-h4 { + font-size: 18px !important; +} + @media screen and (max-width: 1300px) { .ds-plugin-spinner { margin-left: 0px; @@ -955,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 { @@ -1018,3 +1051,22 @@ input { th { text-align: center; } + +td { + text-align: center; +} + +hr { + margin-top: 20px; + margin-bottom: 20px; +} + +h3 { + margin-top: 20px; + margin-bottom: 20px; +} + +h4 { + margin-top: 15px; + margin-bottom: 15px; +} diff --git a/src/cockpit/389-console/src/database.jsx b/src/cockpit/389-console/src/database.jsx index 36b38be..b1c3953 100644 --- a/src/cockpit/389-console/src/database.jsx +++ b/src/cockpit/389-console/src/database.jsx @@ -48,6 +48,7 @@ export class Database extends React.Component { createSuffixEntry: false, createSampleEntries: false, noSuffixInit: true, + disableTree: false, // DB config globalDBConfig: {}, @@ -83,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); @@ -102,6 +102,7 @@ export class Database extends React.Component { // Other this.loadSuffixTree = this.loadSuffixTree.bind(this); + this.enableTree = this.enableTree.bind(this); } componentWillMount () { @@ -449,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") { @@ -976,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 + }); }); } @@ -1047,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 = @@ -1058,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) { @@ -1067,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) { @@ -1077,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 != "") { @@ -1084,10 +1136,9 @@ export class Database extends React.Component { if (this.state.dbtype == "suffix" || this.state.dbtype == "subsuffix") { if (this.state.suffixLoading) { db_element = -
-

+

Loading suffix configuration for {this.state.node_text} ...

- +
; } else { db_element = @@ -1107,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} />; } @@ -1114,10 +1166,9 @@ export class Database extends React.Component { // Chaining if (this.state.chainingLoading) { db_element = -
-

+

Loading chaining configuration for {this.state.node_text} ...

- +
; } else { db_element = @@ -1128,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} />; } @@ -1144,7 +1196,7 @@ export class Database extends React.Component {
-
-

- + Database Name diff --git a/src/cockpit/389-console/src/ds.js b/src/cockpit/389-console/src/ds.js index 1274b3f..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; @@ -355,39 +355,6 @@ function save_all () { 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(''); - } - } - get_and_set_repl_agmts(); - get_and_set_repl_winsync_agmts(); - 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 () { @@ -449,11 +416,6 @@ function load_config (refresh){ get_and_set_schema_tables(); update_progress(); - // Replication page - get_and_set_repl_config(); - get_and_set_cleanallruv(); - update_progress(); - // Initialize the tabs $(".ds-tab-list").css( 'color', '#777'); $("#server-tab").css( 'color', '#228bc0'); @@ -510,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( + , + document.getElementById("replication") + ); + // Monitor tab ReactDOM.render( , diff --git a/src/cockpit/389-console/src/index.html b/src/cockpit/389-console/src/index.html index 3eef2a7..6a0479c 100644 --- a/src/cockpit/389-console/src/index.html +++ b/src/cockpit/389-console/src/index.html @@ -20,7 +20,6 @@ - @@ -35,9 +34,9 @@