You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@roller.apache.org by sn...@apache.org on 2016/09/05 21:30:54 UTC

roller git commit: Bootstrapification of "Roller Configuration" or GlobalConfig page.

Repository: roller
Updated Branches:
  refs/heads/bootstrap-ui 2f4b2b3e1 -> 29adfd3f8


Bootstrapification of "Roller Configuration" or GlobalConfig page.


Project: http://git-wip-us.apache.org/repos/asf/roller/repo
Commit: http://git-wip-us.apache.org/repos/asf/roller/commit/29adfd3f
Tree: http://git-wip-us.apache.org/repos/asf/roller/tree/29adfd3f
Diff: http://git-wip-us.apache.org/repos/asf/roller/diff/29adfd3f

Branch: refs/heads/bootstrap-ui
Commit: 29adfd3f879998271de3d24b9bb8db225f828c87
Parents: 2f4b2b3
Author: Dave Johnson <sn...@gmail.com>
Authored: Mon Sep 5 17:30:10 2016 -0400
Committer: Dave Johnson <sn...@gmail.com>
Committed: Mon Sep 5 17:30:10 2016 -0400

----------------------------------------------------------------------
 .../weblogger/config/runtime/ConfigDef.java     |  29 ++-
 .../weblogger/config/runtime/PropertyDef.java   |   6 +-
 .../ui/struts2/admin/GlobalConfig.java          | 165 ++++++++-------
 .../resources/ApplicationResources.properties   |   8 +-
 .../weblogger/config/runtimeConfigDefs.xml      |   8 +-
 app/src/main/resources/struts.xml               |   1 +
 .../webapp/WEB-INF/jsps/admin/GlobalConfig.jsp  | 201 +++++++++++--------
 7 files changed, 250 insertions(+), 168 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/roller/blob/29adfd3f/app/src/main/java/org/apache/roller/weblogger/config/runtime/ConfigDef.java
----------------------------------------------------------------------
diff --git a/app/src/main/java/org/apache/roller/weblogger/config/runtime/ConfigDef.java b/app/src/main/java/org/apache/roller/weblogger/config/runtime/ConfigDef.java
index 7a19408..f7b583d 100644
--- a/app/src/main/java/org/apache/roller/weblogger/config/runtime/ConfigDef.java
+++ b/app/src/main/java/org/apache/roller/weblogger/config/runtime/ConfigDef.java
@@ -15,27 +15,27 @@
 * copyright in this work, please see the NOTICE file in the top level
 * directory of this distribution.
 */
