Index: /trunk/grails-app/controllers/AppConfigController.groovy
===================================================================
--- /trunk/grails-app/controllers/AppConfigController.groovy	(revision 234)
+++ /trunk/grails-app/controllers/AppConfigController.groovy	(revision 234)
@@ -0,0 +1,99 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+class AppConfigController extends BaseAppAdminController {
+    
+    def index = { redirect(action:list,params:params) }
+
+    // the delete, save and update actions only accept POST requests
+    static allowedMethods = [delete:'POST', save:'POST', update:'POST']
+
+    def list = {
+        params.max = Math.min( params.max ? params.max.toInteger() : 10,  100)
+        [ appConfigInstanceList: AppConfig.list( params ), appConfigInstanceTotal: AppConfig.count() ]
+    }
+
+    def show = {
+        def appConfigInstance = AppConfig.get( params.id )
+
+        if(!appConfigInstance) {
+            flash.message = "AppConfig not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else { return [ appConfigInstance : appConfigInstance ] }
+    }
+
+    def delete = {
+        def appConfigInstance = AppConfig.get( params.id )
+        if(appConfigInstance) {
+            try {
+                appConfigInstance.delete(flush:true)
+                flash.message = "AppConfig ${params.id} deleted"
+                redirect(action:list)
+            }
+            catch(org.springframework.dao.DataIntegrityViolationException e) {
+                flash.message = "AppConfig ${params.id} could not be deleted"
+                redirect(action:show,id:params.id)
+            }
+        }
+        else {
+            flash.message = "AppConfig not found with id ${params.id}"
+            redirect(action:list)
+        }
+    }
+
+    def edit = {
+        def appConfigInstance = AppConfig.get( params.id )
+
+        if(!appConfigInstance) {
+            flash.message = "AppConfig not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else {
+            return [ appConfigInstance : appConfigInstance ]
+        }
+    }
+
+    def update = {
+        def appConfigInstance = AppConfig.get( params.id )
+        if(appConfigInstance) {
+            if(params.version) {
+                def version = params.version.toLong()
+                if(appConfigInstance.version > version) {
+                    
+                    appConfigInstance.errors.rejectValue("version", "appConfig.optimistic.locking.failure", "Another user has updated this AppConfig while you were editing.")
+                    render(view:'edit',model:[appConfigInstance:appConfigInstance])
+                    return
+                }
+            }
+            appConfigInstance.properties = params
+            if(!appConfigInstance.hasErrors() && appConfigInstance.save(flush: true)) {
+                flash.message = "AppConfig ${params.id} updated"
+                redirect(action:show,id:appConfigInstance.id)
+            }
+            else {
+                render(view:'edit',model:[appConfigInstance:appConfigInstance])
+            }
+        }
+        else {
+            flash.message = "AppConfig not found with id ${params.id}"
+            redirect(action:list)
+        }
+    }
+
+    def create = {
+        def appConfigInstance = new AppConfig()
+        appConfigInstance.properties = params
+        return ['appConfigInstance':appConfigInstance]
+    }
+
+    def save = {
+        def appConfigInstance = new AppConfig(params)
+        if(!appConfigInstance.hasErrors() && appConfigInstance.save(flush: true)) {
+            flash.message = "AppConfig ${appConfigInstance.id} created"
+            redirect(action:show,id:appConfigInstance.id)
+        }
+        else {
+            render(view:'create',model:[appConfigInstance:appConfigInstance])
+        }
+    }
+}
Index: /trunk/grails-app/domain/AppConfig.groovy
===================================================================
--- /trunk/grails-app/domain/AppConfig.groovy	(revision 234)
+++ /trunk/grails-app/domain/AppConfig.groovy	(revision 234)
@@ -0,0 +1,19 @@
+/**
+ * Domain class that stores various application configuration (settings) in the database.
+ * Use AppConfigService to interact with the instances.
+ */
+class AppConfig {
+    String name
+    String value
+    String description = ''
+
+    static constraints = {
+        name(maxSize:50,unique:true,blank:false)
+        description(maxSize:100)
+        value(maxSize:50,blank:false)
+    }
+
+    String toString() {
+        "${this.name} = ${this.value}"
+    }
+}
Index: /trunk/grails-app/services/AppConfigService.groovy
===================================================================
--- /trunk/grails-app/services/AppConfigService.groovy	(revision 234)
+++ /trunk/grails-app/services/AppConfigService.groovy	(revision 234)
@@ -0,0 +1,56 @@
+/**
+ * Provides a service class with methods to interact with AppConfig.
+ * AppConfig stores various application configuration (settings) in the database.
+ */
+class AppConfigService {
+
+    boolean transactional = false
+
+    /**
+    * Set the value of an appConfig.
+    * @param name The name of the appConfig.
+    * @param value Set to this value if specified else defaults to "true".
+    * @returns True if success otherwise false.
+    */
+    public Boolean set(String name, String value = "true") {
+        def appConfig = AppConfig.findByName(name)
+        appConfig ? (appConfig.value = value) : (appConfig = new AppConfig(name: name, value: value))
+        appConfig.save() ? true : false
+    }
+
+    /**
+    * Check if an appConfig exists.
+    * @param name The name of the appConfig.
+    * @returns True if success otherwise false.
+    */
+    public Boolean exists(String name) {
+        AppConfig.findByName(name) ? true : false
+    }
+
+    /**
+    * Get the value of an appConfig.
+    * @param name The name of the appConfig.
+    * @returns The value of the appConfig else false.
+    */
+    def getValue(String name) {
+        def appConfig = AppConfig.findByName(name)
+        appConfig ? appConfig.value : false
+    }
+
+    /**
+    * Delete an appConfig.
+    * @param name The name of the appConfig.
+    * @returns True if success otherwise false.
+    */
+    public Boolean delete(String name) {
+        try {
+            AppConfig.findByName(name).delete(flush:true)
+            return true
+        }
+        catch(e) {
+            log.debug("Could not delete, appConfig may not exist.")
+            return false
+        }
+    }
+
+} // end of class
Index: /trunk/grails-app/views/appConfig/create.gsp
===================================================================
--- /trunk/grails-app/views/appConfig/create.gsp	(revision 234)
+++ /trunk/grails-app/views/appConfig/create.gsp	(revision 234)
@@ -0,0 +1,64 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Create AppConfig</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><g:link class="list" action="list">AppConfig List</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Create AppConfig</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${appConfigInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${appConfigInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form action="save" method="post" >
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="name">Name:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:appConfigInstance,field:'name','errors')}">
+                                    <input type="text" maxlength="50" id="name" name="name" value="${fieldValue(bean:appConfigInstance,field:'name')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:appConfigInstance,field:'description','errors')}">
+                                    <input type="text" maxlength="100" id="description" name="description" value="${fieldValue(bean:appConfigInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="value">Value:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:appConfigInstance,field:'value','errors')}">
+                                    <input type="text" id="value" name="value" value="${fieldValue(bean:appConfigInstance,field:'value')}"/>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><input class="save" type="submit" value="Create" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/appConfig/edit.gsp
===================================================================
--- /trunk/grails-app/views/appConfig/edit.gsp	(revision 234)
+++ /trunk/grails-app/views/appConfig/edit.gsp	(revision 234)
@@ -0,0 +1,68 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Edit AppConfig</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><g:link class="list" action="list">AppConfig List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New AppConfig</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Edit AppConfig</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${appConfigInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${appConfigInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form method="post" >
+                <input type="hidden" name="id" value="${appConfigInstance?.id}" />
+                <input type="hidden" name="version" value="${appConfigInstance?.version}" />
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="name">Name:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:appConfigInstance,field:'name','errors')}">
+                                    <input type="text" maxlength="50" id="name" name="name" value="${fieldValue(bean:appConfigInstance,field:'name')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:appConfigInstance,field:'description','errors')}">
+                                    <input type="text" maxlength="100" id="description" name="description" value="${fieldValue(bean:appConfigInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="value">Value:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:appConfigInstance,field:'value','errors')}">
+                                    <input type="text" id="value" name="value" value="${fieldValue(bean:appConfigInstance,field:'value')}"/>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><g:actionSubmit class="save" value="Update" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/appConfig/list.gsp
===================================================================
--- /trunk/grails-app/views/appConfig/list.gsp	(revision 234)
+++ /trunk/grails-app/views/appConfig/list.gsp	(revision 234)
@@ -0,0 +1,55 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>AppConfig List</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><g:link class="create" action="create">New AppConfig</g:link></span>
+        </div>
+        <div class="body">
+            <h1>AppConfig List</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="list">
+                <table>
+                    <thead>
+                        <tr>
+                        
+                   	        <g:sortableColumn property="id" title="Id" />
+                        
+                   	        <g:sortableColumn property="name" title="Name" />
+                        
+                   	        <g:sortableColumn property="description" title="Description" />
+                        
+                   	        <g:sortableColumn property="value" title="Value" />
+                        
+                        </tr>
+                    </thead>
+                    <tbody>
+                    <g:each in="${appConfigInstanceList}" status="i" var="appConfigInstance">
+                        <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+                        
+                            <td><g:link action="show" id="${appConfigInstance.id}">${fieldValue(bean:appConfigInstance, field:'id')}</g:link></td>
+                        
+                            <td>${fieldValue(bean:appConfigInstance, field:'name')}</td>
+                        
+                            <td>${fieldValue(bean:appConfigInstance, field:'description')}</td>
+                        
+                            <td>${fieldValue(bean:appConfigInstance, field:'value')}</td>
+                        
+                        </tr>
+                    </g:each>
+                    </tbody>
+                </table>
+            </div>
+            <div class="paginateButtons">
+                <g:paginate total="${appConfigInstanceTotal}" />
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/appConfig/show.gsp
===================================================================
--- /trunk/grails-app/views/appConfig/show.gsp	(revision 234)
+++ /trunk/grails-app/views/appConfig/show.gsp	(revision 234)
@@ -0,0 +1,64 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Show AppConfig</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><g:link class="list" action="list">AppConfig List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New AppConfig</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Show AppConfig</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="dialog">
+                <table>
+                    <tbody>
+
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Id:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:appConfigInstance, field:'id')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Name:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:appConfigInstance, field:'name')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Description:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:appConfigInstance, field:'description')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Value:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:appConfigInstance, field:'value')}</td>
+                            
+                        </tr>
+                    
+                    </tbody>
+                </table>
+            </div>
+            <div class="buttons">
+                <g:form>
+                    <input type="hidden" name="id" value="${appConfigInstance?.id}" />
+                    <span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </g:form>
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/test/unit/AppConfigServiceTests.groovy
===================================================================
--- /trunk/test/unit/AppConfigServiceTests.groovy	(revision 234)
+++ /trunk/test/unit/AppConfigServiceTests.groovy	(revision 234)
@@ -0,0 +1,106 @@
+import grails.test.*
+
+/**
+* Unit tests for AppConfigService class.
+*/
+class AppConfigServiceTests extends GrailsUnitTestCase {
+
+    def appConfigService // not automatically injected, assignment is bellow.
+
+    // Some test data.
+    def configName01 = "baseDataCreated"
+    def configName02 = "dateFormat"
+    def configName03 = "blankValue"
+    def blankString = ''
+    def configDefaultValue = "true"
+    def configValue01 = "false"
+    def configValue02= "EEE, dd-MMM-yyyy"
+
+    protected void setUp() {
+        super.setUp()
+
+        // Mock the domain class.
+        mockDomain(AppConfig)
+        mockForConstraintsTests(AppConfig)
+
+        // Mock logging with debug enabled/disabled, then get an instance of the service.
+        mockLogging(AppConfigService, true)
+        appConfigService = new AppConfigService()
+    }
+
+    protected void tearDown() {
+        super.tearDown()
+    }
+
+    void testSet() {
+
+        // Set returns false for blank name.
+        assert AppConfig.list().size == 0
+        assertFalse appConfigService.set(blankString)
+        assert AppConfig.list().size == 0
+
+        // Set returns true and saves an object.
+        assertTrue appConfigService.set(configName01)
+        assert AppConfig.list().size == 1
+
+        // Set returns true and does not save a new object for existing appConfig.
+        assertTrue appConfigService.set(configName01, configValue01)
+        assert AppConfig.list().size == 1
+
+        // Set returns true and saves different objects.
+        assertTrue appConfigService.set(configName02, configValue02)
+        assert AppConfig.list().size == 2
+
+        // Set returns false when trying to set a blank value.
+        assertFalse appConfigService.set(configName03, blankString)
+        assert AppConfig.list().size == 2
+    }
+
+    void testExists() {
+
+        // Exists returns false for non existant appConfig.
+        assertFalse appConfigService.exists(configName01)
+
+        // Exists returns true for existing appConfig.
+        appConfigService.set(configName01)
+        assertTrue appConfigService.exists(configName01)
+    }
+
+    void testGetValue() {
+
+        // Get returns false for non existant appConfig.
+        assertFalse appConfigService.getValue(configName01)
+
+        // Set default and check that it returns.
+        appConfigService.set(configName01)
+        assert appConfigService.getValue(configName01) == configDefaultValue
+
+        // Set a value and check that it returns.
+        appConfigService.set(configName01, configValue01)
+        assert appConfigService.getValue(configName01) == configValue01
+    }
+
+    void testDelete() {
+
+        // Deleting returns false for non existant appConfig.
+        assert AppConfig.list().size == 0
+        assertFalse appConfigService.delete(configName01)
+
+        // First set.
+        appConfigService.set(configName01)
+        assert AppConfig.list().size == 1
+
+        // Second set.
+        appConfigService.set(configName02, configValue02)
+        assert AppConfig.list().size == 2
+
+        // Delete first set.
+        assertTrue appConfigService.delete(configName01)
+        assert AppConfig.list().size == 1
+
+        // Delete second set.
+        assertTrue appConfigService.delete(configName02)
+        assert AppConfig.list().size == 0
+    }
+
+}
