Index: trunk/grails-app/controllers/AssetDetailedController.groovy
===================================================================
--- trunk/grails-app/controllers/AssetDetailedController.groovy	(revision 283)
+++ trunk/grails-app/controllers/AssetDetailedController.groovy	(revision 284)
@@ -234,38 +234,25 @@
 
     def create = {
-        def assetInstance = new Asset()
-        assetInstance.properties = params
-        return ['assetInstance':assetInstance]
+        def result = assetService.create(params)
+
+        if(!result.error)
+            return [assetInstance: result.assetInstance]
+
+        flash.errorMessage = g.message(code: result.error.code, args: result.error.args)
+        redirect(action: search)
     }
 
     def copy = {
-        def assetToCopy = Asset.get(params.asset.id)
-
-        if(!assetToCopy) {
-            flash.message = "Please select an asset to copy."
-            redirect(action: list)
-            return
-        }
-
-        def assetInstance = new Asset(name: assetToCopy.name,
-                                                            description: assetToCopy.description,
-                                                            section: assetToCopy.section)
-        assetInstance.properties = params
-        return ['assetInstance':assetInstance, assetToCopy: assetToCopy]
+        def result = assetService.copy(params)
+
+        if(!result.error)
+            return [assetInstance: result.assetInstance, assetToCopy: result.assetToCopy]
+
+        flash.errorMessage = g.message(code: result.error.code, args: result.error.args)
+        redirect(action: search)
     }
 
     def save = {
-        def assetInstance = new Asset(params)
-        if(!assetInstance.hasErrors() && assetInstance.save(flush: true)) {
-            flash.message = "Asset ${assetInstance.id} created"
-            redirect(action:show,id:assetInstance.id)
-        }
-        else {
-            render(view:'create',model:[assetInstance:assetInstance])
-        }
-    }
-
-    def saveCopy = {
-        def result = assetService.saveCopy(params)
+        def result = assetService.save(params)
 
         if(!result.error) {
@@ -275,4 +262,17 @@
         }
 
+        render(view:'create', model:[assetInstance: result.assetInstance])
+    }
+
+
+    def saveCopy = {
+        def result = assetService.saveCopy(params)
+
+        if(!result.error) {
+            flash.message = g.message(code: "default.create.success", args: ["Asset", result.assetInstance.id])
+            redirect(action:show, id: result.assetInstance.id)
+            return
+        }
+
         if(result.error.code == "default.not.found") {
             flash.message = g.message(code: result.error.code, args: ["Asset", params.assetToCopy?.id])
Index: trunk/grails-app/i18n/messages.properties
===================================================================
--- trunk/grails-app/i18n/messages.properties	(revision 283)
+++ trunk/grails-app/i18n/messages.properties	(revision 284)
@@ -9,4 +9,5 @@
 asset.copy.method.help=Link creates a new asset and links it to EXISTING sub items. \
     While copy creates a new asset and new sub items.
+asset.copy.asset.required=Please select an asset to copy.
 
 sub.task.create.confirm=Immediately create and save a new sub task?
Index: trunk/grails-app/services/AssetService.groovy
===================================================================
--- trunk/grails-app/services/AssetService.groovy	(revision 283)
+++ trunk/grails-app/services/AssetService.groovy	(revision 284)
@@ -2,4 +2,58 @@
 
     boolean transactional = false
+
+    def create(params) {
+        def result = [:]
+        def fail = { Map m ->
+            result.error = [ code: m.code, args: ["Asset", params.id] ]
+            return result
+        }
+
+        result.assetInstance = new Asset()
+        result.assetInstance.properties = params
+
+        // success
+        return result
+    }
+
+    def copy(params) {
+        def result = [:]
+        def fail = { Map m ->
+            result.error = [ code: m.code, args: ["Asset", params.id] ]
+            return result
+        }
+
+        result.assetToCopy = Asset.get(params.assetToCopy?.id)
+
+        if(!result.assetToCopy)
+            return fail(code: "asset.copy.asset.required")
+
+        result.assetInstance = new Asset(name: result.assetToCopy.name,
+                                                            description: result.assetToCopy.description,
+                                                            section: result.assetToCopy.section)
+
+        result.assetInstance.properties = params
+
+        // success
+        return result
+    }
+
+    def save(params) {
+        def result = [:]
+        def fail = { Map m ->
+            if(result.assetInstance && m.field) 
+                result.assetInstance.errors.rejectValue(m.field, m.code)
+            result.error = [ code: m.code, args: ["Asset", params.id] ]
+            return result
+        }
+
+        result.assetInstance = new Asset(params)
+
+        if(result.assetInstance.hasErrors() || !result.assetInstance.save(flush: true))
+            return fail(code:"default.create.failure")
+
+        // success
+        return result
+    }
 
     def saveCopy(params) {
Index: trunk/grails-app/taglib/AssetTreeTagLib.groovy
===================================================================
--- trunk/grails-app/taglib/AssetTreeTagLib.groovy	(revision 283)
+++ trunk/grails-app/taglib/AssetTreeTagLib.groovy	(revision 284)
@@ -81,4 +81,7 @@
                                                             img(src: addImg(), alt: 'Add', title: 'Add Sub Item')
                                                         }
+                                                        a(href: assetCopyLink(asset.id)) {
+                                                            img(src: copyImg(), alt: 'Add', title: 'Copy Asset')
+                                                        }
                                                     } // li
 
@@ -240,6 +243,6 @@
     }
 
