### Eclipse Workspace Patch 1.0
#P Ametys2 - 01 Runtime LTE
Index: main/kernel/src/org/ametys/runtime/config/ConfigManager.java
===================================================================
--- main/kernel/src/org/ametys/runtime/config/ConfigManager.java	(revision 26551)
+++ main/kernel/src/org/ametys/runtime/config/ConfigManager.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2012 Anyware Services
+ *  Copyright 2014 Anyware Services
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -564,10 +564,10 @@
                 for (ConfigParameter param : group.getParams())
                 {
                     Object value = _getValue (param.getId(), param.getType(), untypedValues);
+                        
                     ParameterHelper.toSAXParameter (handler, param, value);
                 }
                 XMLUtils.endElement(handler, "parameters");
-                
 
                 XMLUtils.endElement(handler, "group");
             }
@@ -583,7 +583,21 @@
     private Object _getValue (String paramID, ParameterType type, Map<String, String> untypedValues)
     {
         final String unverifiedUntypedValue = untypedValues.get(paramID);
-        Object typedValue = ParameterHelper.castValue(unverifiedUntypedValue, type);
+        
+        Object typedValue;
+        
+        if (unverifiedUntypedValue == null)
+        {
+            typedValue = null;
+        }
+        else if (StringUtils.isEmpty(unverifiedUntypedValue))
+        {
+            typedValue = "";
+        }
+        else 
+        {
+            typedValue = ParameterHelper.castValue(unverifiedUntypedValue, type);
+        }
         
         if (type.equals(ParameterType.PASSWORD) && typedValue != null && ((String) typedValue).length() > 0)
         {
@@ -595,7 +609,7 @@
 
 
     /**
-     * Update the configuartion file with the given values<br>
+     * Update the configuration file with the given values<br>
      * Values are untyped (all are of type String) and might be null.
      * @param untypedValues A map (key, untyped value).
      * @param fileName the config file absolute path
@@ -627,6 +641,7 @@
             String untypedValue = untypedValues.get(id);
             
             Object typedValue = ParameterHelper.castValue(untypedValue, _params.get(id).getType());
+            
             if (typedValue == null && _params.get(id).getType() == ParameterType.PASSWORD)
             {
                 if (Config.getInstance() != null)
Index: main/kernel/src/org/ametys/runtime/util/parameter/ParameterHelper.java
===================================================================
--- main/kernel/src/org/ametys/runtime/util/parameter/ParameterHelper.java	(revision 26551)
+++ main/kernel/src/org/ametys/runtime/util/parameter/ParameterHelper.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2012 Anyware Services
+ *  Copyright 2014 Anyware Services
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -108,6 +108,7 @@
      */
     public static Object castValue(String value, ParameterType type)
     {
+        
         if (value == null)
         {
             return null;
@@ -129,7 +130,7 @@
             }
             else if (type == ParameterType.LONG)
             {
-                return new Long(value);
+                return  new Long(value);
             }
             else if (type == ParameterType.DOUBLE)
             {
@@ -228,10 +229,6 @@
         {
             XMLUtils.createElement(handler, "value", ParameterHelper.valueToString(value));
         }
-        else if (defaultValue != null)
-        {
-            XMLUtils.createElement(handler, "value", ParameterHelper.valueToString(defaultValue));
-        }
         
         if (parameter.getWidget() != null)
         {
Index: main/plugin-core/pages/administrator/config/edit.xsl
===================================================================
--- main/plugin-core/pages/administrator/config/edit.xsl	(revision 26551)
+++ main/plugin-core/pages/administrator/config/edit.xsl	(working copy)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
-   Copyright 2012 Anyware Services
+   Copyright 2014 Anyware Services
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -72,7 +72,7 @@
                                          <xsl:text>, </xsl:text>
                                          [ <xsl:for-each select="parameters/*[not(local-name() = ../../group-switch)]">
                                             <xsl:if test="position() != 1">, </xsl:if> "<xsl:value-of select="local-name()"/>"
-                                         </xsl:for-each> ]
+                                           </xsl:for-each> ]
 							         </xsl:if>
 							);
 							
@@ -82,11 +82,19 @@
 									   <xsl:if test="$switch-value != ''">, "<xsl:value-of select="escape:escapeJavaScript($switch-value)"/>"</xsl:if>
 									);
 							</xsl:for-each>
-							
 							Ametys.plugins.core.administration.Config._form.add(fieldSet);
+				            <xsl:if test="group-switch">
+				                <xsl:variable name="switch-value" select="parameters/*[local-name() = ../../group-switch]/value"/>
+				               Ametys.plugins.core.administration.Config.initGroupCategory(
+                                 <xsl:call-template name="create-config"><xsl:with-param name="config" select="parameters/*[local-name() = ../../group-switch]"/></xsl:call-template>
+                                 <xsl:text>, </xsl:text>
+                                 [ <xsl:for-each select="parameters/*[not(local-name() = ../../group-switch)]">
+                                    <xsl:if test="position() != 1">, </xsl:if> "<xsl:value-of select="local-name()"/>"
+                                   </xsl:for-each> ]
+                                );                            
+                           </xsl:if>
 						</xsl:for-each>
 					</xsl:for-each>
-						
 					function createPanel()
 					{
 						return panel;
@@ -101,11 +109,18 @@
     
     <xsl:template name="create-config">
         <xsl:param name="config" select="."/>
-
         {
             type: "<xsl:value-of select="$config/type"/>",
             name: "<xsl:value-of select="local-name($config)"/>",
-            value: "<xsl:value-of select="escape:escapeJavaScript(value)"/>",
+            value: 
+            <xsl:choose> 
+	            <xsl:when test="$config/value">
+	                "<xsl:value-of select="escape:escapeJavaScript($config/value)"/>"
+	            </xsl:when>
+	            <xsl:otherwise>
+	               "<xsl:value-of select="escape:escapeJavaScript($config/defaultValue)"/>"
+	            </xsl:otherwise>
+            </xsl:choose>,
             label: "<xsl:copy-of select="$config/label/node()"/>", 
             description: "<xsl:copy-of select="$config/description/node()"/>",
             enumeration: <xsl:call-template name="enumeration"><xsl:with-param name="config" select="$config"/></xsl:call-template>,
Index: main/plugin-core/plugin.xml
===================================================================
--- main/plugin-core/plugin.xml	(revision 26551)
+++ main/plugin-core/plugin.xml	(working copy)
@@ -1021,8 +1021,6 @@
         	</extension>
         </extensions>
     </feature>
-    
-    
 
     <feature name="runtime.admin">
         <!-- 
Index: main/plugin-core/resources/css/administrator/config.css
===================================================================
--- main/plugin-core/resources/css/administrator/config.css	(revision 26551)
+++ main/plugin-core/resources/css/administrator/config.css	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2012 Anyware Services
+ *  Copyright 2014 Anyware Services
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -52,5 +52,5 @@
 	font-size: 0.9em;
 
     line-height: 16px;
-	padding : 0 0 10px 39px;
+	padding : 0 0 0 35px;
 }
Index: main/plugin-core/resources/js/Ametys/plugins/core/administration/Config.i18n.js
===================================================================
--- main/plugin-core/resources/js/Ametys/plugins/core/administration/Config.i18n.js	(revision 26551)
+++ main/plugin-core/resources/js/Ametys/plugins/core/administration/Config.i18n.js	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2012 Anyware Services
+ *  Copyright 2014 Anyware Services
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -39,7 +39,7 @@
 	* @private
 	 */
 	_fields: [],
-
+	
 	/**
 	 * @property {Ext.form.FormPanel} _form The configuration form
 	 * @private
@@ -73,6 +73,13 @@
 	 * @property {Object} _widgets The registered widgets for the config screen. The widgets are js class name for widgets.
 	 * @private
 	 */
+	
+	 /**
+     * @cfg {String} blankText
+     * The error text to display if the validation fails
+     */
+     _blankText : 'This field is required',
+     
 	_widgets: {
 		'hour': 'Ametys.plugins.core.administrator.Config.HourField',
 		'time': 'Ametys.plugins.core.administrator.Config.TimeField',
@@ -119,7 +126,7 @@
 			border: false,
 			autoScroll: true,
 			width: 277,
-		    
+			
 			items: [this._drawNavigationPanel (),
 			        this._drawHandlePanel (),
 			        this._drawHelpPanel ()]
@@ -175,29 +182,40 @@
 			width: Ametys.plugins.core.administration.Config.LABEL_WIDTH 
 					+ Ametys.plugins.core.administration.Config.FIELD_WIDTH 
 					+ 100
-		});
+		});15
 	},
 
 	/**
+	 * Initialize the display of a group ( hidden or shown )
+	 * @param {Object} switcher If the group can be switch on/off, this object have the following keys : type, name, label, description, widget, mandatory, regexp and optionally invalidText.
+	 * @param {String[]} elements An array containing the names of the fields the switcher will show/hide 
+	 */	
+	initGroupCategory: function(switcher, elements) {
+		
+		var checked = (switcher.value === 'true');
+		
+		this._showHideGroup(null, checked, null, elements, null);
+	},
+	
+	/**
 	 * @private
 	 * Show or hide the elements of a group
 	 * @param {HTMLElement} input The changed input 
 	 * @param {String} newValue The new value of the input 
 	 * @param {String} oldValue The preceding value of the input
 	 * @param {Object} eOpts The events options 
-	 * @param {String[]} elements The html names of the elements to show of hide
+	 * @param {String[]} elements The html names of the elements to show or hide
 	 */
