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 <[email protected]> Authored: Mon Sep 5 17:30:10 2016 -0400 Committer: Dave Johnson <[email protected]> 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"> </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> +