-    def sectionCreateLink(id) {
-        createLink(controller: 'sectionDetailed', action: 'create', params: ['id': id] ).toString()
+    def sectionCreateLink(siteId) {
+        createLink(controller: 'sectionDetailed', action: 'create', params: ['site.id': siteId] ).toString()
     }
 
@@ -252,6 +255,6 @@
     }
 
-    def assetCreateLink(id) {
-        createLink(controller: 'assetDetailed', action: 'create', params: ['id': id] ).toString()
+    def assetCreateLink(sectionId) {
+        createLink(controller: 'assetDetailed', action: 'create', params: ['section.id': sectionId] ).toString()
     }
 
@@ -265,5 +268,5 @@
 
     def assetCopyLink(id) {
-        createLink(controller: 'assetDetailed', action: 'copy', params: ['asset.id': id] ).toString()
+        createLink(controller: 'assetDetailed', action: 'copy', params: ['assetToCopy.id': id] ).toString()
     }
 
Index: trunk/grails-app/views/assetDetailed/overview.gsp
===================================================================
--- trunk/grails-app/views/assetDetailed/overview.gsp	(revision 283)
+++ trunk/grails-app/views/assetDetailed/overview.gsp	(revision 284)
@@ -14,7 +14,5 @@
         </div>
         <div class="body">
-            <g:if test="${flash.message}">
-            <div class="message">${flash.message}</div>
-            </g:if>
+            <g:render template="/shared/messages" />
 
             <div class="dialog">
Index: trunk/grails-app/views/assetDetailed/search.gsp
===================================================================
--- trunk/grails-app/views/assetDetailed/search.gsp	(revision 283)
+++ trunk/grails-app/views/assetDetailed/search.gsp	(revision 284)
@@ -15,7 +15,6 @@
         </div>
         <div class="body">
-            <g:if test="${flash.message}">
-            <div class="message">${flash.message}</div>
-            </g:if>
+            <g:render template="/shared/messages" />
+
             <filterpane:currentCriteria domainBean="Asset"
                                     action="search"
Index: trunk/grails-app/views/shared/_assetTree.gsp
===================================================================
--- trunk/grails-app/views/shared/_assetTree.gsp	(revision 283)
+++ trunk/grails-app/views/shared/_assetTree.gsp	(revision 284)
@@ -5,5 +5,5 @@
         <img src="${resource(dir:'images/skin',file:'database_add.png')}" alt="Add" title="Add Sub Item"/>
     </g:link>
-    <g:link params="['asset.id':assetInstance?.id]" action="copy">
+    <g:link params="['assetToCopy.id':assetInstance?.id]" action="copy">
         <img src="${resource(dir:'images/skin',file:'page_copy.png')}" alt="Copy" title="Copy Asset"/>
     </g:link>