-	showHideGroup: function(input, newValue, oldValue, eOpts, elements)
+	_showHideGroup: function(input, newValue, oldValue, elements, eOpts)
 	{
 		for (var i = 0; i < elements.length; i++)
 		{
-			var elementId = elements[i];
-			this._form.getForm().findField(elementId).setVisible(newValue);
+			this._form.getForm().findField(elements[i]).setVisible(newValue);
 		}
 	},
-	
+
 	/**
-	 * Add a groups category
+	 * Add a group category
 	 * @param {Ext.form.FieldSet} fd The fieldset where to add the category
 	 * @param {String} name The name of the category
 	 * @param {Object} switcher If the group can be switch on/off, this object have the following keys : type, name, label, description, widget, mandatory, regexp and optionally invalidText.
@@ -210,8 +228,10 @@
 			var modifiedSwitcher = Ext.applyIf ({label: '', description: '', width: 'auto'}, switcher);
 			
 			var input = this.createInputField(modifiedSwitcher);
-			input.on('change', Ext.bind(this.showHideGroup, this, [subitems], true));
 			
+			// Appending a listener to the switcher
+			input.on('change', Ext.bind(this._showHideGroup, this, [subitems], true));
+
 			var items = [
 			      input,
 			      { baseCls: '', html: "<label for='" + input.getInputId() + "'>" + name + "</label>", padding: "3 0 0 5" }
@@ -224,6 +244,7 @@
 				    baseCls: '', 
 				    padding: "3 0 0 5",
 					html: '  ',
+					width: 20,
 					listeners: {
 						'render': function() {
 						    new Ext.ToolTip({
@@ -287,50 +308,56 @@
 	
 	/**
 	 * Creates and return an input field depending on the given configuration
-	 * @param {Object} config this object have the following keys : type, name, value, label, description, enumeration widget, mandatory, regexp and optionally invalidText and width. See config in {#addInputField}
+	 * @param {Object} config this object have the following keys : type, name, value, label, description, enumeration widget, mandatory, regexp and optionally invalidText, width and switchable. See config in {#addInputField}
 	 * @return {Ext.form.field.Field} The created field
 	 * @private
 	 */
 	createInputField: function(config)
 	{
+		
 	    var field = null;
 	    
 		if (config.enumeration != null)
 		{
 		    field = this._createTextField (config.name, config.value, config.label, config.description, config.enumeration, config.mandatory == 'true', null, config.width);
 		}
+		
 		else
 		{
+			if (config.mandatory)
+			{
+				config.label = '* ' + config.label;
+			}
+			
 			switch (config.type) 
 			{
 				case 'double':
-					field = this._createDoubleField (config.name, config.value, config.label, config.description, config.mandatory == 'true', config.regexp != '' ? new RegExp (config.regexp) : null, config.invalidText, config.width);
+					field = this._createDoubleField (config.name, config.value, config.label, config.description, config.mandatory == 'true', config.regexp != '' ? new RegExp (config.regexp) : null, config.invalidText, config.width, config.switchable);
 					break;
 				case 'long':
-					field = this._createLongField (config.name, config.value, config.label, config.description, config.mandatory == 'true', config.regexp != '' ? new RegExp (config.regexp) : null, config.invalidText, config.width);
+					field = this._createLongField (config.name, config.value, config.label, config.description, config.mandatory == 'true', config.regexp != '' ? new RegExp (config.regexp) : null, config.invalidText, config.width, config.switchable);
 					break;
 				case 'password':
-					field = this._createPasswordField (config.name, config.value, config.label, config.description, config.mandatory == 'true', config.width);
+					field = this._createPasswordField (config.name, config.value, config.label, config.description, config.mandatory == 'true', config.width, config.switchable);
 					break;
 				case 'date':
-					field = this._createDateField (config.name, config.value, config.label, config.description, config.mandatory == 'true', config.regexp != '' ? new RegExp (config.regexp) : null, config.invalidText, config.width);
+					field = this._createDateField (config.name, config.value, config.label, config.description, config.mandatory == 'true', config.regexp != '' ? new RegExp (config.regexp) : null, config.invalidText, config.width, config.switchable);
 					break;
 				case 'boolean':
-					field = this._createBooleanField (config.name, config.value, config.label, config.description, config.width);
+					field = this._createBooleanField (config.name, config.value, config.label, config.description, config.width, config.switchable);
 					break;
 				default:
 					if (config.widget != '')
 					{
-						 field = this._createWidgetField(config.widget, config.name, config.value, config.label, config.description, config.mandatory == 'true', config.regexp != '' ? new RegExp (config.regexp) : null, config.invalidText, config.width);
+						 field = this._createWidgetField(config.widget, config.name, config.value, config.label, config.description, config.mandatory == 'true', config.regexp != '' ? new RegExp (config.regexp) : null, config.invalidText, config.width, config.switchable);
 					}
 					else
 					{
-						field = this._createTextField (config.name, config.value, config.label, config.description, null, config.mandatory == 'true', config.regexp != '' ? new RegExp (config.regexp) : null, config.invalidText, config.width);
+						field = this._createTextField (config.name, config.value, config.label, config.description, null, config.mandatory == 'true', config.regexp != '' ? new RegExp (config.regexp) : null, config.invalidText, config.width, config.switchable);
 					}
 					break;
 			}
 		}
-		
 		return field;
 	},
 
