<commit_before>= GR8 CRM - Content Management Plugin
<NAME> <<EMAIL>>
:description: Official documentation for the GR8 CRM Content Management Plugin
:keywords: groovy, grails, crm, gr8crm, documentation
:toc:
:numbered:
:icons: font
:imagesdir: ./images
:source-highlighter: prettify
:homepage: http://gr8crm.github.io
:gr8crm: GR8 CRM
:gr8source: https://github.com/technipelago/grails-crm-content
:license: This plugin is licensed with http://www.apache.org/licenses/LICENSE-2.0.html[Apache License version 2.0]

== Introduction

{gr8crm} is a set of http://www.grails.org/[Grails Web Application Framework]
plugins that makes it easy to develop web applications with CRM functionality.

You can find more information about {gr8crm} on the main documentation site {homepage}.

=== Customer Relationship Management

Customer relationship management (CRM) is a system for managing a company’s interactions with current and future customers.
It involves using technology to organize, automate and synchronize sales, marketing, customer service, and technical support.
http://en.wikipedia.org/wiki/Customer_relationship_management[Wikipedia]

The {gr8crm} "Ecosystem" currently contains over 40 Grails plugins. For a complete list of plugins see {homepage}.

Each {gr8crm} plugin defines a http://martinfowler.com/bliki/BoundedContext.html[Bounded Context]
that focus on one specific domain, for example *contact*, *project* or *document*.

== Content Management Plugin

This plugin provide storage and services for managing content in {gr8crm} applications.
Content can be any type of media like plain text, Microsoft Word, PDF, and images.
Content can be stored in folders or attached to domain instances.
Content can be shared with users of the application or shared publicly to the world.

Note that this plugin does not contain any user interface components. This plugin contains domain classes and services only.
The plugin http://gr8crm.github.io/plugins/crm-content-ui/[crm-content-ui] provides a Twitter Bootstrap based user interface
for managing files, folders and attachments. +crm-content-ui+ depends on +crm-content+ so you only need to include +crm-content-ui+
in your BuildConfig.groovy if you want end-user content management features.

The Content Management plugin is very generic and flexible. It can be used to provide content in any form.
Here are some real-wold examples on what type of content that can be stored and managed by this plugin.

- System HTML email templates that can be edited by an administrator
- Attach documents to customer records, like contracts, tenders or spreadsheets
- User guides in PDF or other eReader formats
- HTML page fragments to be included in Groovy Server Pages
- Complete HTML pages to be rendered as part of a web site

== Storage

The +crm-content+ plugin has configurable *storage providers*. The default storage provider store
content in the web server filesystem. Files on disk can optionally be encrypted for increased security.
Custom storage providers can be developed to integrate with content services like Amazon S3, Google Drive or Dropbox.
One application can have multiple storage providers and select the best storage provides based on some business logic.
Maybe HTML files should be stored in a S3 bucket and customer contracts encrypted on  a local disk.

== Attachments

Content is usually stored in folders but content can also be attached to domain instances.
In fact when content is stored in a folder, it is actually "attached" to a +CrmResourceFolder+ domain instance.
So there is no technical difference between files stored in folders and files attached to domain instances.

A +CrmResourceRef+ instance is used to attach content to a domain instance. The +ref+ property references
the domain instance using a *Resource Identifier* and the +res+ property contains the provider specific URI to the content.
This is nothing that you need to care about, the framework takes care about setting these properties when you create and attach content.


== CrmContentService

This is the main service that you use to create, find, edit and delete files and folders with.

+CrmResourceRef createResource(InputStream inputStream, String filename, Long length, String contentType, Object reference, Map params = [:])+

Create a new file.

+def withInputStream(URI uri, Closure work)+

For content referenced by a +URI+ create a new InputStream and pass it into a closure. This method ensures the stream is closed after the closure returns.

+Map<String, Object> getMetadata(URI resource)+

Get metadata for the content specified by +resource+. The metadata Map contains the following keys:

- *uri* the provider specific URI for the content
- *contentType* = MIME content type
- *bytes* = length in bytes
- *size* = formatted length
- *icon* = name of icon that best described the content
- *created* = Date instance when content was created
- *modified* = Date instance when content was last updated
- *hash* = MD5 hash of the content
- *encrypted* = type of encrypted storage (0 = no encryption)

+long updateResource(CrmResourceRef resource, InputStream inputStream, String contentType = null)+


