Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

significant changes for cleanup

removed dependency on spring security although its the defaul but user
can specify the how the user is fetched now
added ability to configure not only fieldName but type as well. This is
a breaking change. Still need to add ability to do constraints and
mapping too
  • Loading branch information...
commit caaa1854d21c5c099b8ddaa264c89a6598a9d81f 1 parent a4c7cb3
Joshua Burnett authored
View
1  .gitignore
@@ -1,3 +1,4 @@
+.grails/
/plugin.xml
grails-*.zip
web-app/WEB-INF/classes/
View
88 AuditTrailGrailsPlugin.groovy
@@ -1,72 +1,64 @@
+
class AuditTrailGrailsPlugin {
// the plugin version
def version = "1.2.1"
- // the version or versions of Grails the plugin is designed for
def grailsVersion = "1.3 > *"
- // the other plugins this plugin depends on
- def dependsOn = [hibernate:"1.3 > *", springSecurityCore:"1.0.1 > *"]
- // resources that are excluded from plugin packaging
- def pluginExcludes = [
- "grails-app/views/error.gsp",
- 'grails-app/views/login/*',
- 'grails-app/domain/**',
- 'grails-app/controllers/*',
- 'grails-app/conf/*Config*'
- ]
- def title = "Audit Trail" // Headline display name of the plugin
def author = "Joshua Burnett"
def authorEmail = "joshua@greenbill.com"
+ def title = ""
def description = 'provides an annotation and hibernate events to take care of audit trail stamping for your gorm objects'
+ def license = "APACHE"
+ def organization = [ name: "9ci", url: "http://www.9ci.com/" ]
+ def developers = [ [ name: "Konstantinos Kostarellis", email: "kosta.grails@gmail.com" ]]
+ def issueManagement = [ system: "github", url: "https://github.com/9ci/grails-audit-trail/issues" ]
+ def scm = [ url: "https://github.com/9ci/grails-audit-trail" ]
+ def documentation = "http://grails.org/AuditTrail+Plugin"
- // URL to the plugin's documentation
- def documentation = "http://grails.org/plugin/audit-trail"
-
- // Extra (optional) plugin metadata
-
- // License: one of 'APACHE', 'GPL2', 'GPL3'
- def license = "APACHE"
-
- // Details of company behind the plugin (if there is one)
-// def organization = [ name: "My Company", url: "http://www.my-company.com/" ]
-
- // Any additional developers beyond the author specified above.
- def developers = [ [ name: "Konstantinos Kostarellis", email: "kosta.grails@gmail.com" ]]
-
- // Location of the plugin's issue tracker.
- def issueManagement = [ system: "github", url: "https://github.com/9ci/grails-audit-trail/issues" ]
+ def pluginExcludes = [
+ "grails-app/views/**/*",
+ 'grails-app/domain/**',
+ 'grails-app/controllers/**',
+ 'grails-app/conf/*Config*',
+ "web-app/**/*"
+ ]
- // Online location of the plugin's browseable source code.
- def scm = [ url: "https://github.com/9ci/grails-audit-trail" ]
+ def loadAfter = ['hibernate']
- def doWithWebDescriptor = { xml ->
- // TODO Implement additions to web.xml (optional), this event occurs before
- }
+ def doWithSpring = {
- def doWithSpring = {
- entityInterceptor(nineci.hibernate.AuditTrailInterceptor)
- }
+ def cfg = application.config.grails.plugin.audittrail
+ //eventTriggeringInterceptor(AuditStampInterceptor)
+ entityInterceptor(nineci.hibernate.AuditTrailInterceptor){
+ grailsApplication = ref("grailsApplication")
+ createdByField = cfg.createdBy.field?:null
+ editedByField = cfg.editedBy.field?:null
+ editedDateField = cfg.editedDate.field?:null
+ createdDateField = cfg.createdDate.field?:null
+ companyIdField = cfg.companyId.field?:null
+ currentUserClosure = cfg.currentUserClosure?:null
+ }
+ }
def doWithDynamicMethods = { ctx ->
- // TODO Implement registering dynamic methods to classes (optional)
+
}
def doWithApplicationContext = { applicationContext ->
- // TODO Implement post initialization spring config (optional)
+
}
def onChange = { event ->
- // TODO Implement code that is executed when any artefact that this plugin is
- // watching is modified and reloaded. The event contains: event.source,
- // event.application, event.manager, event.ctx, and event.plugin.
+
}
- def onConfigChange = { event ->
- // TODO Implement code that is executed when the project configuration changes.
- // The event is the same as for 'onChange'.
- }
+ def getFieldNames(application){
+ def cfg = application.config.grails.plugin.audittrail
+ //try old way
+ if(!cfg){
+ cfg = application.config.stamp.audit
+ }
+ return cfg.flatten()
+ }
- def onShutdown = { event ->
- // TODO Implement code that is executed when the application shuts down (optional)
- }
}
View
3  application.properties
@@ -1,4 +1,5 @@
#Grails Metadata file
-#Fri Dec 16 23:50:45 CET 2011
+#Tue Sep 28 20:52:55 CDT 2010
app.grails.version=2.0.0
app.name=audit-trail
+
View
143 audit-trail.tmproj
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>currentDocument</key>
+ <string>src/groovy/nineci/hibernate/AuditTrailInterceptor.groovy</string>
+ <key>documents</key>
+ <array>
+ <dict>
+ <key>name</key>
+ <string>grails-app</string>
+ <key>regexFolderFilter</key>
+ <string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
+ <key>sourceDirectory</key>
+ <string>grails-app</string>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>test</string>
+ <key>regexFolderFilter</key>
+ <string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
+ <key>sourceDirectory</key>
+ <string>test</string>
+ </dict>
+ <dict>
+ <key>filename</key>
+ <string>application.properties</string>
+ </dict>
+ <dict>
+ <key>filename</key>
+ <string>AuditTrailGrailsPlugin.groovy</string>
+ <key>lastUsed</key>
+ <date>2011-12-19T16:47:52Z</date>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>lib</string>
+ <key>regexFolderFilter</key>
+ <string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
+ <key>sourceDirectory</key>
+ <string>lib</string>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>scripts</string>
+ <key>regexFolderFilter</key>
+ <string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
+ <key>sourceDirectory</key>
+ <string>scripts</string>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>src</string>
+ <key>regexFolderFilter</key>
+ <string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
+ <key>sourceDirectory</key>
+ <string>src</string>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>web-app</string>
+ <key>regexFolderFilter</key>
+ <string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
+ <key>sourceDirectory</key>
+ <string>web-app</string>
+ </dict>
+ </array>
+ <key>fileHierarchyDrawerWidth</key>
+ <integer>200</integer>
+ <key>metaData</key>
+ <dict>
+ <key>AuditTrailGrailsPlugin.groovy</key>
+ <dict>
+ <key>caret</key>
+ <dict>
+ <key>column</key>
+ <integer>49</integer>
+ <key>line</key>
+ <integer>28</integer>
+ </dict>
+ <key>columnSelection</key>
+ <false/>
+ <key>firstVisibleColumn</key>
+ <integer>0</integer>
+ <key>firstVisibleLine</key>
+ <integer>0</integer>
+ <key>selectFrom</key>
+ <dict>
+ <key>column</key>
+ <integer>37</integer>
+ <key>line</key>
+ <integer>28</integer>
+ </dict>
+ <key>selectTo</key>
+ <dict>
+ <key>column</key>
+ <integer>58</integer>
+ <key>line</key>
+ <integer>28</integer>
+ </dict>
+ </dict>
+ <key>src/groovy/nineci/hibernate/AuditTrailInterceptor.groovy</key>
+ <dict>
+ <key>caret</key>
+ <dict>
+ <key>column</key>
+ <integer>34</integer>
+ <key>line</key>
+ <integer>52</integer>
+ </dict>
+ <key>columnSelection</key>
+ <false/>
+ <key>firstVisibleColumn</key>
+ <integer>0</integer>
+ <key>firstVisibleLine</key>
+ <integer>40</integer>
+ <key>selectFrom</key>
+ <dict>
+ <key>column</key>
+ <integer>30</integer>
+ <key>line</key>
+ <integer>52</integer>
+ </dict>
+ <key>selectTo</key>
+ <dict>
+ <key>column</key>
+ <integer>39</integer>
+ <key>line</key>
+ <integer>52</integer>
+ </dict>
+ </dict>
+ </dict>
+ <key>openDocuments</key>
+ <array>
+ <string>AuditTrailGrailsPlugin.groovy</string>
+ <string>src/groovy/nineci/hibernate/AuditTrailInterceptor.groovy</string>
+ </array>
+ <key>showFileHierarchyDrawer</key>
+ <true/>
+ <key>windowFrame</key>
+ <string>{{65, 0}, {1645, 1178}}</string>
+</dict>
+</plist>
View
26 grails-app/conf/BuildConfig.groovy
@@ -3,7 +3,7 @@ grails.project.test.class.dir = "target/test-classes"
grails.project.test.reports.dir = "target/test-reports"
grails.project.target.level = 1.6
//grails.project.war.file = "target/${appName}-${appVersion}.war"
-
+grails.project.work.dir = '.grails'
grails.project.dependency.resolution = {
// inherit Grails' default dependencies
inherits("global") {
@@ -13,27 +13,27 @@ grails.project.dependency.resolution = {
log "warn" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose'
repositories {
grailsCentral()
- // uncomment the below to enable remote dependency resolution
- // from public Maven repositories
//mavenCentral()
//mavenLocal()
//mavenRepo "http://snapshots.repository.codehaus.org"
- //mavenRepo "http://repository.codehaus.org"
- //mavenRepo "http://download.java.net/maven/2/"
- //mavenRepo "http://repository.jboss.com/maven2/"
+
}
dependencies {
- // specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes eg.
-
- // runtime 'mysql:mysql-connector-java:5.1.5'
+ if(grailsVersion != "2.0.0"){
+ runtime('com.h2database:h2:1.2.147'){ export = false }
+ }
}
plugins {
- build(":tomcat:$grailsVersion",
- ":release:1.0.0") {
+ build(":tomcat:$grailsVersion",":release:1.0.0",":svn:1.0.1") {
export = false
}
- compile ':spring-security-core:1.2.6'
- compile ":hibernate:$grailsVersion"
+ compile (":hibernate:$grailsVersion"){
+ export = false
+ }
+ compile (':spring-security-core:1.2.6'){
+ export = false
+ }
+
}
}
View
83 grails-app/conf/Config.groovy
@@ -1,39 +1,29 @@
-// configuration for plugin testing - will not be included in the plugin zip
-
-log4j = {
- // Example of changing the log pattern for the default console
- // appender:
- //
- //appenders {
- // console name:'stdout', layout:pattern(conversionPattern: '%c{2} %m%n')
- //}
-
- error 'org.codehaus.groovy.grails.web.servlet', // controllers
- 'org.codehaus.groovy.grails.web.pages', // GSP
- 'org.codehaus.groovy.grails.web.sitemesh', // layouts
- 'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping
- 'org.codehaus.groovy.grails.web.mapping', // URL mapping
- 'org.codehaus.groovy.grails.commons', // core / classloading
- 'org.codehaus.groovy.grails.plugins', // plugins
- 'org.codehaus.groovy.grails.orm.hibernate', // hibernate integration
- 'org.springframework',
- 'org.hibernate',
- 'net.sf.ehcache.hibernate'
-
- warn 'org.mortbay.log'
-}
+import static grails.util.Environment.*
+
+ log4j = {
+ // Example of changing the log pattern for the default console
+ // appender:
+ //
+ //appenders {
+ // console name:'stdout', layout:pattern(conversionPattern: '%c{2} %m%n')
+ //}
+
+ error 'org.codehaus.groovy.grails.web.servlet', // controllers
+ 'org.codehaus.groovy.grails.web.pages', // GSP
+ 'org.codehaus.groovy.grails.web.sitemesh', // layouts
+ 'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping
+ 'org.codehaus.groovy.grails.web.mapping', // URL mapping
+ 'org.codehaus.groovy.grails.commons', // core / classloading
+ 'org.codehaus.groovy.grails.plugins', // plugins
+ 'org.codehaus.groovy.grails.orm.hibernate', // hibernate integration
+ 'org.springframework',
+ 'org.hibernate',
+ 'net.sf.ehcache.hibernate'
-stamp{
- audit{
- //the created and edited fields should be present or they won't get added during AST
- createdBy="createdBy" //id who created
- createdDate="createdDate" //
- editedBy="updatedBy" //id who updated/edited
- editedDate="editedDate"//date edited
- //the following are optional and are for mapping
- companyId="companyId" //used for multi-tenant, who this is for
+ warn 'org.mortbay.log'
}
-}
+
+
grails.gorm.default.mapping = {
id column: 'OID', generator:'nineci.hibernate.NewObjectIdGenerator'
@@ -43,3 +33,28 @@ grails.gorm.default.mapping = {
grails.plugins.springsecurity.userLookup.userDomainClassName = 'nine.tests.TestUser'
grails.plugins.springsecurity.userLookup.authorityJoinClassName = 'nine.tests.TestUserTestRole'
grails.plugins.springsecurity.authority.className = 'nine.tests.TestRole'
+
+grails{
+ plugin{
+ audittrail{
+ createdBy.field = "createdBy"
+ createdBy.type = "java.lang.Long" //fully qualified class name if not a java.lang.(String,Long,etc..)
+ createdBy.fieldValue = '0' //can be a string like '"system"' or an expression like ''
+
+ createdDate{
+ field = "createdDate"
+ type = "java.util.Date"
+ fieldValue = 'new java.util.Date()'
+ constraints = "nullable:true"
+ mapping = "column: 'date_created'"
+ }
+ //Will try a joda time on this one
+ editedDate.field = "editedDate"//date edited
+
+ editedBy.field = "updatedBy" //id who updated/edited
+
+ companyId.field = "companyId" //used for multi-tenant apps
+ }
+ }
+}
+
View
7 grails-app/conf/DataSource.groovy
@@ -1,13 +1,12 @@
dataSource {
pooled = true
driverClassName = "org.h2.Driver"
+ //dialect = 'org.hibernate.dialect.HSQLDialect'
username = "sa"
password = ""
}
hibernate {
- cache.use_second_level_cache = true
- cache.use_query_cache = true
- cache.region.factory_class = 'net.sf.ehcache.hibernate.EhCacheRegionFactory'
+ cache.use_second_level_cache = false
naming_strategy = 'org.hibernate.cfg.DefaultNamingStrategy'
}
// environment specific settings
@@ -20,7 +19,7 @@ environments {
}
test {
dataSource {
- dbCreate = "update"
+ dbCreate = "create-drop"
url = "jdbc:h2:mem:testDb;MVCC=TRUE"
}
}
View
18 grails-app/conf/UrlMappings.groovy
@@ -1,13 +1,11 @@
class UrlMappings {
-
- static mappings = {
- "/$controller/$action?/$id?"{
- constraints {
- // apply constraints here
- }
- }
-
- "/"(view:"/index")
- "500"(view:'/error')
+ static mappings = {
+ "/$controller/$action?/$id?"{
+ constraints {
+ // apply constraints here
+ }
+ }
+ "/"(view:"/index")
+ "500"(view:'/error')
}
}
View
201 src/groovy/nineci/hibernate/AuditStampInterceptor.groovy
@@ -1,201 +0,0 @@
-package nineci.hibernate
-
-import org.codehaus.groovy.grails.commons.GrailsDomainClassProperty
-import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsDomainBinder
-import org.codehaus.groovy.grails.orm.hibernate.cfg.Mapping
-import org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventTriggeringInterceptor
-import org.springframework.beans.factory.config.AutowireCapableBeanFactory
-import org.springframework.context.ApplicationContext
-import org.springframework.context.ApplicationContextAware
-import org.hibernate.event.SaveOrUpdateEvent
-import org.hibernate.event.PreUpdateEventListener
-import org.hibernate.event.PostUpdateEventListener
-import org.hibernate.event.PostLoadEventListener
-import org.hibernate.event.PostDeleteEventListener
-import org.hibernate.event.PostInsertEventListener
-import org.hibernate.event.PreLoadEventListener
-import org.hibernate.event.PreDeleteEventListener
-import org.hibernate.event.PreLoadEvent
-import org.hibernate.event.PostInsertEvent
-import org.hibernate.event.PostDeleteEvent
-import org.hibernate.event.PreUpdateEvent
-import org.hibernate.event.PreDeleteEvent
-import org.hibernate.event.PostUpdateEvent
-import org.hibernate.event.PostLoadEvent
-import org.hibernate.event.PreInsertEvent
-import org.hibernate.event.PreInsertEventListener
-import org.codehaus.groovy.grails.orm.hibernate.events.SaveOrUpdateEventListener
-import org.apache.commons.lang.ArrayUtils
-import org.hibernate.event.AbstractEvent
-import org.hibernate.persister.entity.EntityPersister
-import org.hibernate.EntityMode
-import org.hibernate.engine.EntityEntry
-import org.hibernate.type.Type
-import org.hibernate.Transaction
-import org.hibernate.CallbackException
-import org.springframework.security.core.context.SecurityContextHolder as SCH
-import org.apache.log4j.Logger
-
-/**
- * This was for the old way of doing it pre 1.2
- * left it here for reference
- */
-class AuditStampInterceptor extends ClosureEventTriggeringInterceptor {
- //in 1.2 we can do this diferently
- // implements org.hibernate.Interceptor, Serializable {
- private static final Logger log = Logger.getLogger(AuditStampInterceptor)
-
- static final CREATED_BY = 'createdBy'
- static final EDITED_BY = 'editedBy'
- static final CREATED_DATE = 'createdDate'
- static final EDITED_DATE = 'editedDate'
- static final COMPANY_ID = 'companyId'
- static Long ANONYMOUS_USER = 0
-
- public void onSaveOrUpdate(SaveOrUpdateEvent event) {
- def entity = event.getObject()
- def authPrincipal = SCH?.context?.authentication?.principal
- println "onSaveOrUpdate called"
- if(entity) {
- boolean newEntity = !event.session.contains(entity)
- if(newEntity) {
- log.debug "onSaveOrUpdate ${entity.id} ${entity.class.name} is new and will be stamped"
- def metaClass = entity.metaClass
- MetaProperty property = metaClass.hasProperty(entity, CREATED_DATE)
- def time = System.currentTimeMillis()
- if(property) {
- def now = property.getType().newInstance([time] as Object[] )
- entity."$property.name" = now
- }
- property = metaClass.hasProperty(entity,EDITED_DATE)
- if(property) {
- def now = property.getType().newInstance([time] as Object[] )
- entity."$property.name" = now
- }
- property = metaClass.hasProperty(entity,EDITED_BY)
- if(property) {
- entity."$property.name" = getUserID()
- }
- property = metaClass.hasProperty(entity,CREATED_BY)
- if(property) {
- entity."$property.name" = getUserID()
- }
- property = metaClass.hasProperty(entity,COMPANY_ID)
- // FIX for GB-347 Added check for error coming while logging in to new company since if we don't check at this point,
- // then 0 value gets set for companyId of user object which gives error on dashboard
- if(property && authPrincipal && authPrincipal != "anonymousUser") {
- entity."$property.name" = getCompanyId(authPrincipal)
- }
- }
- }
-
- super.onSaveOrUpdate event
- }
-
- Long getUserID() {
- def authPrincipal = SCH?.context?.authentication?.principal
- // Added check for error coming while creating new company
- if(authPrincipal && authPrincipal != "anonymousUser"){
- return authPrincipal.domainClass.id
- } else {
- return ANONYMOUS_USER
- }
- }
- Long getCompanyId(authPrincipal) {
- return authPrincipal.domainClass?.companyId
- }
-
-
- public void onPreLoad(PreLoadEvent event) {
- super.onPreLoad(event)
- }
-
- public void onPostLoad(PostLoadEvent event) {
- super.onPostLoad(event)
- }
-
- public void onPostInsert(PostInsertEvent event) {
- super.onPostInsert(event)
- }
-
- public boolean onPreUpdate(PreUpdateEvent event) {
- println "onSaveOrUpdate called"
- def entity = event.getEntity()
- def result = super.triggerEvent(BEFORE_UPDATE_EVENT, event.entity, event)
-
- Mapping m = GrailsDomainBinder.getMapping(entity.getClass())
- boolean shouldTimestamp = m && !m.autoTimestamp ? false : true
-
- MetaProperty property = entity.metaClass.hasProperty(entity, EDITED_DATE)
- if(property) {
- //log.debug "onPreUpdate ${entity.id} ${entity.class.name} hase been changed and will be stamped"
- def now = property.getType().newInstance([System.currentTimeMillis()] as Object[] )
- event.getState()[ArrayUtils.indexOf(event.persister.propertyNames, EDITED_DATE)] = now;
- entity."$EDITED_DATE" = now
- //log.debug "onPreUpdate ${entity.id} ${entity.class.name} has been stamped with ${entity.editedDate}"
- }
- property = entity.metaClass.hasProperty(entity, EDITED_BY)
- if(property) {
- event.getState()[ArrayUtils.indexOf(event.persister.propertyNames, EDITED_BY)] = getUserID();
- entity."$EDITED_BY" = getUserID()
- //log.debug "onPreUpdate ${entity.id} ${entity.class.name} hase been stamped with editedBy user ${entity.editedBy}"
- }
- return result
- //return super.onPreUpdate(event)
- }
-
- public void onPostUpdate(PostUpdateEvent event) {
- super.onPostUpdate(event)
- }
-
- public void onPostDelete(PostDeleteEvent event) {
- super.onPostDelete(event)
- }
-
- public boolean onPreDelete(PreDeleteEvent event) {
- return super.onPreDelete(event)
- }
-
-
-//*******implements the hibernate interceptor interface. wont work until 1.2 and then we can register it with
-//beans = { entityInterceptor(AuditStampInterceptor) }
-
- /*public boolean onFlushDirty( Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
- //setValue(currentState, propertyNames, "updatedBy", UserUtils.getCurrentUsername())
- //println "onFlushDirty "
- //setValue(currentState, propertyNames, EDITED_DATE, new Date())
- //return true;
- false
- }
-
- private void setValue(Object[] currentState, String[] propertyNames,String propertyToSet, Object value) {
- def index = propertyNames.toList().indexOf(propertyToSet)
- if (index >= 0) {
- //println "setValue for $propertyToSet "
- currentState[index] = value
- }
- }
-
-
- public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {}
- public boolean onLoad( Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { return false; }
- public boolean onSave( Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { return false; }
- public void postFlush(Iterator entities) {}
- public void preFlush(Iterator entities) {}
- public Boolean isTransient(Object entity) { return null; }
- public Object instantiate(String entityName, EntityMode entityMode, Serializable id) { return null; }
- public int[] findDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
- return null;
- }
- public String getEntityName(Object object) { return null; }
- public Object getEntity(String entityName, Serializable id) { return null; }
- public void afterTransactionBegin(Transaction tx) {}
- public void afterTransactionCompletion(Transaction tx) {}
- public void beforeTransactionCompletion(Transaction tx) {}
- public String onPrepareStatement(String sql) { return sql; }
- public void onCollectionRemove(Object collection, Serializable key) throws CallbackException {}
- public void onCollectionRecreate(Object collection, Serializable key) throws CallbackException {}
- public void onCollectionUpdate(Object collection, Serializable key) throws CallbackException {}
-*/
-
-}
View
105 src/groovy/nineci/hibernate/AuditTrailInterceptor.groovy
@@ -1,91 +1,110 @@
package nineci.hibernate //grails.plugin.audittrail
import org.hibernate.EmptyInterceptor
import org.hibernate.type.Type
-import org.springframework.security.core.context.SecurityContextHolder as SCH
import org.apache.log4j.Logger
-import org.codehaus.groovy.grails.commons.ConfigurationHolder
+import org.springframework.context.ApplicationContextAware
+import org.springframework.context.ApplicationContext
-
-class AuditTrailInterceptor extends EmptyInterceptor {
+class AuditTrailInterceptor extends EmptyInterceptor implements ApplicationContextAware{
private static final Logger log = Logger.getLogger(AuditTrailInterceptor)
- static final Properties CONF = ConfigurationHolder.config.toProperties()
- static final String CREATED_BY = CONF.getProperty("stamp.audit.createdBy")
- static final String EDITED_BY = CONF.getProperty("stamp.audit.editedBy")
- static final String EDITED_DATE = CONF.getProperty("stamp.audit.editedDate")
- static final String CREATED_DATE = CONF.getProperty("stamp.audit.createdDate")
- static final String COMPANY_ID = CONF.getProperty("stamp.audit.companyId","companyId")
+ //injected
+ def grailsApplication
+ def currentUserClosure
+ String createdByField
+ String editedByField
+ String editedDateField
+ String createdDateField
+ String companyIdField
+
+ ApplicationContext applicationContext
+
static Long ANONYMOUS_USER = 0
boolean onFlushDirty(Object entity, Serializable id, Object[] currentState,Object[] previousState, String[] propertyNames,Type[] types) {
def metaClass = entity.metaClass
- MetaProperty property = metaClass.hasProperty(entity, EDITED_DATE)
+ MetaProperty property = metaClass.hasProperty(entity, editedDateField)
List fieldList = propertyNames.toList()
-
+
if(property) {
def now = property.getType().newInstance([System.currentTimeMillis()] as Object[] )
- setValue(currentState, fieldList, EDITED_DATE, now)
+ setValue(currentState, fieldList, editedDateField, now)
}
- property = metaClass.hasProperty(entity,EDITED_BY)
+ property = metaClass.hasProperty(entity,editedByField)
if(property) {
- setValue(currentState, fieldList, EDITED_BY, getUserID())
+ setValue(currentState, fieldList, editedByField, getUserID())
}
return true
}
- boolean onSave(Object entity, Serializable id, Object[] state,String[] propertyNames, Type[] types) {
+ boolean onSave(Object entity, Serializable id, Object[] state,String[] propertyNames, Type[] types) {
def metaClass = entity.metaClass
- MetaProperty property = metaClass.hasProperty(entity, CREATED_DATE)
+ MetaProperty property = metaClass.hasProperty(entity, createdDateField)
def time = System.currentTimeMillis()
List fieldList = propertyNames.toList()
- Long userId = getUserID()
+ def userId = getUserID()
if(property) {
def now = property.getType().newInstance([time] as Object[] )
- setValue(state, fieldList, CREATED_DATE, now)
+ setValue(state, fieldList, createdDateField, now)
}
- property = metaClass.hasProperty(entity,EDITED_DATE)
+ property = metaClass.hasProperty(entity,editedDateField)
if(property) {
def now = property.getType().newInstance([time] as Object[] )
- setValue(state, fieldList, EDITED_DATE, now)
+ setValue(state, fieldList, editedDateField, now)
}
- property = metaClass.hasProperty(entity,EDITED_BY)
+ property = metaClass.hasProperty(entity,editedByField)
if(property) {
- setValue(state, fieldList, EDITED_BY, userId)
+ setValue(state, fieldList, editedByField, userId)
}
- property = metaClass.hasProperty(entity,CREATED_BY)
+ property = metaClass.hasProperty(entity,createdByField)
if(property) {
- setValue(state, fieldList, CREATED_BY, userId)
+ setValue(state, fieldList, createdByField, userId)
}
- property = metaClass.hasProperty(entity,COMPANY_ID)
- def authPrincipal = SCH.context.authentication?.principal
- if(property && authPrincipal && authPrincipal != "anonymousUser") {
- def curvalue = entity."$COMPANY_ID"
- if(curvalue==null || curvalue==0){ //only update if its 0 or null
- setValue(state, fieldList, COMPANY_ID, getCompanyId(authPrincipal))
+ property = metaClass.hasProperty(entity,companyIdField)
+
+ if(property) {
+ def curvalue = entity."$companyIdField"
+ if(curvalue==null || curvalue==0 && userGoodForCompanyId() ){ //only update if its 0 or null
+ setValue(state, fieldList, companyIdField, getCompanyId())
}
}
return true
- }
+ }
- def setValue(Object[] currentState, List fieldList, String propertyToSet, Object value) {
- int index = fieldList.indexOf(propertyToSet)
- if (index >= 0) {
- currentState[index] = value
- }
- }
+ def setValue(Object[] currentState, List fieldList, String propertyToSet, Object value) {
+ int index = fieldList.indexOf(propertyToSet)
+ if (index >= 0) {
+ currentState[index] = value
+ }
+ }
- Long getUserID() {
- def authPrincipal = SCH.context.authentication?.principal
+ def getUserID() {
+ def userClos = currentUserClosure?:getSpringSecurityUser
+ return userClos(applicationContext)
+ }
+
+ def getSpringSecurityUser = { ctx ->
+ def authPrincipal = ctx.springSecurityService.principal
// Added check for error coming while creating new company
if(authPrincipal && authPrincipal != "anonymousUser"){
return authPrincipal.id
} else {
- return ANONYMOUS_USER
+ return 0 //fall back
+ }
+ }
+
+ def userGoodForCompanyId(){
+ def authPrincipal = applicationContext.springSecurityService.principal
+ if(authPrincipal && authPrincipal != "anonymousUser"){
+ return true
+ }else{
+ return false
}
}
- Long getCompanyId(authPrincipal) {
- return authPrincipal.hasProperty('companyId')?authPrincipal.companyId:0
+ Long getCompanyId() {
+ def authPrincipal = applicationContext.springSecurityService.principal
+ return authPrincipal.hasProperty(companyIdField)?authPrincipal.companyId:0
}
}
View
277 src/java/gorm/AuditStampASTTransformation.java
@@ -31,176 +31,102 @@
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.transform.ASTTransformation;
import org.codehaus.groovy.transform.GroovyASTTransformation;
+import org.codehaus.groovy.ast.builder.AstBuilder;
+
/**
- * Performs an AST transformation on a class - adds createdBy/createdDate
- * editedBy/EditedDate id and table properties to the subject class.
+ * Performs an ast transformation on a class - adds createdBy/createdDate editedBy/EditedDate id and table
+ * properties to the subject class.
*/
@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
public class AuditStampASTTransformation implements ASTTransformation {
-
- // private static final Log LOG =
- // LogFactory.getLog(AuditStampASTTransformation.class);
- private static final ConfigObject CO = new ConfigSlurper()
- .parse(getContents(new File("./grails-app/conf/Config.groovy")));
- private static final Properties CONF = (new ConfigSlurper()
- .parse(getContents(new File("./grails-app/conf/Config.groovy"))))
- .toProperties();
-
- static {
- // System.out.println("greenbill is [" +
- // getMap(CO,"stamp.mapping.pluralTable").getClass()+ "]");
- // ConfigurationHolder.setConfig(CO);
- // Map confmap = ConfigurationHolder.getFlatConfig();
- }
+ //private static final Log LOG = LogFactory.getLog(AuditStampASTTransformation.class);
+
+ private static final ConfigObject CO = new ConfigSlurper().parse(getContents(new File("./grails-app/conf/Config.groovy")));
+ private static final Properties CONF = (new ConfigSlurper().parse(getContents(new File("./grails-app/conf/Config.groovy")))).toProperties();
public void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
+ //System.out.println("1. ConfigObject : " + CO);
+ Map configMap = (Map)CO.flatten();
+
+ NewFieldProps createdByField = NewFieldProps.init("createdBy","java.lang.Long",configMap);
+ NewFieldProps editedByField = NewFieldProps.init("editedBy","java.lang.Long",configMap);
- String createdByField = CONF.getProperty("stamp.audit.createdBy");
- String editedByField = CONF.getProperty("stamp.audit.editedBy");
- String editedDateField = CONF.getProperty("stamp.audit.editedDate");
- String createdDateField = CONF.getProperty("stamp.audit.createdDate");
+ NewFieldProps editedDateField = NewFieldProps.init("editedDate","java.util.Date",configMap);
+ NewFieldProps createdDateField = NewFieldProps.init("createdDate","java.util.Date",configMap);
for (ASTNode astNode : astNodes) {
if (astNode instanceof ClassNode) {
ClassNode classNode = (ClassNode) astNode;
- // List<FieldNode> fnlist = classNode.getFields();
- // LOG.info("[Audit stamp ASTTransformation] Adding propertie [edited..created] to class ["
- // + classNode.getName() + "]");
- // System.out.println(classNode.getName() +
- // " - [Audit stamp ASTTransformation] Adding propertie [edited..created]");
- if (editedByField != null) {
- classNode.addProperty(editedByField, Modifier.PUBLIC,
- new ClassNode(Long.class),
- new ConstantExpression(0), null, null);
+ List<FieldNode> fnlist = classNode.getFields();
+ //LOG.info("[Audit stamp ASTTransformation] Adding propertie [edited..created] to class [" + classNode.getName() + "]");
+ //System.out.println(classNode.getName() + " - [Audit stamp ASTTransformation] Adding propertie [edited..created]");
+ if(editedByField!=null){
+ //addFieldNode(classNode,editedByField);
+ classNode.addProperty(editedByField.name, Modifier.PUBLIC, new ClassNode(editedByField.type), new ConstantExpression(0), null, null);
}
- if (createdByField != null) {
- classNode.addProperty(createdByField, Modifier.PUBLIC,
- new ClassNode(Long.class),
- new ConstantExpression(0), null, null);
+ if(createdByField!=null){
+ classNode.addProperty(createdByField.name, Modifier.PUBLIC, new ClassNode(createdByField.type), new ConstantExpression(0), null, null);
}
- Expression now = new ConstructorCallExpression(new ClassNode(
- java.util.Date.class),
- MethodCallExpression.NO_ARGUMENTS);
- if (createdDateField != null) {
- classNode.addProperty(createdDateField, Modifier.PUBLIC,
- new ClassNode(java.util.Date.class), now, null,
- null);
- addNullableConstraint(classNode, createdDateField);
+
+ if(editedDateField!=null){
+ Expression enow = new ConstructorCallExpression(new ClassNode(editedDateField.type),MethodCallExpression.NO_ARGUMENTS);
+ classNode.addProperty(editedDateField.name, Modifier.PUBLIC, new ClassNode(editedDateField.type), enow, null, null);
+ addNullableConstraint(classNode,editedDateField.name);
}
- if (editedDateField != null) {
- classNode.addProperty(editedDateField, Modifier.PUBLIC,
- new ClassNode(java.util.Date.class), now, null,
- null);
- addNullableConstraint(classNode, editedDateField);
+ if(createdDateField!=null){
+ Expression cnow = new ConstructorCallExpression(new ClassNode(createdDateField.type),MethodCallExpression.NO_ARGUMENTS);
+ classNode.addProperty(createdDateField.name, Modifier.PUBLIC, new ClassNode(createdDateField.type), cnow, null, null);
+ addNullableConstraint(classNode,createdDateField.name);
}
-
- /****
- * comment this out if you don't want our the custom ID and
- * table name stuff
- ****/
- addTableAndIdMapping(classNode);
+
}
}
}
- public void addTableAndIdMapping(ClassNode classNode) {
- FieldNode closure = classNode.getDeclaredField("mapping");
-
- if (closure != null) {
- boolean hasTable = hasFieldInClosure(closure, "table");
- boolean hasId = hasFieldInClosure(closure, "id");
-
- ClosureExpression exp = (ClosureExpression) closure
- .getInitialExpression();
- BlockStatement block = (BlockStatement) exp.getCode();
-
- // this just adds an s to the class name for the table if its not
- // specified
- Boolean pluralize = (Boolean) getMap(CO,
- "stamp.mapping.pluralTable");
- if (!hasTable && pluralize != null && pluralize) {
- String tablename = GrailsNameUtils.getShortName(classNode
- .getName()) + "s";
- // LOG.info("Added new mapping to assign table: " + tablename);
- MethodCallExpression tableMeth = new MethodCallExpression(
- VariableExpression.THIS_EXPRESSION,
- new ConstantExpression("table"),
- new ArgumentListExpression(new ConstantExpression(
- tablename)));
- // block = (BlockStatement) exp.getCode();
- block.addStatement(new ExpressionStatement(tableMeth));
- // System.out.println(classNode.getName()+" - Added table mapping "
- // + tablename );
- }
- // This adds the ID generator that we use for domian classes
- @SuppressWarnings("rawtypes")
- Map tableconf = (Map) getMap(CO, "stamp.mapping.id");
- if (!hasId && tableconf != null) {
- NamedArgumentListExpression namedarg = new NamedArgumentListExpression();
- if (tableconf.get("column") != null) {
- namedarg.addMapEntryExpression(new ConstantExpression(
- "column"),
- new ConstantExpression(tableconf.get("column")
- .toString()));
- }
- if (tableconf.get("generator") != null) {
- namedarg.addMapEntryExpression(new ConstantExpression(
- "generator"),
- new ConstantExpression(tableconf.get("generator")
- .toString()));
- }
- MethodCallExpression tableMeth = new MethodCallExpression(
- VariableExpression.THIS_EXPRESSION,
- new ConstantExpression("id"), namedarg);
- // block = (BlockStatement) exp.getCode();
- block.addStatement(new ExpressionStatement(tableMeth));
- // System.out.println(classNode.getName() +
- // " - Added ID mapping with "+ tableconf);
- }
- }
- // System.out.println(block.toString());
+ public void addFieldNode(ClassNode classNode,String fieldName){
+ String checkVersion = "Long " + fieldName + " = 0";
+ List<ASTNode> nodes = new AstBuilder().buildFromString(CompilePhase.CLASS_GENERATION,false,checkVersion);
+ FieldNode fnode = (FieldNode)nodes.get(0);
+ classNode.addField(fnode);
}
- public void addNullableConstraint(ClassNode classNode, String fieldName) {
+ public void addNullableConstraint(ClassNode classNode,String fieldName){
FieldNode closure = classNode.getDeclaredField("constraints");
- if (closure != null) {
+ if(closure!=null){
- ClosureExpression exp = (ClosureExpression) closure
- .getInitialExpression();
+ ClosureExpression exp = (ClosureExpression)closure.getInitialExpression();
BlockStatement block = (BlockStatement) exp.getCode();
- if (!hasFieldInClosure(closure, fieldName)) {
+ if(!hasFieldInClosure(closure,fieldName)){
NamedArgumentListExpression namedarg = new NamedArgumentListExpression();
- namedarg.addMapEntryExpression(new ConstantExpression(
- "nullable"), new ConstantExpression(true));
+ namedarg.addMapEntryExpression(new ConstantExpression("nullable"), new ConstantExpression(true));
MethodCallExpression constExpr = new MethodCallExpression(
- VariableExpression.THIS_EXPRESSION,
- new ConstantExpression(fieldName), namedarg);
+ VariableExpression.THIS_EXPRESSION,
+ new ConstantExpression(fieldName),
+ namedarg
+ );
block.addStatement(new ExpressionStatement(constExpr));
- // System.out.println(classNode.getName() +
- // " - Added nullabel constraint for "+ fieldName);
+ //System.out.println(classNode.getName() + " - Added nullabel constraint for "+ fieldName);
}
}
- // System.out.println(block.toString());
+ //System.out.println(block.toString());
}
- public boolean hasFieldInClosure(FieldNode closure, String fieldName) {
- if (closure != null) {
- ClosureExpression exp = (ClosureExpression) closure
- .getInitialExpression();
+
+
+ public boolean hasFieldInClosure(FieldNode closure, String fieldName){
+ if(closure != null){
+ ClosureExpression exp = (ClosureExpression) closure.getInitialExpression();
BlockStatement block = (BlockStatement) exp.getCode();
List<Statement> ments = block.getStatements();
- for (Statement expstat : ments) {
- if (expstat instanceof ExpressionStatement
- && ((ExpressionStatement) expstat).getExpression() instanceof MethodCallExpression) {
- MethodCallExpression methexp = (MethodCallExpression) ((ExpressionStatement) expstat)
- .getExpression();
- ConstantExpression conexp = (ConstantExpression) methexp
- .getMethod();
- if (conexp.getValue().equals(fieldName)) {
+ for(Statement expstat : ments){
+ if(expstat instanceof ExpressionStatement && ((ExpressionStatement)expstat).getExpression() instanceof MethodCallExpression){
+ MethodCallExpression methexp = (MethodCallExpression)((ExpressionStatement)expstat).getExpression();
+ ConstantExpression conexp = (ConstantExpression)methexp.getMethod();
+ if(conexp.getValue().equals(fieldName)){
return true;
}
}
@@ -209,50 +135,100 @@ public boolean hasFieldInClosure(FieldNode closure, String fieldName) {
return false;
}
+
static public String getContents(File aFile) {
- // ...checks on aFile are elided
+ //...checks on aFile are elided
StringBuilder contents = new StringBuilder();
try {
- // use buffering, reading one line at a time
- // FileReader always assumes default encoding is OK!
- BufferedReader input = new BufferedReader(new FileReader(aFile));
+ //use buffering, reading one line at a time
+ //FileReader always assumes default encoding is OK!
+ BufferedReader input = new BufferedReader(new FileReader(aFile));
try {
- String line = null;
- while ((line = input.readLine()) != null) {
+ String line = null;
+ while (( line = input.readLine()) != null){
contents.append(line);
contents.append(System.getProperty("line.separator"));
}
- } finally {
+ }
+ finally {
input.close();
}
- } catch (IOException ex) {
+ }
+ catch (IOException ex){
ex.printStackTrace();
}
return contents.toString();
}
- @SuppressWarnings("rawtypes")
static public Object getMap(Map configMap, String keypath) {
String keys[] = keypath.split("\\.");
Map map = configMap;
- for (String key : keys) {
+ for(String key : keys){
Object val = map.get(key);
- if (val != null) {
- // System.out.println("got a key for are " +key);
- if (val instanceof Map) {
- map = (Map) map.get(key);
- } else {
+ if(val !=null){
+ //System.out.println("got a key for are " +key);
+ if(val instanceof Map){
+ map = (Map)map.get(key);
+ } else{
return val;
}
- } else {
+ }else{
return null;
}
}
- return map;
+ return map;
}
+ //old but kept for reference
+ /*
+ public void addTableAndIdMapping(ClassNode classNode){
+ FieldNode closure = classNode.getDeclaredField("mapping");
+
+ if(closure!=null){
+ boolean hasTable=hasFieldInClosure(closure,"table");
+ boolean hasId=hasFieldInClosure(closure,"id");
+
+ ClosureExpression exp = (ClosureExpression)closure.getInitialExpression();
+ BlockStatement block = (BlockStatement) exp.getCode();
+
+ //this just adds an s to the class name for the table if its not specified
+ Boolean pluralize = (Boolean)getMap(CO,"stamp.mapping.pluralTable");
+ if(!hasTable && pluralize!=null && pluralize){
+ String tablename = GrailsClassUtils.getShortName(classNode.getName())+"s";
+ //LOG.info("Added new mapping to assign table: " + tablename);
+ MethodCallExpression tableMeth = new MethodCallExpression(
+ VariableExpression.THIS_EXPRESSION,
+ new ConstantExpression("table"),
+ new ArgumentListExpression(new ConstantExpression(tablename))
+ );
+ //block = (BlockStatement) exp.getCode();
+ block.addStatement(new ExpressionStatement(tableMeth));
+ //System.out.println(classNode.getName()+" - Added table mapping " + tablename );
+ }
+ //This adds the ID generator that we use for domian classes
+ Map tableconf = (Map)getMap(CO,"stamp.mapping.id");
+ if(!hasId && tableconf!=null){
+ NamedArgumentListExpression namedarg = new NamedArgumentListExpression();
+ if(tableconf.get("column") != null){
+ namedarg.addMapEntryExpression(new ConstantExpression("column"), new ConstantExpression(tableconf.get("column").toString()));
+ }
+ if(tableconf.get("generator") != null){
+ namedarg.addMapEntryExpression(new ConstantExpression("generator"), new ConstantExpression(tableconf.get("generator").toString()));
+ }
+ MethodCallExpression tableMeth = new MethodCallExpression(
+ VariableExpression.THIS_EXPRESSION,
+ new ConstantExpression("id"),
+ namedarg
+ );
+ //block = (BlockStatement) exp.getCode();
+ block.addStatement(new ExpressionStatement(tableMeth));
+ //System.out.println(classNode.getName() + " - Added ID mapping with "+ tableconf);
+ }
+ }
+ */
+ //System.out.println(block.toString());
}
@@ -327,3 +303,4 @@ public void addConstraintDefaults(ClassNode classNode){
*/
+
View
29 src/java/gorm/NewFieldProps.java
@@ -0,0 +1,29 @@
+package gorm;
+import groovy.util.ConfigObject;
+import java.util.Map;
+
+class NewFieldProps {
+
+ String name;
+ Class type;
+
+ public static NewFieldProps init(String defaultName,String defaultType, Map co) {
+ //System.out.println("ConfigObject : " + co);
+ if(co == null || co.isEmpty()) return null;
+ NewFieldProps newField = new NewFieldProps();
+ newField.name = (String)co.get("grails.plugin.audittrail." + defaultName + ".field");
+ if(newField.name == null){
+ newField.name = defaultName;
+ }
+ String className = (String)co.get("grails.plugin.audittrail." + defaultName + ".type");
+ if(className == null || className==""){
+ className = defaultType;
+ }
+ try {
+ newField.type = Class.forName(className);
+ }catch (ClassNotFoundException e) {
+ throw new RuntimeException("Class " + className + " could not be found for audittrail setting " + defaultName);
+ }
+ return newField;
+ }
+}
View
2  test/unit/nine/tests/TestDomainTests.groovy
@@ -9,7 +9,7 @@ import org.junit.*
/**
* See the API for {@link grails.test.mixin.domain.DomainClassUnitTestMixin} for usage instructions
*/
-@TestFor(TestDomain)
+//@TestFor(TestDomain)
class TestDomainTests {
void testSomething() {
Please sign in to comment.
Something went wrong with that request. Please try again.