From dc0b0d82fdd81e0e7325c512658854f0723320cb Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Fri, 4 Apr 2014 11:52:37 -0400
Subject: [PATCH] Added general configuration page.

A new page has been added to manage general TPS configuration
properties. The properties are read-only by default. In edit
mode the property name will become a link which will show a
dialog to edit the property value.

The config REST service has been updated to use PATCH for
update operation and handle possible null collection of
properties.

Fixed a bug in TableItem.reset() where the code didn't clear
the table cell properly.

Fixed a bug in ConfigDatabase.getProperties() where the code
didn't handle null property key properly.

Ticket #654
---
 .../netscape/certsrv/tps/config/ConfigData.java    |  33 +++----
 .../certsrv/tps/config/ConfigResource.java         |   4 +-
 .../netscape/cmstools/tps/config/ConfigCLI.java    |   8 +-
 base/server/share/webapps/pki/js/pki-ui.js         |   4 -
 base/tps-tomcat/shared/webapps/tps/js/config.js    |  70 ++++++++++++++
 base/tps-tomcat/shared/webapps/tps/js/tps.js       |   5 +-
 base/tps-tomcat/shared/webapps/tps/ui/config.html  | 104 +++++++++++++++++++++
 base/tps-tomcat/shared/webapps/tps/ui/index.html   |   7 ++
 .../server/tps/config/ConfigDatabase.java          |   2 +-
 .../dogtagpki/server/tps/config/ConfigService.java |  14 +--
 10 files changed, 212 insertions(+), 39 deletions(-)
 create mode 100644 base/tps-tomcat/shared/webapps/tps/js/config.js
 create mode 100644 base/tps-tomcat/shared/webapps/tps/ui/config.html

diff --git a/base/common/src/com/netscape/certsrv/tps/config/ConfigData.java b/base/common/src/com/netscape/certsrv/tps/config/ConfigData.java
index 2a36f3fa87bea873a3ad436955c5553bce6a8719..7bfb86ea8e1e5f51b593a19363559ee842f8b2a4 100644
--- a/base/common/src/com/netscape/certsrv/tps/config/ConfigData.java
+++ b/base/common/src/com/netscape/certsrv/tps/config/ConfigData.java
@@ -21,14 +21,16 @@ package com.netscape.certsrv.tps.config;
 import java.io.StringReader;
 import java.io.StringWriter;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.TreeMap;
 
 import javax.xml.bind.JAXBContext;
 import javax.xml.bind.Marshaller;
 import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlAttribute;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
@@ -42,6 +44,7 @@ import org.jboss.resteasy.plugins.providers.atom.Link;
  * @author Endi S. Dewata
  */
 @XmlRootElement(name="Configuration")