[source,groovy]
.UpdateContent.groovy
----
def folder = crmContentService.createFolder(null, "test")
def bytes = "This is a test".getBytes()
def inputStream = new ByteArrayInputStream(bytes)
def ref = crmContentService.createResource(inputStream, "test1.txt", bytes.length, "text/plain", folder) <1>
bytes = "This is an updated test".getBytes()
inputStream = new ByteArrayInputStream(bytes)
crmContentService.updateResource(ref, inputStream) <2>
def result = new ByteArrayOutputStream()
ref.writeTo(result)
def s = new String(result.toByteArray())
assert s == "This is an updated test"
----
<1> Create a file with content "This is a test"
<2> Update the content to "This is an updated test"


== CrmFreeMarkerService

The *FreeMarker* service is used when you want to store FreeMarker templates with the +crm-content+ plugin.
You can use FreeMarker templates when you send email or render HTML pages.
If used together with the +crm-content-ui+ plugin you can let administrators edit templates.

+void process(String templatePath, Map binding, Writer out)+

Let FreeMarker parse the template located at +templatePath+ in the current tenant.
Values in +binding+ can be referenced from the template. The output is written to +out+.

+void process(Long tenant, String templatePath, Map binding, Writer out)+

Same as above but a tenant can be specified from which templates will be retrieved.

+void process(CrmResourceRef ref, Map binding, Writer out)+

Same as above but an instance of +CrmResourceRef+ will be used as template.


=== Events

You can also send an asynchronous event that results in a FreeMarker template being parsed.

[source,groovy]
.EventBasedTemplateParsingTests.groovy
----
def reply = event(namespace: 'crm', topic: 'parseTemplate', data: [template: '/templates/hello.txt', greet: 'Groovy'])
assert reply.value == 'Hello Groovy World'
----


== Code Samples

=== Create a folder

[source,groovy]
.CreateFolders.groovy
----
def rootFolder = crmContentService.createFolder(null, "templates")
def subFolder = crmContentService.createFolder(rootFolder, "powerpoint")
----

=== Create a file
[source.groovy]
.CreateFile.groovy
----
def bytes = "Hello World".getBytes()
def inputStream = new ByteArrayInputStream(bytes) <1>
def folder = crmContentService.createFolder(null, "files")
def doc = crmContentService.createResource(inputStream, "hello.txt", bytes.length, "text/plain", folder)
assert doc.title == "test1"
assert doc.name == "test1.txt"
assert doc.text == "Hello World"
----
<1> The stream is closed by createResource(...)


== Configuration

+crm.content.encryption.algorithm+

This property defines what encryption algorithm to use when storing files.
File are by default stored in the filesystem on the application server.
One of the following algorithms can be used:

+grails.plugins.crm.content.CrmFileResource.NO_ENCRYPTION+ (default)

Files are not encrypted, they are stored in original form.

+grails.plugins.crm.content.CrmFileResource.AES_ENCRYPTION+

Files are encrypted with AES encryption
<commit_msg>More docs<commit_after>= GR8 CRM - Content Management Plugin
<NAME> <<EMAIL>>
:description: Official documentation for the GR8 CRM Content Management Plugin
:keywords: groovy, grails, crm, gr8crm, documentation
:toc:
:numbered:
:icons: font
:imagesdir: ./images
:source-highlighter: prettify
:homepage: http://gr8crm.github.io
:gr8crm: GR8 CRM
:gr8source: https://github.com/technipelago/grails-crm-content
:license: This plugin is licensed with http://www.apache.org/licenses/LICENSE-2.0.html[Apache License version 2.0]

== Introduction

{gr8crm} is a set of http://www.grails.org/[Grails Web Application Framework]
plugins that makes it easy to develop web applications with CRM functionality.

You can find more information about {gr8crm} on the main documentation site {homepage}.

=== Customer Relationship Management

Customer relationship management (CRM) is a system for managing a company’s interactions with current and future customers.
It involves using technology to organize, automate and synchronize sales, marketing, customer service, and technical support.
http://en.wikipedia.org/wiki/Customer_relationship_management[Wikipedia]

The {gr8crm} "Ecosystem" currently contains over 40 Grails plugins. For a complete list of plugins see {homepage}.

Each {gr8crm} plugin defines a http://martinfowler.com/bliki/BoundedContext.html[Bounded Context]
that focus on one specific domain, for example *contact*, *project* or *document*.

== Content Management Plugin

This plugin provide storage and services for managing content in {gr8crm} applications.
Content can be any type of media like plain text, Microsoft Word, PDF, and images.
Content can be stored in folders or attached to domain instances.
Content can be shared with users of the application or shared publicly to the world.

