anchor.diff:  ----
   
   === Create a file
  -[source.groovy]
  +[source,groovy]
   .CreateFile.groovy
   ----
   def bytes = "Hello World".getBytes()
  
positive.code: 
  == Changes
  
  2.4.5:: Make it easier to use multiple storage providers in the same application
  2.4.4:: withInputStream now returns what the closure returns (was void).
  2.4.3:: Fix for threading issues when loading FreeMarker templates from different tenants.
  
negative.code: 
  TIP: You can look at the source code for the https://github.com/technipelago/grails-crm-content/tree/master/test/integration/grails/plugins/crm/content[integration tests^] to find more code examples.
  
  === Save photos to Amazon S3
  
  The bean `crmContentRouter` is responsible for routing content from/to a `CrmContentProvider` when reading and writing content.
  The following example replaces the default content router with an implementation that looks at the content type and size.
  All large images attached to contacts (`CrmContact`) are stored in Amazon S3 and all other content are stored by the default content provider (the local file system).
  The `awsContentProvider` bean is provided by `crm-content-aws` plugin.
  
  [source,groovy]
  .grails-app/conf/spring/resources.groovy
  ----
  import grails.plugins.crm.contact.CrmContact
  
  beans = {
      crmContentRouter(grails.plugins.crm.content.PatternContentRouter, ref("crmCoreService")) { bean ->
          bean.autowire = 'byName'
  
          pattern = /.*\.(jpg|jpeg|png)$/
          minLength = 131072
          referenceClass = CrmContact
          defaultProvider = ref("crmFileContentProvider")
          provider = ref("awsContentProvider")
      }
  }
  ----
  
  == Tag Library
  
  === render
  