+@XmlAccessorType(XmlAccessType.NONE)
 public class ConfigData {
 
     public static Marshaller marshaller;
@@ -58,7 +61,7 @@ public class ConfigData {
     }
 
     String status;
-    Map<String, String> properties = new LinkedHashMap<String, String>();
+    Map<String, String> properties;
 
     Link link;
 
@@ -78,24 +81,7 @@ public class ConfigData {
     }
 
     public void setProperties(Map<String, String> properties) {
-        this.properties.clear();
-        this.properties.putAll(properties);
-    }
-
-    public Collection<String> getPropertyNames() {
-        return properties.keySet();
-    }
-
-    public String getProperty(String name) {
-        return properties.get(name);
-    }
-
-    public void setProperty(String name, String value) {
-        properties.put(name, value);
-    }
-
-    public String removeProperty(String name) {
-        return properties.remove(name);
+        this.properties = properties;
     }
 
     public static class MapAdapter extends XmlAdapter<PropertyList, Map<String, String>> {
@@ -203,8 +189,11 @@ public class ConfigData {
 
         ConfigData before = new ConfigData();
         before.setStatus("ENABLED");
-        before.setProperty("param1", "value1");
-        before.setProperty("param2", "value2");
+
+        Map<String, String> properties = new TreeMap<String, String>();
+        properties.put("param1", "value1");
+        properties.put("param2", "value2");
+        before.setProperties(properties);
 
         String string = before.toString();
         System.out.println(string);
diff --git a/base/common/src/com/netscape/certsrv/tps/config/ConfigResource.java b/base/common/src/com/netscape/certsrv/tps/config/ConfigResource.java
index a7f33fada74b35d3801057866353381833851fe4..1461b06c0c9885fd2cfc3c712c47507c07986949 100644
--- a/base/common/src/com/netscape/certsrv/tps/config/ConfigResource.java
+++ b/base/common/src/com/netscape/certsrv/tps/config/ConfigResource.java
@@ -18,7 +18,6 @@
 package com.netscape.certsrv.tps.config;
 
 import javax.ws.rs.GET;
-import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.core.Response;
 
@@ -26,6 +25,7 @@ import org.jboss.resteasy.annotations.ClientResponseType;
 
 import com.netscape.certsrv.acls.ACLMapping;
 import com.netscape.certsrv.authentication.AuthMethodMapping;
+import com.netscape.certsrv.base.PATCH;
 
 
 /**
@@ -40,7 +40,7 @@ public interface ConfigResource {
     @ClientResponseType(entityType=ConfigData.class)
     public Response getConfig();
 
-    @PUT
+    @PATCH
     @ClientResponseType(entityType=ConfigData.class)
     @ACLMapping("config.modify")
     public Response updateConfig(ConfigData configData);
diff --git a/base/java-tools/src/com/netscape/cmstools/tps/config/ConfigCLI.java b/base/java-tools/src/com/netscape/cmstools/tps/config/ConfigCLI.java
index 584cf81af3af4adfb4c7e499ed2b82ed45a11720..f7b53ed63ba55cb3afed541bf4e821d2b345156f 100644
--- a/base/java-tools/src/com/netscape/cmstools/tps/config/ConfigCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/tps/config/ConfigCLI.java
@@ -55,9 +55,11 @@ public class ConfigCLI extends CLI {
 
         System.out.println("  Properties:");
         Map<String, String> properties = configData.getProperties();
-        for (String name : properties.keySet()) {
-            String value = properties.get(name);
-            System.out.println("    " + name + ": " + value);
+        if (properties != null) {
+            for (String name : properties.keySet()) {
+                String value = properties.get(name);
+                System.out.println("    " + name + ": " + value);
+            }
         }
 
         Link link = configData.getLink();
diff --git a/base/server/share/webapps/pki/js/pki-ui.js b/base/server/share/webapps/pki/js/pki-ui.js
index 56a53ab8ab7df6b9ee5dca1169ea8321bf905864..5f4251bbef0b54b2dc4f7b515056b5f536b27040 100644
--- a/base/server/share/webapps/pki/js/pki-ui.js
+++ b/base/server/share/webapps/pki/js/pki-ui.js
@@ -341,10 +341,6 @@ var TableItem = Backbone.View.extend({
                 // hide checkbox by hiding the label
                 $("label", td).hide();
 
-            } else if (name == "id") {
-                // hide the content
-                td.children().hide();
-
             } else {
                 // empty the content
                 td.html("&nbsp;");
diff --git a/base/tps-tomcat/shared/webapps/tps/js/config.js b/base/tps-tomcat/shared/webapps/tps/js/config.js
new file mode 100644
index 0000000000000000000000000000000000000000..3bd2492dfe2f197f5f0b377f36e9137c15f296e4
--- /dev/null
+++ b/base/tps-tomcat/shared/webapps/tps/js/config.js
@@ -0,0 +1,70 @@
+/* --- BEGIN COPYRIGHT BLOCK ---
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ * All rights reserved.
+ * --- END COPYRIGHT BLOCK ---
+ *
+ * @author Endi S. Dewata
+ */
+
+var ConfigModel = Model.extend({
+    url: function() {
+        return "/tps/rest/config";
+    },
+    parseResponse: function(response) {
+        return {
+            id: "config",
+            status: response.Status,
+            properties: response.Properties.Property
+        };
+    },
+    createRequest: function(entry) {
+        return {
+            Status: entry.status,
+            Properties: {
+                Property: entry.properties
+            }
+        };
+    }
+});
+
+var PropertiesTableItem = TableItem.extend({
+    initialize: function(options) {
+        var self = this;
+        PropertiesTableItem.__super__.initialize.call(self, options);
+    },
+    renderIDColumn: function(td) {
+        var self = this;
+
+        // in view mode all properties are read-only
+        if (self.table.mode == "view") {
+            self.renderColumn(td);
+            return;
+        }
+
+        // in edit mode all properties are editable
+        PropertiesTableItem.__super__.renderIDColumn.call(self, td);
+    }
+});
+
+var ConfigPage = EntryWithPropertiesPage.extend({
+    initialize: function(options) {
+        var self = this;
+        options.model = new ConfigModel();
+        options.tableItem = PropertiesTableItem;
+        options.tableSize = 15;
+        ConfigPage.__super__.initialize.call(self, options);
+    }
+});
diff --git a/base/tps-tomcat/shared/webapps/tps/js/tps.js b/base/tps-tomcat/shared/webapps/tps/js/tps.js
index 9c4792ebeb78e0226014147936882929bf7e9b7e..16199cb4f46ed8bb471b821893b2a163494eacfc 100644
--- a/base/tps-tomcat/shared/webapps/tps/js/tps.js
+++ b/base/tps-tomcat/shared/webapps/tps/js/tps.js
@@ -53,6 +53,8 @@ var EntryWithPropertiesPage = EntryPage.extend({
         var self = this;
         EntryWithPropertiesPage.__super__.initialize.call(self, options);
         self.parentPage = options.parentPage;
+        self.tableItem = options.tableItem;
+        self.tableSize = options.tableSize || 10;
     },
     setup: function() {
         var self = this;
@@ -121,7 +123,8 @@ var EntryWithPropertiesPage = EntryPage.extend({
             addDialog: addDialog,
             editDialog: editDialog,
             viewDialog: viewDialog,
-            pageSize: 10
+            tableItem: self.tableItem,
+            pageSize: self.tableSize
         });
     },
     renderContent: function() {
diff --git a/base/tps-tomcat/shared/webapps/tps/ui/config.html b/base/tps-tomcat/shared/webapps/tps/ui/config.html
new file mode 100644
index 0000000000000000000000000000000000000000..69dd06b12c18fb0fb95bcd162382597ae11e8ccb
--- /dev/null
+++ b/base/tps-tomcat/shared/webapps/tps/ui/config.html
@@ -0,0 +1,104 @@
+<!-- --- BEGIN COPYRIGHT BLOCK ---
+     This program is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published by
+     the Free Software Foundation; version 2 of the License.
+
+     This program is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+     GNU General Public License for more details.
+
+     You should have received a copy of the GNU General Public License along
+     with this program; if not, write to the Free Software Foundation, Inc.,
+     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+     Copyright (C) 2014 Red Hat, Inc.
+     All rights reserved.
+     --- END COPYRIGHT BLOCK --- -->
+<div class="pki-header">
+
+<span class="pki-title">General</span></span>
+
+<span class="pki-actions">
+
+<span class="pki-menu" style="display: none;">
+<a name="edit" href="#">Edit</a><br>
+</span>
+
+<span class="pki-buttons" style="display: none;">
+<button name="cancel">Cancel</button>
+<button name="save" class="primary">Save</button>
+</span>
+
+</span>
+
+</div>
+
+<table name="properties">
+<thead>
+    <tr>
+         <th class="pki-table-actions" colspan="3">
+             <span name="search">
+                 <input name="search" type="text" placeholder="Search...">
+             </span>
+             <span class="pki-table-buttons" style="display: none;">
+                 <button name="add">Add</button>
+                 <button name="remove">Remove</button>
+             </span>
+         </th>
+    </tr>
+    <tr>
+        <th class="pki-select-column"><input id="properties_selectall" type="checkbox"><label for="properties_selectall">&nbsp;</label></th>
+        <th class="pki-property-name-column">Name</th>
+        <th>Value</th>
+    </tr>
+</thead>
+<tbody>
+    <tr>
+        <td class="pki-select-column"><input id="properties_select" type="checkbox"><label for="properties_select">&nbsp;</label></td>
+        <td name="id" class="pki-property-name-column"><a href="#">&nbsp;</a></td>
+        <td name="value">&nbsp;</td>
+    </tr>
+</tbody>
+<tfoot>
+    <tr>
+         <th class="pki-table-actions" colspan="3">
+             <div class="pki-table-info">
+                 Total: <span name="totalEntries">0</span> entries
+             </div>
+             <div class="pki-page-controls">
+                 <ul class="pagination">
+                     <li><a href="#" name="first"><span class="i fa fa-angle-double-left"></span></a></li>
+                     <li><a href="#" name="prev"><span class="i fa fa-angle-left"></span></a></li>
+                 </ul>
+                 <span class="pki-page-jump">
+                     <input name="page" type="text" value="1"> of <span name="totalPages">1</span>
+                 </span>
+                 <ul class="pagination">
+                     <li><a href="#" name="next"><span class="i fa fa-angle-right"></span></a></li>
+                     <li><a href="#" name="last"><span class="i fa fa-angle-double-right"></span></a></li>
+                 </ul>
+             </div>
+         </th>
+    </tr>
+</tfoot>
+</table>
+
+<div id="property-dialog" class="rcue-dialog-background">
+    <div class="rcue-dialog">
+        <header>
+            <h1>Edit Property</h1>
+            <a class="rcue-button-close" href="#"></a>
+        </header>
+        <fieldset>
+            <label>Name</label><input name="name" type="text"><br>
+            <label>Value</label><input name="value" type="text"><br>
+        </fieldset>
+        <footer>
+            <button name="add" class="primary">Add</button>
+            <button name="save" class="primary">Save</button>
+            <button name="close" class="primary">Close</button>
+            <button name="cancel">Cancel</button>
+        </footer>
+    </div>
+</div>
diff --git a/base/tps-tomcat/shared/webapps/tps/ui/index.html b/base/tps-tomcat/shared/webapps/tps/ui/index.html
index fd950cad5ee9030d7d3c9da873d843b9eaf6e821..727fcc9853d7eb97dbae2533f5e2f880eaba21f2 100644
--- a/base/tps-tomcat/shared/webapps/tps/ui/index.html
+++ b/base/tps-tomcat/shared/webapps/tps/ui/index.html
@@ -31,6 +31,7 @@
     <script src="/tps/js/audit.js"></script>
     <script src="/tps/js/authenticator.js"></script>
     <script src="/tps/js/cert.js"></script>
+    <script src="/tps/js/config.js"></script>
     <script src="/tps/js/connection.js"></script>
     <script src="/tps/js/group.js"></script>
     <script src="/tps/js/profile.js"></script>
@@ -80,6 +81,11 @@ $(function() {
         url: "certs.html"
     }));
 
+    navigation.page("config", new ConfigPage({
+        el: content,
+        url: "config.html"
+    }));
+
     navigation.page("connections", new ConnectionsPage({
         el: content,
         url: "connections.html"
@@ -179,6 +185,7 @@ $(function() {
         <b class="caret"></b>
       </a>
       <ul class="dropdown-menu">
+        <li><a href="#config">General</a></li>
         <li><a href="#authenticators">Authenticators</a></li>
         <li><a href="#connections">Connections</a></li>
         <li><a href="#audit">Audit</a></li>
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigDatabase.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigDatabase.java
index 8cb874f3dd6f1e5fdddbcf12b16c7448fcef6d1d..b55bbba28fc104749ba6f9329d261a5b10bfb403 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigDatabase.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigDatabase.java
@@ -130,7 +130,7 @@ public class ConfigDatabase extends Database<ConfigRecord> {
 
         CMS.debug("ConfigDatabase.getProperties(\"" + record.getID() + "\", \"" + key + "\")");
 
-        if (!record.containsKey(key)) {
+        if (key != null && !record.containsKey(key)) {
             throw new ResourceNotFoundException("Entry does not exist: " + key);
         }
 
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigService.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigService.java
index 5604eae5d2600918e90f186e083ba3e36ff4f2f6..84d9d786e4494934ecd7bcd52afe0823213a8ba1 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigService.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigService.java
@@ -105,15 +105,17 @@ public class ConfigService extends PKIService implements ConfigResource {
             ConfigDatabase configDatabase = new ConfigDatabase();
             ConfigRecord configRecord = configDatabase.getRecord("Generals");
 
-            // validate new properties
             Map<String, String> properties = configData.getProperties();
-            configDatabase.validateProperties(configRecord, null, properties);
+            if (properties != null) {
+                // validate new properties
+                configDatabase.validateProperties(configRecord, null, properties);
 
-            // remove old properties
-            configDatabase.removeProperties(configRecord, null);
+                // remove old properties
+                configDatabase.removeProperties(configRecord, null);
 
-            // add new properties
-            configDatabase.addProperties(configRecord, null, properties);
+                // add new properties
+                configDatabase.addProperties(configRecord, null, properties);
+            }
 
             configDatabase.commit();
 
-- 
1.8.4.2