Note that this plugin does not contain any user interface components. This plugin contains domain classes and services only.
The plugin http://gr8crm.github.io/plugins/crm-content-ui/[crm-content-ui] provides a Twitter Bootstrap based user interface
for managing files, folders and attachments. +crm-content-ui+ depends on +crm-content+ so you only need to include +crm-content-ui+
in your BuildConfig.groovy if you want end-user content management features.

The Content Management plugin is very generic and flexible. It can be used to provide content in any form.
Here are some real-wold examples on what type of content that can be stored and managed by this plugin.

- System HTML email templates that can be edited by an administrator
- Attach documents to customer records, like contracts, tenders or spreadsheets
- User guides in PDF or other eReader formats
- HTML page fragments to be included in Groovy Server Pages
- Complete HTML pages to be rendered as part of a web site

== Storage

The +crm-content+ plugin has configurable *storage providers*. The default storage provider store
content in the web server filesystem. Files on disk can optionally be encrypted for increased security.
Custom storage providers can be developed to integrate with content services like Amazon S3, Google Drive or Dropbox.
One application can have multiple storage providers and select the best storage provides based on some business logic.
Maybe HTML files should be stored in a S3 bucket and customer contracts encrypted on  a local disk.

== Attachments

Content is usually stored in folders but content can also be attached to domain instances.
In fact when content is stored in a folder, it is actually "attached" to a +CrmResourceFolder+ domain instance.
So there is no technical difference between files stored in folders and files attached to domain instances.

A +CrmResourceRef+ instance is used to attach content to a domain instance. The +ref+ property references
the domain instance using a *Resource Identifier* and the +res+ property contains the provider specific URI to the content.
This is nothing that you need to care about, the framework takes care about setting these properties when you create and attach content.

== Security

Content can contain sensitive information, therefore security is a very important in {gr8crm} applications.
All access to content is checked to make sure the user accessing content has the right privileges.
An instance of the domain class +CrmResourceRef+ is always involved when working with content. It represents a "handle"
to the content. It has properties like +filename+, +title+ and +description+. Another important property is +status+ that
specifies the status and access rights for the content. The class +CrmResourceRef+ has one static +int+ constant for each
possible status a content instance can have. The constants are listed below.

.Content Status
[options="header"]
|===
| Constant                        | Description
| CrmResourceRef.STATUS_ARCHIVED  | Content is archived and only accessible by authenticated users
| CrmResourceRef.STATUS_DRAFT     | Content is in draft state and only accessible by authenticated users
| CrmResourceRef.STATUS_PUBLISHED | Content is active/current but only accessible by authenticated users
| CrmResourceRef.STATUS_SHARED    | Content is accessible by anyone, no authentication needed = *public access if URL is known!*
|===

== CrmContentService

This is the main service that you use to create, find, edit and delete files and folders with.

=== createResource

+CrmResourceRef createResource(InputStream inputStream, String filename, Long length, String contentType, Object reference, Map params = [:])+

Create a new file from an InputStream.

[options="header"]
|===
| Parameter    | Description
| inputStream  | The input stream to read content from
| filename     | Name of content, this is later used when accessing this content
| length       | Content length in bytes
| contentType  | MIME content type
| reference    | a domain instance or a reference identifier to attach the content to
| params       | optional parameters like +status+, +title+ and +description+ for the content
|===

If the content creation succeeds an instance of +CrmResourceRef+ is returned. This is an active "handle" to the content.

The +resource+ property on CrmResourceRef return a +URI+ instance. This URI is used by other service methods, for example when reading and writing content.

The following code copies (moves) a PDF file from the server to a */presentations* folder in {gr8crm}.

[source,groovy]
.CreateContent.groovy
----
def folder = crmContentService.createFolder(null, "presentations")
def serverFile = new File("presentation.pdf")
def pdf = serverFile.withInputStream{inputStream->
    crmContentService.createResource(inputStream, serverFile.name, serverFile.length(), "application/pdf", folder)
}
serverFile.delete() // <1>

assert pdf.name == "presentation.pdf"
----
<1> The server file is copied into, and managed by {gr8crm} so it's not needed anymore.

=== withInputStream

+def withInputStream(URI uri, Closure work)+

For content referenced by a +URI+ create a new InputStream and pass it into a closure. This method ensures the stream is closed after the closure returns.

[source,groovy]
.CopyContentToServerFile.groovy
----
def content = crmContentService.getContentByPath("/presentations/2014/gr8conf/eu/goeh-feature-plugins.pdf")
crmContentService.withInputStream(content.resource) { inputStream ->
    new File("/tmp/feature-plugins.pdf").withOutputStream{ outputStream ->
        outputStream << inputStream
    }
}
----