@@ -342,22 +369,41 @@
 	 * @param {String} label The label of the field
 	 * @param {String} description The associated description
 	 * @param {Boolean} mandatory True if the field can not be empty
-	 * @param {String} regexp The regexp to use to validate the field value
+	 * @param {String} regexp The regular expression to use to validate the field value
 	 * @param {String} invalidText The text to display when the field value is not valid
-	 * @param {String/Number} width The optionnal width. The default one is the constants FIELD_WIDTH + LABEL_WIDTH
+	 * @param {String/Number} width The optional width. The default one is the constants FIELD_WIDTH + LABEL_WIDTH
 	 * @return {Ext.form.field.Field} The created field
 	 */
 	_createDoubleField: function (name, value, label, description, mandatory, regexp, invalidText, width)
 	{
+		var me = this;
+		
 		return new Ext.form.field.Number({
 			name: name,
 	        fieldLabel: label,
 	        ametysDescription: description,
 	        
 	        value: value,
-	        allowBlank: !mandatory,
-	        regex: regexp,
-			regexText: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_CONFIG_INVALID_REGEXP'/>" + regexp,
+	        validator: function(value)
+	        {	
+	        	var enabled = me._form.getForm().findField(name).isVisible() && !me._form.getForm().findField(name).isDisabled();
+	    		if(!enabled)
+	    		{
+	    			return true;
+	    		}
+	    		
+	      		// Otherwise the field has to be checked
+	    		{
+	    			var trimmed =  Ext.String.trim(value);
+	    			
+	    	        if (trimmed.length < 1 || (value === this.emptyText && this.valueContainsPlaceholder)) {
+	    	        	if (mandatory) {
+	    	                return me._blankText;
+	    	            }
+	    	        }
+	    	        return true;
+	    		}
+	        },
 			invalidText: invalidText != null ? invalidText : null,
 	        msgTarget: 'side',
 	        
@@ -383,6 +429,8 @@
 	 */
 	_createLongField: function (name, value, label, description, mandatory, regexp, invalidText, width)
 	{
+		var me = this;
+
 		return new Ext.form.field.Number ({
 			name: name,
 			fieldLabel: label,
@@ -391,9 +439,27 @@
 	        allowDecimals: false,
 	        
 	        value: value,
-	        allowBlank: !mandatory,
-	        regex: regexp,
-			regexText: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_CONFIG_INVALID_REGEXP'/>" + regexp,
+	        validator: function(value)
+	        {	
+	        	var enabled = me._form.getForm().findField(name).isVisible() && !me._form.getForm().findField(name).isDisabled();
+	        	if(!enabled)
+	    		{
+	    			return true;
+	    		}
+	    		
+	      		// Otherwise the field has to be checked
+	    		{
+	    			var trimmed =  Ext.String.trim(value);
+	    			
+	    	        if (trimmed.length < 1 || (value === this.emptyText && this.valueContainsPlaceholder)) {
+	    	        	if (mandatory) {
+	    	                return me._blankText;
+	    	            }
+	    	        }
+	    	        return true;
+	    		}
+	        },
+	        
 			invalidText: invalidText != null ? invalidText : null,
 			msgTarget: 'side',
 			
@@ -417,13 +483,34 @@
 	 */
 	_createPasswordField: function (name, value, label, description, mandatory, width)
 	{
+		var me = this;
+		
 		return new Ametys.form.field.ChangePassword({
 			name: name,
 			fieldLabel: label,
 		    ametysDescription: description,
 		    
-	        allowBlank: !mandatory,
 		    value: value,
+		    validator: function(value)
+	        {	
+		    	var enabled = me._form.getForm().findField(name).isVisible() && !me._form.getForm().findField(name).isDisabled();
+		    	if(!enabled)
+	    		{
+	    			return true;
+	    		}
+	    		
+	      		// Otherwise the field has to be checked
+	    		{
+	    			var trimmed =  Ext.String.trim(value);
+	    			
+	    	        if (trimmed.length < 1 || (value === this.emptyText && this.valueContainsPlaceholder)) {
+	    	        	if (mandatory) {
+	    	                return me._blankText;
+	    	            }
+	    	        }
+	    	        return true;
+	    		}
+	        },
 		    
 			labelAlign: 'right',
 	        labelWidth: Ametys.plugins.core.administration.Config.LABEL_WIDTH,
@@ -448,6 +535,7 @@
 	 */
 	_createDateField: function (name, value, label, description, mandatory, regexp, invalidText, width)
 	{
+		var me = this;
 	    var dateValue = value;
 //	    if (typeof value == 'string')
 //	    {
@@ -462,9 +550,32 @@
 	        altFormats: 'c',
 	        format: "<i18n:text i18n:key='DATE_FIELD_FORMAT'/>",
 	        value: dateValue,
-	        allowBlank: !mandatory,
-	        regex: regexp,
-			regexText: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_CONFIG_INVALID_REGEXP'/>" + regexp,
+	        
+	        validator: function(value)
+	        {	
+	    		var enabled = me._form.getForm().findField(name).isVisible() && !me._form.getForm().findField(name).isDisabled();
+	    		// AND IS ENABLED
+	    		if(!enabled)
+	    		{
+	    			return true;
+	    		}
+	    		
+	      		// Otherwise the field has to be checked
+	    		{
+	    			var trimmed =  Ext.String.trim(value);
+	    			
+	    	        if (trimmed.length < 1 || (value === this.emptyText && this.valueContainsPlaceholder)) {
+	    	        	if (mandatory) {
+	    	                return me._blankText;
+	    	            }
+	    	        }
+	    	        if (regexp && !regexp.test(value)) {
+	    	            return this.regexText;
+	    	        }
+	    	        return true;
+	    		}
+	        },
+	        regexText: invalidText != null ? invalidText :  "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_CONFIG_INVALID_REGEXP'/>"  + regexp,
 			invalidText: invalidText != null ? invalidText : null,
 			msgTarget: 'side',
 			
@@ -521,6 +632,8 @@
 	 */
 	_createTextField: function (name, value, label, description, enumeration, mandatory, regexp, invalidText, width)
 	{
+		var me = this;
+		
 		if (enumeration != null)
 		{
 			return new Ext.form.field.ComboBox ({
@@ -530,12 +643,32 @@
 		        ametysDescription: description,
 		        value: value,
 		        
+		        validator: function(value)
+		        {	
+		        	var enabled = me._form.getForm().findField(name).isVisible() && !me._form.getForm().findField(name).isDisabled();
+		        	if(!enabled)
+		    		{
+		    			return true;
+		    		}
+		    		
+		      		// Otherwise the field has to be checked
+		    		{
+		    			var trimmed =  Ext.String.trim(value);
+		    			
+		    	        if (trimmed.length < 1 || (value === this.emptyText && this.valueContainsPlaceholder)) {
+		    	        	if (mandatory) {
+		    	                return me._blankText;
+		    	            }
+		    	        }
+		    	        return true;
+		    		}
+		        },
+		        
 		    	labelAlign: 'right',
 		        labelWidth: Ametys.plugins.core.administration.Config.LABEL_WIDTH,
 		        labelSeparator: '',
 		        width: width || Ametys.plugins.core.administration.Config.FIELD_WIDTH + Ametys.plugins.core.administration.Config.LABEL_WIDTH,
 		        
-		        allowBlank: !mandatory,
 		        msgTarget: 'side',
 		        
 		        mode: 'local',
@@ -564,15 +697,37 @@
 		        fieldLabel: label,
 		        ametysDescription: description,
 		        value: value,
+		        validator: function(value)
+		        {	
+		        	var enabled = me._form.getForm().findField(name).isVisible() && !me._form.getForm().findField(name).isDisabled();
+		        	if(!enabled)
+		    		{
+		    			return true;
+		    		}
+		    		
+		      		// Otherwise the field has to be checked
+		    		{
+		    			var trimmed =  Ext.String.trim(value);
+		    			
+		    	        if (trimmed.length < 1 || (value === this.emptyText && this.valueContainsPlaceholder)) {
+		    	        	if (mandatory) {
+		    	        		return me._blankText;
+		    	            }
+		    	        }
+		    	        
+		    	        if (regexp && !regexp.test(value)) {
+		    	            return this.regexText;
+		    	        }
+		    	        return true;
+		    		}
+		        },
 		        
 		    	labelAlign: 'right',
 		        labelWidth: Ametys.plugins.core.administration.Config.LABEL_WIDTH,
 		        labelSeparator: '',
 		        width: width || Ametys.plugins.core.administration.Config.FIELD_WIDTH + Ametys.plugins.core.administration.Config.LABEL_WIDTH,
 		        
-		        allowBlank: !mandatory,
-		        regex: regexp,
-		        regexText: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_CONFIG_INVALID_REGEXP'/>" + regexp,
+		        regexText: invalidText != null ? invalidText :  "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_CONFIG_INVALID_REGEXP'/>"  + regexp,
 				invalidText: invalidText != null ? invalidText : null,
 				msgTarget: 'side'
 			});
@@ -595,22 +750,45 @@
 	 */
 	_createWidgetField: function (widgetId, name, value, label, description, mandatory, regexp, invalidText, width)
 	{
+		var me = this;
+		
 		var widgetCfg = {
 				name: name,
 				
 		        fieldLabel: label,
 		        ametysDescription: description,
 		        value: value,
+		        validator: function(value)
+		        {	
+		        	var enabled = me._form.getForm().findField(name).isVisible() && !me._form.getForm().findField(name).isDisabled();
+		        	if(!enabled)
+		    		{
+		    			return true;
+		    		}
+		    		
+		      		// Otherwise the field has to be checked
+		    		{
+		    			var trimmed =  Ext.String.trim(value);
+		    			
+		    	        if (trimmed.length < 1 || (value === this.emptyText && this.valueContainsPlaceholder)) {
+		    	        	if (mandatory) {
+		    	        		return me._blankText;
+		    	            }
+		    	        }
+		    	        
+		    	        if (regexp && !regexp.test(value)) {
+		    	            return this.regexText;
+		    	        }
+		    	        return true;
+		    		}
+		        },
 		        
 		    	labelAlign: 'right',
 		        labelWidth: Ametys.plugins.core.administration.Config.LABEL_WIDTH,
 		        labelSeparator: '',
 		        width: width || Ametys.plugins.core.administration.Config.FIELD_WIDTH + Ametys.plugins.core.administration.Config.LABEL_WIDTH,
 
-		        
-		        allowBlank: !mandatory,
-		        regex: regexp,
-				regexText: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_CONFIG_INVALID_REGEXP'/>" + regexp,
+		        regexText: invalidText != null ? invalidText :  "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_CONFIG_INVALID_REGEXP'/>"  + regexp,
 				invalidText: invalidText != null ? invalidText : null,
 		        msgTarget: 'side'
 		};
@@ -711,16 +889,16 @@
 			return;
 	    }
 	    
-	    this.save._mask = new Ext.LoadMask({target: Ext.getBody()});
+		this.save._mask = new Ext.LoadMask({target: Ext.getBody()});
 	    this.save._mask.show();
-	    Ext.defer(this.save2, 1, this);
+	    Ext.defer(this._save2, 1, this);
 	},
 	
 	/**
 	 * @private
 	 * Second part of the #save process (due to asynchronous process)
 	 */
-	save2: function ()
+	_save2: function ()
 	{
 	    var url = Ametys.getPluginDirectPrefix(this.pluginName) + "/administrator/config/set";
 
#P Ametys2 - 07 CMS LTE
Index: main/plugin-cms/plugin.xml
===================================================================
--- main/plugin-cms/plugin.xml	(revision 26551)
+++ main/plugin-cms/plugin.xml	(working copy)
@@ -19,38 +19,42 @@
         xsi:schemaLocation="http://www.ametys.org/schema/plugin http://www.ametys.org/schema/plugin-2.0.xsd"
         runtimeVersion="2.0" version="2.0">
 
                 <mandatory/>
                 <regexp>^https?://.*$</regexp>
                 <invalidText i18n="true">plugin.core:PLUGINS_CORE_REGEXP_INVALID_URL</invalidText>
             </validation>
             <category i18n="true">PLUGINS_CMS_CONFIG_CATEGORY_CMS</category>
-			<group i18n="true">PLUGINS_CMS_CONFIG_CATEGORY_CMS_PARAMS_GROUP</group>
-			<order>10</order>
-		</param>
-        <param type="boolean" id="consistency.scheduler.enabled">
+            <group i18n="true">PLUGINS_CMS_CONFIG_CATEGORY_CMS_PARAMS_GROUP</group>
+            <order>10</order>
+        </param>
+        
+        <!-- BROKEN LINKS -->
+        <param type="boolean" id="consistency.scheduler.enabled" group-switch="true">
             <label i18n="true">PLUGINS_CMS_GLOBAL_CONTENT_CONSISTENCY_CONFIG_SCHEDULER_ENABLED_LABEL</label>
             <description i18n="true">PLUGINS_CMS_GLOBAL_CONTENT_CONSISTENCY_CONFIG_SCHEDULER_ENABLED_DESC</description>
             <default-value>true</default-value>
@@ -58,16 +62,22 @@
             <group i18n="true">PLUGINS_CMS_GLOBAL_CONTENT_CONSISTENCY_CONFIG_GROUP</group>
             <order>10</order>
         </param>
+        
         <param type="string" id="consistency.scheduler.hour">
             <label i18n="true">PLUGINS_CMS_GLOBAL_CONTENT_CONSISTENCY_CONFIG_SCHEDULER_HOUR_LABEL</label>
             <description i18n="true">PLUGINS_CMS_GLOBAL_CONTENT_CONSISTENCY_CONFIG_SCHEDULER_HOUR_DESC</description>
+            <validation>
+                <mandatory/>
+            </validation>
             <default-value>2:00</default-value>
             <category i18n="true">plugin.repository:PLUGINS_REPOSITORY_CONFIG_CATEGORY</category>
             <group i18n="true">PLUGINS_CMS_GLOBAL_CONTENT_CONSISTENCY_CONFIG_GROUP</group>
             <order>20</order>
             <widget>hour</widget>
         </param>
-        <param type="boolean" id="automatic.save.enabled">
+        
+        <!-- AUTOMATIC SAVE -->
+        <param type="boolean" id="automatic.save.enabled" group-switch="true">
             <label i18n="true">PLUGINS_CMS_AUTOMATIC_SAVE_CONFIG_ENABLED_LABEL</label>
             <description i18n="true">PLUGINS_CMS_AUTOMATIC_SAVE_CONFIG_ENABLED_DESC</description>
             <default-value>true</default-value>
@@ -75,15 +85,19 @@
             <group i18n="true">PLUGINS_CMS_AUTOMATIC_SAVE_CONFIG_GROUP</group>
             <order>10</order>
         </param>
+
         <param type="long" id="automatic.save.frequency">
             <label i18n="true">PLUGINS_CMS_AUTOMATIC_SAVE_CONFIG_FREQUENCY_LABEL</label>
             <description i18n="true">PLUGINS_CMS_AUTOMATIC_SAVE_CONFIG_FREQUENCY_DESC</description>
+            <validation>
+                <mandatory/>
+            </validation>
             <default-value>15</default-value>
             <category i18n="true">plugin.repository:PLUGINS_REPOSITORY_CONFIG_CATEGORY</category>
             <group i18n="true">PLUGINS_CMS_AUTOMATIC_SAVE_CONFIG_GROUP</group>
             <order>20</order>
         </param>
-	</config>
+    </config>

@@ -6653,9 +6728,10 @@
         </config>
     </feature>
     
+    <!-- CONTENTS PURGE -->
     <feature name="org.ametys.cms.workflow.contents.purge.config">
         <config>
-            <param type="boolean" id="purge.scheduler.enabled">
+            <param type="boolean" id="purge.scheduler.enabled" group-switch="true">
                 <label i18n="true">PLUGINS_CMS_PURGE_CONTENTS_CONFIG_SCHEDULER_ENABLED_LABEL</label>
                 <description i18n="true">PLUGINS_CMS_PURGE_CONTENTS_CONFIG_SCHEDULER_ENABLED_DESC</description>
                 <default-value>false</default-value>
@@ -6666,6 +6742,9 @@
             <param type="long" id="purge.scheduler.period">
                 <label i18n="true">PLUGINS_CMS_PURGE_CONTENTS_CONFIG_SCHEDULER_PERIOD_LABEL</label>
                 <description i18n="true">PLUGINS_CMS_PURGE_CONTENTS_CONFIG_SCHEDULER_PERIOD_DESC</description>
+                <validation>
+                    <mandatory/>
+                </validation>
                 <default-value>7</default-value>
                 <category i18n="true">plugin.repository:PLUGINS_REPOSITORY_CONFIG_CATEGORY</category>
                 <group i18n="true">PLUGINS_CMS_PURGE_CONTENTS_CONFIG_GROUP</group>
@@ -6674,6 +6753,9 @@
             <param type="string" id="purge.scheduler.hour">
                 <label i18n="true">PLUGINS_CMS_PURGE_CONTENTS_CONFIG_SCHEDULER_HOUR_LABEL</label>
                 <description i18n="true">PLUGINS_CMS_PURGE_CONTENTS_CONFIG_SCHEDULER_HOUR_DESC</description>
+                <validation>
+                    <mandatory/>
+                </validation>
                 <default-value>2:00</default-value>
                 <category i18n="true">plugin.repository:PLUGINS_REPOSITORY_CONFIG_CATEGORY</category>
                 <group i18n="true">PLUGINS_CMS_PURGE_CONTENTS_CONFIG_GROUP</group>
@@ -6683,6 +6765,9 @@
             <param type="long" id="purge.before.days">
                 <label i18n="true">PLUGINS_CMS_PURGE_CONTENTS_CONFIG_BEFORE_DAYS_LABEL</label>
                 <description i18n="true">PLUGINS_CMS_PURGE_CONTENTS_CONFIG_BEFORE_DAYS_DESC</description>
+                <validation>
+                    <mandatory/>
+                </validation>
                 <default-value>60</default-value>
                 <category i18n="true">plugin.repository:PLUGINS_REPOSITORY_CONFIG_CATEGORY</category>
                 <group i18n="true">PLUGINS_CMS_PURGE_CONTENTS_CONFIG_GROUP</group>
@@ -6691,6 +6776,9 @@
             <param type="long" id="purge.keep.validated.versions">
                 <label i18n="true">PLUGINS_CMS_PURGE_CONTENTS_CONFIG_KEEP_VALIDATED_VERSIONS_LABEL</label>
                 <description i18n="true">PLUGINS_CMS_PURGE_CONTENTS_CONFIG_KEEP_VALIDATED_VERSIONS_DESC</description>
+                <validation>
+                    <mandatory/>
+                </validation>
                 <default-value>3</default-value>
                 <category i18n="true">plugin.repository:PLUGINS_REPOSITORY_CONFIG_CATEGORY</category>
                 <group i18n="true">PLUGINS_CMS_PURGE_CONTENTS_CONFIG_GROUP</group>
@@ -8187,4 +8275,4 @@
             </extension>
         </extensions>
     </feature>    
-</plugin>
+</plugin>
\ No newline at end of file
#P Ametys2 - 03 Plugin Repository LTE
Index: main/plugin-repository/plugin.xml
===================================================================
--- main/plugin-repository/plugin.xml	(revision 26551)
+++ main/plugin-repository/plugin.xml	(working copy)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!--
-   Copyright 2010 Anyware Services
+   Copyright 2014 Anyware Services
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -93,9 +93,10 @@
         </extensions>
     </feature>
     
+    <!-- AUTOMATIC UNLOCKING -->
 	<feature name="unlock-scheduler">
         <config>
-            <param id="content.unlocktimer.activate" type="boolean">
+            <param id="content.unlocktimer.activate" type="boolean" group-switch="true">
                 <label i18n="true">PLUGINS_REPOSITORY_CONFIG_CONTENT_UNLOCKTIMER_ACTIVATE</label>
                 <description i18n="true">PLUGINS_REPOSITORY_CONFIG_CONTENT_UNLOCKTIMER_ACTIVATE</description>
                 <default-value>true</default-value>
@@ -106,6 +107,9 @@
             <param id="content.unlocktimer.period" type="long">
                 <label i18n="true">PLUGINS_REPOSITORY_CONFIG_CONTENT_UNLOCKTIMER_PERIOD</label>
                 <description i18n="true">PLUGINS_REPOSITORY_CONFIG_CONTENT_UNLOCKTIMER_PERIOD</description>
+                <validation>
+                    <mandatory/>
+                </validation>
                 <default-value>2</default-value>
                 <category i18n="true">PLUGINS_REPOSITORY_CONFIG_CATEGORY</category>
                 <group i18n="true">PLUGINS_REPOSITORY_CONFIG_UNLOCKTIMER</group>