-/*
- * ConfigDef.java
- *
- * Created on June 4, 2005, 1:10 PM
- */
-
 package org.apache.roller.weblogger.config.runtime;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+
 
 /**
  * Represents a logic grouping of runtime configuration properties.
  * Each ConfigDef may contain 0 or more DisplayGroups.
  *
+ * Created on June 4, 2005, 1:10 PM
  * @author Allen Gilliland
  */
 public class ConfigDef {
     
     private List<DisplayGroup> displayGroups = null;
     private String name = null;
+
+    Map<String, PropertyDef> propertyDefs = null;
     
     
     public ConfigDef() {
@@ -53,8 +53,8 @@ public class ConfigDef {
     public boolean removeDisplayGroup(DisplayGroup group) {
         return this.displayGroups.remove(group);
     }
-    
-    
+
+
     public String toString() {
         return name;
     }
@@ -74,5 +74,16 @@ public class ConfigDef {
     public void setName(String name) {
         this.name = name;
     }
-    
+
+    public PropertyDef getPropertyDef( String name ) {
+        if ( propertyDefs == null ) {
+            propertyDefs = new HashMap<>();
+            for (DisplayGroup displayGroup : getDisplayGroups()) {
+                for (PropertyDef propertyDef : displayGroup.getPropertyDefs()) {
+                    propertyDefs.put( propertyDef.getName(), propertyDef );
+                }
+            }
+        }
+        return propertyDefs.get( name );
+    }
 }

http://git-wip-us.apache.org/repos/asf/roller/blob/29adfd3f/app/src/main/java/org/apache/roller/weblogger/config/runtime/PropertyDef.java
----------------------------------------------------------------------
diff --git a/app/src/main/java/org/apache/roller/weblogger/config/runtime/PropertyDef.java b/app/src/main/java/org/apache/roller/weblogger/config/runtime/PropertyDef.java
index 636d754..032bd15 100644
--- a/app/src/main/java/org/apache/roller/weblogger/config/runtime/PropertyDef.java
+++ b/app/src/main/java/org/apache/roller/weblogger/config/runtime/PropertyDef.java
@@ -52,11 +52,15 @@ public class PropertyDef {
     public String toString() {
         return "["+name+","+key+","+type+","+defaultValue+","+rows+","+cols+"]";
     }
-    
+
     public String getName() {
         return name;
     }
 
+    public String getNameWithUnderbars() {
+        return name.replace(".", "_");
+    }
+
     public void setName(String name) {
         this.name = name;
     }

http://git-wip-us.apache.org/repos/asf/roller/blob/29adfd3f/app/src/main/java/org/apache/roller/weblogger/ui/struts2/admin/GlobalConfig.java
----------------------------------------------------------------------
diff --git a/app/src/main/java/org/apache/roller/weblogger/ui/struts2/admin/GlobalConfig.java b/app/src/main/java/org/apache/roller/weblogger/ui/struts2/admin/GlobalConfig.java
index 6f474b7..9ab3d79 100644
--- a/app/src/main/java/org/apache/roller/weblogger/ui/struts2/admin/GlobalConfig.java
+++ b/app/src/main/java/org/apache/roller/weblogger/ui/struts2/admin/GlobalConfig.java
@@ -18,11 +18,9 @@
 
 package org.apache.roller.weblogger.ui.struts2.admin;
 
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import javax.servlet.http.HttpServletRequest;
+
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -34,6 +32,7 @@ import org.apache.roller.weblogger.business.plugins.PluginManager;
 import org.apache.roller.weblogger.business.plugins.comment.WeblogEntryCommentPlugin;
 import org.apache.roller.weblogger.config.WebloggerRuntimeConfig;
 import org.apache.roller.weblogger.config.runtime.ConfigDef;
+import org.apache.roller.weblogger.config.runtime.PropertyDef;
 import org.apache.roller.weblogger.config.runtime.RuntimeConfigDefs;
 import org.apache.roller.weblogger.pojos.GlobalPermission;
 import org.apache.roller.weblogger.pojos.RuntimeConfigProperty;
@@ -47,21 +46,21 @@ import org.apache.struts2.interceptor.ServletRequestAware;
  * Action which handles editing of global configuration.
  */
 public class GlobalConfig extends UIAction implements ParameterAware, ServletRequestAware {
-    
+
     private static Log log = LogFactory.getLog(GlobalConfig.class);
-    
+
     // the request parameters
     private Map<String, String[]> params = Collections.emptyMap();
-    
+
     // map of config properties
     private Map<String, RuntimeConfigProperty> properties = Collections.emptyMap();
-    
+
     // the runtime config def used to populate the display
     private ConfigDef globalConfigDef = null;
-    
+
     // list of comment plugins
     private List<WeblogEntryCommentPlugin> pluginsList = Collections.emptyList();
-    
+
     // comment plugins that are enabled.  this is what the html form submits to
     private String[] commentPlugins = new String[0];
 
@@ -69,28 +68,30 @@ public class GlobalConfig extends UIAction implements ParameterAware, ServletReq
     // GET on the GlobalConfig!save URL and thus sets all checkboxes to false
     private String httpMethod = "GET";
 
+    private ResourceBundle bundle = ResourceBundle.getBundle("ApplicationResources");
+
     // weblogs for frontpage blog chooser
     private Collection<Weblog> weblogs;
 
-    
+
     public GlobalConfig() {
         this.actionName = "globalConfig";
         this.desiredMenu = "admin";
         this.pageTitle = "configForm.title";
     }
-    
-    
+
+
     @Override
     public boolean isWeblogRequired() {
         return false;
     }
-    
+
     @Override
     public List<String> requiredGlobalPermissionActions() {
         return Collections.singletonList(GlobalPermission.ADMIN);
     }
-    
-    
+
+
     /**
      * Prepare action by loading runtime properties map.
      */
@@ -104,9 +105,9 @@ public class GlobalConfig extends UIAction implements ParameterAware, ServletReq
             log.error("Error getting runtime properties map", ex);
             addError("Unexpected error accessing Roller properties");
         }
-        
+
         try {
-            WeblogManager mgr =  WebloggerFactory.getWeblogger().getWeblogManager();
+            WeblogManager mgr = WebloggerFactory.getWeblogger().getWeblogManager();
             setWeblogs(mgr.getWeblogs(true, null, null, null, 0, -1));
         } catch (WebloggerException ex) {
             log.error("Error getting weblogs", ex);
@@ -121,28 +122,28 @@ public class GlobalConfig extends UIAction implements ParameterAware, ServletReq
                 setGlobalConfigDef(configDef);
             }
         }
-        
+
         // load plugins list
         PluginManager pmgr = WebloggerFactory.getWeblogger().getPluginManager();
         setPluginsList(pmgr.getCommentPlugins());
     }
-    
-    
+
+
     /**
      * Display global properties editor form.
      */
     @Override
     public String execute() {
-        
+
         // setup array of configured plugins
         if (!StringUtils.isEmpty(WebloggerRuntimeConfig.getProperty("users.comments.plugins"))) {
             setCommentPlugins(StringUtils.split(WebloggerRuntimeConfig.getProperty("users.comments.plugins"), ","));
         }
-        
+
         return SUCCESS;
     }
-    
-    
+
+
     /**
      * Save global properties.
      */
@@ -150,71 +151,101 @@ public class GlobalConfig extends UIAction implements ParameterAware, ServletReq
         if (!"POST".equals(httpMethod)) {
             return ERROR;
         }
-        
+
         // only set values for properties that are already defined
         RuntimeConfigProperty updProp;
         String incomingProp;
         for (String propName : getProperties().keySet()) {
             updProp = getProperties().get(propName);
             incomingProp = this.getParameter(updProp.getName());
-            
-            log.debug("Checking property ["+propName+"]");
-            log.debug("Request value is ["+incomingProp+"]");
-            
-            // some special treatment for booleans
-            // this is a bit hacky since we are assuming that any prop
-            // with a value of "true" or "false" is meant to be a boolean
-            // it may not always be the case, but we should be okay for now
-            // null check below needed w/Oracle
-            if( updProp.getValue() != null
-                    && (updProp.getValue().equals("true") || updProp.getValue().equals("false"))) {
-                
-                if(incomingProp == null || !incomingProp.equals("on")) {
-                    incomingProp = "false";
+
+            log.debug("Checking property [" + propName + "]");
+            log.debug("Request value is [" + incomingProp + "]");
+
+            PropertyDef propertyDef = globalConfigDef.getPropertyDef( propName );
+            if ( propertyDef == null) {
+                // we're only processing defined properties, i.e. ones shown in the UI
+                continue;
+            }
+
+            if ( propertyDef.getType().equals("boolean") ) {
+
+                try {
+                    Boolean.parseBoolean(incomingProp);
+                    updProp.setValue(incomingProp);
+                } catch ( Exception nfe ) {
+                    String propDesc = bundle.getString( propertyDef.getKey() );
+                    addError("ConfigForm.invalidBooleanProperty",
+                            Arrays.asList( new Object[] { propDesc, propName } ));
                 }
-                else {
-                    incomingProp = "true";
+
+            } else if ( propertyDef.getType().equals("integer") ) {
+
+                try {
+                    Integer.parseInt(incomingProp);
+                    updProp.setValue(incomingProp);
+                } catch ( NumberFormatException nfe ) {
+                    String propDesc = bundle.getString( propertyDef.getKey() );
+                    addError("ConfigForm.invalidIntegerProperty",
+                            Arrays.asList( new Object[] { propDesc, propName } ));
                 }
+
+            } else if ( propertyDef.getType().equals("float") ) {
+
+                try {
+                    Float.parseFloat(incomingProp);
+                    updProp.setValue(incomingProp);
+                } catch ( NumberFormatException nfe ) {
+                    String propDesc = bundle.getString( propertyDef.getKey() );
+                    addError("ConfigForm.invalidFloatProperty",
+                            Arrays.asList( new Object[] { propDesc, propName } ));
+                }
+
+            } else if ( incomingProp != null ){
+                updProp.setValue( incomingProp.trim() );
+
+            } else if ( propertyDef.getName().equals("users.comments.plugins") ) {
+                // not a problem
+
+            } else {
+                addError("ConfigForm.invalidProperty", propName);
             }
-            
-            // only work on props that were submitted with the request
-            if(incomingProp != null) {
-                log.debug("Setting new value for ["+propName+"]");
-                
-                // NOTE: the old way had some locale sensitive way to do this??
-                updProp.setValue(incomingProp.trim());
-            }
+
         }
-        
+
+        if ( this.hasActionErrors() ) {
+            return ERROR;
+        }
+
         // special handling for comment plugins
         String enabledPlugins = "";
-        if(getCommentPlugins().length > 0) {
+        if (getCommentPlugins().length > 0) {
             enabledPlugins = StringUtils.join(getCommentPlugins(), ",");
         }
         RuntimeConfigProperty prop = getProperties().get("users.comments.plugins");
         prop.setValue(enabledPlugins);
-            
+
         try {
             // save 'em and flush
             PropertiesManager mgr = WebloggerFactory.getWeblogger().getPropertiesManager();
             mgr.saveProperties(getProperties());
             WebloggerFactory.getWeblogger().flush();
-            
+
             // notify user of our success
             addMessage("generic.changes.saved");
-            
+
         } catch (WebloggerException ex) {
             log.error("Error saving roller properties", ex);
             addError("generic.error.check.logs");
         }
-                
+
         return SUCCESS;
     }
-    
-    
+
+
     public void setParameters(Map<String, String[]> parameters) {
         this.params = parameters;
-        
+
         if (log.isDebugEnabled()) {
             log.debug("Parameter map:");
 
@@ -223,18 +254,18 @@ public class GlobalConfig extends UIAction implements ParameterAware, ServletReq
             }
         }
     }
-    
+
     // convenience method for getting a single parameter as a String
     private String getParameter(String key) {
-        
+
         String[] p = this.params.get(key);
-        if(p != null && p.length > 0) {
+        if (p != null && p.length > 0) {
             return p[0];
         }
         return null;
     }
-    
-    
+
+
     public Map<String, RuntimeConfigProperty> getProperties() {
         return properties;
     }
@@ -250,7 +281,7 @@ public class GlobalConfig extends UIAction implements ParameterAware, ServletReq
     public void setGlobalConfigDef(ConfigDef globalConfigDef) {
         this.globalConfigDef = globalConfigDef;
     }
-    
+
     public List<WeblogEntryCommentPlugin> getPluginsList() {
         return pluginsList;
     }
@@ -258,7 +289,7 @@ public class GlobalConfig extends UIAction implements ParameterAware, ServletReq
     public void setPluginsList(List<WeblogEntryCommentPlugin> pluginsList) {
         this.pluginsList = pluginsList;
     }
-    
+
     public String[] getCommentPlugins() {
         return commentPlugins.clone();
     }
@@ -270,7 +301,7 @@ public class GlobalConfig extends UIAction implements ParameterAware, ServletReq
     public void setServletRequest(HttpServletRequest req) {
         httpMethod = req.getMethod();
     }
-    
+
     public Collection<Weblog> getWeblogs() {
         return weblogs;
     }

http://git-wip-us.apache.org/repos/asf/roller/blob/29adfd3f/app/src/main/resources/ApplicationResources.properties
----------------------------------------------------------------------
diff --git a/app/src/main/resources/ApplicationResources.properties b/app/src/main/resources/ApplicationResources.properties
index 4d7fdbb..c16d9aa 100644
--- a/app/src/main/resources/ApplicationResources.properties
+++ b/app/src/main/resources/ApplicationResources.properties
@@ -349,7 +349,7 @@ configForm.ignoreSpamComments=Don''t save comments thought to be spam
 configForm.enableTrackbacks=Allow weblog trackbacks?
 configForm.ignoreSpamTrackbacks=Don''t save trackbacks thought to be spam
 configForm.commentHtmlAllowed=Allow html in comments?
-configForm.commentPlugins=Enabled/Disable comment formatting plugins
+configForm.commentPlugins=Enabled comment formatting plugins
 configForm.emailComments=Allow email notification of comments?
 configForm.moderationRequired=Require comment moderation for all weblogs
 configForm.enableTrackbackValidation=Enable verification of trackback links?
@@ -1139,6 +1139,10 @@ ConfigForm.proxyPort=Proxy port for feed fetcher
 ConfigForm.message.saveSucceeded=Saved Planet configuration
 ConfigForm.error.saveFailed=Error saving Planet configuration
 
+ConfigForm.invalidBooleanProperty=Property {0} must be a boolean: {1}
+ConfigForm.invalidIntegerProperty=Property {0} must be an integer: {1}
+ConfigForm.invalidFloatProperty=Property {0} must be a float: {1}
+ConfigForm.invalidProperty=Property {0} is null
 
 # ----------------------------------------------------- PlanetSubscriptions.jsp
 
@@ -1793,7 +1797,7 @@ yourWebsites.createWeblog=Create new weblog
 yourWebsites.createWeblog.desc=\
 Feel like you''ve got more to say? Maybe another weblog is what you need. 
 
-yourWebsites.editProfile=Update your user info including password, email, locale and timezone.
+yourWebsites.editProfile=Your Profile
 yourWebsites.editProfile.desc=Change user info, password, timezone
 
 yourWebsites.oauthKeys=OAuth Credentials

http://git-wip-us.apache.org/repos/asf/roller/blob/29adfd3f/app/src/main/resources/org/apache/roller/weblogger/config/runtimeConfigDefs.xml
----------------------------------------------------------------------
diff --git a/app/src/main/resources/org/apache/roller/weblogger/config/runtimeConfigDefs.xml b/app/src/main/resources/org/apache/roller/weblogger/config/runtimeConfigDefs.xml
index 8c84ba6..a55f7f1 100644
--- a/app/src/main/resources/org/apache/roller/weblogger/config/runtimeConfigDefs.xml
+++ b/app/src/main/resources/org/apache/roller/weblogger/config/runtimeConfigDefs.xml
@@ -136,11 +136,11 @@
    <display-group name="weblogSettings" key="configForm.weblogSettings" >
        
       <property-def  name="site.pages.maxEntries"  key="configForm.pageMaxEntries">
-         <type>string</type>
+         <type>integer</type>
          <default-value>30</default-value>
       </property-def>
       <property-def  name="site.newsfeeds.defaultEntries"  key="configForm.newsfeedMaxEntries">
-         <type>string</type>
+         <type>integer</type>
          <default-value>30</default-value>
       </property-def>
       <property-def  name="site.newsfeeds.styledFeeds"  key="configForm.styledFeeds">
@@ -210,11 +210,11 @@
          <default-value>exe</default-value>
       </property-def>
       <property-def  name="uploads.file.maxsize"  key="configForm.maxFileSize">
-         <type>string</type>
+         <type>float</type>
          <default-value>2.00</default-value>
       </property-def>
       <property-def  name="uploads.dir.maxsize"  key="configForm.maxDirSize">
-         <type>string</type>
+         <type>float</type>
          <default-value>20.00</default-value>
       </property-def>
 

http://git-wip-us.apache.org/repos/asf/roller/blob/29adfd3f/app/src/main/resources/struts.xml
----------------------------------------------------------------------
diff --git a/app/src/main/resources/struts.xml b/app/src/main/resources/struts.xml
index fd24563..8020f7c 100644
--- a/app/src/main/resources/struts.xml
+++ b/app/src/main/resources/struts.xml
@@ -162,6 +162,7 @@
         <action name="globalConfig!*" method="{1}"
                 class="org.apache.roller.weblogger.ui.struts2.admin.GlobalConfig">
             <result name="success" type="tiles">.GlobalConfig</result>
+            <result name="error" type="tiles">.GlobalConfig</result>
         </action>
                
         <action name="userAdmin"

http://git-wip-us.apache.org/repos/asf/roller/blob/29adfd3f/app/src/main/webapp/WEB-INF/jsps/admin/GlobalConfig.jsp
----------------------------------------------------------------------
diff --git a/app/src/main/webapp/WEB-INF/jsps/admin/GlobalConfig.jsp b/app/src/main/webapp/WEB-INF/jsps/admin/GlobalConfig.jsp
index 6856b6b..af3e2fc 100644
--- a/app/src/main/webapp/WEB-INF/jsps/admin/GlobalConfig.jsp
+++ b/app/src/main/webapp/WEB-INF/jsps/admin/GlobalConfig.jsp
@@ -17,96 +17,127 @@
 --%>
 <%@ include file="/WEB-INF/jsps/taglibs-struts2.jsp" %>
 
-<p class="subtitle"><s:text name="configForm.subtitle" /></p>
-<p><s:text name="configForm.prompt" /></p>
+<p class="subtitle"><s:text name="configForm.subtitle"/></p>
+<p><s:text name="configForm.prompt"/></p>
 
-<s:form action="globalConfig!save">
-	<s:hidden name="salt" />
 
-    <table class="formtableNoDesc">
-    
+<s:form action="globalConfig!save" theme="bootstrap" cssClass="form-horizontal">
+
+    <s:hidden name="salt"/>
+
     <s:iterator id="dg" value="globalConfigDef.displayGroups">
-    
-        <tr>
-            <td colspan="3"><h2><s:text name="%{#dg.key}" /></h2></td>
-        </tr>
-    
+
+        <h2><s:text name="%{#dg.key}"/></h2>
+
         <s:iterator id="pd" value="#dg.propertyDefs">
-            
-            <tr>
-                <td class="label"><s:text name="%{#pd.key}" /></td>
-                
-                  <%-- special condition for comment plugins --%>
-                  <s:if test="#pd.name == 'users.comments.plugins'">
-                      <td class="field"><s:checkboxlist theme="roller" list="pluginsList"
-                        name="commentPlugins" listKey="id" listValue="name" /></td>
-                  </s:if>
-
-                  <%-- special condition for front page blog --%>
-                  <s:elseif test="#pd.name == 'site.frontpage.weblog.handle'">
-                      <td class="field">
-                          <select name='<s:property value="#pd.name"/>'>
-                                <option value=''>
-                                    <s:text name="configForm.none" />
-                                </option>                              <s:iterator id="weblog" value="weblogs">
-                                <option value='<s:property value="#weblog.handle"/>'
-                                    <s:if test='properties[#pd.name].value == #weblog.handle'>selected='true'</s:if> >
-                                    <s:property value="#weblog.name"/>
-                                </option>
-                              </s:iterator>
-                          </select>
-                      </td>
-                  </s:elseif>
-
-                  <%-- "string" type means use a simple textbox --%>
-                  <s:elseif test="#pd.type == 'string'">
-                    <td class="field"><input type="text" name='<s:property value="#pd.name"/>'
-                        value='<s:property value="properties[#pd.name].value"/>' size="35" /></td>
-                  </s:elseif>
-                  
-                  <%-- "text" type means use a full textarea --%>
-                  <s:elseif test="#pd.type == 'text'">
-                    <td class="field">
-                      <textarea name='<s:property value="#pd.name"/>'
-                                rows="<s:property value="#pd.rows"/>"
-                                cols="<s:property value="#pd.cols"/>"><s:property value="properties[#pd.name].value"/>
-                      </textarea>
-                    </td>
-                  </s:elseif>
-                  
-                  <%-- "boolean" type means use a checkbox --%>
-                  <s:elseif test="#pd.type == 'boolean'">
-                      <s:if test="properties[#pd.name].value == 'true'">
-                          <td class="field"><input type="checkbox" 
-                            name='<s:property value="#pd.name"/>' CHECKED></td>
-                      </s:if>
-                      <s:else>
-                          <td class="field"><input type="checkbox"
-                            name='<s:property value="#pd.name"/>'></td>
-                      </s:else>
-                  </s:elseif>
-                  
-                  <%-- if it's something we don't understand then use textbox --%>
-                  <s:else>
-                    <td class="field"><input type="text"
-                        name='<s:property value="#pd.name"/>' size="50" /></td>
-                  </s:else>
-                
-                <td class="description"><%-- <s:text name="" /> --%></td>
-            </tr>
-          
+
+            <%-- special condition for comment plugins --%>
+            <s:if test="#pd.name == 'users.comments.plugins'">
+                <s:checkboxlist label="%{getText(#pd.key)}" name="commentPlugins"
+                                list="pluginsList" listKey="id" listValue="name"/>
+            </s:if>
+
+            <%-- special condition for front page blog --%>
+            <s:elseif test="#pd.name == 'site.frontpage.weblog.handle'">
+                <s:select name="%{#pd.name}" label="%{getText(#pd.key)}"
+                          list="weblogs" listValue="name"/>
+            </s:elseif>
+
+            <%-- "string" type means use a simple textbox --%>
+            <s:elseif test="#pd.type == 'string'">
+                <s:textfield name="%{#pd.name}" label="%{getText(#pd.key)}" size="35"
+                             value="%{properties[#pd.name].value}"/>
+            </s:elseif>
+
+            <%-- "text" type means use a full textarea --%>
+            <s:elseif test="#pd.type == 'text'">
+                <s:textarea name="%{#pd.name}" label="%{getText(#pd.key)}" rows="#pd.rows" cols="#pd.cols"
+                            value="%{properties[#pd.name].value}"/>
+            </s:elseif>
+
+            <%-- "boolean" type means use a checkbox --%>
+            <s:elseif test="#pd.type == 'boolean'">
+                <s:if test="properties[#pd.name].value == 'true'">
+                    <s:checkbox name="%{#pd.name}" label="%{getText(#pd.key)}" checked="checked" />
+                </s:if>
+                <s:if test="properties[#pd.name].value == 'false'">
+                    <s:checkbox name="%{#pd.name}" label="%{getText(#pd.key)}" />
+                </s:if>
+            </s:elseif>
+
+            <s:elseif test="#pd.type == 'integer'">
+                <div class="form-group ">
+                    <label class="col-sm-3 control-label"
+                        for='globalConfig_<s:property value="#pd.nameWithUnderbars" />'>
+                        <s:text name="%{#pd.key}"/>
+                    </label>
+                    <div class="col-sm-9 controls">
+                        <input type="number" name='<s:property value="#pd.name" />'
+                            size="35" value="30" id='globalConfig_<s:property value="#pd.nameWithUnderbars" />'
+                            class="form-control integer" onkeyup="formChanged()" />
+                    </div>
+                </div>
+            </s:elseif>
+
+            <s:elseif test="#pd.type == 'float'">
+                <div class="form-group ">
+                    <label class="col-sm-3 control-label"
+                        for='globalConfig_<s:property value="#pd.nameWithUnderbars" />'>
+                        <s:text name="%{#pd.key}"/>
+                    </label>
+                    <div class="col-sm-9 controls">
+                        <input type="number" name='<s:property value="#pd.name" />'
+                            size="35" value="30" id='globalConfig_<s:property value="#pd.nameWithUnderbars" />'
+                            class="form-control float" onkeyup="formChanged()" />
+                    </div>
+                </div>
+            </s:elseif>
+
+            <%-- if it's something we don't understand then use textbox --%>
+            <s:else>
+                <s:textfield name="%{#pd.name}" label="%{getText(#pd.key)}" size="35"
+                             value="%{properties[#pd.name].value}" />
+            </s:else>
+
         </s:iterator>
-      
-        <tr>
-            <td colspan="2">&nbsp;</td>
-        </tr>
-        
     </s:iterator>
 
-    </table>
-    
-    <div class="control">
-        <input class="buttonBox" type="submit" value="<s:text name="generic.save"/>"/>
-    </div>
-    
+    <input id="saveButton" class="btn" type="submit" value="<s:text name="generic.save"/>"/>
+
 </s:form>
+
+
+<script type="text/javascript">
+
+    function formChanged() {
+
+        var saveBookmarkButton = $('#saveButton:first');
+
+        var error = false;
+
+        $("input").each( function() {
+
+            var isInteger = $(this).hasClass("integer");
+
+            if ( $(this).attr("type") == "number") {
+
+                if ( isNaN( this.valueAsNumber )) {
+                    $(this).css("background", "#FBB")
+                    error = true;
+
+                } else if ( isInteger && !Number.isInteger( this.valueAsNumber ) ) {
+                    $(this).css("background", "#FBB")
+                    error = true;
+
+                } else {
+                    $(this).css("background", "white")
+                }
+            }
+
+        });
+
+        saveBookmarkButton.attr("disabled", error );
+    }
+
+</script>
+