=== writeTo

+long writeTo(URI uri, OutputStream out)+

Write content to an OutputStream.

[source,groovy]
.MyDocController.groovy
----
def show(Long id) {
    def content = crmContentService.getResourceRef(id) // <1>
    def metadata = content.metadata
    response.setContentType(metadata.contentType)
    response.setContentLength(metadata.bytes.intValue())
    crmContentService.writeTo(content.resource, response.outputStream) // <2>
}
----
<1> Lookup content by ID
<2> Render content to the response stream. This line can be shortened to: +content.writeTo(response.outputStream)+

=== getMetadata

+Map<String, Object> getMetadata(URI resource)+

Get metadata for the content specified by +resource+. The metadata Map contains the following keys:

[options="header"]
|===
| Key         | Description
| uri         | the provider specific URI for the content
| contentType | MIME content type
| bytes       | length in bytes
| size        | formatted length
| icon        | name of icon that best described the content
| created     | Date instance when content was created
| modified    | Date instance when content was last updated
| hash        | MD5 hash of the content
| encrypted   | type of encrypted storage (0 = no encryption)
|===

=== updateResource

+long updateResource(CrmResourceRef resource, InputStream inputStream, String contentType = null)+


[source,groovy]
.UpdateContent.groovy
----
def folder = crmContentService.createFolder(null, "test")
def bytes = "This is a test".getBytes()
def inputStream = new ByteArrayInputStream(bytes)
def ref = crmContentService.createResource(inputStream, "test1.txt", bytes.length, "text/plain", folder) <1>
bytes = "This is an updated test".getBytes()
inputStream = new ByteArrayInputStream(bytes)
crmContentService.updateResource(ref, inputStream) <2>
def result = new ByteArrayOutputStream()
ref.writeTo(result)
def s = new String(result.toByteArray())
assert s == "This is an updated test"
----
<1> Create a file with content "This is a test"
<2> Update the content to "This is an updated test"


== CrmFreeMarkerService

The *FreeMarker* service is used when you want to store http://freemarker.org[FreeMarker^] templates with the +crm-content+ plugin.
You can use FreeMarker templates when you send email or render HTML pages.
If used together with the +crm-content-ui+ plugin you can let administrators edit templates with an HTML editor.

=== process

+void process(String templatePath, Map binding, Writer out)+

Let FreeMarker parse the template located at +templatePath+ in the current tenant.
Values in +binding+ can be referenced from the template. The output is written to +out+.

+void process(Long tenant, String templatePath, Map binding, Writer out)+

Same as above but a tenant can be specified from which templates will be retrieved.

+void process(CrmResourceRef ref, Map binding, Writer out)+

Same as above but an instance of +CrmResourceRef+ will be used as template.


== Events

You can also send an asynchronous event that results in a template being parsed.

=== parseTemplate

[source,groovy]
.EventBasedTemplateParsingTests.groovy
----
def reply = event(namespace: 'crm', topic: 'parseTemplate', data: [template: '/templates/hello.txt', greet: 'Groovy'])
assert reply.value == 'Hello Groovy World'
----


== Code Samples

=== Create a folder

[source,groovy]
.CreateFolders.groovy
----
def rootFolder = crmContentService.createFolder(null, "templates")
def subFolder = crmContentService.createFolder(rootFolder, "powerpoint")
----

=== Create a file
[source.groovy]
.CreateFile.groovy
----
def bytes = "Hello World".getBytes()
def inputStream = new ByteArrayInputStream(bytes) <1>
def folder = crmContentService.createFolder(null, "files")
def doc = crmContentService.createResource(inputStream, "hello.txt", bytes.length, "text/plain", folder)
assert doc.title == "test1"
assert doc.name == "test1.txt"
assert doc.text == "Hello World"
----
<1> The stream is closed by createResource(...)

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.

== Configuration

+crm.content.encryption.algorithm+

This property defines what encryption algorithm to use when storing files.
File are by default stored in the filesystem on the application server.
One of the following algorithms can be used:

+grails.plugins.crm.content.CrmFileResource.NO_ENCRYPTION+ (default)

Files are not encrypted, they are stored in original form.

+grails.plugins.crm.content.CrmFileResource.AES_ENCRYPTION+

Files are encrypted with AES encryption

{'fraction_non_alphanumeric': 0.07776355940545329, 'fraction_numerical': 0.0038389605276109855, 'mean_word_length': 4.833763996554694, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 32, 'https://': 3, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4469990', 'n_tokens_mistral': 5409, 'n_tokens_neox': 5226, 'n_words': 2473}