mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
Initial cut of IMAP support (disabled by default, to enable move imap sample files into extension folder)
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@14279 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -473,6 +473,9 @@
|
||||
<property name="companyHomePath">
|
||||
<value>/${spaces.company_home.childname}</value>
|
||||
</property>
|
||||
<property name="webApplicationContextUrl">
|
||||
<value>${web.application.context.url}</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="counter" class="org.alfresco.repo.action.executer.CounterIncrementActionExecuter" parent="action-executer">
|
||||
|
@@ -52,6 +52,7 @@
|
||||
<import resource="classpath:alfresco/form-services-context.xml"/>
|
||||
<import resource="classpath:alfresco/invitation-service-context.xml"/>
|
||||
<import resource="classpath:alfresco/cmis-api-context.xml" />
|
||||
<import resource="classpath:alfresco/imap-server-context.xml"/>
|
||||
<import resource="classpath*:alfresco/patch/*-context.xml" />
|
||||
<import resource="classpath*:alfresco/domain/*-context.xml" />
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
<view:view xmlns:view="http://www.alfresco.org/view/repository/1.0"
|
||||
xmlns:cm="http://www.alfresco.org/model/content/1.0"
|
||||
xmlns:app="http://www.alfresco.org/model/application/1.0">
|
||||
xmlns:app="http://www.alfresco.org/model/application/1.0"
|
||||
xmlns:emailserver="http://www.alfresco.org/model/emailserver/1.0">
|
||||
|
||||
<!-- NOTE: all replaced properties referenced from repository.properties file must also be
|
||||
mapped in the bootstrap-context.xml spacesBootstrap/configuration section -->
|
||||
@@ -24,34 +25,34 @@
|
||||
<view:permission>Consumer</view:permission>
|
||||
</view:ace>
|
||||
</view:acl>
|
||||
<app:uifacets/>
|
||||
<cm:name>${spaces.dictionary.name}</cm:name>
|
||||
<app:icon>space-icon-default</app:icon>
|
||||
<cm:title>${spaces.dictionary.name}</cm:title>
|
||||
<cm:description>${spaces.dictionary.description}</cm:description>
|
||||
<cm:contains>
|
||||
<cm:folder view:childName="${spaces.templates.childname}">
|
||||
<app:uifacets/>
|
||||
<cm:name>${spaces.templates.name}</cm:name>
|
||||
<app:icon>space-icon-default</app:icon>
|
||||
<cm:title>${spaces.templates.name}</cm:title>
|
||||
<cm:description>${spaces.templates.description}</cm:description>
|
||||
</cm:folder>
|
||||
<cm:folder
|
||||
view:childName="${spaces.templates.content.childname}">
|
||||
<app:uifacets/>
|
||||
<cm:name>${spaces.templates.content.name}</cm:name>
|
||||
<app:icon>space-icon-default</app:icon>
|
||||
<cm:title>${spaces.templates.content.name}</cm:title>
|
||||
<cm:description>${spaces.templates.content.description}</cm:description>
|
||||
</cm:folder>
|
||||
<cm:folder
|
||||
view:childName="${spaces.templates.email.childname}">
|
||||
<app:uifacets/>
|
||||
<cm:name>${spaces.templates.email.name}</cm:name>
|
||||
<app:icon>space-icon-default</app:icon>
|
||||
<cm:title>${spaces.templates.email.name}</cm:title>
|
||||
<cm:description>${spaces.templates.email.description}</cm:description>
|
||||
<app:uifacets/>
|
||||
<cm:name>${spaces.dictionary.name}</cm:name>
|
||||
<app:icon>space-icon-default</app:icon>
|
||||
<cm:title>${spaces.dictionary.name}</cm:title>
|
||||
<cm:description>${spaces.dictionary.description}</cm:description>
|
||||
<cm:contains>
|
||||
<cm:folder view:childName="${spaces.templates.childname}">
|
||||
<app:uifacets/>
|
||||
<cm:name>${spaces.templates.name}</cm:name>
|
||||
<app:icon>space-icon-default</app:icon>
|
||||
<cm:title>${spaces.templates.name}</cm:title>
|
||||
<cm:description>${spaces.templates.description}</cm:description>
|
||||
</cm:folder>
|
||||
<cm:folder
|
||||
view:childName="${spaces.templates.content.childname}">
|
||||
<app:uifacets/>
|
||||
<cm:name>${spaces.templates.content.name}</cm:name>
|
||||
<app:icon>space-icon-default</app:icon>
|
||||
<cm:title>${spaces.templates.content.name}</cm:title>
|
||||
<cm:description>${spaces.templates.content.description}</cm:description>
|
||||
</cm:folder>
|
||||
<cm:folder
|
||||
view:childName="${spaces.templates.email.childname}">
|
||||
<app:uifacets/>
|
||||
<cm:name>${spaces.templates.email.name}</cm:name>
|
||||
<app:icon>space-icon-default</app:icon>
|
||||
<cm:title>${spaces.templates.email.name}</cm:title>
|
||||
<cm:description>${spaces.templates.email.description}</cm:description>
|
||||
<cm:contains>
|
||||
<cm:folder
|
||||
view:childName="${spaces.templates.email.invite.childname}">
|
||||
@@ -80,69 +81,68 @@
|
||||
</cm:contains>
|
||||
</cm:folder>
|
||||
</cm:contains>
|
||||
</cm:folder>
|
||||
<cm:folder
|
||||
view:childName="${spaces.templates.rss.childname}">
|
||||
<view:acl>
|
||||
<view:ace view:access="ALLOWED">
|
||||
<view:authority>${alfresco_user_store.guestusername}</view:authority>
|
||||
<view:permission>Consumer</view:permission>
|
||||
</view:ace>
|
||||
</view:acl>
|
||||
<app:uifacets/>
|
||||
<cm:name>${spaces.templates.rss.name}</cm:name>
|
||||
<app:icon>space-icon-default</app:icon>
|
||||
<cm:title>${spaces.templates.rss.name}</cm:title>
|
||||
<cm:description>${spaces.templates.rss.description}</cm:description>
|
||||
</cm:folder>
|
||||
<cm:folder
|
||||
view:childName="${spaces.savedsearches.childname}">
|
||||
<view:acl view:inherit="false">
|
||||
<view:ace view:access="ALLOWED">
|
||||
<view:authority>GROUP_EVERYONE</view:authority>
|
||||
<view:permission>Contributor</view:permission>
|
||||
</view:ace>
|
||||
</view:acl>
|
||||
<app:uifacets/>
|
||||
<cm:name>${spaces.savedsearches.name}</cm:name>
|
||||
<app:icon>space-icon-default</app:icon>
|
||||
<cm:title>${spaces.savedsearches.name}</cm:title>
|
||||
<cm:description>${spaces.savedsearches.description}</cm:description>
|
||||
</cm:folder>
|
||||
<cm:folder view:childName="${spaces.scripts.childname}">
|
||||
<app:uifacets/>
|
||||
<cm:name>${spaces.scripts.name}</cm:name>
|
||||
<app:icon>space-icon-default</app:icon>
|
||||
<cm:title>${spaces.scripts.name}</cm:title>
|
||||
<cm:description>${spaces.scripts.description}</cm:description>
|
||||
</cm:folder>
|
||||
</cm:contains>
|
||||
</cm:folder>
|
||||
<cm:folder view:childName="${spaces.guest_home.childname}">
|
||||
<view:acl view:inherit="false">
|
||||
<view:ace view:access="ALLOWED">
|
||||
<view:authority>${alfresco_user_store.guestusername}</view:authority>
|
||||
<view:permission>Consumer</view:permission>
|
||||
</view:ace>
|
||||
<view:ace view:access="ALLOWED">
|
||||
<view:authority>GROUP_EVERYONE</view:authority>
|
||||
<view:permission>Consumer</view:permission>
|
||||
</view:ace>
|
||||
</view:acl>
|
||||
<app:uifacets/>
|
||||
<cm:name>${spaces.guest_home.name}</cm:name>
|
||||
<app:icon>space-icon-default</app:icon>
|
||||
<cm:title>${spaces.guest_home.name}</cm:title>
|
||||
<cm:description>${spaces.guest_home.description}</cm:description>
|
||||
</cm:folder>
|
||||
<cm:folder view:childName="${spaces.user_homes.childname}">
|
||||
<app:uifacets/>
|
||||
<cm:name>${spaces.user_homes.name}</cm:name>
|
||||
<app:icon>space-icon-default</app:icon>
|
||||
<cm:title>${spaces.user_homes.name}</cm:title>
|
||||
<cm:description>${spaces.user_homes.description}</cm:description>
|
||||
</cm:folder>
|
||||
</cm:contains>
|
||||
</cm:folder>
|
||||
|
||||
</cm:folder>
|
||||
<cm:folder
|
||||
view:childName="${spaces.templates.rss.childname}">
|
||||
<view:acl>
|
||||
<view:ace view:access="ALLOWED">
|
||||
<view:authority>${alfresco_user_store.guestusername}</view:authority>
|
||||
<view:permission>Consumer</view:permission>
|
||||
</view:ace>
|
||||
</view:acl>
|
||||
<app:uifacets/>
|
||||
<cm:name>${spaces.templates.rss.name}</cm:name>
|
||||
<app:icon>space-icon-default</app:icon>
|
||||
<cm:title>${spaces.templates.rss.name}</cm:title>
|
||||
<cm:description>${spaces.templates.rss.description}</cm:description>
|
||||
</cm:folder>
|
||||
<cm:folder
|
||||
view:childName="${spaces.savedsearches.childname}">
|
||||
<view:acl view:inherit="false">
|
||||
<view:ace view:access="ALLOWED">
|
||||
<view:authority>GROUP_EVERYONE</view:authority>
|
||||
<view:permission>Contributor</view:permission>
|
||||
</view:ace>
|
||||
</view:acl>
|
||||
<app:uifacets/>
|
||||
<cm:name>${spaces.savedsearches.name}</cm:name>
|
||||
<app:icon>space-icon-default</app:icon>
|
||||
<cm:title>${spaces.savedsearches.name}</cm:title>
|
||||
<cm:description>${spaces.savedsearches.description}</cm:description>
|
||||
</cm:folder>
|
||||
<cm:folder view:childName="${spaces.scripts.childname}">
|
||||
<app:uifacets/>
|
||||
<cm:name>${spaces.scripts.name}</cm:name>
|
||||
<app:icon>space-icon-default</app:icon>
|
||||
<cm:title>${spaces.scripts.name}</cm:title>
|
||||
<cm:description>${spaces.scripts.description}</cm:description>
|
||||
</cm:folder>
|
||||
</cm:contains>
|
||||
</cm:folder>
|
||||
<cm:folder view:childName="${spaces.guest_home.childname}">
|
||||
<view:acl view:inherit="false">
|
||||
<view:ace view:access="ALLOWED">
|
||||
<view:authority>${alfresco_user_store.guestusername}</view:authority>
|
||||
<view:permission>Consumer</view:permission>
|
||||
</view:ace>
|
||||
<view:ace view:access="ALLOWED">
|
||||
<view:authority>GROUP_EVERYONE</view:authority>
|
||||
<view:permission>Consumer</view:permission>
|
||||
</view:ace>
|
||||
</view:acl>
|
||||
<app:uifacets/>
|
||||
<cm:name>${spaces.guest_home.name}</cm:name>
|
||||
<app:icon>space-icon-default</app:icon>
|
||||
<cm:title>${spaces.guest_home.name}</cm:title>
|
||||
<cm:description>${spaces.guest_home.description}</cm:description>
|
||||
</cm:folder>
|
||||
<cm:folder view:childName="${spaces.user_homes.childname}">
|
||||
<app:uifacets/>
|
||||
<cm:name>${spaces.user_homes.name}</cm:name>
|
||||
<app:icon>space-icon-default</app:icon>
|
||||
<cm:title>${spaces.user_homes.name}</cm:title>
|
||||
<cm:description>${spaces.user_homes.description}</cm:description>
|
||||
</cm:folder>
|
||||
</cm:contains>
|
||||
</cm:folder>
|
||||
</view:view>
|
@@ -49,7 +49,7 @@
|
||||
<cm:userName>${alfresco_user_store.adminusername}</cm:userName>
|
||||
<cm:firstName>Administrator</cm:firstName>
|
||||
<cm:lastName></cm:lastName>
|
||||
<cm:email></cm:email>
|
||||
<cm:email>admin@alfresco.com</cm:email>
|
||||
<cm:organizationId></cm:organizationId>
|
||||
<cm:homeFolder>/${spaces.company_home.childname}</cm:homeFolder>
|
||||
<cm:homeFolderProvider>bootstrapHomeFolderProvider</cm:homeFolderProvider>
|
||||
|
@@ -928,6 +928,7 @@
|
||||
<value>alfresco/model/bpmModel.xml</value>
|
||||
<value>alfresco/model/wcmModel.xml</value>
|
||||
<value>alfresco/model/forumModel.xml</value>
|
||||
<value>alfresco/model/imapModel.xml</value>
|
||||
|
||||
<!-- Content models -->
|
||||
<value>alfresco/model/applicationModel.xml</value>
|
||||
|
19
config/alfresco/extension/custom-imap-server-context.xml.sample
Executable file
19
config/alfresco/extension/custom-imap-server-context.xml.sample
Executable file
@@ -0,0 +1,19 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
|
||||
|
||||
<beans>
|
||||
|
||||
<bean id="imapServerConfigurationProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
|
||||
<property name="ignoreUnresolvablePlaceholders">
|
||||
<value>true</value>
|
||||
</property>
|
||||
<property name="locations">
|
||||
<list>
|
||||
<value>classpath:alfresco/imap-server.properties</value>
|
||||
<!-- Override -->
|
||||
<value>classpath:alfresco/extension/custom-imap-server.properties</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
2
config/alfresco/extension/custom-imap-server.properties.sample
Executable file
2
config/alfresco/extension/custom-imap-server.properties.sample
Executable file
@@ -0,0 +1,2 @@
|
||||
imap.server.enabled=true
|
||||
imap.server.port=143
|
40
config/alfresco/extension/imap-bootsrap-context.xml.sample
Executable file
40
config/alfresco/extension/imap-bootsrap-context.xml.sample
Executable file
@@ -0,0 +1,40 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
|
||||
|
||||
<beans>
|
||||
<bean id="patch.imapFolders" class="org.alfresco.repo.admin.patch.impl.ImapFoldersPatch" parent="basePatch" >
|
||||
<property name="id"><value>patch.imapFolders</value></property>
|
||||
<property name="description"><value>patch.imapFolders.description</value></property>
|
||||
<property name="fixesFromSchema"><value>0</value></property>
|
||||
<property name="fixesToSchema"><value>${version.schema}</value></property>
|
||||
<property name="targetSchema"><value>10000</value></property>
|
||||
<property name="importerBootstrap">
|
||||
<ref bean="spacesBootstrap" />
|
||||
</property>
|
||||
<property name="messageSource">
|
||||
<ref bean="bootstrapSpacesMessageSource" />
|
||||
</property>
|
||||
<property name="importerService">
|
||||
<ref bean="importerComponent" />
|
||||
</property>
|
||||
<property name="configFoldersACP"><value>alfresco/templates/imap/imap_config_space.acp</value></property>
|
||||
<property name="emailActionsACP"><value>alfresco/templates/imap/email_actions_space.acp</value></property>
|
||||
<property name="scriptsACP"><value>alfresco/templates/imap/command_processor_scripts.acp</value></property>
|
||||
</bean>
|
||||
|
||||
<bean id="patch.imapUserFolders" class="org.alfresco.repo.admin.patch.impl.ImapUsersPatch" parent="basePatch" >
|
||||
<property name="id"><value>patch.imapUserFolders</value></property>
|
||||
<property name="description"><value>patch.imapUserFolders.description</value></property>
|
||||
<property name="fixesFromSchema"><value>0</value></property>
|
||||
<property name="fixesToSchema"><value>${version.schema}</value></property>
|
||||
<property name="targetSchema"><value>10000</value></property>
|
||||
<!-- helper beans for execution -->
|
||||
<property name="messageSource">
|
||||
<ref bean="bootstrapSpacesMessageSource" />
|
||||
</property>
|
||||
<property name="importerBootstrap">
|
||||
<ref bean="spacesBootstrap" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
14
config/alfresco/imap-config.xml
Executable file
14
config/alfresco/imap-config.xml
Executable file
@@ -0,0 +1,14 @@
|
||||
<alfresco-config>
|
||||
<config>
|
||||
<imapConfig>
|
||||
<imap name="Repository_virtual" mode="virtual">
|
||||
<store>workspace://SpacesStore</store>
|
||||
<rootPath>/app:company_home</rootPath>
|
||||
</imap>
|
||||
<imap name="Repository_archive" mode="archive">
|
||||
<store>workspace://SpacesStore</store>
|
||||
<rootPath>/app:company_home</rootPath>
|
||||
</imap>
|
||||
</imapConfig>
|
||||
</config>
|
||||
</alfresco-config>
|
91
config/alfresco/imap-server-context.xml
Executable file
91
config/alfresco/imap-server-context.xml
Executable file
@@ -0,0 +1,91 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
|
||||
|
||||
<beans>
|
||||
|
||||
<bean id="imapResourceBundles" class="org.alfresco.i18n.ResourceBundleBootstrapComponent">
|
||||
<property name="resourceBundles">
|
||||
<list>
|
||||
<value>alfresco.messages.imap-service</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="imapServerConfigurationProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
|
||||
<property name="ignoreUnresolvablePlaceholders">
|
||||
<value>true</value>
|
||||
</property>
|
||||
<property name="locations">
|
||||
<list>
|
||||
<value>classpath:alfresco/imap-server.properties</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="imapHelper" class="org.alfresco.repo.imap.ImapHelper">
|
||||
<property name="nodeService" ref="NodeService" />
|
||||
<property name="fileFolderService" ref="FileFolderService" />
|
||||
<property name="searchService" ref="SearchService" />
|
||||
<property name="namespaceService" ref="NamespaceService" />
|
||||
<property name="templateService" ref="TemplateService" />
|
||||
<property name="permissionService" ref="PermissionService" />
|
||||
<property name="configService" ref="webClientConfigService" />
|
||||
<property name="dictionaryService" ref="DictionaryService" />
|
||||
<property name="preferenceService" ref="PreferenceService" />
|
||||
<property name="siteService" ref="SiteService" />
|
||||
<property name="patchService" ref="patchComponent" />
|
||||
|
||||
|
||||
<property name="serviceRegistry" ref="ServiceRegistry" />
|
||||
|
||||
<property name="defaultFromAddress">
|
||||
<value>${mail.from.default}</value>
|
||||
</property>
|
||||
|
||||
<property name="webApplicationContextUrl">
|
||||
<value>${web.application.context.url}</value>
|
||||
</property>
|
||||
|
||||
<property name="imapRoot">
|
||||
<value>${spaces.store}/${spaces.company_home.childname}/${spaces.imap_home.childname}</value>
|
||||
</property>
|
||||
|
||||
<property name="repositoryTemplatePath">
|
||||
<value>${spaces.store}/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.imapConfig.childname}/${spaces.imap_templates.childname}</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="imapServer" class="org.alfresco.repo.imap.AlfrescoImapServer">
|
||||
<property name="port">
|
||||
<value>${imap.server.port}</value>
|
||||
</property>
|
||||
<property name="imapServerEnabled">
|
||||
<value>${imap.server.enabled}</value>
|
||||
</property>
|
||||
<property name="imapHostManager">
|
||||
<ref local="imapHostManager"/>
|
||||
</property>
|
||||
<property name="imapUserManager">
|
||||
<ref local="imapUserManager"/>
|
||||
</property>
|
||||
<property name="imapHelper">
|
||||
<ref local="imapHelper"/>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="imapHostManager" class="org.alfresco.repo.imap.AlfrescoImapHostManager" >
|
||||
<property name="serviceRegistry" ref="ServiceRegistry" />
|
||||
<property name="nodeService" ref="NodeService" />
|
||||
<property name="fileFolderService" ref="FileFolderService" />
|
||||
<property name="imapHelper" ref="imapHelper" />
|
||||
</bean>
|
||||
|
||||
<bean id="imapUserManager" class="org.alfresco.repo.imap.AlfrescoImapUserManager">
|
||||
<property name="imapHostManager">
|
||||
<ref local="imapHostManager" />
|
||||
</property>
|
||||
<property name="authenticationService" ref="AuthenticationService" />
|
||||
<property name="personService" ref="PersonService" />
|
||||
<property name="nodeService" ref="NodeService" />
|
||||
</bean>
|
||||
</beans>
|
4
config/alfresco/imap-server.properties
Executable file
4
config/alfresco/imap-server.properties
Executable file
@@ -0,0 +1,4 @@
|
||||
imap.server.enabled=false
|
||||
imap.server.port=143
|
||||
|
||||
#imap.server.web.application.context.url=http://localhost:8080/alfresco
|
77
config/alfresco/imap/scripts/command-processor.js
Executable file
77
config/alfresco/imap/scripts/command-processor.js
Executable file
@@ -0,0 +1,77 @@
|
||||
<import resource="/Company Home/Data Dictionary/Scripts/command-utils.js">
|
||||
|
||||
function processCommand()
|
||||
{
|
||||
var isEmailed = document.hasAspect("emailserver:emailed");
|
||||
|
||||
logger.log("Command Processor: isEmailed=" + isEmailed);
|
||||
|
||||
if (isEmailed)
|
||||
{
|
||||
// Delete email attachments
|
||||
var attachments = document.assocs["attachments"];
|
||||
if (attachments != null)
|
||||
{
|
||||
for (var i = 0; i < attachments.length; i++)
|
||||
{
|
||||
attachments[i].remove();
|
||||
}
|
||||
}
|
||||
|
||||
var command = document.properties["cm:title"];
|
||||
logger.log("Command Processor: command=" + command);
|
||||
|
||||
var parts = new Array();
|
||||
var str = command;
|
||||
var i = 0;
|
||||
while (true)
|
||||
{
|
||||
var index = str.indexOf("-");
|
||||
if (index == -1)
|
||||
{
|
||||
parts[i] = str;
|
||||
break;
|
||||
}
|
||||
parts[i] = str.substring(0, index);
|
||||
str = str.substr(index + 1);
|
||||
i++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// do-<commandName>-<arg1>-...-<argN>
|
||||
if (parts.length < 3 || parts[0].toLowerCase() != "do")
|
||||
{
|
||||
var message = "Unknown command: " + command;
|
||||
logger.log(message);
|
||||
createEmail(message, message, message, false);
|
||||
return;
|
||||
}
|
||||
|
||||
var commandName = parts[1].toLowerCase();
|
||||
var commandFolder = space.childByNamePath(commandName);
|
||||
logger.log("Found '" + commandName + "' command folder: '" + commandFolder + "'");
|
||||
if (commandFolder == null)
|
||||
{
|
||||
var message = "Command Processor: wrong command=" + command;
|
||||
createEmail(message, message, message, false);
|
||||
logger.log(message);
|
||||
return;
|
||||
}
|
||||
|
||||
document.move(commandFolder);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
processCommand();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
243
config/alfresco/imap/scripts/command-search.js
Executable file
243
config/alfresco/imap/scripts/command-search.js
Executable file
@@ -0,0 +1,243 @@
|
||||
<import resource="/Company Home/Data Dictionary/Scripts/command-utils.js">
|
||||
|
||||
/**
|
||||
* Resources
|
||||
*/
|
||||
|
||||
/* var templatePath = "/Data Dictionary/Imap Configs/Templates/imap_search_response_text_html.ftl";*/
|
||||
var errorParameter = "Error: The query parameter is not set!";
|
||||
var errorXPathNotValid = "Error: The Xpath query is not valid.";
|
||||
var unknownCommand = "Unknown command";
|
||||
|
||||
/**
|
||||
* Globals
|
||||
*/
|
||||
var title;
|
||||
var command;
|
||||
|
||||
/**
|
||||
* Create content for e-mail in text format
|
||||
*
|
||||
* @nodes (Array) ScriptNodes array
|
||||
* @return content for e-mail in text format
|
||||
*/
|
||||
function createContentTextPlain(nodes)
|
||||
{
|
||||
var content = "Command: " + title + "\n\n";
|
||||
for (var i = 0; i < nodes.length; i++)
|
||||
{
|
||||
content = content + "Name: " + nodes[i].getName() + "\nUrl: " +
|
||||
webApplicationContextUrl + nodes[i].getUrl();
|
||||
|
||||
if (nodes[i].isDocument)
|
||||
{
|
||||
content = content + "\nDownload Url: " +
|
||||
webApplicationContextUrl + nodes[i].getDownloadUrl();
|
||||
}
|
||||
|
||||
content = content + "\n\n";
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* This for possible processing. It need to be investigated.
|
||||
* The possible solution is to send a search request into FreeMarker template and let the template do search!
|
||||
* @param nodes
|
||||
* @return
|
||||
*/
|
||||
function createResponseTextHtml(nodes)
|
||||
{
|
||||
var template = companyhome.childByNamePath(templatePath);
|
||||
var result;
|
||||
if (template != null)
|
||||
{
|
||||
var args = new Array();
|
||||
args["title"] = title;
|
||||
args["nodes"] = nodes; /*it does not work; need to investigate how to send this to freemarker processing*/
|
||||
args["webApplicationContextUrl"] = webApplicationContextUrl;
|
||||
result = document.processTemplate(template, args);
|
||||
logger.log("Response template is found. Response body is created using template.");
|
||||
}
|
||||
else
|
||||
{
|
||||
result = createContentTextHtml(nodes);
|
||||
logger.log("Response template is NOT found. Response is created using default function.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create content for e-mail in html format
|
||||
*
|
||||
* @nodes (Array) ScriptNodes array
|
||||
* @return content for e-mail in html format
|
||||
*/
|
||||
function createContentTextHtml(nodes)
|
||||
{
|
||||
var content = "Command: " + title + "\n<br/><br/>\n";
|
||||
content += "<table class=\"links\">\n";
|
||||
content += "<thead align=\"center\">";
|
||||
content += "<tr>";
|
||||
content += "<td>Name</td>";
|
||||
content += "<td>Url</td>";
|
||||
content += "<td>Download Url</td>";
|
||||
content += "</tr>";
|
||||
content += "</thead>\n"
|
||||
|
||||
|
||||
for (var i = 0; i < nodes.length; i++)
|
||||
{
|
||||
content += "<tr>\n";
|
||||
content += "<td>" + nodes[i].getName() + "</td>";
|
||||
content += "<td><a href=\"" + webApplicationContextUrl + nodes[i].getUrl() + "\">" +
|
||||
webApplicationContextUrl + nodes[i].getUrl() + "</a></td>";
|
||||
content += "<td> ";
|
||||
if (nodes[i].isDocument)
|
||||
{
|
||||
content += "<a href=\"" + webApplicationContextUrl + nodes[i].getDownloadUrl() + "\">" +
|
||||
webApplicationContextUrl + nodes[i].getDownloadUrl() + "</a>";
|
||||
}
|
||||
content += "</td>\n";
|
||||
content += "</tr>\n";
|
||||
}
|
||||
content += "</table>";
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute search command
|
||||
*
|
||||
* @params (string) command parameters
|
||||
*/
|
||||
function commandSearch(params)
|
||||
{
|
||||
var store = "workspace://SpacesStore";
|
||||
var query;
|
||||
var subject = "Search result";
|
||||
var type = "lucene";
|
||||
var paramArray = params.split(";");
|
||||
for (var i = 0; i < paramArray.length; i++)
|
||||
{
|
||||
var param = paramArray[i].split("=");
|
||||
param[0] = param[0].toLowerCase();
|
||||
|
||||
switch (param[0])
|
||||
{
|
||||
case "store": store = param[1]; break;
|
||||
case "query": query = param[1]; break;
|
||||
case "subject": subject = param[1]; break;
|
||||
case "type": type = param[1].toLowerCase(); break;
|
||||
}
|
||||
}
|
||||
|
||||
if (query == null)
|
||||
{
|
||||
createEmail(errorParameter, errorParameter, errorParameter, false);
|
||||
return;
|
||||
}
|
||||
|
||||
var nodes;
|
||||
|
||||
try
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case "lucene":
|
||||
nodes = search.luceneSearch(store, query);
|
||||
break;
|
||||
case "xpath":
|
||||
var isValid = search.isValidXpathQuery(query);
|
||||
if (isValid == true)
|
||||
{
|
||||
nodes = search.xpathSearch(store, query);
|
||||
}
|
||||
else
|
||||
{
|
||||
createEmail(errorXPathNotValid, errorXPathNotValid, errorXPathNotValid, false);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "node":
|
||||
var node = search.findNode(query);
|
||||
if (node == null) break;
|
||||
nodes = new Array(node);
|
||||
break;
|
||||
case "tag":
|
||||
nodes = search.tagSearch(store, query);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (exception)
|
||||
{
|
||||
createEmail(exception.message, exception.message, "Search Error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (nodes == null || nodes.length == 0)
|
||||
{
|
||||
var message = "Nothing was found using query: '" + subject + "'.";
|
||||
createEmail(message, message, subject);
|
||||
return;
|
||||
}
|
||||
/*createEmail(createResponseTextHtml(nodes), createContentTextPlain(nodes), subject, true);*/
|
||||
createEmail(createContentTextHtml(nodes), createContentTextPlain(nodes), subject, false);
|
||||
}
|
||||
/**
|
||||
* Decode subject
|
||||
*
|
||||
* @subject (string) subject
|
||||
*/
|
||||
function decodeSubject(subject)
|
||||
{
|
||||
var s = new Array();
|
||||
s[0] = new Array("\\", "%5c");
|
||||
s[1] = new Array("/", "%2f");
|
||||
s[2] = new Array("*", "%2a");
|
||||
s[3] = new Array("|", "%7c");
|
||||
s[4] = new Array(":", "%3a");
|
||||
s[5] = new Array("\"", "%22");
|
||||
s[6] = new Array("<", "%3c");
|
||||
s[7] = new Array(">", "%3e");
|
||||
s[8] = new Array("?", "%3f");
|
||||
|
||||
for (var i = 0; i < s.length; i++)
|
||||
{
|
||||
var re = new RegExp(s[i][1], 'g');
|
||||
subject = subject.replace(re, s[i][0]);
|
||||
}
|
||||
|
||||
return subject;
|
||||
}
|
||||
|
||||
|
||||
function main()
|
||||
{
|
||||
title = decodeSubject(document.properties["cm:title"]);
|
||||
command = title.split("-");
|
||||
if (command[0].toLowerCase() == "do")
|
||||
{
|
||||
if (command[1].toLowerCase() == "search")
|
||||
{
|
||||
commandSearch(title.substring(4 + command[1].length));
|
||||
}
|
||||
else
|
||||
{
|
||||
var message = unknownCommand + ": '" + title + "'";
|
||||
createEmail(message, message, message, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var message = unknownCommand + ": '" + title + "'";
|
||||
createEmail(message, message, message, false);
|
||||
}
|
||||
|
||||
document.remove();
|
||||
}
|
||||
|
||||
|
||||
logger.log("Start search command.");
|
||||
main();
|
||||
logger.log("End search command.");
|
||||
|
56
config/alfresco/imap/scripts/command-utils.js
Executable file
56
config/alfresco/imap/scripts/command-utils.js
Executable file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Create e-mail
|
||||
* contentTextHtml (string) html content
|
||||
* contentTextPlain (string) text content
|
||||
*/
|
||||
function createEmail(contentTextHtml, contentTextPlain, subject, templateUsed)
|
||||
{
|
||||
var command = document.properties["cm:title"];
|
||||
var userName = person.properties["cm:userName"];
|
||||
|
||||
var inboxFolder = companyhome.childByNamePath("IMAP Home/" + userName + "/INBOX");
|
||||
if (inboxFolder == null)
|
||||
{
|
||||
logger.log("Command Processor: INBOX folder does't exists.");
|
||||
return;
|
||||
}
|
||||
|
||||
var nextMessageUID = inboxFolder.properties["imap:nextMessageUID"];
|
||||
inboxFolder.properties["imap:nextMessageUID"] = nextMessageUID + 1;
|
||||
inboxFolder.save();
|
||||
|
||||
var response = inboxFolder.createNode("response" + Date.now(), "imap:imapContent");
|
||||
response.properties["imap:messageFrom"] = "command@alfresco.com";
|
||||
response.properties["imap:messageSubject"] = subject;
|
||||
response.properties["imap:messageTo"] = document.properties["cm:originator"];
|
||||
response.properties["imap:messageCc"] = "";
|
||||
response.properties["imap:messageUID"] = nextMessageUID;
|
||||
|
||||
response.save();
|
||||
|
||||
var textBody = response.createNode("Body.txt", "imap:imapBody");
|
||||
textBody.content = contentTextPlain;
|
||||
textBody.save();
|
||||
|
||||
var htmlBody = response.createNode("Body.html", "imap:imapBody");
|
||||
if (templateUsed == true)
|
||||
{
|
||||
htmlBody.content = contentTextHtml;
|
||||
}
|
||||
else
|
||||
{
|
||||
htmlBody.content = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">" +
|
||||
"<html><head>" +
|
||||
"<meta http-equiv=Content-Type content=\"text/html; charset=UTF-8\">" +
|
||||
"<style type=\"text/css\">" +
|
||||
"* {font-family:Verdana,Arial,sans-serif;font-size:11px;}" +
|
||||
".links {border:1px dotted #555555;border-collapse:collapse;width:99%;}" +
|
||||
".links td {border:1px dotted #555555;padding:5px;}" +
|
||||
"</style>" +
|
||||
"</head>" +
|
||||
"<body>" +
|
||||
"<div>" + contentTextHtml + "</div></body></html>";
|
||||
}
|
||||
htmlBody.save();
|
||||
|
||||
}
|
@@ -326,6 +326,9 @@
|
||||
<prop key="spaces.user_homes.childname">${spaces.user_homes.childname}</prop>
|
||||
<prop key="spaces.sites.childname">${spaces.sites.childname}</prop>
|
||||
<prop key="spaces.templates.email.invite.childname">${spaces.templates.email.invite.childname}</prop>
|
||||
<prop key="spaces.imap_home.childname">${spaces.imap_home.childname}</prop>
|
||||
<prop key="spaces.imapConfig.childname">${spaces.imapConfig.childname}</prop>
|
||||
<prop key="spaces.imap_templates.childname">${spaces.imap_templates.childname}</prop>
|
||||
</props>
|
||||
</property>
|
||||
</bean>
|
||||
@@ -516,6 +519,7 @@
|
||||
<prop key="location">alfresco/bootstrap/sitesSpace.xml</prop>
|
||||
<prop key="messages">alfresco/messages/bootstrap-spaces</prop>
|
||||
</props>
|
||||
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
12
config/alfresco/messages/bootstrap-imapScripts.properties
Executable file
12
config/alfresco/messages/bootstrap-imapScripts.properties
Executable file
@@ -0,0 +1,12 @@
|
||||
imap.command_processor.name=command-processor.js
|
||||
imap.command_processor.title=Command Processor
|
||||
imap.command_processor.description=Email Command Processor Script
|
||||
|
||||
imap.command_search.name=command-search.js
|
||||
imap.command_search.title=Search Command
|
||||
imap.command_search.description=Email Search Command Script
|
||||
|
||||
imap.command_utils.name=command-utils.js
|
||||
imap.command_utils.title=Command Utils
|
||||
imap.command_utils.description=Email Command Utils
|
||||
|
@@ -6,6 +6,12 @@ spaces.company_home.description=The company root space
|
||||
spaces.dictionary.name=Data Dictionary
|
||||
spaces.dictionary.description=User managed definitions
|
||||
|
||||
spaces.imapConfig.name=Imap Configs
|
||||
spaces.imapConfig.description=Imap Configs
|
||||
|
||||
spaces.imap_templates.name=Templates
|
||||
spaces.imap_templates.description=Templates for IMAP generated messages
|
||||
|
||||
spaces.templates.name=Space Templates
|
||||
spaces.templates.description=Space folder templates
|
||||
|
||||
@@ -44,3 +50,6 @@ spaces.sites.description=Site Collaboration Spaces
|
||||
|
||||
spaces.templates.email.invite.name=invite
|
||||
spaces.templates.email.invite.description=Invite email templates
|
||||
|
||||
spaces.imap_home.name=IMAP Home
|
||||
spaces.imap_home.description=IMAP Home
|
10
config/alfresco/messages/imap-service.properties
Executable file
10
config/alfresco/messages/imap-service.properties
Executable file
@@ -0,0 +1,10 @@
|
||||
#
|
||||
# Imap I18N messages
|
||||
#
|
||||
|
||||
# Information messages. prefix 'imap.server.info'
|
||||
imap.server.info.message_body_not_found = "The message body parts are not found."
|
||||
|
||||
# Error messages. prefix 'imap.server.error'
|
||||
imap.server.error.properties_dont_exist = "Appropriate properties do not exist."
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# PatchService messages
|
||||
# PatchService messages
|
||||
patch.service.preceeded_by_alternative=Preceeded by alternative patch ''{0}''.
|
||||
patch.service.not_relevant=Not relevant to schema {0}
|
||||
patch.executer.checking=Checking for patches to apply ...
|
||||
@@ -261,3 +261,11 @@ patch.mtShareExistingTenants.result=Update existing tenants for MT Share.
|
||||
patch.mtShareExistingTenants.result.not_applicable=Patch applied, although no changes made since MT is not enabled.
|
||||
|
||||
patch.redeployInvitationProcess.description=Re-deploy Invitation Process Definitions.
|
||||
|
||||
patch.imapFolders.description=Creates folders tree necessary for IMAP functionality
|
||||
patch.imapFolders.result.exists=The 'Imap Configs' folder already exists
|
||||
patch.imapFolders.result.created=The 'Imap Configs' folder was successfully created
|
||||
|
||||
patch.imapUserFolders.description=Creates folders tree necessary for IMAP functionality
|
||||
patch.imapUserFolders.result.exists=The 'IMAP Home' folder already exists
|
||||
patch.imapUserFolders.result.created=The 'IMAP Home' folder was successfully created
|
||||
|
100
config/alfresco/model/imapModel.xml
Executable file
100
config/alfresco/model/imapModel.xml
Executable file
@@ -0,0 +1,100 @@
|
||||
<model name="imap:imapmodel" xmlns="http://www.alfresco.org/model/dictionary/1.0">
|
||||
|
||||
<description>IMAP Content Model</description>
|
||||
<author>Alfresco</author>
|
||||
<published>2009-01-20</published>
|
||||
<version>1.0</version>
|
||||
|
||||
<imports>
|
||||
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm" />
|
||||
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d" />
|
||||
</imports>
|
||||
|
||||
<namespaces>
|
||||
<namespace uri="http://www.alfresco.org/model/imap/1.0" prefix="imap" />
|
||||
</namespaces>
|
||||
|
||||
<types>
|
||||
<type name="imap:imapFolder">
|
||||
<title>IMAP Folder</title>
|
||||
<parent>cm:folder</parent>
|
||||
<properties>
|
||||
<property name="imap:subscribed">
|
||||
<type>d:boolean</type>
|
||||
</property>
|
||||
<property name="imap:selectable">
|
||||
<type>d:boolean</type>
|
||||
<mandatory>true</mandatory>
|
||||
<default>true</default>
|
||||
</property>
|
||||
</properties>
|
||||
</type>
|
||||
|
||||
<type name="imap:imapContent">
|
||||
<title>IMAP File</title>
|
||||
<parent>cm:folder</parent>
|
||||
<properties>
|
||||
<!-- Message -->
|
||||
<property name="imap:messageFrom">
|
||||
<type>d:text</type>
|
||||
</property>
|
||||
<property name="imap:messageTo">
|
||||
<type>d:text</type>
|
||||
</property>
|
||||
<property name="imap:messageCc">
|
||||
<type>d:text</type>
|
||||
</property>
|
||||
<property name="imap:messageSubject">
|
||||
<type>d:text</type>
|
||||
</property>
|
||||
</properties>
|
||||
<mandatory-aspects>
|
||||
<aspect>imap:flaggable</aspect>
|
||||
</mandatory-aspects>
|
||||
</type>
|
||||
|
||||
<type name="imap:imapAttach">
|
||||
<title>Attachment to the IMAP message</title>
|
||||
<parent>cm:content</parent>
|
||||
<properties>
|
||||
<property name="imap:attachID">
|
||||
<type>d:text</type>
|
||||
</property>
|
||||
</properties>
|
||||
</type>
|
||||
|
||||
<type name="imap:imapBody">
|
||||
<title>Body of the IMAP message</title>
|
||||
<parent>cm:content</parent>
|
||||
</type>
|
||||
</types>
|
||||
|
||||
<aspects>
|
||||
<aspect name="imap:flaggable">
|
||||
<properties>
|
||||
<property name="imap:flagAnswered">
|
||||
<type>d:boolean</type>
|
||||
</property>
|
||||
<property name="imap:flagDeleted">
|
||||
<type>d:boolean</type>
|
||||
</property>
|
||||
<property name="imap:flagDraft">
|
||||
<type>d:boolean</type>
|
||||
</property>
|
||||
<property name="imap:flagSeen">
|
||||
<type>d:boolean</type>
|
||||
</property>
|
||||
<property name="imap:flagRecent">
|
||||
<type>d:boolean</type>
|
||||
</property>
|
||||
<property name="imap:flagFlagged">
|
||||
<type>d:boolean</type>
|
||||
</property>
|
||||
|
||||
</properties>
|
||||
</aspect>
|
||||
<aspect name="imap:subscribed"/>
|
||||
<aspect name="imap:nonselectable"/>
|
||||
</aspects>
|
||||
|
||||
</model>
|
@@ -6,6 +6,8 @@ repository.name=Main Repository
|
||||
|
||||
dir.root=./alf_data
|
||||
|
||||
web.application.context.url=http://localhost:8080/alfresco
|
||||
|
||||
dir.contentstore=${dir.root}/contentstore
|
||||
dir.contentstore.deleted=${dir.root}/contentstore.deleted
|
||||
|
||||
@@ -226,6 +228,8 @@ spaces.company_home.childname=app:company_home
|
||||
spaces.guest_home.childname=app:guest_home
|
||||
spaces.dictionary.childname=app:dictionary
|
||||
spaces.templates.childname=app:space_templates
|
||||
spaces.imapConfig.childname=app:imap_configs
|
||||
spaces.imap_templates.childname=app:imap_templates
|
||||
spaces.templates.content.childname=app:content_templates
|
||||
spaces.templates.email.childname=app:email_templates
|
||||
spaces.templates.rss.childname=app:rss_templates
|
||||
@@ -235,6 +239,7 @@ spaces.wcm.childname=app:wcm
|
||||
spaces.wcm_content_forms.childname=app:wcm_forms
|
||||
spaces.content_forms.childname=app:forms
|
||||
spaces.user_homes.childname=app:user_homes
|
||||
spaces.imap_home.childname=imap:imap_home
|
||||
spaces.sites.childname=st:sites
|
||||
spaces.templates.email.invite.childname=cm:invite
|
||||
|
||||
|
BIN
config/alfresco/templates/imap/command_processor_scripts.acp
Executable file
BIN
config/alfresco/templates/imap/command_processor_scripts.acp
Executable file
Binary file not shown.
BIN
config/alfresco/templates/imap/email_actions_space.acp
Executable file
BIN
config/alfresco/templates/imap/email_actions_space.acp
Executable file
Binary file not shown.
BIN
config/alfresco/templates/imap/imap_config_space.acp
Executable file
BIN
config/alfresco/templates/imap/imap_config_space.acp
Executable file
Binary file not shown.
173
config/alfresco/templates/imap/imap_message_text_html.ftl
Executable file
173
config/alfresco/templates/imap/imap_message_text_html.ftl
Executable file
@@ -0,0 +1,173 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="Generator" content="Alfresco Repository">
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
background-color:#FFFFFF;
|
||||
color:#000000;
|
||||
}
|
||||
* {
|
||||
font-family:Verdana,Arial,sans-serif;
|
||||
font-size:11px;
|
||||
}
|
||||
h1 {
|
||||
text-align:left;
|
||||
font-size:15px;
|
||||
}
|
||||
h2 {
|
||||
text-align:left;
|
||||
font-size:13px;
|
||||
}
|
||||
fieldset {
|
||||
border:1px dotted #555555;
|
||||
margin:15px 5px 15px 5px;
|
||||
}
|
||||
fieldset legend {
|
||||
font-weight:bold;
|
||||
border:1px dotted #555555;
|
||||
background-color: #FFFFFF;
|
||||
padding:7px;
|
||||
}
|
||||
.links {
|
||||
border:0;
|
||||
border-collapse:collapse;
|
||||
width:99%;
|
||||
}
|
||||
.links td {
|
||||
border:0;
|
||||
padding:5px;
|
||||
}
|
||||
.description {
|
||||
border:0;
|
||||
border-collapse:collapse;
|
||||
width:99%;
|
||||
}
|
||||
.description td {
|
||||
/*border:1px dotted #555555;*/
|
||||
padding:5px;
|
||||
}
|
||||
#start_workflow input, #start_workflow select, #start_workflow textarea
|
||||
{
|
||||
border:1px solid #555555;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<hr/>
|
||||
<h1> Document (name): ${document.name} </h1>
|
||||
<hr/>
|
||||
<fieldset>
|
||||
<legend> Metadata </legend>
|
||||
<table class="description">
|
||||
<#if document.properties.title?exists>
|
||||
<tr><td valign="top">Title:</td><td>${document.properties.title}</td></tr>
|
||||
<#else>
|
||||
<tr><td valign="top">Title:</td><td> </td></tr>
|
||||
</#if>
|
||||
<#if document.properties.description?exists>
|
||||
<tr><td valign="top">Description:</td><td>${document.properties.description}</td></tr>
|
||||
<#else>
|
||||
<tr><td valign="top">Description:</td><td> </td></tr>
|
||||
</#if>
|
||||
<tr><td>Creator:</td><td>${document.properties.creator}</td></tr>
|
||||
<tr><td>Created:</td><td>${document.properties.created?datetime}</td></tr>
|
||||
<tr><td>Modifier:</td><td>${document.properties.modifier}</td></tr>
|
||||
<tr><td>Modified:</td><td>${document.properties.modified?datetime}</td></tr>
|
||||
<tr><td>Size:</td><td>${document.size / 1024} Kb</td></tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend> Content links </legend>
|
||||
<table class="links">
|
||||
<tr>
|
||||
<td>Content folder:</td><td><a href="${contextUrl}${document.displayPath}">${contextUrl}${document.displayPath}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Content URL:</td><td><a href="${contextUrl}${document.url}">${contextUrl}${document.url}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Download URL:</td><td><a href="${contextUrl}${document.downloadUrl}">${contextUrl}${document.downloadUrl}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>WebDAV URL:</td><td><a href="${contextUrl}${document.webdavUrl}">${contextUrl}${document.webdavUrl}</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend> Start Workflow </legend>
|
||||
<form id="start_workflow"
|
||||
name="start_workflow"
|
||||
method="get"
|
||||
action="${contextUrl}/service/imap/start-workflow"
|
||||
enctype="application/x-www-form-urlencoded">
|
||||
<input type="hidden" name="alfTicket" value="${alfTicket}" />
|
||||
<input type="hidden" name="nodeRefId" value="${document.id}" />
|
||||
<table>
|
||||
<tr>
|
||||
<td>Workflow type:</td>
|
||||
<td>
|
||||
<!-- Workflow list is hardcoded for now -->
|
||||
<select id="workflowType" name="workflowType" style="width:134px">
|
||||
<option value="adhoc">Adhoc Task</option>
|
||||
<option value="review">Review & Approve</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Asign to:</td>
|
||||
<td>
|
||||
<input type="text" id="assignTo" name="assignTo"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Priority:</td>
|
||||
<td>
|
||||
<select size="1" name="workflowPriority" id="workflowPriority">
|
||||
<option value="1">1</option>
|
||||
<option selected="selected" value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
</select>
|
||||
</td>
|
||||
<tr>
|
||||
<td>Due date:</td>
|
||||
<td>
|
||||
Day: <select size="1" name="workflowDueDateDay" id="workflowDueDateDay">
|
||||
<option selected="selected" value="1">1</option>
|
||||
<#list 2..31 as i>
|
||||
<option value="${i}">${i}</option>
|
||||
</#list>
|
||||
</select>
|
||||
</select>
|
||||
Month: <select size="1" name="workflowDueDateMonth" id="workflowDueDateMonth">
|
||||
<option selected="selected" value="1">1</option>
|
||||
<#list 2..12 as i>
|
||||
<option value="${i}">${i}</option>
|
||||
</#list>
|
||||
</select>
|
||||
Year: <input type="text" size="5" id="workflowDueDateYear" name="workflowDueDateYear"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description:</td><td><textarea style="width:132px" id="description" name="description"></textarea></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><center><input id="submitButton" type="submit" value="Start workflow" style="margin:20px 5px 5px 5px"/></center></td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</fieldset>
|
||||
<!--
|
||||
JBPM ids for debug....
|
||||
id=jbpm$4,name=jbpm$wcmwf:changerequest,version=1,title=Change Request
|
||||
id=jbpm$3,name=jbpm$wcmwf:submit,version=1,title=Web Site Submission
|
||||
id=jbpm$5,name=jbpm$wcmwf:submitdirect,version=1,title=Web Site Submission (Direct)
|
||||
id=jbpm$2,name=jbpm$wf:adhoc,version=1,title=Adhoc Task
|
||||
id=jbpm$6,name=jbpm$wf:invite,version=1,title=Site Invite
|
||||
id=jbpm$1,name=jbpm$wf:review,version=1,title=Review & Approve
|
||||
-->
|
||||
</body>
|
||||
</html>
|
35
config/alfresco/templates/imap/imap_message_text_plain.ftl
Executable file
35
config/alfresco/templates/imap/imap_message_text_plain.ftl
Executable file
@@ -0,0 +1,35 @@
|
||||
------------------------------------------------------------------------------
|
||||
Document name: ${document.name}
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
<#if document.properties.title?exists>
|
||||
Title: ${document.properties.title}
|
||||
<#else>
|
||||
Title: NONE
|
||||
</#if>
|
||||
<#if document.properties.description?exists>
|
||||
Description: ${document.properties.description}
|
||||
<#else>
|
||||
Description: NONE
|
||||
</#if>
|
||||
Creator: ${document.properties.creator}
|
||||
Created: ${document.properties.created?datetime}
|
||||
Modifier: ${document.properties.modifier}
|
||||
Modified: ${document.properties.modified?datetime}
|
||||
Size: ${document.size / 1024} Kb
|
||||
|
||||
|
||||
CONTENT LINKS
|
||||
|
||||
Content folder: ${contextUrl}${document.displayPath}
|
||||
Content URL: ${contextUrl}${document.url}
|
||||
Download URL: ${contextUrl}${document.downloadUrl}
|
||||
WebDAV URL: ${contextUrl}${document.webdavUrl}
|
||||
|
||||
START WORKFLOW
|
||||
|
||||
{It is not possible to create a customizible workflow in txt format!
|
||||
It is possible to create static links to IMAP Workflow Handler webscript,
|
||||
But, in this case all parameters must be hardcoded in the link.
|
||||
See http://localhost:8080/alfresco/service/description/org/alfresco/imap/start-workflow.get
|
||||
for usage information.}
|
157
config/alfresco/templates/imap/template_test.html
Executable file
157
config/alfresco/templates/imap/template_test.html
Executable file
@@ -0,0 +1,157 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="Generator" content="Alfresco Repository">
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
background-color:#FFFFFF;
|
||||
color:#000000;
|
||||
}
|
||||
* {
|
||||
font-family:Verdana,Arial,sans-serif;
|
||||
font-size:11px;
|
||||
}
|
||||
h1 {
|
||||
text-align:left;
|
||||
font-size:15px;
|
||||
}
|
||||
h2 {
|
||||
text-align:left;
|
||||
font-size:13px;
|
||||
}
|
||||
fieldset {
|
||||
border:1px dotted #555555;
|
||||
margin:15px 5px 15px 5px;
|
||||
}
|
||||
fieldset legend {
|
||||
font-weight:bold;
|
||||
border:1px dotted #555555;
|
||||
background-color: #FFFFFF;
|
||||
padding:7px;
|
||||
}
|
||||
.links {
|
||||
border:0;
|
||||
border-collapse:collapse;
|
||||
width:99%;
|
||||
}
|
||||
.links td {
|
||||
border:0;
|
||||
padding:5px;
|
||||
}
|
||||
.description {
|
||||
border:0;
|
||||
border-collapse:collapse;
|
||||
width:99%;
|
||||
}
|
||||
.description td {
|
||||
/*border:1px dotted #555555;*/
|
||||
padding:5px;
|
||||
}
|
||||
#start_workflow input, #start_workflow select, #start_workflow textarea
|
||||
{
|
||||
border:1px solid #555555;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<hr/>
|
||||
<h1> Document (name): ${document.name} </h1>
|
||||
<hr/>
|
||||
<fieldset>
|
||||
<legend> Metadata </legend>
|
||||
<table class="description">
|
||||
<#if document.properties.title?exists>
|
||||
<tr><td valign="top">Title:</td><td>${document.properties.title}</td></tr>
|
||||
<#else>
|
||||
<tr><td valign="top">Title:</td><td> </td></tr>
|
||||
</#if>
|
||||
<#if document.properties.description?exists>
|
||||
<tr><td valign="top">Description:</td><td>${document.properties.description}</td></tr>
|
||||
<#else>
|
||||
<tr><td valign="top">Description:</td><td> </td></tr>
|
||||
</#if>
|
||||
<tr><td>Creator:</td><td>${document.properties.creator}</td></tr>
|
||||
<tr><td>Created:</td><td>${document.properties.created?datetime}</td></tr>
|
||||
<tr><td>Modifier:</td><td>${document.properties.modifier}</td></tr>
|
||||
<tr><td>Modified:</td><td>${document.properties.modified?datetime}</td></tr>
|
||||
<tr><td>Size:</td><td>${document.size / 1024} Kb</td></tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend> Content links </legend>
|
||||
<table class="links">
|
||||
<tr>
|
||||
<td>Content folder:</td><td><a href="${contextUrl}${document.displayPath}">${contextUrl}${document.displayPath}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Content URL:</td><td><a href="${contextUrl}${document.url}">${contextUrl}${document.url}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Download URL:</td><td><a href="${contextUrl}${document.downloadUrl}">${contextUrl}${document.downloadUrl}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>WebDAV URL:</td><td><a href="${contextUrl}${document.webdavUrl}">${contextUrl}${document.webdavUrl}</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend> Start Workflow </legend>
|
||||
<form id="start_workflow"
|
||||
name="start_workflow"
|
||||
method="get"
|
||||
action="${contextUrl}/service/imap/start-workflow"
|
||||
enctype="application/x-www-form-urlencoded">
|
||||
<input type="hidden" name="alfTicket" value="${alfTicket}" />
|
||||
<input type="hidden" name="nodeRefId" value="${document.id}" />
|
||||
<table>
|
||||
<tr>
|
||||
<td>Workflow type:</td>
|
||||
<td>
|
||||
<!-- Workflow list is hardcoded for now -->
|
||||
<select id="workflowType" name="workflowType" style="width:134px">
|
||||
<option value="adhoc">Adhoc Task</option>
|
||||
<option value="review">Review & Approve</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Asign to:</td>
|
||||
<td>
|
||||
<input type="text" id="assignTo" name="assignTo"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Priority:</td>
|
||||
<td>
|
||||
<select size="1" name="workflowPriority" id="workflowPriority">
|
||||
<option value="1">1</option>
|
||||
<option selected="selected" value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
</select>
|
||||
</td>
|
||||
<tr>
|
||||
<td>Due date:</td><td><input type="text" id="dueDate" name="dueDate"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description:</td><td><textarea style="width:132px" id="description" name="description"></textarea></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><center><input id="submitButton" type="submit" value="Start workflow" style="margin:20px 5px 5px 5px"/></center></td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</fieldset>
|
||||
<!--
|
||||
JBPM ids for debug....
|
||||
id=jbpm$4,name=jbpm$wcmwf:changerequest,version=1,title=Change Request
|
||||
id=jbpm$3,name=jbpm$wcmwf:submit,version=1,title=Web Site Submission
|
||||
id=jbpm$5,name=jbpm$wcmwf:submitdirect,version=1,title=Web Site Submission (Direct)
|
||||
id=jbpm$2,name=jbpm$wf:adhoc,version=1,title=Adhoc Task
|
||||
id=jbpm$6,name=jbpm$wf:invite,version=1,title=Site Invite
|
||||
id=jbpm$1,name=jbpm$wf:review,version=1,title=Review & Approve
|
||||
-->
|
||||
</body>
|
||||
</html>
|
@@ -0,0 +1,11 @@
|
||||
<webscript>
|
||||
<shortname>IMAP Server Status</shortname>
|
||||
<description>
|
||||
This script return status of IMAP server (enabled/diabled)
|
||||
</description>
|
||||
<url>/imap/servstatus</url>
|
||||
<format default="html">extension</format>
|
||||
<authentication>user</authentication>
|
||||
<transaction>required</transaction>
|
||||
<family>IMAP</family>
|
||||
</webscript>
|
@@ -0,0 +1,12 @@
|
||||
<webscript>
|
||||
<shortname>IMAP Workflow Handler</shortname>
|
||||
<description>
|
||||
This webscript starts a different workflows. It have used in the IMAP email body links.
|
||||
(The optional feature is the reply email with a report.)
|
||||
</description>
|
||||
<url>/imap/start-workflow?alfTicket={ticket}&nodeRefId={id}&workflowType={wt}&assignTo={at}&workflowDueDateDay={ddd}&workflowDueDateMonth={ddm}&workflowDueDateYear={ddy}&description={desc}</url>
|
||||
<format default="html">extension</format>
|
||||
<authentication>user</authentication>
|
||||
<transaction>required</transaction>
|
||||
<family>IMAP</family>
|
||||
</webscript>
|
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="Generator" content="Alfresco Repository">
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
background-color:#FFFFFF;
|
||||
color:#000000;
|
||||
}
|
||||
* {
|
||||
font-family:Verdana,Arial,sans-serif;
|
||||
font-size:11px;
|
||||
}
|
||||
h1 {
|
||||
text-align:left;
|
||||
font-size:15px;
|
||||
}
|
||||
h2 {
|
||||
text-align:left;
|
||||
font-size:13px;
|
||||
}
|
||||
.links {
|
||||
border:0;
|
||||
border-collapse:collapse;
|
||||
width:99%;
|
||||
}
|
||||
.links td {
|
||||
border:0;
|
||||
padding:5px;
|
||||
}
|
||||
.description {
|
||||
border:0;
|
||||
border-collapse:collapse;
|
||||
width:99%;
|
||||
}
|
||||
.description td {
|
||||
border:1px dotted #555555;
|
||||
padding:5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Workflow has started successfully.</h1>
|
||||
<table class="description">
|
||||
<tr>
|
||||
<td>Ticket:</td><td>${args.alfTicket}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NodeRef id:</td><td>${args.nodeRefId}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Workflow type:</td><td>${args.workflowType}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Asign to:</td><td>${args.assignTo}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Due date:</td><td>${args.workflowDueDateDay}/${args.workflowDueDateMonth}/${args.workflowDueDateYear}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description:</td><td>${args.description}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Result:</td><td> --- </td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
42
config/alfresco/templates/webscripts/org/alfresco/imap/start-workflow.get.js
Executable file
42
config/alfresco/templates/webscripts/org/alfresco/imap/start-workflow.get.js
Executable file
@@ -0,0 +1,42 @@
|
||||
function main()
|
||||
{
|
||||
logger.log("Start workflow form 'Start workflow' webscript");
|
||||
var docNode = search.findNode("workspace://SpacesStore/" + args.nodeRefId);
|
||||
if (docNode == undefined)
|
||||
{
|
||||
status.code = 404;
|
||||
status.message = "Content with NodeRef id '" + args.nodeRefId + "' not found.";
|
||||
status.redirect = true;
|
||||
return;
|
||||
}
|
||||
var workflowType = "jbpm$wf:" + args.workflowType;
|
||||
var assignTo = people.getPerson(args.assignTo);
|
||||
if (assignTo == undefined)
|
||||
{
|
||||
status.code = 404;
|
||||
status.message = "Person with username '" + args.assignTo + "' not found.";
|
||||
status.redirect = true;
|
||||
return;
|
||||
}
|
||||
var day = args.workflowDueDateDay;
|
||||
var month = args.workflowDueDateMonth;
|
||||
var year = args.workflowDueDateYear;
|
||||
if (year != null && year.length == 2)
|
||||
{
|
||||
year = "20" + year;
|
||||
}
|
||||
var dueDate = new Date(year, month - 1, day);
|
||||
var description = args.description;
|
||||
|
||||
var workflow = actions.create("start-workflow");
|
||||
workflow.parameters.workflowName = workflowType;
|
||||
workflow.parameters["bpm:workflowDescription"] = description;
|
||||
workflow.parameters["bpm:assignee"] = assignTo;
|
||||
workflow.parameters["bpm:workflowPriority"] = args.workflowPriority;
|
||||
if (dueDate != null)
|
||||
{
|
||||
workflow.parameters["bpm:workflowDueDate"] = dueDate;
|
||||
}
|
||||
workflow.execute(docNode);
|
||||
}
|
||||
main();
|
@@ -40,6 +40,8 @@ import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.content.MimetypeMap;
|
||||
import org.alfresco.service.cmr.email.EmailMessage;
|
||||
import org.alfresco.service.cmr.email.EmailMessageException;
|
||||
import org.alfresco.service.cmr.repository.ContentService;
|
||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||
import org.alfresco.service.cmr.repository.MimetypeService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
@@ -120,24 +122,54 @@ public class FolderEmailMessageHandler extends AbstractEmailMessageHandler
|
||||
// Write the message content
|
||||
if (message.getBody() != null)
|
||||
{
|
||||
InputStream contentIs = message.getBody().getContent();
|
||||
// The message body is plain text, unless an extension has been provided
|
||||
MimetypeService mimetypeService = getMimetypeService();
|
||||
String mimetype = mimetypeService.guessMimetype(messageSubject);
|
||||
if (mimetype.equals(MimetypeMap.MIMETYPE_BINARY))
|
||||
if (message.getBody().getSize() == -1)
|
||||
{
|
||||
mimetype= MimetypeMap.MIMETYPE_TEXT_PLAIN;
|
||||
// If message body is empty we write space as a content
|
||||
// to make possible rule processing
|
||||
// (Rules don't work on empty documents)
|
||||
writeSpace(contentNodeRef);
|
||||
}
|
||||
// Use the default encoding. It will get overridden if the body is text.
|
||||
String encoding = message.getBody().getEncoding();
|
||||
else
|
||||
{
|
||||
InputStream contentIs = message.getBody().getContent();
|
||||
// The message body is plain text, unless an extension has been provided
|
||||
MimetypeService mimetypeService = getMimetypeService();
|
||||
String mimetype = mimetypeService.guessMimetype(messageSubject);
|
||||
if (mimetype.equals(MimetypeMap.MIMETYPE_BINARY))
|
||||
{
|
||||
mimetype = MimetypeMap.MIMETYPE_TEXT_PLAIN;
|
||||
}
|
||||
// Use the default encoding. It will get overridden if the body is text.
|
||||
String encoding = message.getBody().getEncoding();
|
||||
|
||||
writeContent(contentNodeRef, contentIs, mimetype, encoding);
|
||||
writeContent(contentNodeRef, contentIs, mimetype, encoding);
|
||||
}
|
||||
}
|
||||
|
||||
// Add attachments
|
||||
addAttachments(spaceNodeRef, contentNodeRef, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method writes space as a content. We need this space because rules doesn't proceed documents with empty content. We need rule processing for command email messages with
|
||||
* empty body.
|
||||
*
|
||||
* @param nodeRef Reference to the parent node
|
||||
*/
|
||||
private void writeSpace(NodeRef nodeRef)
|
||||
{
|
||||
if (log.isDebugEnabled())
|
||||
{
|
||||
log.debug("Write space string");
|
||||
}
|
||||
|
||||
ContentService contentService = getContentService();
|
||||
ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
|
||||
writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
|
||||
writer.setEncoding("UTF-8");
|
||||
writer.putContent(" ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds titled aspect to the specified node.
|
||||
*
|
||||
|
@@ -153,7 +153,7 @@ public class SubethaEmailMessage implements EmailMessage
|
||||
|
||||
try
|
||||
{
|
||||
subject = mimeMessage.getSubject();
|
||||
subject = encodeSubject(mimeMessage.getSubject());
|
||||
}
|
||||
catch (MessagingException e)
|
||||
{
|
||||
@@ -373,7 +373,8 @@ public class SubethaEmailMessage implements EmailMessage
|
||||
|
||||
for (EmailMessagePart attachment : attachments)
|
||||
{
|
||||
if (attachment instanceof SubethaEmailMessagePart) {
|
||||
if (attachment instanceof SubethaEmailMessagePart)
|
||||
{
|
||||
((SubethaEmailMessagePart) attachment).setRmiRegistry(rmiRegistryHost, rmiRegistryPort);
|
||||
}
|
||||
}
|
||||
@@ -410,4 +411,24 @@ public class SubethaEmailMessage implements EmailMessage
|
||||
return attachments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces characters \/*|:"<>? on their hex values. Subject field is used as name of the content, so we need to replace characters that are forbidden in content names.
|
||||
*
|
||||
* @param subject String representing subject
|
||||
* @return Encoded string
|
||||
*/
|
||||
static private String encodeSubject(String subject)
|
||||
{
|
||||
String result = subject.trim();
|
||||
String[][] s = new String[][] { { "\\", "%5c" }, { "/", "%2f" }, { "*", "%2a" }, { "|", "%7c" }, { ":", "%3a" }, { "\"", "%22" }, { "<", "%3c" }, { ">", "%3e" },
|
||||
{ "?", "%3f" } };
|
||||
|
||||
for (int i = 0; i < s.length; i++)
|
||||
{
|
||||
result = result.replace(s[i][0], s[i][1]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
60
source/java/org/alfresco/model/ImapModel.java
Executable file
60
source/java/org/alfresco/model/ImapModel.java
Executable file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.model;
|
||||
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* IMAP Model Constants
|
||||
*
|
||||
* @author Mike Shavnev
|
||||
*/
|
||||
public interface ImapModel
|
||||
{
|
||||
static final String IMAP_MODEL_1_0_URI = "http://www.alfresco.org/model/imap/1.0";
|
||||
|
||||
static final QName ASPECT_IMAP_FOLDER_SUBSCRIBED = QName.createQName(IMAP_MODEL_1_0_URI, "subscribed");
|
||||
static final QName ASPECT_IMAP_FOLDER_NONSELECTABLE = QName.createQName(IMAP_MODEL_1_0_URI, "nonselectable");
|
||||
|
||||
static final QName TYPE_IMAP_CONTENT = QName.createQName(IMAP_MODEL_1_0_URI, "imapContent");
|
||||
static final QName PROP_MESSAGE_FROM = QName.createQName(IMAP_MODEL_1_0_URI, "messageFrom");
|
||||
static final QName PROP_MESSAGE_TO = QName.createQName(IMAP_MODEL_1_0_URI, "messageTo");
|
||||
static final QName PROP_MESSAGE_CC = QName.createQName(IMAP_MODEL_1_0_URI, "messageCc");
|
||||
static final QName PROP_MESSAGE_SUBJECT = QName.createQName(IMAP_MODEL_1_0_URI, "messageSubject");
|
||||
|
||||
static final QName ASPECT_FLAGGABLE = QName.createQName(IMAP_MODEL_1_0_URI, "flaggable");
|
||||
static final QName PROP_FLAG_ANSWERED = QName.createQName(IMAP_MODEL_1_0_URI, "flagAnswered");
|
||||
static final QName PROP_FLAG_DELETED = QName.createQName(IMAP_MODEL_1_0_URI, "flagDeleted");
|
||||
static final QName PROP_FLAG_DRAFT = QName.createQName(IMAP_MODEL_1_0_URI, "flagDraft");
|
||||
static final QName PROP_FLAG_SEEN = QName.createQName(IMAP_MODEL_1_0_URI, "flagSeen");
|
||||
static final QName PROP_FLAG_RECENT = QName.createQName(IMAP_MODEL_1_0_URI, "flagRecent");
|
||||
static final QName PROP_FLAG_FLAGGED = QName.createQName(IMAP_MODEL_1_0_URI, "flagFlagged");
|
||||
|
||||
static final QName TYPE_IMAP_BODY = QName.createQName(IMAP_MODEL_1_0_URI, "imapBody");
|
||||
|
||||
static final QName TYPE_IMAP_ATTACH = QName.createQName(IMAP_MODEL_1_0_URI, "imapAttach");
|
||||
static final QName PROP_ATTACH_ID = QName.createQName(IMAP_MODEL_1_0_URI, "attachID");
|
||||
|
||||
}
|
@@ -58,6 +58,7 @@ public class ScriptActionExecuter extends ActionExecuterAbstractBase
|
||||
private String companyHomePath;
|
||||
private StoreRef storeRef;
|
||||
private ScriptLocation scriptLocation;
|
||||
private String webApplicationContextUrl;
|
||||
|
||||
/**
|
||||
* @param serviceRegistry The serviceRegistry to set.
|
||||
@@ -95,6 +96,16 @@ public class ScriptActionExecuter extends ActionExecuterAbstractBase
|
||||
this.scriptLocation = scriptLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the web application context url
|
||||
*
|
||||
* @param webApplicationContextUrl web application context url
|
||||
*/
|
||||
public void setWebApplicationContextUrl(String webApplicationContextUrl)
|
||||
{
|
||||
this.webApplicationContextUrl = webApplicationContextUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow adhoc properties to be passed to this action
|
||||
*
|
||||
@@ -150,6 +161,8 @@ public class ScriptActionExecuter extends ActionExecuterAbstractBase
|
||||
ScriptAction scriptAction = new ScriptAction(this.serviceRegistry, action, this.actionDefinition);
|
||||
model.put("action", scriptAction);
|
||||
|
||||
model.put("webApplicationContextUrl", webApplicationContextUrl);
|
||||
|
||||
Object result = null;
|
||||
if (this.scriptLocation == null)
|
||||
{
|
||||
|
264
source/java/org/alfresco/repo/admin/patch/impl/ImapFoldersPatch.java
Executable file
264
source/java/org/alfresco/repo/admin/patch/impl/ImapFoldersPatch.java
Executable file
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.admin.patch.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.alfresco.i18n.I18NUtil;
|
||||
import org.alfresco.repo.admin.patch.AbstractPatch;
|
||||
import org.alfresco.repo.importer.ACPImportPackageHandler;
|
||||
import org.alfresco.repo.importer.ImporterBootstrap;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||
import org.alfresco.service.cmr.admin.PatchException;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.view.ImporterService;
|
||||
import org.alfresco.service.cmr.view.Location;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
/**
|
||||
* Builds folders tree necessary for IMAP functionality and imports email action scripts.
|
||||
*
|
||||
* 1. Company Home > Data Dictionary > Imap Config > Templates
|
||||
* 2. Company Home > Data Dictionary > Email Actions > search
|
||||
* 3. Company Home > Data Dictionary > Scripts > command-processor.js, command-search.js
|
||||
*
|
||||
* @author Arseny Kovalchuk
|
||||
*/
|
||||
public class ImapFoldersPatch extends AbstractPatch
|
||||
{
|
||||
// messages' ids
|
||||
private static final String MSG_EXISTS = "patch.imapFolders.result.exists";
|
||||
private static final String MSG_CREATED = "patch.imapFolders.result.created";
|
||||
|
||||
// folders' names for path building
|
||||
private static final String PROPERTY_COMPANY_HOME_CHILDNAME = "spaces.company_home.childname";
|
||||
private static final String PROPERTY_DICTIONARY_CHILDNAME = "spaces.dictionary.childname";
|
||||
private static final String PROPERTY_SCRIPTS_CHILDNAME = "spaces.scripts.childname";
|
||||
private static final String PROPERTY_IMAP_CONFIG_CHILDNAME = "spaces.imapConfig.childname";
|
||||
|
||||
private ImporterBootstrap importerBootstrap;
|
||||
private MessageSource messageSource;
|
||||
protected Properties configuration;
|
||||
private ImporterService importerService;
|
||||
|
||||
private NodeRef companyHomeNodeRef;
|
||||
private NodeRef dictionaryNodeRef;
|
||||
private NodeRef scriptsNodeRef;
|
||||
private NodeRef imapConfigFolderNodeRef;
|
||||
private String configFoldersACP;
|
||||
private String emailActionsACP;
|
||||
private String scriptsACP;
|
||||
|
||||
public void setImporterBootstrap(ImporterBootstrap importerBootstrap)
|
||||
{
|
||||
this.importerBootstrap = importerBootstrap;
|
||||
}
|
||||
|
||||
public void setMessageSource(MessageSource messageSource)
|
||||
{
|
||||
this.messageSource = messageSource;
|
||||
}
|
||||
|
||||
public void setImporterService(ImporterService importerService)
|
||||
{
|
||||
this.importerService = importerService;
|
||||
}
|
||||
|
||||
public void setConfigFoldersACP(String configFoldersACP)
|
||||
{
|
||||
this.configFoldersACP = configFoldersACP;
|
||||
}
|
||||
|
||||
public void setEmailActionsACP(String emailActionsACP)
|
||||
{
|
||||
this.emailActionsACP = emailActionsACP;
|
||||
}
|
||||
|
||||
public void setScriptsACP(String scriptsACP)
|
||||
{
|
||||
this.scriptsACP = scriptsACP;
|
||||
}
|
||||
|
||||
protected void checkCommonProperties() throws Exception
|
||||
{
|
||||
checkPropertyNotNull(importerBootstrap, "importerBootstrap");
|
||||
checkPropertyNotNull(messageSource, "messageSource");
|
||||
checkPropertyNotNull(importerService, "importerService");
|
||||
}
|
||||
|
||||
protected void setUp() throws Exception
|
||||
{
|
||||
// get the node store that we must work against
|
||||
StoreRef storeRef = importerBootstrap.getStoreRef();
|
||||
if (storeRef == null)
|
||||
{
|
||||
throw new PatchException("Bootstrap store has not been set");
|
||||
}
|
||||
NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef);
|
||||
|
||||
this.configuration = importerBootstrap.getConfiguration();
|
||||
// get the association names that form the path
|
||||
String companyHomeChildName = configuration.getProperty(PROPERTY_COMPANY_HOME_CHILDNAME);
|
||||
if (companyHomeChildName == null || companyHomeChildName.length() == 0)
|
||||
{
|
||||
throw new PatchException("Bootstrap property '" + PROPERTY_COMPANY_HOME_CHILDNAME + "' is not present");
|
||||
}
|
||||
String dictionaryChildName = configuration.getProperty(PROPERTY_DICTIONARY_CHILDNAME);
|
||||
if (dictionaryChildName == null || dictionaryChildName.length() == 0)
|
||||
{
|
||||
throw new PatchException("Bootstrap property '" + PROPERTY_DICTIONARY_CHILDNAME + "' is not present");
|
||||
}
|
||||
String scriptsChildName = configuration.getProperty(PROPERTY_SCRIPTS_CHILDNAME);
|
||||
if (scriptsChildName == null || scriptsChildName.length() == 0)
|
||||
{
|
||||
throw new PatchException("Bootstrap property '" + PROPERTY_SCRIPTS_CHILDNAME + "' is not present");
|
||||
}
|
||||
|
||||
String imapConfigChildName = configuration.getProperty(PROPERTY_IMAP_CONFIG_CHILDNAME);
|
||||
if (imapConfigChildName == null || imapConfigChildName.length() == 0)
|
||||
{
|
||||
throw new PatchException("Bootstrap property '" + PROPERTY_IMAP_CONFIG_CHILDNAME + "' is not present");
|
||||
}
|
||||
|
||||
// build the search string to get the company home node
|
||||
StringBuilder sb = new StringBuilder(256);
|
||||
sb.append("/").append(companyHomeChildName);
|
||||
String xpath = sb.toString();
|
||||
// get the company home
|
||||
List<NodeRef> nodeRefs = searchService.selectNodes(storeRootNodeRef, xpath, null, namespaceService, false);
|
||||
if (nodeRefs.size() == 0)
|
||||
{
|
||||
throw new PatchException("XPath didn't return any results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath);
|
||||
}
|
||||
else if (nodeRefs.size() > 1)
|
||||
{
|
||||
throw new PatchException("XPath returned too many results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath + "\n" + " results: " + nodeRefs);
|
||||
}
|
||||
this.companyHomeNodeRef = nodeRefs.get(0);
|
||||
|
||||
// build the search string to get the dictionary node
|
||||
sb.append("/").append(dictionaryChildName);
|
||||
xpath = sb.toString();
|
||||
// get the dictionary node
|
||||
nodeRefs = searchService.selectNodes(storeRootNodeRef, xpath, null, namespaceService, false);
|
||||
if (nodeRefs.size() == 0)
|
||||
{
|
||||
throw new PatchException("XPath didn't return any results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath);
|
||||
}
|
||||
else if (nodeRefs.size() > 1)
|
||||
{
|
||||
throw new PatchException("XPath returned too many results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath + "\n" + " results: " + nodeRefs);
|
||||
}
|
||||
this.dictionaryNodeRef = nodeRefs.get(0);
|
||||
sb.append("/").append(scriptsChildName);
|
||||
xpath = sb.toString();
|
||||
nodeRefs = searchService.selectNodes(storeRootNodeRef, xpath, null, namespaceService, false);
|
||||
if (nodeRefs.size() == 0)
|
||||
{
|
||||
throw new PatchException("XPath didn't return any results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath);
|
||||
}
|
||||
else if (nodeRefs.size() > 1)
|
||||
{
|
||||
throw new PatchException("XPath returned too many results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath + "\n" + " results: " + nodeRefs);
|
||||
}
|
||||
this.scriptsNodeRef = nodeRefs.get(0);
|
||||
// get the ImapConfig node
|
||||
sb.delete((sb.length() - (scriptsChildName.length() + 1)), sb.length());
|
||||
sb.append("/").append(imapConfigChildName);
|
||||
xpath = sb.toString();
|
||||
nodeRefs = searchService.selectNodes(storeRootNodeRef, xpath, null, namespaceService, false);
|
||||
if (nodeRefs.size() > 1)
|
||||
{
|
||||
throw new PatchException("XPath returned too many results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath + "\n" + " results: " + nodeRefs);
|
||||
}
|
||||
else if (nodeRefs.size() == 0)
|
||||
{
|
||||
this.imapConfigFolderNodeRef = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.imapConfigFolderNodeRef = nodeRefs.get(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String applyInternal() throws Exception
|
||||
{
|
||||
checkCommonProperties();
|
||||
setUp();
|
||||
String msg = null;
|
||||
if (imapConfigFolderNodeRef == null)
|
||||
{
|
||||
// import the content
|
||||
RunAsWork<Object> importRunAs = new RunAsWork<Object>()
|
||||
{
|
||||
public Object doWork() throws Exception
|
||||
{
|
||||
importImapConfig();
|
||||
importScripts();
|
||||
importEmailActions();
|
||||
return null;
|
||||
}
|
||||
};
|
||||
AuthenticationUtil.runAs(importRunAs, authenticationContext.getSystemUserName());
|
||||
msg = I18NUtil.getMessage(MSG_CREATED);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = I18NUtil.getMessage(MSG_EXISTS, imapConfigFolderNodeRef);
|
||||
}
|
||||
return msg;
|
||||
|
||||
}
|
||||
|
||||
private void importImapConfig() throws IOException
|
||||
{
|
||||
importInternal(this.configFoldersACP, this.dictionaryNodeRef);
|
||||
}
|
||||
|
||||
private void importEmailActions() throws IOException
|
||||
{
|
||||
importInternal(this.emailActionsACP, this.dictionaryNodeRef);
|
||||
}
|
||||
|
||||
private void importScripts() throws IOException
|
||||
{
|
||||
importInternal(this.scriptsACP, this.scriptsNodeRef);
|
||||
}
|
||||
|
||||
private void importInternal(String acpName, NodeRef space) throws IOException
|
||||
{
|
||||
ClassPathResource acpResource = new ClassPathResource(acpName);
|
||||
ACPImportPackageHandler acpHandler = new ACPImportPackageHandler(acpResource.getFile(), null);
|
||||
Location importLocation = new Location(space);
|
||||
importerService.importView(acpHandler, importLocation, null, null);
|
||||
}
|
||||
|
||||
}
|
257
source/java/org/alfresco/repo/admin/patch/impl/ImapUsersPatch.java
Executable file
257
source/java/org/alfresco/repo/admin/patch/impl/ImapUsersPatch.java
Executable file
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* As a special exception to the terms and conditions of version 2.0 of.
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.admin.patch.impl;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import org.alfresco.i18n.I18NUtil;
|
||||
import org.alfresco.model.ApplicationModel;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.admin.patch.AbstractPatch;
|
||||
import org.alfresco.repo.importer.ImporterBootstrap;
|
||||
import org.alfresco.service.cmr.admin.PatchException;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.search.ResultSet;
|
||||
import org.alfresco.service.cmr.search.SearchParameters;
|
||||
import org.alfresco.service.cmr.search.SearchService;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.springframework.context.MessageSource;
|
||||
|
||||
/**
|
||||
* @author Dmitry Vaserin
|
||||
*/
|
||||
public class ImapUsersPatch extends AbstractPatch
|
||||
{
|
||||
// messages' ids
|
||||
private static final String MSG_EXISTS = "patch.imapUserFolders.result.exists";
|
||||
private static final String MSG_CREATED = "patch.imapUserFolders.result.created";
|
||||
|
||||
// folders' names for path building
|
||||
private static final String PROPERTY_COMPANY_HOME_CHILDNAME = "spaces.company_home.childname";
|
||||
private static final String PROPERTY_IMAP_HOME_FOLDER_CHILDNAME = "spaces.imap_home.childname";
|
||||
|
||||
private static final String PROPERTY_ICON = "space-icon-default";
|
||||
private static final String MSG_IMAP_HOME_FOLDER_NAME = "spaces.imap_home.name";
|
||||
private static final String MSG_IMAP_HOME_FOLDER_DESCRIPTION = "spaces.imap_home.description";
|
||||
|
||||
private static final String INBOX_NAME = "INBOX";
|
||||
private static final String INBOX_DECSRIPTION = "INBOX mail box";
|
||||
|
||||
private ImporterBootstrap importerBootstrap;
|
||||
private MessageSource messageSource;
|
||||
|
||||
protected NodeRef companyHomeNodeRef;
|
||||
protected Properties configuration;
|
||||
protected NodeRef imapHomeNodeRef;
|
||||
|
||||
public void setImporterBootstrap(ImporterBootstrap importerBootstrap)
|
||||
{
|
||||
this.importerBootstrap = importerBootstrap;
|
||||
}
|
||||
|
||||
public void setMessageSource(MessageSource messageSource)
|
||||
{
|
||||
this.messageSource = messageSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that required common properties have been set
|
||||
*/
|
||||
protected void checkCommonProperties() throws Exception
|
||||
{
|
||||
checkPropertyNotNull(importerBootstrap, "importerBootstrap");
|
||||
checkPropertyNotNull(namespaceService, "namespaceService");
|
||||
checkPropertyNotNull(searchService, "searchService");
|
||||
checkPropertyNotNull(nodeService, "nodeService");
|
||||
checkPropertyNotNull(messageSource, "messageSource");
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts pertinent references and properties that are common to execution of this and derived patches.
|
||||
*/
|
||||
protected void setUp() throws Exception
|
||||
{
|
||||
// get the node store that we must work against
|
||||
StoreRef storeRef = importerBootstrap.getStoreRef();
|
||||
if (storeRef == null)
|
||||
{
|
||||
throw new PatchException("Bootstrap store has not been set");
|
||||
}
|
||||
NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef);
|
||||
|
||||
this.configuration = importerBootstrap.getConfiguration();
|
||||
// get the association names that form the path
|
||||
String companyHomeChildName = configuration.getProperty(PROPERTY_COMPANY_HOME_CHILDNAME);
|
||||
if (companyHomeChildName == null || companyHomeChildName.length() == 0)
|
||||
{
|
||||
throw new PatchException("Bootstrap property '" + PROPERTY_COMPANY_HOME_CHILDNAME + "' is not present");
|
||||
}
|
||||
String imapHomeChildName = configuration.getProperty(PROPERTY_IMAP_HOME_FOLDER_CHILDNAME);
|
||||
if (imapHomeChildName == null || imapHomeChildName.length() == 0)
|
||||
{
|
||||
throw new PatchException("Bootstrap property '" + PROPERTY_IMAP_HOME_FOLDER_CHILDNAME + "' is not present");
|
||||
}
|
||||
|
||||
// build the search string to get the company home node
|
||||
StringBuilder sb = new StringBuilder(256);
|
||||
sb.append("/").append(companyHomeChildName);
|
||||
String xpath = sb.toString();
|
||||
// get the company home
|
||||
List<NodeRef> nodeRefs = searchService.selectNodes(storeRootNodeRef, xpath, null, namespaceService, false);
|
||||
if (nodeRefs.size() == 0)
|
||||
{
|
||||
throw new PatchException("XPath didn't return any results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath);
|
||||
}
|
||||
else if (nodeRefs.size() > 1)
|
||||
{
|
||||
throw new PatchException("XPath returned too many results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath + "\n" + " results: " + nodeRefs);
|
||||
}
|
||||
this.companyHomeNodeRef = nodeRefs.get(0);
|
||||
|
||||
xpath = imapHomeChildName;
|
||||
nodeRefs = searchService.selectNodes(companyHomeNodeRef, xpath, null, namespaceService, false);
|
||||
if (nodeRefs.size() > 1)
|
||||
{
|
||||
throw new PatchException("XPath returned too many results: \n" + " dictionary node: " + companyHomeNodeRef + "\n" + " xpath: " + xpath + "\n" + " results: "
|
||||
+ nodeRefs);
|
||||
}
|
||||
else if (nodeRefs.size() == 0)
|
||||
{
|
||||
// the node does not exist
|
||||
this.imapHomeNodeRef = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.imapHomeNodeRef = nodeRefs.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String applyInternal() throws Exception
|
||||
{
|
||||
// properties must be set
|
||||
checkCommonProperties();
|
||||
// get useful values
|
||||
setUp();
|
||||
|
||||
String msg = null;
|
||||
|
||||
if (imapHomeNodeRef == null)
|
||||
{
|
||||
// create it
|
||||
createImapHomeFolders();
|
||||
msg = I18NUtil.getMessage(MSG_CREATED, imapHomeNodeRef);
|
||||
}
|
||||
else
|
||||
{
|
||||
// it already exists
|
||||
msg = I18NUtil.getMessage(MSG_EXISTS, imapHomeNodeRef);
|
||||
}
|
||||
|
||||
// done
|
||||
return msg;
|
||||
}
|
||||
|
||||
private void createImapHomeFolders()
|
||||
{
|
||||
// get required properties
|
||||
String imapHomeChildName = configuration.getProperty(PROPERTY_IMAP_HOME_FOLDER_CHILDNAME);
|
||||
if (imapHomeChildName == null)
|
||||
{
|
||||
throw new PatchException("Bootstrap property '" + PROPERTY_IMAP_HOME_FOLDER_CHILDNAME + "' is not present");
|
||||
}
|
||||
|
||||
String name = messageSource.getMessage(MSG_IMAP_HOME_FOLDER_NAME, null, I18NUtil.getLocale());
|
||||
if (name == null || name.length() == 0)
|
||||
{
|
||||
throw new PatchException("Bootstrap property '" + MSG_IMAP_HOME_FOLDER_NAME + "' is not present");
|
||||
}
|
||||
|
||||
String description = messageSource.getMessage(MSG_IMAP_HOME_FOLDER_DESCRIPTION, null, I18NUtil.getLocale());
|
||||
if (description == null || description.length() == 0)
|
||||
{
|
||||
throw new PatchException("Bootstrap property '" + MSG_IMAP_HOME_FOLDER_DESCRIPTION + "' is not present");
|
||||
}
|
||||
|
||||
imapHomeNodeRef = createSpace(companyHomeNodeRef, name, description, imapHomeChildName);
|
||||
|
||||
// Create IMAP Home and "INBOX" for each user
|
||||
createImapUserHomes();
|
||||
|
||||
}
|
||||
|
||||
private void createImapUserHomes()
|
||||
{
|
||||
StringBuilder query = new StringBuilder(128);
|
||||
query.append("@").append(NamespaceService.CONTENT_MODEL_PREFIX).append("\\:userName:*");
|
||||
|
||||
SearchParameters params = new SearchParameters();
|
||||
params.setLanguage(SearchService.LANGUAGE_LUCENE);
|
||||
params.addStore(importerBootstrap.getStoreRef());
|
||||
params.setQuery(query.toString());
|
||||
|
||||
ResultSet results = searchService.query(params);
|
||||
List<NodeRef> people;
|
||||
try
|
||||
{
|
||||
people = results.getNodeRefs();
|
||||
}
|
||||
finally
|
||||
{
|
||||
results.close();
|
||||
}
|
||||
|
||||
for (NodeRef nodeRef : people)
|
||||
{
|
||||
String userName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME);
|
||||
String desc = userName + " " + messageSource.getMessage(MSG_IMAP_HOME_FOLDER_NAME, null, I18NUtil.getLocale());
|
||||
NodeRef userHome = createSpace(imapHomeNodeRef, userName, desc, userName);
|
||||
// Create Inbox
|
||||
createSpace(userHome, INBOX_NAME, INBOX_DECSRIPTION, INBOX_NAME);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private NodeRef createSpace(NodeRef parent, String name, String desc, String childName)
|
||||
{
|
||||
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(7);
|
||||
properties.put(ContentModel.PROP_NAME, name);
|
||||
properties.put(ContentModel.PROP_TITLE, name);
|
||||
properties.put(ContentModel.PROP_DESCRIPTION, desc);
|
||||
properties.put(ApplicationModel.PROP_ICON, PROPERTY_ICON);
|
||||
// create the node
|
||||
ChildAssociationRef childAssocRef = nodeService.createNode(parent, ContentModel.ASSOC_CONTAINS, QName.resolveToQName(namespaceService, childName),
|
||||
ContentModel.TYPE_FOLDER, properties);
|
||||
NodeRef result = childAssocRef.getChildRef();
|
||||
|
||||
// add the required aspects
|
||||
nodeService.addAspect(result, ApplicationModel.ASPECT_UIFACETS, null);
|
||||
return result;
|
||||
}
|
||||
}
|
72
source/java/org/alfresco/repo/imap/AlfrescoImapConst.java
Executable file
72
source/java/org/alfresco/repo/imap/AlfrescoImapConst.java
Executable file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.imap;
|
||||
|
||||
/**
|
||||
* @author Mike Shavnev
|
||||
*/
|
||||
public interface AlfrescoImapConst
|
||||
{
|
||||
|
||||
public static final char HIERARCHY_DELIMITER = '.';
|
||||
public static final String NAMESPACE_PREFIX = "#";
|
||||
public static final String USER_NAMESPACE = "#mail";
|
||||
public static final String INBOX_NAME = "INBOX";
|
||||
|
||||
public static final String BODY_TEXT_PLAIN_NAME = "Body.txt";
|
||||
public static final String BODY_TEXT_HTML_NAME = "Body.html";
|
||||
public static final String MESSAGE_PREFIX = "Message_";
|
||||
// Separator for user enties in flag and subscribe properties
|
||||
public static final String USER_SEPARATOR = ";";
|
||||
|
||||
/**
|
||||
* Defines {@link AlfrescoImapMailFolder} view mode as archive mode. Used for Email Archive View.
|
||||
*/
|
||||
public static final String MODE_ARCHIVE = "archive";
|
||||
/**
|
||||
* Defines {@link AlfrescoImapMailFolder} view mode as virtual mode. Used for IMAP Virtualised View.
|
||||
*/
|
||||
public static final String MODE_VIRTUAL = "virtual";
|
||||
|
||||
// Default content model email message templates
|
||||
public static final String CLASSPATH_TEXT_PLAIN_TEMPLATE = "/alfresco/templates/imap/imap_message_text_plain.ftl";
|
||||
public static final String CLASSPATH_TEXT_HTML_TEMPLATE = "/alfresco/templates/imap/imap_message_text_html.ftl";
|
||||
|
||||
public static final String DICTIONARY_TEMPLATE_PREFIX = "emailbody";
|
||||
public static final String PREF_IMAP_FAVOURITE_SITES = "org.alfresco.share.sites.imap.favourites";
|
||||
|
||||
// AlfrescoImapMessage constants
|
||||
public static final String MIME_VERSION = "MIME-Version";
|
||||
public static final String CONTENT_TYPE = "Content-Type";
|
||||
public static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
|
||||
public static final String MULTIPART_MIXED = "mixed";
|
||||
public static final String CONTENT_ID = "Content-ID";
|
||||
public static final String X_ALF_NODEREF_ID = "X-Alfresco-NodeRef-ID"; // The NodeRef id header
|
||||
public static final String X_ALF_SERVER_UID = "X-Alfresco-Server-UID"; // The unique identifier of Alfresco server
|
||||
public static final String EIGHT_BIT_ENCODING = "8bit";
|
||||
public static final String BASE_64_ENCODING = "base64";
|
||||
public static final String UTF_8 = "UTF-8";
|
||||
|
||||
}
|
958
source/java/org/alfresco/repo/imap/AlfrescoImapHostManager.java
Executable file
958
source/java/org/alfresco/repo/imap/AlfrescoImapHostManager.java
Executable file
@@ -0,0 +1,958 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.imap;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.model.ImapModel;
|
||||
import org.alfresco.repo.imap.config.ImapConfigElement.ImapConfig;
|
||||
import org.alfresco.repo.imap.exception.AlfrescoImapFolderException;
|
||||
import org.alfresco.repo.model.filefolder.FileFolderServiceImpl;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.model.FileExistsException;
|
||||
import org.alfresco.service.cmr.model.FileFolderService;
|
||||
import org.alfresco.service.cmr.model.FileInfo;
|
||||
import org.alfresco.service.cmr.model.FileNotFoundException;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.security.AccessStatus;
|
||||
import org.alfresco.service.cmr.security.PermissionService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import com.icegreen.greenmail.imap.AuthorizationException;
|
||||
import com.icegreen.greenmail.imap.ImapHostManager;
|
||||
import com.icegreen.greenmail.store.FolderException;
|
||||
import com.icegreen.greenmail.store.MailFolder;
|
||||
import com.icegreen.greenmail.user.GreenMailUser;
|
||||
import com.icegreen.greenmail.util.GreenMailUtil;
|
||||
|
||||
/**
|
||||
* @author Mike Shavnev
|
||||
*/
|
||||
public class AlfrescoImapHostManager implements ImapHostManager
|
||||
{
|
||||
|
||||
private Log logger = LogFactory.getLog(AlfrescoImapHostManager.class);
|
||||
|
||||
private ServiceRegistry serviceRegistry;
|
||||
|
||||
private NodeService nodeService;
|
||||
private FileFolderService fileFolderService;
|
||||
private ImapHelper imapHelper;
|
||||
|
||||
/**
|
||||
* Returns the hierarchy delimiter for mailboxes on this host.
|
||||
*
|
||||
* @return The hierarchy delimiter character.
|
||||
*/
|
||||
public char getHierarchyDelimiter()
|
||||
{
|
||||
return AlfrescoImapConst.HIERARCHY_DELIMITER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an collection of mailboxes. Method searches mailboxes under mount points defined for a specific user. Mount points include user's IMAP Virtualised Views and Email
|
||||
* Archive Views. This method serves LIST command of the IMAP protocol.
|
||||
*
|
||||
* @param user User making the request
|
||||
* @param mailboxPattern String name of a mailbox possible including a wildcard.
|
||||
* @return Collection of mailboxes matching the pattern.
|
||||
* @throws com.icegreen.greenmail.store.FolderException
|
||||
*/
|
||||
public Collection<MailFolder> listMailboxes(GreenMailUser user, String mailboxPattern) throws FolderException
|
||||
{
|
||||
mailboxPattern = GreenMailUtil.convertFromUtf7(mailboxPattern);
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Listing mailboxes: mailboxPattern=" + mailboxPattern);
|
||||
}
|
||||
mailboxPattern = imapHelper.getMailPathInRepo(mailboxPattern);
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Listing mailboxes: mailboxPattern in alfresco=" + mailboxPattern);
|
||||
}
|
||||
|
||||
Collection<MailFolder> result = new LinkedList<MailFolder>();
|
||||
|
||||
Map<String, NodeRef> mountPoints = imapHelper.getMountPoints();
|
||||
Map<String, ImapConfig> imapConfigs = imapHelper.getImapConfigs();
|
||||
|
||||
NodeRef mountPoint;
|
||||
for (String mountPointName : mountPoints.keySet())
|
||||
{
|
||||
|
||||
mountPoint = mountPoints.get(mountPointName);
|
||||
FileInfo mountPointFileInfo = imapHelper.getFileFolderService().getFileInfo(mountPoint);
|
||||
|
||||
NodeRef mountParent = imapHelper.getNodeService().getParentAssocs(mountPoint).get(0).getParentRef();
|
||||
String mode = imapConfigs.get(mountPointName).getMode();
|
||||
|
||||
if (!mailboxPattern.equals("*"))
|
||||
{
|
||||
mountPoint = mountParent;
|
||||
}
|
||||
|
||||
boolean isVirtualView = imapConfigs.get(mountPointName).getMode().equals(AlfrescoImapConst.MODE_VIRTUAL);
|
||||
Collection<FileInfo> folders = listFolder(mountPoint, user, mailboxPattern, isVirtualView);
|
||||
if (folders != null)
|
||||
{
|
||||
for (FileInfo folder : folders)
|
||||
{
|
||||
result.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), folder, folder.getName(), mode, mountParent, mountPointName, imapHelper));
|
||||
}
|
||||
}
|
||||
if (mailboxPattern.equals("*"))
|
||||
{
|
||||
result.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), mountPointFileInfo, mountPointName, mode, mountParent, mountPointName, imapHelper));
|
||||
}
|
||||
}
|
||||
mountPoint = imapHelper.getUserImapHomeRef(user.getLogin());
|
||||
Collection<FileInfo> imapFolders = listFolder(mountPoint, user, mailboxPattern, false);
|
||||
if (imapFolders != null)
|
||||
{
|
||||
for (FileInfo imapFolder : imapFolders)
|
||||
{
|
||||
result.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), imapFolder, imapFolder.getName(), AlfrescoImapConst.MODE_ARCHIVE, mountPoint, null,
|
||||
imapHelper));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Collection<FileInfo> listFolder(NodeRef root, GreenMailUser user, String mailboxPattern, boolean isVirtualView) throws FolderException
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Listing mailboxes: mailboxPattern=" + mailboxPattern);
|
||||
}
|
||||
|
||||
Collection<FileInfo> result = new LinkedList<FileInfo>();
|
||||
|
||||
int index = mailboxPattern.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER);
|
||||
|
||||
String name = null;
|
||||
String remainName = null;
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
name = mailboxPattern;
|
||||
}
|
||||
else
|
||||
{
|
||||
name = mailboxPattern.substring(0, index);
|
||||
remainName = mailboxPattern.substring(index + 1);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Listing mailboxes: name=" + name);
|
||||
}
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
if ("*".equals(name))
|
||||
{
|
||||
List<FileInfo> list = imapHelper.searchFolders(root, name, true, isVirtualView);
|
||||
if (list.size() > 0)
|
||||
{
|
||||
return list;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else if (name.endsWith("*"))
|
||||
{
|
||||
List<FileInfo> list = imapHelper.searchFolders(root, name.replace('%', '*'), false, isVirtualView);
|
||||
if (list.size() > 0)
|
||||
{
|
||||
result.addAll(list);
|
||||
for (FileInfo fileInfo : list)
|
||||
{
|
||||
List<FileInfo> childList = imapHelper.searchFolders(fileInfo.getNodeRef(), "*", true, isVirtualView);
|
||||
result.addAll(childList);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else if (name.contains("%") || name.contains("*"))
|
||||
{
|
||||
List<FileInfo> list = imapHelper.searchFolders(root, name.replace('%', '*'), false, isVirtualView);
|
||||
if (list.size() > 0)
|
||||
{
|
||||
return list;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<FileInfo> list = imapHelper.searchFolders(root, name, false, isVirtualView);
|
||||
if (list.size() > 0)
|
||||
{
|
||||
return list;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
List<FileInfo> list = imapHelper.searchFolders(root, name.replace('%', '*'), false, isVirtualView);
|
||||
for (FileInfo folder : list)
|
||||
{
|
||||
Collection<FileInfo> childFolders = listFolder(folder.getNodeRef(), user, remainName, isVirtualView);
|
||||
if (childFolders != null)
|
||||
{
|
||||
result.addAll(childFolders);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an collection of subscribed mailboxes. To appear in search result mailboxes should have {http://www.alfresco.org/model/imap/1.0}subscribed property specified for
|
||||
* user. Method searches subscribed mailboxes under mount points defined for a specific user. Mount points include user's IMAP Virtualised Views and Email Archive Views. This
|
||||
* method serves LSUB command of the IMAP protocol.
|
||||
*
|
||||
* @param user User making the request
|
||||
* @param mailboxPattern String name of a mailbox possible including a wildcard.
|
||||
* @return Collection of mailboxes matching the pattern.
|
||||
* @throws com.icegreen.greenmail.store.FolderException
|
||||
*/
|
||||
public Collection<MailFolder> listSubscribedMailboxes(GreenMailUser user, String mailboxPattern) throws FolderException
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Listing subscribed mailboxes: mailboxPattern=" + mailboxPattern);
|
||||
}
|
||||
mailboxPattern = imapHelper.getMailPathInRepo(mailboxPattern);
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Listing subscribed mailboxes: mailboxPattern in alfresco=" + mailboxPattern);
|
||||
}
|
||||
|
||||
Collection<MailFolder> result = new LinkedList<MailFolder>();
|
||||
|
||||
Map<String, NodeRef> mountPoints = imapHelper.getMountPoints();
|
||||
Map<String, ImapConfig> imapConfigs = imapHelper.getImapConfigs();
|
||||
|
||||
NodeRef mountPoint;
|
||||
|
||||
for (String mountPointName : mountPoints.keySet())
|
||||
{
|
||||
|
||||
mountPoint = mountPoints.get(mountPointName);
|
||||
FileInfo mountPointFileInfo = imapHelper.getFileFolderService().getFileInfo(mountPoint);
|
||||
NodeRef mountParent = imapHelper.getNodeService().getParentAssocs(mountPoint).get(0).getParentRef();
|
||||
String viewMode = imapConfigs.get(mountPointName).getMode();
|
||||
|
||||
if (!mailboxPattern.equals("*"))
|
||||
{
|
||||
mountPoint = mountParent;
|
||||
}
|
||||
|
||||
boolean isVirtualView = imapConfigs.get(mountPointName).getMode().equals(AlfrescoImapConst.MODE_VIRTUAL);
|
||||
Collection<MailFolder> folders = listSubscribedFolder(mountPoint, mountPoint, user, mailboxPattern, isVirtualView);
|
||||
if (folders != null)
|
||||
{
|
||||
for (MailFolder mailFolder : folders)
|
||||
{
|
||||
AlfrescoImapMailFolder folder = (AlfrescoImapMailFolder) mailFolder;
|
||||
folder.setMountPointName(mountPointName);
|
||||
folder.setViewMode(viewMode);
|
||||
folder.setMountParent(mountParent);
|
||||
}
|
||||
result.addAll(folders);
|
||||
}
|
||||
|
||||
if (mailboxPattern.equals("*"))
|
||||
{
|
||||
if (isSubscribed(mountPointFileInfo, user.getLogin()))
|
||||
{
|
||||
result.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), mountPointFileInfo, mountPointName, viewMode, mountParent, mountPointName, imapHelper));
|
||||
}
|
||||
// \NoSelect
|
||||
else if (hasSubscribedChild(mountPointFileInfo, user.getLogin(), isVirtualView))
|
||||
{
|
||||
result.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), mountPointFileInfo, mountPointName, viewMode, mountParent, mountPointName, imapHelper,
|
||||
false));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NodeRef root = imapHelper.getSpacesStoreNodeRef();
|
||||
|
||||
root = imapHelper.getUserImapHomeRef(user.getLogin());
|
||||
Collection<MailFolder> imapFolders = listSubscribedFolder(root, root, user, mailboxPattern, false);
|
||||
|
||||
if (imapFolders != null)
|
||||
{
|
||||
result.addAll(imapFolders);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Collection<MailFolder> listSubscribedFolder(NodeRef mailboxRoot, NodeRef root, GreenMailUser user, String mailboxPattern, boolean isVirtualView) throws FolderException
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Listing mailboxes: mailboxPattern=" + mailboxPattern);
|
||||
}
|
||||
|
||||
int index = mailboxPattern.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER);
|
||||
|
||||
String name = null;
|
||||
String remainName = null;
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
name = mailboxPattern;
|
||||
}
|
||||
else
|
||||
{
|
||||
name = mailboxPattern.substring(0, index);
|
||||
remainName = mailboxPattern.substring(index + 1);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Listing mailboxes: name=" + name);
|
||||
}
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
if ("*".equals(name))
|
||||
{
|
||||
List<FileInfo> list = imapHelper.searchFolders(root, name, true, isVirtualView);
|
||||
Collection<FileInfo> subscribedList = getSubscribed(list, user.getLogin());
|
||||
|
||||
if (subscribedList.size() > 0)
|
||||
{
|
||||
return createMailFolderList(user, subscribedList, mailboxRoot);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else if (name.endsWith("*"))
|
||||
{
|
||||
List<FileInfo> fullList = new LinkedList<FileInfo>();
|
||||
List<FileInfo> list = imapHelper.searchFolders(root, name.replace('%', '*'), false, isVirtualView);
|
||||
Collection<FileInfo> subscribedList = getSubscribed(list, user.getLogin());
|
||||
|
||||
if (list.size() > 0)
|
||||
{
|
||||
fullList.addAll(subscribedList);
|
||||
for (FileInfo fileInfo : list)
|
||||
{
|
||||
List<FileInfo> childList = imapHelper.searchFolders(fileInfo.getNodeRef(), "*", true, isVirtualView);
|
||||
fullList.addAll(getSubscribed(childList, user.getLogin()));
|
||||
}
|
||||
return createMailFolderList(user, fullList, mailboxRoot);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else if ("%".equals(name))
|
||||
{
|
||||
List<FileInfo> list = imapHelper.searchFolders(root, "*", false, isVirtualView);
|
||||
LinkedList<MailFolder> subscribedList = new LinkedList<MailFolder>();
|
||||
|
||||
for (FileInfo fileInfo : list)
|
||||
{
|
||||
if (isSubscribed(fileInfo, user.getLogin()))
|
||||
{
|
||||
// folderName, viewMode, mountPointName will be setted in listSubscribedMailboxes() method
|
||||
subscribedList.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), fileInfo, null, null, mailboxRoot, null, imapHelper));
|
||||
}
|
||||
// \NoSelect
|
||||
else if (hasSubscribedChild(fileInfo, user.getLogin(), isVirtualView))
|
||||
{
|
||||
// folderName, viewMode, mountPointName will be setted in listSubscribedMailboxes() method
|
||||
subscribedList.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), fileInfo, null, null, mailboxRoot, null, imapHelper, false));
|
||||
}
|
||||
}
|
||||
|
||||
return subscribedList;
|
||||
}
|
||||
else if (name.contains("%") || name.contains("*"))
|
||||
{
|
||||
List<FileInfo> list = imapHelper.searchFolders(root, name.replace('%', '*'), false, isVirtualView);
|
||||
Collection<FileInfo> subscribedList = getSubscribed(list, user.getLogin());
|
||||
|
||||
if (subscribedList.size() > 0)
|
||||
{
|
||||
return createMailFolderList(user, subscribedList, mailboxRoot);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<FileInfo> list = imapHelper.searchFolders(root, name, false, isVirtualView);
|
||||
Collection<FileInfo> subscribedList = getSubscribed(list, user.getLogin());
|
||||
|
||||
if (subscribedList.size() > 0)
|
||||
{
|
||||
return createMailFolderList(user, subscribedList, mailboxRoot);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// If (index != -1) this is not the last level
|
||||
Collection<MailFolder> result = new LinkedList<MailFolder>();
|
||||
|
||||
List<FileInfo> list = imapHelper.searchFolders(root, name.replace('%', '*'), false, isVirtualView);
|
||||
for (FileInfo folder : list)
|
||||
{
|
||||
Collection<MailFolder> childFolders = listSubscribedFolder(mailboxRoot, folder.getNodeRef(), user, remainName, isVirtualView);
|
||||
|
||||
if (childFolders != null)
|
||||
{
|
||||
result.addAll(childFolders);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames an existing mailbox. The specified mailbox must already exist, the requested name must not exist already but must be able to be created and the user must have rights
|
||||
* to delete the existing mailbox and create a mailbox with the new name. Any inferior hierarchical names must also be renamed. If INBOX is renamed, the contents of INBOX are
|
||||
* transferred to a new mailbox with the new name, but INBOX is not deleted. If INBOX has inferior mailbox these are not renamed. This method serves RENAME command of the IMAP
|
||||
* protocol. <p/> Method searches mailbox under mount points defined for a specific user. Mount points include user's IMAP Virtualised Views and Email Archive Views.
|
||||
*
|
||||
* @param user User making the request.
|
||||
* @param oldMailboxName String name of the existing folder
|
||||
* @param newMailboxName String target new name
|
||||
* @throws com.icegreen.greenmail.store.FolderException if an existing folder with the new name.
|
||||
* @throws AlfrescoImapFolderException if user does not have rights to create the new mailbox.
|
||||
*/
|
||||
|
||||
public void renameMailbox(GreenMailUser user, String oldMailboxName, String newMailboxName) throws FolderException, AuthorizationException
|
||||
{
|
||||
oldMailboxName = GreenMailUtil.convertFromUtf7(oldMailboxName);
|
||||
newMailboxName = GreenMailUtil.convertFromUtf7(newMailboxName);
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Renaming folder: oldMailboxName=" + oldMailboxName + " newMailboxName=" + newMailboxName);
|
||||
}
|
||||
|
||||
AlfrescoImapMailFolder sourceNode = (AlfrescoImapMailFolder) getFolder(user, GreenMailUtil.convertInUtf7(oldMailboxName));
|
||||
|
||||
NodeRef root = imapHelper.getMailboxRootRef(oldMailboxName, user.getLogin());
|
||||
String mailboxRepoName = imapHelper.getMailPathInRepo(newMailboxName);
|
||||
|
||||
StringTokenizer tokenizer = new StringTokenizer(mailboxRepoName, String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER));
|
||||
|
||||
NodeRef parentNodeRef = root;
|
||||
while (tokenizer.hasMoreTokens())
|
||||
{
|
||||
String folderName = tokenizer.nextToken();
|
||||
|
||||
if (!tokenizer.hasMoreTokens())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (oldMailboxName.equalsIgnoreCase(AlfrescoImapConst.INBOX_NAME))
|
||||
{
|
||||
// If you trying to rename INBOX
|
||||
// - just copy it to another folder with new name
|
||||
// and leave INBOX (with children) intact.
|
||||
fileFolderService.copy(sourceNode.getFolderInfo().getNodeRef(), parentNodeRef, folderName);
|
||||
List<FileInfo> itemsForRemove = fileFolderService.list(sourceNode.getFolderInfo().getNodeRef());
|
||||
for (FileInfo fileInfo : itemsForRemove)
|
||||
{
|
||||
fileFolderService.delete(fileInfo.getNodeRef());
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
fileFolderService.move(sourceNode.getFolderInfo().getNodeRef(), parentNodeRef, folderName);
|
||||
}
|
||||
return;
|
||||
}
|
||||
catch (FileExistsException e)
|
||||
{
|
||||
throw new FolderException(FolderException.ALREADY_EXISTS_LOCALLY);
|
||||
}
|
||||
catch (FileNotFoundException e)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
List<FileInfo> folders = imapHelper.searchFolders(parentNodeRef, folderName, false, true);
|
||||
|
||||
if (folders.size() == 0)
|
||||
{
|
||||
AccessStatus status = imapHelper.hasPermission(parentNodeRef, PermissionService.WRITE);
|
||||
if (status == AccessStatus.DENIED)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Creating folder: Cant't create folder - Permission denied");
|
||||
}
|
||||
throw new AlfrescoImapFolderException(AlfrescoImapFolderException.PERMISSION_DENIED);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Create mailBox: " + folderName);
|
||||
}
|
||||
FileFolderServiceImpl.makeFolders(fileFolderService, parentNodeRef, Arrays.asList(folderName), ContentModel.TYPE_FOLDER);
|
||||
}
|
||||
else
|
||||
{
|
||||
parentNodeRef = folders.get(0).getNodeRef();
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("MailBox: " + folderName + " already exists");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to a newly created mailbox. The request should specify a mailbox that does not already exist on this server, that could exist on this server and that the
|
||||
* user has rights to create. This method serves CREATE command of the IMAP protocol.
|
||||
*
|
||||
* @param user User making the request.
|
||||
* @param mailboxName String name of the target
|
||||
* @return an Mailbox reference.
|
||||
* @throws com.icegreen.greenmail.store.FolderException if mailbox already exists
|
||||
* @throws AlfrescoImapFolderException if user does not have rights to create the new mailbox.
|
||||
*/
|
||||
public MailFolder createMailbox(GreenMailUser user, String mailboxName) throws AuthorizationException, FolderException
|
||||
{
|
||||
mailboxName = GreenMailUtil.convertFromUtf7(mailboxName);
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Creating folder: " + mailboxName);
|
||||
}
|
||||
|
||||
NodeRef root = imapHelper.getMailboxRootRef(mailboxName, user.getLogin());
|
||||
|
||||
String mountPointName = imapHelper.getMountPointName(mailboxName);
|
||||
String mailboxRepoNam = imapHelper.getMailPathInRepo(mailboxName);
|
||||
StringTokenizer tokenizer = new StringTokenizer(mailboxRepoNam, String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER));
|
||||
|
||||
NodeRef parentNodeRef = root;
|
||||
|
||||
while (tokenizer.hasMoreTokens())
|
||||
{
|
||||
String folderName = tokenizer.nextToken();
|
||||
|
||||
List<FileInfo> folders = imapHelper.searchFolders(parentNodeRef, folderName, false, true);
|
||||
|
||||
if (folders.size() == 0)
|
||||
{
|
||||
AccessStatus status = imapHelper.hasPermission(parentNodeRef, PermissionService.WRITE);
|
||||
if (status == AccessStatus.DENIED)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Creating folder: Cant't create folder - Permission denied");
|
||||
}
|
||||
throw new AlfrescoImapFolderException(AlfrescoImapFolderException.PERMISSION_DENIED);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Create mailBox: " + mailboxName);
|
||||
}
|
||||
FileInfo mailFolder = FileFolderServiceImpl.makeFolders(fileFolderService, parentNodeRef, Arrays.asList(folderName), ContentModel.TYPE_FOLDER);
|
||||
|
||||
|
||||
return new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), mailFolder, folderName, imapHelper.getViewMode(mailboxName), root, mountPointName, imapHelper);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
parentNodeRef = folders.get(0).getNodeRef();
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("MailBox: " + folderName + " already exists");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new FolderException(FolderException.ALREADY_EXISTS_LOCALLY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an existing MailBox. Specified mailbox must already exist on this server, and the user must have rights to delete it. <p/> This method serves DELETE command of the
|
||||
* IMAP protocol.
|
||||
*
|
||||
* @param user User making the request.
|
||||
* @param mailboxName String name of the target
|
||||
* @throws com.icegreen.greenmail.store.FolderException if mailbox has a non-selectable store with children
|
||||
*/
|
||||
public void deleteMailbox(GreenMailUser user, String mailboxName) throws FolderException, AuthorizationException
|
||||
{
|
||||
AlfrescoImapMailFolder folder = (AlfrescoImapMailFolder) getFolder(user, mailboxName);
|
||||
NodeRef nodeRef = folder.getFolderInfo().getNodeRef();
|
||||
|
||||
List<FileInfo> childFolders = imapHelper.searchFolders(nodeRef, "*", false, false);
|
||||
|
||||
if (childFolders.isEmpty())
|
||||
{
|
||||
folder.signalDeletion();
|
||||
// Delete child folders and messages
|
||||
fileFolderService.delete(nodeRef);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (folder.isSelectable())
|
||||
{
|
||||
// Delete all messages for this folder
|
||||
// Don't delete subfolders and their messages
|
||||
List<FileInfo> messages = imapHelper.searchFiles(nodeRef, "*", ImapModel.TYPE_IMAP_CONTENT, false);
|
||||
for (FileInfo message : messages)
|
||||
{
|
||||
fileFolderService.delete(message.getNodeRef());
|
||||
}
|
||||
nodeService.addAspect(nodeRef, ImapModel.ASPECT_IMAP_FOLDER_NONSELECTABLE, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FolderException(mailboxName + " - Can't delete a non-selectable store with children.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to an existing Mailbox. The requested mailbox must already exists on this server and the requesting user must have at least lookup rights. <p/> It is
|
||||
* also can be used by to obtain hierarchy delimiter by the LIST command: <p/> C: 2 list "" "" <p/> S: * LIST () "." "" <p/> S: 2 OK LIST completed. <p/> Method searches
|
||||
* mailbox under mount points defined for a specific user. Mount points include user's IMAP Virtualised Views and Email Archive Views.
|
||||
*
|
||||
* @param user User making the request.
|
||||
* @param mailboxName String name of the target.
|
||||
* @return an Mailbox reference.
|
||||
*/
|
||||
public MailFolder getFolder(GreenMailUser user, String mailboxName)
|
||||
{
|
||||
mailboxName = GreenMailUtil.convertFromUtf7(mailboxName);
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Getting folder: " + mailboxName);
|
||||
}
|
||||
|
||||
// If MailFolder object is used to obtain hierarchy delimiter by LIST command:
|
||||
// Example:
|
||||
// C: 2 list "" ""
|
||||
// S: * LIST () "." ""
|
||||
// S: 2 OK LIST completed.
|
||||
if ("".equals(mailboxName))
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Request for the hierarchy delimiter");
|
||||
}
|
||||
return new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
NodeRef root = imapHelper.getMailboxRootRef(mailboxName, user.getLogin());
|
||||
String mountPointName = imapHelper.getMountPointName(mailboxName);
|
||||
String mailboxRepoName = imapHelper.getMailPathInRepo(mailboxName);
|
||||
|
||||
StringTokenizer tokenizer = new StringTokenizer(mailboxRepoName, String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER));
|
||||
int count = tokenizer.countTokens();
|
||||
NodeRef nodeRef = root;
|
||||
|
||||
while (tokenizer.hasMoreTokens())
|
||||
{
|
||||
String t = tokenizer.nextToken();
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("token=" + t);
|
||||
}
|
||||
count--;
|
||||
|
||||
List<FileInfo> list = imapHelper.searchFolders(nodeRef, t, false, true);
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
if (!list.isEmpty())
|
||||
{
|
||||
|
||||
return new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), list.get(0), list.get(0).getName(), imapHelper.getViewMode(mailboxName), root,
|
||||
mountPointName, imapHelper);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), null, null, null, null, null, null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!list.isEmpty())
|
||||
{
|
||||
nodeRef = list.get(0).getNodeRef();
|
||||
}
|
||||
else
|
||||
{
|
||||
return new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), null, null, null, null, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Error state");
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply calls {@link #getFolder(GreenMailUser, String)}. <p/> Added to implement {@link ImapHostManager}.
|
||||
*/
|
||||
public MailFolder getFolder(GreenMailUser user, String mailboxName, boolean mustExist) throws FolderException
|
||||
{
|
||||
return getFolder(user, mailboxName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the user's INBOX.
|
||||
*
|
||||
* @param user The user making the request.
|
||||
* @return The user's Inbox.
|
||||
*/
|
||||
public MailFolder getInbox(GreenMailUser user) throws FolderException
|
||||
{
|
||||
return getFolder(user, AlfrescoImapConst.INBOX_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Not supported. May be used by GreenMailUser.create() method. <p/> Added to implement {@link ImapHostManager}.
|
||||
*/
|
||||
public void createPrivateMailAccount(GreenMailUser user) throws FolderException
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes a user to a mailbox. The mailbox must exist locally and the user must have rights to modify it. <p/> This method serves SUBSCRIBE command of the IMAP protocol.
|
||||
*
|
||||
* @param user User making the request
|
||||
* @param mailbox String representation of a mailbox name.
|
||||
*/
|
||||
public void subscribe(final GreenMailUser user, final String mailbox) throws FolderException
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Subscribing: " + mailbox);
|
||||
}
|
||||
AlfrescoImapMailFolder mailFolder = (AlfrescoImapMailFolder) getFolder(user, mailbox);
|
||||
nodeService.addAspect(mailFolder.getFolderInfo().getNodeRef(), ImapModel.ASPECT_IMAP_FOLDER_SUBSCRIBED, null);
|
||||
// This is a multiuser support. Commented due new requirements
|
||||
// AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Void>()
|
||||
// {
|
||||
// public Void doWork() throws Exception
|
||||
// {
|
||||
// AlfrescoImapMailFolder mailFolder = (AlfrescoImapMailFolder) getFolder(user, mailbox);
|
||||
// FileInfo fileInfo = mailFolder.getFolderInfo();
|
||||
// if (fileInfo != null)
|
||||
// {
|
||||
// String subscribedList = (String) nodeService.getProperty(fileInfo.getNodeRef(), ImapModel.PROP_IMAP_FOLDER_SUBSCRIBED);
|
||||
// if (subscribedList == null)
|
||||
// {
|
||||
// subscribedList = "";
|
||||
// }
|
||||
// subscribedList = subscribedList.replaceAll(imapHelper.formatUserEntry(user.getLogin()), "");
|
||||
// subscribedList += imapHelper.formatUserEntry(user.getLogin());
|
||||
// nodeService.setProperty(fileInfo.getNodeRef(), ImapModel.PROP_IMAP_FOLDER_SUBSCRIBED, subscribedList);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// logger.debug("MailBox: " + mailbox + "doesn't exsist. Maybe it was deleted earlier.");
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
// }, AuthenticationUtil.getSystemUserName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribes from a given mailbox. <p/> This method serves UNSUBSCRIBE command of the IMAP protocol.
|
||||
*
|
||||
* @param user User making the request
|
||||
* @param mailbox String representation of a mailbox name.
|
||||
*/
|
||||
public void unsubscribe(final GreenMailUser user, final String mailbox) throws FolderException
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Unsubscribing: " + mailbox);
|
||||
}
|
||||
AlfrescoImapMailFolder mailFolder = (AlfrescoImapMailFolder) getFolder(user, mailbox);
|
||||
nodeService.removeAspect(mailFolder.getFolderInfo().getNodeRef(), ImapModel.ASPECT_IMAP_FOLDER_SUBSCRIBED);
|
||||
|
||||
// This is a multiuser support. Commented due new requirements
|
||||
// AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Void>()
|
||||
// {
|
||||
// public Void doWork() throws Exception
|
||||
// {
|
||||
// AlfrescoImapMailFolder mailFolder = (AlfrescoImapMailFolder) getFolder(user, mailbox);
|
||||
// if (mailFolder.getFolderInfo() != null)
|
||||
// {
|
||||
// FileInfo fileInfo = mailFolder.getFolderInfo();
|
||||
// String subscribedList = (String) nodeService.getProperty(fileInfo.getNodeRef(), ImapModel.PROP_IMAP_FOLDER_SUBSCRIBED);
|
||||
// if (subscribedList == null)
|
||||
// {
|
||||
// subscribedList = "";
|
||||
// }
|
||||
// subscribedList = subscribedList.replaceAll(imapHelper.formatUserEntry(user.getLogin()), "");
|
||||
// nodeService.setProperty(fileInfo.getNodeRef(), ImapModel.PROP_IMAP_FOLDER_SUBSCRIBED, subscribedList);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// logger.debug("MailBox: " + mailbox + " doesn't exsist. Maybe it was deleted earlier.");
|
||||
// }
|
||||
//
|
||||
// return null;
|
||||
// }
|
||||
// }, AuthenticationUtil.getSystemUserName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Not supported. Used by GreenMail class.
|
||||
*/
|
||||
public List getAllMessages()
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private boolean isSubscribed(FileInfo fileInfo, String userName)
|
||||
{
|
||||
return nodeService.hasAspect(fileInfo.getNodeRef(), ImapModel.ASPECT_IMAP_FOLDER_SUBSCRIBED);
|
||||
// This is a multiuser support. Commented due new requirements
|
||||
|
||||
// Map<QName, Serializable> properties = fileInfo.getProperties();
|
||||
// String subscribedList = (String) properties.get(ImapModel.PROP_IMAP_FOLDER_SUBSCRIBED);
|
||||
// if (subscribedList == null)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return subscribedList.contains(imapHelper.formatUserEntry(userName));
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
private Collection<FileInfo> getSubscribed(List<FileInfo> list, String userName)
|
||||
{
|
||||
Collection<FileInfo> result = new LinkedList<FileInfo>();
|
||||
|
||||
for (FileInfo folderInfo : list)
|
||||
{
|
||||
if (isSubscribed(folderInfo, userName))
|
||||
{
|
||||
result.add(folderInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean hasSubscribedChild(FileInfo parent, String userName, boolean isVirtualView)
|
||||
{
|
||||
List<FileInfo> list = imapHelper.searchFolders(parent.getNodeRef(), "*", true, isVirtualView);
|
||||
|
||||
for (FileInfo fileInfo : list)
|
||||
{
|
||||
if (isSubscribed(fileInfo, userName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private Collection<MailFolder> createMailFolderList(GreenMailUser user, Collection<FileInfo> list, NodeRef imapUserHomeRef)
|
||||
{
|
||||
Collection<MailFolder> result = new LinkedList<MailFolder>();
|
||||
|
||||
for (FileInfo folderInfo : list)
|
||||
{
|
||||
// folderName, viewMode, mountPointName will be setted in listSubscribedMailboxes() method
|
||||
result.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), folderInfo, null, null, imapUserHomeRef, null, imapHelper));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
// ----------------------Getters and Setters----------------------------
|
||||
|
||||
public void setServiceRegistry(ServiceRegistry serviceRegistry)
|
||||
{
|
||||
this.serviceRegistry = serviceRegistry;
|
||||
}
|
||||
|
||||
public ServiceRegistry getServiceRegistry()
|
||||
{
|
||||
return serviceRegistry;
|
||||
}
|
||||
|
||||
public void setNodeService(NodeService nodeService)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
}
|
||||
|
||||
public void setFileFolderService(FileFolderService fileFolderService)
|
||||
{
|
||||
this.fileFolderService = fileFolderService;
|
||||
}
|
||||
|
||||
public void setImapHelper(ImapHelper imapHelper)
|
||||
{
|
||||
this.imapHelper = imapHelper;
|
||||
}
|
||||
|
||||
}
|
1084
source/java/org/alfresco/repo/imap/AlfrescoImapMailFolder.java
Executable file
1084
source/java/org/alfresco/repo/imap/AlfrescoImapMailFolder.java
Executable file
File diff suppressed because it is too large
Load Diff
508
source/java/org/alfresco/repo/imap/AlfrescoImapMessage.java
Executable file
508
source/java/org/alfresco/repo/imap/AlfrescoImapMessage.java
Executable file
@@ -0,0 +1,508 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.imap;
|
||||
|
||||
import static org.alfresco.repo.imap.AlfrescoImapConst.BASE_64_ENCODING;
|
||||
import static org.alfresco.repo.imap.AlfrescoImapConst.CONTENT_ID;
|
||||
import static org.alfresco.repo.imap.AlfrescoImapConst.CONTENT_TRANSFER_ENCODING;
|
||||
import static org.alfresco.repo.imap.AlfrescoImapConst.CONTENT_TYPE;
|
||||
import static org.alfresco.repo.imap.AlfrescoImapConst.MIME_VERSION;
|
||||
import static org.alfresco.repo.imap.AlfrescoImapConst.UTF_8;
|
||||
import static org.alfresco.repo.imap.AlfrescoImapConst.X_ALF_NODEREF_ID;
|
||||
import static org.alfresco.repo.imap.AlfrescoImapConst.X_ALF_SERVER_UID;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.activation.DataHandler;
|
||||
import javax.activation.DataSource;
|
||||
import javax.mail.Address;
|
||||
import javax.mail.Flags;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.Multipart;
|
||||
import javax.mail.Session;
|
||||
import javax.mail.internet.AddressException;
|
||||
import javax.mail.internet.ContentType;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import javax.mail.internet.MimeBodyPart;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import javax.mail.internet.MimeMultipart;
|
||||
import javax.mail.internet.MimeUtility;
|
||||
import javax.mail.util.ByteArrayDataSource;
|
||||
|
||||
import org.alfresco.i18n.I18NUtil;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.model.ImapModel;
|
||||
import org.alfresco.repo.imap.ImapHelper.EmailBodyType;
|
||||
import org.alfresco.service.cmr.model.FileInfo;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Extended MimeMessage to represent a content stored in the Alfresco repository.
|
||||
*
|
||||
* @author Arseny Kovalchuk
|
||||
*/
|
||||
public class AlfrescoImapMessage extends MimeMessage
|
||||
{
|
||||
/** Used if imapHelper.getDefaultFromAddress is not set */
|
||||
private static final String DEFAULT_EMAIL_FROM = "alfresco@alfresco.org";
|
||||
private static final String DEFAULT_EMAIL_TO = DEFAULT_EMAIL_FROM;
|
||||
private static final String KOI8R_CHARSET = "koi8-r";
|
||||
|
||||
private static Log logger = LogFactory.getLog(AlfrescoImapMessage.class);
|
||||
|
||||
private ImapHelper imapHelper;
|
||||
private FileInfo messageInfo;
|
||||
|
||||
/**
|
||||
* Constructs {@link AlfrescoImapMessage} object.
|
||||
*
|
||||
* @param fileInfo - reference to the {@link FileInfo} object representing the message.
|
||||
* @param imapHelper - reference to the {@link ImapHelper} object.
|
||||
* @param generateBody - if {@code true} message body will be generated.
|
||||
*
|
||||
* @throws MessagingException if generation of the body fails.
|
||||
*/
|
||||
public AlfrescoImapMessage(FileInfo fileInfo, ImapHelper imapHelper, boolean generateBody) throws MessagingException
|
||||
{
|
||||
super(Session.getDefaultInstance(new Properties()));
|
||||
this.messageInfo = fileInfo;
|
||||
this.imapHelper = imapHelper;
|
||||
if (generateBody)
|
||||
{
|
||||
setMessageHeaders();
|
||||
buildMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs {@link AlfrescoImapMessage} object.
|
||||
*
|
||||
* @param fileInfo - reference to the {@link FileInfo} object representing the message.
|
||||
* @param imapHelper - reference to the {@link ImapHelper} object.
|
||||
* @param message - {@link MimeMessage}
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public AlfrescoImapMessage(FileInfo fileInfo, ImapHelper imapHelper, MimeMessage message) throws MessagingException
|
||||
{
|
||||
super(message);
|
||||
this.messageInfo = fileInfo;
|
||||
this.imapHelper = imapHelper;
|
||||
|
||||
setMessageHeaders();
|
||||
final NodeRef nodeRef = fileInfo.getNodeRef();
|
||||
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
|
||||
props.put(ImapModel.PROP_MESSAGE_FROM, InternetAddress.toString(message.getFrom()));
|
||||
props.put(ImapModel.PROP_MESSAGE_TO, InternetAddress.toString(message.getRecipients(RecipientType.TO)));
|
||||
props.put(ImapModel.PROP_MESSAGE_CC, InternetAddress.toString(message.getRecipients(RecipientType.CC)));
|
||||
|
||||
String[] subj = message.getHeader("Subject");
|
||||
if (subj.length > 0)
|
||||
{
|
||||
props.put(ImapModel.PROP_MESSAGE_SUBJECT, subj[0]);
|
||||
imapHelper.getNodeService().setProperty(nodeRef, ContentModel.PROP_DESCRIPTION, subj[0]);
|
||||
}
|
||||
|
||||
Map<QName, Serializable> allprops = imapHelper.getNodeService().getProperties(fileInfo.getNodeRef());
|
||||
allprops.putAll(props);
|
||||
imapHelper.getNodeService().setProperties(nodeRef, allprops);
|
||||
// setContent(buildMultipart(fileInfo)); - disabled for better performance.
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns message flags.
|
||||
*
|
||||
* @return {@link Flags}
|
||||
*/
|
||||
@Override
|
||||
public synchronized Flags getFlags()
|
||||
{
|
||||
return imapHelper.getFlags(messageInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets message flags.
|
||||
*
|
||||
* @param flags - {@link Flags} object.
|
||||
* @param value - flags value.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void setFlags(Flags flags, boolean value) throws MessagingException
|
||||
{
|
||||
imapHelper.setFlags(messageInfo, flags, value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@link FileInfo} object representing message in Alfresco.
|
||||
*
|
||||
* @return reference to the {@link FileInfo} object.
|
||||
*/
|
||||
public FileInfo getMessageInfo()
|
||||
{
|
||||
return messageInfo;
|
||||
}
|
||||
|
||||
private void setMessageHeaders() throws MessagingException
|
||||
{
|
||||
setHeader(MIME_VERSION, "1.0");
|
||||
// Optional headers for further implementation of multiple Alfresco server support.
|
||||
setHeader(X_ALF_NODEREF_ID, messageInfo.getNodeRef().getId());
|
||||
setHeader(X_ALF_SERVER_UID, imapHelper.getAlfrescoServerUID());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method builds MimeMessage based on either ImapModel or ContentModel type.
|
||||
*
|
||||
* @param fileInfo - Source file information {@link FileInfo}
|
||||
* @throws MessagingException
|
||||
*/
|
||||
private void buildMessage() throws MessagingException
|
||||
{
|
||||
final NodeRef nodeRef = messageInfo.getNodeRef();
|
||||
if (ImapModel.TYPE_IMAP_CONTENT.equals(imapHelper.getNodeService().getType(nodeRef)))
|
||||
{
|
||||
buildImapModelMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
buildContentModelMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method builds MimeMessage based on {@link ImapModel}
|
||||
*
|
||||
* @param fileInfo - Source file information {@link FileInfo}
|
||||
* @throws MessagingException
|
||||
*/
|
||||
private void buildImapModelMessage() throws MessagingException
|
||||
{
|
||||
Map<QName, Serializable> properties = messageInfo.getProperties();
|
||||
setSentDate(messageInfo.getModifiedDate());
|
||||
String prop = (String) properties.get(ImapModel.PROP_MESSAGE_FROM);
|
||||
addFromInternal(prop);
|
||||
prop = (String) properties.get(ImapModel.PROP_MESSAGE_TO);
|
||||
|
||||
if (prop != null && prop.length() > 0)
|
||||
{
|
||||
addRecipients(RecipientType.TO, InternetAddress.parse(prop));
|
||||
}
|
||||
else
|
||||
{
|
||||
addRecipients(RecipientType.TO, DEFAULT_EMAIL_TO);
|
||||
}
|
||||
|
||||
prop = (String) properties.get(ImapModel.PROP_MESSAGE_CC);
|
||||
if (prop != null && prop.length() > 0)
|
||||
{
|
||||
addRecipients(RecipientType.CC, InternetAddress.parse(prop));
|
||||
}
|
||||
|
||||
prop = (String) properties.get(ImapModel.PROP_MESSAGE_SUBJECT);
|
||||
setSubject(prop == null ? messageInfo.getName() : prop);
|
||||
|
||||
setContent(buildImapModelMultipart());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method builds {@link MimeMessage} based on {@link ContentModel}
|
||||
*
|
||||
* @param fileInfo - Source file information {@link FileInfo}
|
||||
* @throws MessagingException
|
||||
*/
|
||||
private void buildContentModelMessage() throws MessagingException
|
||||
{
|
||||
Map<QName, Serializable> properties = messageInfo.getProperties();
|
||||
String prop = null;
|
||||
setSentDate(messageInfo.getModifiedDate());
|
||||
// Add FROM address
|
||||
Address[] addressList = buildSenderFromAddress(properties);
|
||||
addFrom(addressList);
|
||||
// Add TO address
|
||||
addressList = buildRecipientToAddress();
|
||||
addRecipients(RecipientType.TO, addressList);
|
||||
prop = (String) properties.get(ContentModel.PROP_TITLE);
|
||||
try
|
||||
{
|
||||
prop = (prop == null) ? MimeUtility.encodeText(messageInfo.getName(), KOI8R_CHARSET, null) : MimeUtility.encodeText(prop, KOI8R_CHARSET, null);
|
||||
}
|
||||
catch (UnsupportedEncodingException e)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
setSubject(prop);
|
||||
setContent(buildContentModelMultipart());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method builds {@link Multipart} based on {@link ContentModel}
|
||||
*
|
||||
* @param fileInfo - Source file information {@link FileInfo}
|
||||
* @throws MessagingException
|
||||
*/
|
||||
private Multipart buildContentModelMultipart() throws MessagingException
|
||||
{
|
||||
MimeMultipart rootMultipart = new MimeMultipart("alternative");
|
||||
// Cite MOB-395: "email agent will be used to select an appropriate template" - we are not able to
|
||||
// detect an email agent so we use a default template for all messages.
|
||||
// See AlfrescoImapConst to see the possible templates to use.
|
||||
String bodyTxt = imapHelper.getEmailBodyText(messageInfo.getNodeRef(), EmailBodyType.TEXT_PLAIN);
|
||||
rootMultipart.addBodyPart(getTextBodyPart(bodyTxt, EmailBodyType.TEXT_PLAIN.getSubtype()));
|
||||
String bodyHtml = imapHelper.getEmailBodyText(messageInfo.getNodeRef(), EmailBodyType.TEXT_HTML);
|
||||
rootMultipart.addBodyPart(getTextBodyPart(bodyHtml, EmailBodyType.TEXT_HTML.getSubtype()));
|
||||
return rootMultipart;
|
||||
}
|
||||
|
||||
private MimeBodyPart getTextBodyPart(String bodyText, String subtype) throws MessagingException
|
||||
{
|
||||
MimeBodyPart result = new MimeBodyPart();
|
||||
result.setText(bodyText, UTF_8, subtype);
|
||||
result.addHeader(CONTENT_TRANSFER_ENCODING, BASE_64_ENCODING);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method builds {@link Multipart} based on {@link ImapModel}
|
||||
*
|
||||
* @param fileInfo - Source file information {@link FileInfo}
|
||||
* @throws MessagingException
|
||||
*/
|
||||
private Multipart buildImapModelMultipart() throws MessagingException
|
||||
{
|
||||
DataSource source = null;
|
||||
String errorMessage = null;
|
||||
|
||||
// Root multipart - multipart/mixed
|
||||
MimeMultipart rootMultipart = new MimeMultipart("mixed");
|
||||
// Message body - multipart/alternative - consists of two parts: text/plain and text/html
|
||||
MimeMultipart messageBody = new MimeMultipart("alternative");
|
||||
// <------------------------ text html body part ------------------------>
|
||||
List<FileInfo> bodyHtmls = imapHelper.searchFiles(messageInfo.getNodeRef(), "*.html", ImapModel.TYPE_IMAP_BODY, false);
|
||||
ContentType contentType = null;
|
||||
MimeBodyPart textHtmlBodyPart = null;
|
||||
if (bodyHtmls != null && bodyHtmls.size() > 0)
|
||||
{
|
||||
textHtmlBodyPart = new MimeBodyPart();
|
||||
FileInfo bodyHtml = bodyHtmls.get(0);
|
||||
contentType = new ContentType(bodyHtml.getContentData().getMimetype());
|
||||
ContentReader reader = imapHelper.getFileFolderService().getReader(bodyHtml.getNodeRef());
|
||||
try
|
||||
{
|
||||
source = new ByteArrayDataSource(reader.getContentInputStream(), contentType.toString());
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
logger.error(e);
|
||||
errorMessage = e.getMessage();
|
||||
}
|
||||
if (source != null)
|
||||
{
|
||||
textHtmlBodyPart.setDataHandler(new DataHandler(source));
|
||||
textHtmlBodyPart.addHeader(CONTENT_TYPE, bodyHtml.getContentData().getMimetype());
|
||||
// textHtmlBodyPart.addHeader(CONTENT_TRANSFER_ENCODING, EIGHT_BIT_ENCODING);
|
||||
textHtmlBodyPart.addHeader(CONTENT_TRANSFER_ENCODING, BASE_64_ENCODING);
|
||||
}
|
||||
else
|
||||
{
|
||||
textHtmlBodyPart.setText(errorMessage, UTF_8);
|
||||
}
|
||||
messageBody.addBodyPart(textHtmlBodyPart);
|
||||
}
|
||||
// </------------------------ text html body part ------------------------>
|
||||
// <------------------------ text plain body part ------------------------>
|
||||
List<FileInfo> results = imapHelper.searchFiles(messageInfo.getNodeRef(), "*.txt", ImapModel.TYPE_IMAP_BODY, false);
|
||||
MimeBodyPart textPlainBodyPart = null;
|
||||
String text = null;
|
||||
if (results != null && results.size() > 0)
|
||||
{
|
||||
textPlainBodyPart = new MimeBodyPart();
|
||||
FileInfo bodyTxt = results.get(0);
|
||||
text = imapHelper.getFileFolderService().getReader(bodyTxt.getNodeRef()).getContentString();
|
||||
contentType = new ContentType(bodyTxt.getContentData().getMimetype());
|
||||
}
|
||||
else if (textHtmlBodyPart == null)
|
||||
{
|
||||
text = I18NUtil.getMessage("imap.server.info.message_body_not_found");
|
||||
contentType = new ContentType(EmailBodyType.TEXT_PLAIN.getMimeType() + "; charset=UTF-8");
|
||||
}
|
||||
|
||||
textPlainBodyPart.setText(text, contentType.getParameter("charset"), contentType.getSubType());
|
||||
textPlainBodyPart.addHeader(CONTENT_TYPE, contentType.toString());
|
||||
messageBody.addBodyPart(textPlainBodyPart);
|
||||
// </------------------------ text plain body part ------------------------>
|
||||
|
||||
// Body part for multipart/alternative
|
||||
MimeBodyPart messageBodyPart = new MimeBodyPart();
|
||||
messageBodyPart.setContent(messageBody);
|
||||
// Add multipart/alternative into root multipart/mixed...
|
||||
rootMultipart.addBodyPart(messageBodyPart);
|
||||
|
||||
// Process attachments
|
||||
List<FileInfo> attaches = imapHelper.searchFiles(messageInfo.getNodeRef(), "*", ImapModel.TYPE_IMAP_ATTACH, false);
|
||||
|
||||
for (FileInfo attach : attaches)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
errorMessage = null;
|
||||
messageBodyPart = new MimeBodyPart();
|
||||
ContentReader reader = imapHelper.getFileFolderService().getReader(attach.getNodeRef());
|
||||
source = new ByteArrayDataSource(reader.getContentInputStream(), attach.getContentData().getMimetype());
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
logger.error(e);
|
||||
errorMessage = e.getMessage();
|
||||
}
|
||||
if (source != null)
|
||||
{
|
||||
String attachID = (String) imapHelper.getNodeService().getProperty(attach.getNodeRef(), ImapModel.PROP_ATTACH_ID);
|
||||
if (attachID != null)
|
||||
{
|
||||
messageBodyPart.addHeader(CONTENT_ID, attachID);
|
||||
}
|
||||
StringBuilder ct = new StringBuilder(attach.getContentData().getMimetype()).append("; name=\"").append(attach.getName()).append("\"");
|
||||
messageBodyPart.addHeader(CONTENT_TYPE, ct.toString());
|
||||
messageBodyPart.addHeader(CONTENT_TRANSFER_ENCODING, BASE_64_ENCODING);
|
||||
messageBodyPart.setDataHandler(new DataHandler(source));
|
||||
try
|
||||
{
|
||||
messageBodyPart.setFileName(MimeUtility.encodeText(attach.getName(), KOI8R_CHARSET, null));
|
||||
}
|
||||
catch (UnsupportedEncodingException e)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
messageBodyPart.setText(errorMessage, UTF_8);
|
||||
}
|
||||
rootMultipart.addBodyPart(messageBodyPart);
|
||||
}
|
||||
return rootMultipart;
|
||||
}
|
||||
|
||||
private void addFromInternal(String addressesString) throws MessagingException
|
||||
{
|
||||
if (addressesString != null)
|
||||
{
|
||||
addFrom(InternetAddress.parse(addressesString));
|
||||
}
|
||||
else
|
||||
{
|
||||
addFrom(new Address[] { new InternetAddress(DEFAULT_EMAIL_FROM) });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO USE CASE 2: "The To/addressee will be the first email alias found in the parent folders or a default one (TBD)". It seems to be more informative as alike
|
||||
* {@code <user>@<current.domain>}...
|
||||
*
|
||||
* @return Generated TO address {@code <user>@<current.domain>}
|
||||
* @throws AddressException
|
||||
*/
|
||||
private InternetAddress[] buildRecipientToAddress() throws AddressException
|
||||
{
|
||||
InternetAddress[] result = null;
|
||||
String defaultEmailTo = null;
|
||||
// TODO : search first email alias found in the parent folders
|
||||
// if (found) defaultEmailTo = foundAlias
|
||||
// else
|
||||
final String escapedUserName = imapHelper.getCurrentUser().replaceAll("[/,\\,@]", ".");
|
||||
final String userDomain = DEFAULT_EMAIL_TO.split("@")[1];
|
||||
defaultEmailTo = escapedUserName + "@" + userDomain;
|
||||
try
|
||||
{
|
||||
result = InternetAddress.parse(defaultEmailTo);
|
||||
}
|
||||
catch (AddressException e)
|
||||
{
|
||||
logger.error(String.format("Wrong email address '%s'.", defaultEmailTo), e);
|
||||
result = InternetAddress.parse(DEFAULT_EMAIL_TO);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the InternetAddress from the Content Author name if provided. If name not specified, it takes Content Creator name. If content creator does not exists, the default
|
||||
* from address will be returned.
|
||||
*
|
||||
* @param contentAuthor The content author full name.
|
||||
* @return Generated InternetAddress[] array.
|
||||
* @throws AddressException
|
||||
*/
|
||||
private InternetAddress[] buildSenderFromAddress(Map<QName, Serializable> properties) throws AddressException
|
||||
{
|
||||
// Generate FROM address (Content author)
|
||||
InternetAddress[] addressList = null;
|
||||
String prop = (String) properties.get(ContentModel.PROP_AUTHOR);
|
||||
String defaultFromAddress = imapHelper.getDefaultFromAddress();
|
||||
defaultFromAddress = defaultFromAddress == null ? DEFAULT_EMAIL_FROM : defaultFromAddress;
|
||||
try
|
||||
{
|
||||
|
||||
if (prop != null)
|
||||
{
|
||||
StringBuilder contentAuthor = new StringBuilder();
|
||||
contentAuthor.append("\"").append(prop).append("\" <").append(defaultFromAddress).append(">");
|
||||
addressList = InternetAddress.parse(contentAuthor.toString());
|
||||
}
|
||||
else
|
||||
{
|
||||
prop = (String) properties.get(ContentModel.PROP_CREATOR);
|
||||
if (prop != null)
|
||||
{
|
||||
StringBuilder creator = new StringBuilder();
|
||||
creator.append("\"").append(prop).append("\" <").append(defaultFromAddress).append(">");
|
||||
addressList = InternetAddress.parse(creator.toString());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new AddressException(I18NUtil.getMessage("imap.server.error.properties_dont_exist"));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (AddressException e)
|
||||
{
|
||||
addressList = InternetAddress.parse(DEFAULT_EMAIL_FROM);
|
||||
}
|
||||
return addressList;
|
||||
}
|
||||
|
||||
}
|
123
source/java/org/alfresco/repo/imap/AlfrescoImapServer.java
Executable file
123
source/java/org/alfresco/repo/imap/AlfrescoImapServer.java
Executable file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.imap;
|
||||
|
||||
import org.alfresco.util.AbstractLifecycleBean;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
import com.icegreen.greenmail.Managers;
|
||||
import com.icegreen.greenmail.imap.ImapHostManager;
|
||||
import com.icegreen.greenmail.imap.ImapServer;
|
||||
import com.icegreen.greenmail.user.UserManager;
|
||||
import com.icegreen.greenmail.util.ServerSetup;
|
||||
|
||||
/**
|
||||
* @author Mike Shavnev
|
||||
*/
|
||||
public class AlfrescoImapServer extends AbstractLifecycleBean
|
||||
{
|
||||
|
||||
private static Log logger = LogFactory.getLog(AlfrescoImapServer.class);
|
||||
|
||||
private ImapServer serverImpl;
|
||||
|
||||
private int port = 143;
|
||||
|
||||
private ImapHostManager imapHostManager;
|
||||
|
||||
private UserManager imapUserManager;
|
||||
|
||||
private boolean imapServerEnabled;
|
||||
|
||||
private ImapHelper imapHelper;
|
||||
|
||||
public void setImapServerEnabled(boolean imapServerEnabled)
|
||||
{
|
||||
this.imapServerEnabled = imapServerEnabled;
|
||||
}
|
||||
|
||||
public void setPort(int port)
|
||||
{
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public void setImapHostManager(ImapHostManager imapHostManager)
|
||||
{
|
||||
this.imapHostManager = imapHostManager;
|
||||
}
|
||||
|
||||
public void setImapUserManager(UserManager imapUserManager)
|
||||
{
|
||||
this.imapUserManager = imapUserManager;
|
||||
}
|
||||
|
||||
public void setImapHelper(ImapHelper imapHelper)
|
||||
{
|
||||
this.imapHelper = imapHelper;
|
||||
}
|
||||
|
||||
protected void onBootstrap(ApplicationEvent event)
|
||||
{
|
||||
if (imapServerEnabled && imapHelper.isPatchApplied())
|
||||
{
|
||||
Managers imapManagers = new Managers()
|
||||
{
|
||||
public ImapHostManager getImapHostManager()
|
||||
{
|
||||
return imapHostManager;
|
||||
}
|
||||
|
||||
public UserManager getUserManager()
|
||||
{
|
||||
return imapUserManager;
|
||||
}
|
||||
};
|
||||
serverImpl = new ImapServer(new ServerSetup(port, null, ServerSetup.PROTOCOL_IMAP), imapManagers);
|
||||
serverImpl.startService(null);
|
||||
if (logger.isInfoEnabled())
|
||||
{
|
||||
logger.info("IMAP service started on port " + this.port + ".");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("IMAP service is disabled.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void onShutdown(ApplicationEvent event)
|
||||
{
|
||||
if (serverImpl != null)
|
||||
{
|
||||
serverImpl.stopService(null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
107
source/java/org/alfresco/repo/imap/AlfrescoImapUser.java
Executable file
107
source/java/org/alfresco/repo/imap/AlfrescoImapUser.java
Executable file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.imap;
|
||||
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import com.icegreen.greenmail.imap.ImapHostManager;
|
||||
import com.icegreen.greenmail.mail.MovingMessage;
|
||||
import com.icegreen.greenmail.user.GreenMailUser;
|
||||
import com.icegreen.greenmail.user.UserException;
|
||||
|
||||
/**
|
||||
* Alfresco implementation of the GreenMailUser interface.
|
||||
*
|
||||
* @author Arseny Kovalchuk
|
||||
*/
|
||||
public class AlfrescoImapUser implements GreenMailUser
|
||||
{
|
||||
private String userName;
|
||||
private char[] password;
|
||||
private String email;
|
||||
|
||||
private ImapHostManager imapHostManager;
|
||||
|
||||
public AlfrescoImapUser(String email, String login, String password, ImapHostManager imapHostManager)
|
||||
{
|
||||
this.email = email;
|
||||
this.userName = login;
|
||||
this.password = password.toCharArray();
|
||||
this.imapHostManager = imapHostManager;
|
||||
}
|
||||
|
||||
public void authenticate(String password) throws UserException
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
// This method is used in the POP3 greenmail implementation, so it is disabled for IMAP
|
||||
// See AlfrescoImapUserManager.test() method.
|
||||
}
|
||||
|
||||
public void create() throws UserException
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void delete() throws UserException
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void deliver(MovingMessage msg) throws UserException
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void deliver(MimeMessage msg) throws UserException
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public String getEmail()
|
||||
{
|
||||
return this.email;
|
||||
}
|
||||
|
||||
public String getLogin()
|
||||
{
|
||||
return this.userName;
|
||||
}
|
||||
|
||||
public String getPassword()
|
||||
{
|
||||
return new String(this.password);
|
||||
}
|
||||
|
||||
public String getQualifiedMailboxName()
|
||||
{
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setPassword(String password)
|
||||
{
|
||||
this.password = password.toCharArray();
|
||||
}
|
||||
|
||||
}
|
168
source/java/org/alfresco/repo/imap/AlfrescoImapUserManager.java
Executable file
168
source/java/org/alfresco/repo/imap/AlfrescoImapUserManager.java
Executable file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.imap;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationException;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.security.AuthenticationService;
|
||||
import org.alfresco.service.cmr.security.PersonService;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import com.icegreen.greenmail.imap.ImapHostManager;
|
||||
import com.icegreen.greenmail.user.GreenMailUser;
|
||||
import com.icegreen.greenmail.user.UserException;
|
||||
import com.icegreen.greenmail.user.UserManager;
|
||||
|
||||
/**
|
||||
* @author Arseny Kovalchuk
|
||||
*/
|
||||
public class AlfrescoImapUserManager extends UserManager
|
||||
{
|
||||
private Log logger = LogFactory.getLog(AlfrescoImapUserManager.class);
|
||||
|
||||
protected Map<String, GreenMailUser> userMap = Collections.synchronizedMap(new HashMap<String, GreenMailUser>());
|
||||
protected ImapHostManager imapHostManager;
|
||||
|
||||
protected AuthenticationService authenticationService;
|
||||
protected PersonService personService;
|
||||
protected NodeService nodeService;
|
||||
|
||||
public AlfrescoImapUserManager()
|
||||
{
|
||||
super(null);
|
||||
}
|
||||
|
||||
public AlfrescoImapUserManager(ImapHostManager imapHostManager)
|
||||
{
|
||||
this();
|
||||
this.imapHostManager = imapHostManager;
|
||||
}
|
||||
|
||||
public GreenMailUser createUser(String email, String login, String password) throws UserException
|
||||
{
|
||||
// TODO: User creation/addition code should be implemented here (in the AlfrescoImapUserManager).
|
||||
// Following code is not need and not used in the current implementation.
|
||||
GreenMailUser user = new AlfrescoImapUser(email, login, password, imapHostManager);
|
||||
user.create();
|
||||
addUser(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
protected void addUser(GreenMailUser user)
|
||||
{
|
||||
userMap.put(user.getLogin(), user);
|
||||
}
|
||||
|
||||
public GreenMailUser getUser(String login)
|
||||
{
|
||||
return (GreenMailUser) userMap.get(login);
|
||||
}
|
||||
|
||||
public GreenMailUser getUserByEmail(String email)
|
||||
{
|
||||
GreenMailUser ret = getUser(email);
|
||||
if (null == ret)
|
||||
{
|
||||
for (GreenMailUser user : userMap.values())
|
||||
{
|
||||
// TODO: NPE!
|
||||
if (user.getEmail().trim().equalsIgnoreCase(email.trim()))
|
||||
{
|
||||
return user;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void deleteUser(GreenMailUser user) throws UserException
|
||||
{
|
||||
user = (GreenMailUser) userMap.remove(user.getLogin());
|
||||
if (user != null)
|
||||
{
|
||||
user.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The login method.
|
||||
*
|
||||
* @see com.icegreen.greenmail.imap.commands.LoginCommand#doProcess()
|
||||
*/
|
||||
public boolean test(String userid, String password)
|
||||
{
|
||||
try
|
||||
{
|
||||
authenticationService.authenticate(userid, password.toCharArray());
|
||||
String email = null;
|
||||
if (personService.personExists(userid))
|
||||
{
|
||||
NodeRef personNodeRef = personService.getPerson(userid);
|
||||
email = (String) nodeService.getProperty(personNodeRef, ContentModel.PROP_EMAIL);
|
||||
}
|
||||
GreenMailUser user = new AlfrescoImapUser(email, userid, password, imapHostManager);
|
||||
addUser(user);
|
||||
}
|
||||
catch (AuthenticationException ex)
|
||||
{
|
||||
logger.error("IMAP authentication failed for userid: " + userid);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public ImapHostManager getImapHostManager()
|
||||
{
|
||||
return this.imapHostManager;
|
||||
}
|
||||
|
||||
public void setImapHostManager(ImapHostManager imapHostManager)
|
||||
{
|
||||
this.imapHostManager = imapHostManager;
|
||||
}
|
||||
|
||||
public void setAuthenticationService(AuthenticationService authenticationService)
|
||||
{
|
||||
this.authenticationService = authenticationService;
|
||||
}
|
||||
|
||||
public void setPersonService(PersonService personService)
|
||||
{
|
||||
this.personService = personService;
|
||||
}
|
||||
|
||||
public void setNodeService(NodeService nodeService)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
}
|
||||
|
||||
}
|
980
source/java/org/alfresco/repo/imap/ImapHelper.java
Executable file
980
source/java/org/alfresco/repo/imap/ImapHelper.java
Executable file
@@ -0,0 +1,980 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.imap;
|
||||
|
||||
import static org.alfresco.repo.imap.AlfrescoImapConst.CLASSPATH_TEXT_HTML_TEMPLATE;
|
||||
import static org.alfresco.repo.imap.AlfrescoImapConst.CLASSPATH_TEXT_PLAIN_TEMPLATE;
|
||||
import static org.alfresco.repo.imap.AlfrescoImapConst.DICTIONARY_TEMPLATE_PREFIX;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.mail.Flags;
|
||||
import javax.mail.Flags.Flag;
|
||||
|
||||
import org.alfresco.config.Config;
|
||||
import org.alfresco.config.ConfigService;
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.model.ImapModel;
|
||||
import org.alfresco.repo.admin.patch.PatchInfo;
|
||||
import org.alfresco.repo.admin.patch.PatchService;
|
||||
import org.alfresco.repo.imap.config.ImapConfigElement;
|
||||
import org.alfresco.repo.imap.config.ImapConfigElement.ImapConfig;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.service.cmr.site.SiteInfo;
|
||||
import org.alfresco.service.cmr.site.SiteService;
|
||||
import org.alfresco.repo.template.TemplateNode;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.model.FileFolderService;
|
||||
import org.alfresco.service.cmr.model.FileInfo;
|
||||
import org.alfresco.service.cmr.preference.PreferenceService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.repository.TemplateService;
|
||||
import org.alfresco.service.cmr.search.ResultSet;
|
||||
import org.alfresco.service.cmr.search.SearchService;
|
||||
import org.alfresco.service.cmr.security.AccessStatus;
|
||||
import org.alfresco.service.cmr.security.PermissionService;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.AbstractLifecycleBean;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* Helper class to access repository services by IMAP components. Also contains a common helper methods to search and manage IMAP content and other usefull methods. Configured as
|
||||
* {@code <bean id="imapHelper" class="org.alfresco.repo.imap.ImapHelper">} in the {@code imap-server-context.xml} file.
|
||||
*
|
||||
* @author Dmitry Vaserin
|
||||
*/
|
||||
public class ImapHelper extends AbstractLifecycleBean
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(ImapHelper.class);
|
||||
|
||||
private static String PATCH_ID = "patch.imapFolders";
|
||||
|
||||
private NodeService nodeService;
|
||||
private SearchService searchService;
|
||||
private FileFolderService fileFolderService;
|
||||
private TemplateService templateService;
|
||||
private NamespaceService namespaceService;
|
||||
private PermissionService permissionService;
|
||||
private ConfigService configService;
|
||||
private DictionaryService dictionaryService;
|
||||
private PreferenceService preferenceService;
|
||||
private SiteService siteService;
|
||||
|
||||
private ServiceRegistry serviceRegistry;
|
||||
|
||||
private PatchService patchService;
|
||||
|
||||
private String defaultFromAddress;
|
||||
private String webApplicationContextUrl = "http://localhost:8080/alfresco";
|
||||
private String repositoryTemplatePath;
|
||||
private String imapRoot;
|
||||
private NodeRef spacesStoreNodeRef;
|
||||
private NodeRef companyHomeNodeRef;
|
||||
private NodeRef imapRootNodeRef;
|
||||
|
||||
private boolean patchApplied = false;
|
||||
|
||||
private final static Map<QName, Flags.Flag> qNameToFlag;
|
||||
private final static Map<Flags.Flag, QName> flagToQname;
|
||||
|
||||
static
|
||||
{
|
||||
qNameToFlag = new HashMap<QName, Flags.Flag>();
|
||||
qNameToFlag.put(ImapModel.PROP_FLAG_ANSWERED, Flags.Flag.ANSWERED);
|
||||
qNameToFlag.put(ImapModel.PROP_FLAG_DELETED, Flags.Flag.DELETED);
|
||||
qNameToFlag.put(ImapModel.PROP_FLAG_DRAFT, Flags.Flag.DRAFT);
|
||||
qNameToFlag.put(ImapModel.PROP_FLAG_SEEN, Flags.Flag.SEEN);
|
||||
qNameToFlag.put(ImapModel.PROP_FLAG_RECENT, Flags.Flag.RECENT);
|
||||
qNameToFlag.put(ImapModel.PROP_FLAG_FLAGGED, Flags.Flag.FLAGGED);
|
||||
|
||||
flagToQname = new HashMap<Flags.Flag, QName>();
|
||||
flagToQname.put(Flags.Flag.ANSWERED, ImapModel.PROP_FLAG_ANSWERED);
|
||||
flagToQname.put(Flags.Flag.DELETED, ImapModel.PROP_FLAG_DELETED);
|
||||
flagToQname.put(Flags.Flag.DRAFT, ImapModel.PROP_FLAG_DRAFT);
|
||||
flagToQname.put(Flags.Flag.SEEN, ImapModel.PROP_FLAG_SEEN);
|
||||
flagToQname.put(Flags.Flag.RECENT, ImapModel.PROP_FLAG_RECENT);
|
||||
flagToQname.put(Flags.Flag.FLAGGED, ImapModel.PROP_FLAG_FLAGGED);
|
||||
}
|
||||
|
||||
public static enum EmailBodyType
|
||||
{
|
||||
TEXT_PLAIN, TEXT_HTML;
|
||||
|
||||
public String getSubtype()
|
||||
{
|
||||
return name().toLowerCase().substring(5);
|
||||
}
|
||||
|
||||
public String getTypeSubtype()
|
||||
{
|
||||
return name().toLowerCase().replaceAll("_", "");
|
||||
}
|
||||
|
||||
public String getMimeType()
|
||||
{
|
||||
return name().toLowerCase().replaceAll("_", "/");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onShutdown(ApplicationEvent event)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBootstrap(ApplicationEvent event)
|
||||
{
|
||||
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Void>()
|
||||
{
|
||||
public Void doWork() throws Exception
|
||||
{
|
||||
List<PatchInfo> patches = getPatchService().getPatches(null, null);
|
||||
for (PatchInfo patch : patches)
|
||||
{
|
||||
if (patch.getId().equals(PATCH_ID))
|
||||
{
|
||||
patchApplied = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!patchApplied)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int indexOfStoreDelim = imapRoot.indexOf(StoreRef.URI_FILLER);
|
||||
|
||||
if (indexOfStoreDelim == -1)
|
||||
{
|
||||
throw new RuntimeException("Bad path format, " + StoreRef.URI_FILLER + " not found");
|
||||
}
|
||||
|
||||
indexOfStoreDelim += StoreRef.URI_FILLER.length();
|
||||
|
||||
int indexOfPathDelim = imapRoot.indexOf("/", indexOfStoreDelim);
|
||||
|
||||
if (indexOfPathDelim == -1)
|
||||
{
|
||||
throw new java.lang.RuntimeException("Bad path format, / not found");
|
||||
}
|
||||
|
||||
String storePath = imapRoot.substring(0, indexOfPathDelim);
|
||||
String rootPathInStore = imapRoot.substring(indexOfPathDelim);
|
||||
|
||||
StoreRef storeRef = new StoreRef(storePath);
|
||||
|
||||
if (nodeService.exists(storeRef) == false)
|
||||
{
|
||||
throw new RuntimeException("No store for path: " + storeRef);
|
||||
}
|
||||
|
||||
NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef);
|
||||
|
||||
List<NodeRef> nodeRefs = searchService.selectNodes(storeRootNodeRef, rootPathInStore, null, namespaceService, false);
|
||||
|
||||
if (nodeRefs.size() > 1)
|
||||
{
|
||||
throw new RuntimeException("Multiple possible roots for : \n" + " root path: " + rootPathInStore + "\n" + " results: " + nodeRefs);
|
||||
}
|
||||
else if (nodeRefs.size() == 0)
|
||||
{
|
||||
throw new RuntimeException("No root found for : \n" + " root path: " + rootPathInStore);
|
||||
}
|
||||
|
||||
imapRootNodeRef = nodeRefs.get(0);
|
||||
|
||||
// Get "Company Home" node reference
|
||||
StoreRef store = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
|
||||
ResultSet rs = searchService.query(store, SearchService.LANGUAGE_XPATH, "/app:company_home");
|
||||
try
|
||||
{
|
||||
if (rs.length() == 0)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("'Company Home' space doesn't exists.");
|
||||
}
|
||||
companyHomeNodeRef = rs.getNodeRef(0);
|
||||
}
|
||||
finally
|
||||
{
|
||||
rs.close();
|
||||
}
|
||||
|
||||
spacesStoreNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
|
||||
|
||||
return null;
|
||||
}
|
||||
}, AuthenticationUtil.getSystemUserName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for files in specified context
|
||||
*
|
||||
* @param contextNodeRef context folder for search
|
||||
* @param namePattern name pattern for search
|
||||
* @param searchType type for search
|
||||
* @param includeSubFolders include SubFolders
|
||||
* @return list of files with specifed type
|
||||
*/
|
||||
public List<FileInfo> searchFiles(NodeRef contextNodeRef, String namePattern, QName searchType, boolean includeSubFolders)
|
||||
{
|
||||
return search(contextNodeRef, namePattern, searchType, true, false, includeSubFolders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for mailboxes in specified context
|
||||
*
|
||||
* @param contextNodeRef context folder for search
|
||||
* @param namePattern name pattern for search
|
||||
* @param includeSubFolders include SubFolders
|
||||
* @param isVirtualView is folder in "Virtual" View
|
||||
* @return list of mailboxes
|
||||
*/
|
||||
public List<FileInfo> searchFolders(NodeRef contextNodeRef, String namePattern, boolean includeSubFolders, boolean isVirtualView)
|
||||
{
|
||||
QName searchType = ContentModel.TYPE_FOLDER;
|
||||
if (isVirtualView)
|
||||
{
|
||||
searchType = null;
|
||||
}
|
||||
|
||||
List<FileInfo> result = search(contextNodeRef, namePattern, searchType, false, true, includeSubFolders);
|
||||
if (isVirtualView)
|
||||
{
|
||||
List<SiteInfo> nonFavSites = getNonFavouriteSites(getCurrentUser());
|
||||
for (SiteInfo siteInfo : nonFavSites)
|
||||
{
|
||||
FileInfo nonFavSite = fileFolderService.getFileInfo(siteInfo.getNodeRef());
|
||||
List<FileInfo> siteChilds = search(nonFavSite.getNodeRef(), namePattern, null, false, true, true);
|
||||
result.removeAll(siteChilds);
|
||||
result.remove(nonFavSite);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove folders from Sites
|
||||
List<SiteInfo> sites = siteService.listSites(getCurrentUser());
|
||||
for (SiteInfo siteInfo : sites)
|
||||
{
|
||||
List<FileInfo> siteChilds = search(siteInfo.getNodeRef(), namePattern, null, false, true, true);
|
||||
result.removeAll(siteChilds);
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for emails in specified folder depend on view mode.
|
||||
*
|
||||
* @param contextNodeRef context folder for search
|
||||
* @param namePattern name pattern for search
|
||||
* @param viewMode context folder view mode
|
||||
* @param includeSubFolders includeSubFolders
|
||||
* @return list of emails that context folder contains.
|
||||
*/
|
||||
public List<FileInfo> searchMails(NodeRef contextNodeRef, String namePattern, String viewMode, boolean includeSubFolders)
|
||||
{
|
||||
|
||||
List<FileInfo> result = new LinkedList<FileInfo>();
|
||||
if (viewMode.equals(AlfrescoImapConst.MODE_ARCHIVE))
|
||||
{
|
||||
result = search(contextNodeRef, namePattern, ImapModel.TYPE_IMAP_CONTENT, false, true, includeSubFolders);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (viewMode.equals(AlfrescoImapConst.MODE_VIRTUAL))
|
||||
{
|
||||
result = search(contextNodeRef, namePattern, null, true, false, includeSubFolders);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<FileInfo> search(NodeRef contextNodeRef, String namePattern, QName searchType, boolean fileSearch, boolean folderSearch, boolean includeSubFolders)
|
||||
{
|
||||
List<FileInfo> result = new LinkedList<FileInfo>();
|
||||
List<FileInfo> searchResult = fileFolderService.search(contextNodeRef, namePattern, fileSearch, folderSearch, includeSubFolders);
|
||||
|
||||
if (searchType == null)
|
||||
{
|
||||
return searchResult;
|
||||
}
|
||||
|
||||
for (FileInfo fileInfo : searchResult)
|
||||
{
|
||||
if (nodeService.getType(fileInfo.getNodeRef()).equals(searchType))
|
||||
{
|
||||
result.add(fileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get root reference for the specified mailbox
|
||||
*
|
||||
* @param mailboxName mailbox name in IMAP client.
|
||||
* @param userName
|
||||
* @return
|
||||
*/
|
||||
public NodeRef getMailboxRootRef(String mailboxName, String userName)
|
||||
{
|
||||
String rootFolder;
|
||||
int index = mailboxName.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER);
|
||||
if (index > 0)
|
||||
{
|
||||
rootFolder = mailboxName.substring(0, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootFolder = mailboxName;
|
||||
}
|
||||
|
||||
Map<String, ImapConfig> imapConfigs = getImapConfigs();
|
||||
if (imapConfigs.keySet().contains(rootFolder))
|
||||
{
|
||||
Map<String, NodeRef> mountPoints = getMountPoints();
|
||||
NodeRef mountRef = mountPoints.get(rootFolder);
|
||||
return nodeService.getParentAssocs(mountRef).get(0).getParentRef();
|
||||
}
|
||||
else
|
||||
{
|
||||
return getUserImapHomeRef(userName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param userName user name
|
||||
* @return user IMAP home reference
|
||||
*/
|
||||
public NodeRef getUserImapHomeRef(String userName)
|
||||
{
|
||||
return fileFolderService.searchSimple(imapRootNodeRef, userName);
|
||||
}
|
||||
|
||||
public String getCurrentUser()
|
||||
{
|
||||
return AuthenticationUtil.getFullyAuthenticatedUser();
|
||||
}
|
||||
|
||||
public String getUserImapHomeId(String userName)
|
||||
{
|
||||
return getUserImapHomeRef(userName).getId();
|
||||
}
|
||||
|
||||
public NodeRef getImapRootNodeRef()
|
||||
{
|
||||
return imapRootNodeRef;
|
||||
}
|
||||
|
||||
public NodeRef getCompanyHomeNodeRef()
|
||||
{
|
||||
return companyHomeNodeRef;
|
||||
}
|
||||
|
||||
public NodeRef getSpacesStoreNodeRef()
|
||||
{
|
||||
return spacesStoreNodeRef;
|
||||
}
|
||||
|
||||
public void setImapRoot(String imapRoot)
|
||||
{
|
||||
this.imapRoot = imapRoot;
|
||||
}
|
||||
|
||||
public String getDefaultFromAddress()
|
||||
{
|
||||
return defaultFromAddress;
|
||||
}
|
||||
|
||||
public void setDefaultFromAddress(String defaultFromAddress)
|
||||
{
|
||||
this.defaultFromAddress = defaultFromAddress;
|
||||
}
|
||||
|
||||
public String getWebApplicationContextUrl()
|
||||
{
|
||||
return this.webApplicationContextUrl;
|
||||
}
|
||||
|
||||
public void setWebApplicationContextUrl(String webApplicationContextUrl)
|
||||
{
|
||||
this.webApplicationContextUrl = webApplicationContextUrl;
|
||||
}
|
||||
|
||||
public String getRepositoryTemplatePath()
|
||||
{
|
||||
return repositoryTemplatePath;
|
||||
}
|
||||
|
||||
public void setRepositoryTemplatePath(String repositoryTemplatePath)
|
||||
{
|
||||
this.repositoryTemplatePath = repositoryTemplatePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return flags that belong to the specified imap folder.
|
||||
*
|
||||
* @param messageInfo imap folder info.
|
||||
* @return flags.
|
||||
*/
|
||||
public Flags getFlags(FileInfo messageInfo)
|
||||
{
|
||||
Flags flags = new Flags();
|
||||
checkForFlaggableAspect(messageInfo.getNodeRef());
|
||||
Map<QName, Serializable> props = nodeService.getProperties(messageInfo.getNodeRef());
|
||||
|
||||
for (QName key : qNameToFlag.keySet())
|
||||
{
|
||||
Boolean value = (Boolean) props.get(key);
|
||||
if (value != null && value)
|
||||
{
|
||||
flags.add(qNameToFlag.get(key));
|
||||
}
|
||||
}
|
||||
// This is a multiuser flag support. Commented due new requirements
|
||||
// for (QName key : qNameToFlag.keySet())
|
||||
// {
|
||||
// if (key.equals(ImapModel.PROP_FLAG_DELETED))
|
||||
// {
|
||||
// Boolean value = (Boolean) props.get(key);
|
||||
// if (value != null && value)
|
||||
// {
|
||||
// flags.add(qNameToFlag.get(key));
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// String users = (String) props.get(key);
|
||||
//
|
||||
// if (users != null && users.indexOf(formatUserEntry(getCurrentUser())) >= 0)
|
||||
// {
|
||||
// flags.add(qNameToFlag.get(key));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set flags to the specified imapFolder.
|
||||
*
|
||||
* @param messageInfo FileInfo of imap Folder.
|
||||
* @param flags flags to set.
|
||||
* @param value value to set.
|
||||
*/
|
||||
public void setFlags(FileInfo messageInfo, Flags flags, boolean value)
|
||||
{
|
||||
checkForFlaggableAspect(messageInfo.getNodeRef());
|
||||
for (Flags.Flag flag : flags.getSystemFlags())
|
||||
{
|
||||
setFlag(messageInfo, flag, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set flags to the specified imapFolder.
|
||||
*
|
||||
* @param messageInfo FileInfo of imap Folder
|
||||
* @param flag flag to set.
|
||||
* @param value value value to set.
|
||||
*/
|
||||
public void setFlag(final FileInfo messageInfo, final Flag flag, final boolean value)
|
||||
{
|
||||
checkForFlaggableAspect(messageInfo.getNodeRef());
|
||||
nodeService.setProperty(messageInfo.getNodeRef(), flagToQname.get(flag), value);
|
||||
|
||||
// This is a multiuser flag support. Commented due new requirements
|
||||
// if (flagToQname.get(flag).equals(ImapModel.PROP_FLAG_DELETED))
|
||||
// {
|
||||
// nodeService.setProperty(messageInfo.getNodeRef(), flagToQname.get(flag), value);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Void>()
|
||||
// {
|
||||
// public Void doWork() throws Exception
|
||||
// {
|
||||
//
|
||||
// String users = (String) nodeService.getProperty(messageInfo.getNodeRef(), flagToQname.get(flag));
|
||||
// if (value)
|
||||
// {
|
||||
// if (users == null)
|
||||
// {
|
||||
// users = "";
|
||||
// }
|
||||
// users += formatUserEntry(getCurrentUser());
|
||||
//
|
||||
// }
|
||||
// else if (users != null)
|
||||
// {
|
||||
// users = users.replace(formatUserEntry(getCurrentUser()), "");
|
||||
//
|
||||
// }
|
||||
// nodeService.setProperty(messageInfo.getNodeRef(), flagToQname.get(flag), users);
|
||||
// return null;
|
||||
// }
|
||||
// }, AuthenticationUtil.getSystemUserName());
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the given authentication has a particular permission for the given node.
|
||||
*
|
||||
* @param nodeRef nodeRef of the node
|
||||
* @param permission permission for check
|
||||
* @return the access status
|
||||
*/
|
||||
public AccessStatus hasPermission(NodeRef nodeRef, String permission)
|
||||
{
|
||||
return permissionService.hasPermission(nodeRef, permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change userName into following format ;userName;
|
||||
*
|
||||
* @param userName
|
||||
* @return
|
||||
*/
|
||||
public String formatUserEntry(String userName)
|
||||
{
|
||||
return AlfrescoImapConst.USER_SEPARATOR + userName + AlfrescoImapConst.USER_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should returns a unique identifier of Alfresco server. The possible UID may be calculated based on IP address, Server port, MAC address, Web Application context.
|
||||
* This UID should be parseable into initial components. This necessary for the implementation of the following case: If the message being copied (e.g. drag-and-drop) between
|
||||
* two different Alfresco accounts in the IMAP client, we must unambiguously identify from which Alfresco server this message being copied. The message itself does not contain
|
||||
* content data, so we must download it from the initial server (e.g. using download content servlet) and save it into destination repository.
|
||||
*
|
||||
* @return String representation of unique identifier of Alfresco server
|
||||
*/
|
||||
public String getAlfrescoServerUID()
|
||||
{
|
||||
// TODO Implement as javadoc says.
|
||||
return "Not-Implemented";
|
||||
}
|
||||
|
||||
/**
|
||||
* Map of mount points. Name of mount point == key in the map.
|
||||
*
|
||||
* @return Map of mount points.
|
||||
*/
|
||||
public Map<String, NodeRef> getMountPoints()
|
||||
{
|
||||
Map<String, ImapConfig> imapConfigs = getImapConfigs();
|
||||
Map<String, NodeRef> mountPoints = new HashMap<String, NodeRef>();
|
||||
|
||||
for (ImapConfig config : imapConfigs.values())
|
||||
{
|
||||
// Get node reference
|
||||
StoreRef store = new StoreRef(config.getStore());
|
||||
ResultSet rs = searchService.query(store, SearchService.LANGUAGE_XPATH, config.getRootPath());
|
||||
if (rs.length() == 0)
|
||||
{
|
||||
logger.warn("Didn't find " + config.getName());
|
||||
}
|
||||
else
|
||||
{
|
||||
NodeRef nodeRef = rs.getNodeRef(0);
|
||||
mountPoints.put(config.getName(), nodeRef);
|
||||
}
|
||||
rs.close();
|
||||
}
|
||||
return mountPoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return map of imap configs. Name of config == key in the map
|
||||
*
|
||||
* @return map of imap configs.
|
||||
*/
|
||||
public Map<String, ImapConfig> getImapConfigs()
|
||||
{
|
||||
Config imapConfig = configService.getConfig("imapConfig");
|
||||
ImapConfigElement imapConfigElement = (ImapConfigElement) imapConfig.getConfigElement(ImapConfigElement.CONFIG_ELEMENT_ID);
|
||||
return imapConfigElement.getImapConfigs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return view mode ("virtual" or "archive") for specified mailbox.
|
||||
*
|
||||
* @param mailboxName name of the mailbox in IMAP client.
|
||||
* @return view mode of the specified mailbox.
|
||||
*/
|
||||
public String getViewMode(String mailboxName)
|
||||
{
|
||||
String rootFolder;
|
||||
int index = mailboxName.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER);
|
||||
if (index > 0)
|
||||
{
|
||||
rootFolder = mailboxName.substring(0, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootFolder = mailboxName;
|
||||
}
|
||||
Map<String, ImapConfig> imapConfigs = getImapConfigs();
|
||||
if (imapConfigs.keySet().contains(rootFolder))
|
||||
{
|
||||
return imapConfigs.get(rootFolder).getMode();
|
||||
}
|
||||
else
|
||||
{
|
||||
return AlfrescoImapConst.MODE_ARCHIVE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return mount point name, which was specified in imap-config.xml for the current mailbox.
|
||||
*
|
||||
* @param mailboxName mailbox name in IMAP client.
|
||||
* @return mount point name or null.
|
||||
*/
|
||||
public String getMountPointName(String mailboxName)
|
||||
{
|
||||
String rootFolder;
|
||||
int index = mailboxName.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER);
|
||||
if (index > 0)
|
||||
{
|
||||
rootFolder = mailboxName.substring(0, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootFolder = mailboxName;
|
||||
}
|
||||
Map<String, ImapConfig> imapConfigs = getImapConfigs();
|
||||
if (imapConfigs.keySet().contains(rootFolder))
|
||||
{
|
||||
return rootFolder;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert mailpath from IMAP client representation to the alfresco representation view. (e.g. with default settings "getMailPathInRepo(Repository_virtual.Imap Home)" will
|
||||
* return "Company Home.Imap Home")
|
||||
*
|
||||
* @param mailPath mailbox path in IMAP client
|
||||
* @return mailbox path in alfresco
|
||||
*/
|
||||
public String getMailPathInRepo(String mailPath)
|
||||
{
|
||||
String rootFolder;
|
||||
String remain = "";
|
||||
int index = mailPath.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER);
|
||||
if (index > 0)
|
||||
{
|
||||
rootFolder = mailPath.substring(0, index);
|
||||
remain = mailPath.substring(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootFolder = mailPath;
|
||||
}
|
||||
Map<String, ImapConfig> imapConfigs = getImapConfigs();
|
||||
if (imapConfigs.keySet().contains(rootFolder))
|
||||
{
|
||||
Map<String, NodeRef> mountPoints = getMountPoints();
|
||||
NodeRef rootRef = mountPoints.get(rootFolder);
|
||||
String rootName = nodeService.getProperty(rootRef, ContentModel.PROP_NAME).toString();
|
||||
|
||||
return rootName + remain;
|
||||
}
|
||||
else
|
||||
{
|
||||
return mailPath;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of sites, that belong to the specified user and not marked as "Imap favourite"
|
||||
*
|
||||
* @param userName name of user
|
||||
* @return List of nonFavourite sites.
|
||||
*/
|
||||
public List<SiteInfo> getNonFavouriteSites(String userName)
|
||||
{
|
||||
List<SiteInfo> nonFavSites = new LinkedList<SiteInfo>();
|
||||
Map<String, Serializable> prefs = preferenceService.getPreferences(userName, AlfrescoImapConst.PREF_IMAP_FAVOURITE_SITES);
|
||||
List<SiteInfo> sites = siteService.listSites(userName);
|
||||
for (SiteInfo siteInfo : sites)
|
||||
{
|
||||
String key = AlfrescoImapConst.PREF_IMAP_FAVOURITE_SITES + "." + siteInfo.getShortName();
|
||||
Boolean isImapFavourite = (Boolean) prefs.get(key);
|
||||
if (isImapFavourite == null || !isImapFavourite)
|
||||
{
|
||||
nonFavSites.add(siteInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return nonFavSites;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text representing email body for ContentModel node.
|
||||
*
|
||||
* @param nodeRef NodeRef of the target content.
|
||||
* @param type The type of the returned body. May be the one of {@link EmailBodyType}.
|
||||
* @return Text representing email body for ContentModel node.
|
||||
*/
|
||||
public String getEmailBodyText(NodeRef nodeRef, EmailBodyType type)
|
||||
{
|
||||
return templateService.processTemplate(getDefaultEmailBodyTemplate(type), createEmailTemplateModel(nodeRef));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns default email body template. This method trying to find a template on the path in the repository first e.g. {@code "Data Dictionary > IMAP Templates >"}. This path
|
||||
* should be set as the property of the "imapHelper" bean. In this case it returns {@code NodeRef.toString()} of the template. If there are no template in the repository it
|
||||
* returns a default template on the classpath.
|
||||
*
|
||||
* @param type One of the {@link EmailBodyType}.
|
||||
* @return String representing template classpath path or NodeRef.toString().
|
||||
*/
|
||||
public String getDefaultEmailBodyTemplate(EmailBodyType type)
|
||||
{
|
||||
String result = null;
|
||||
switch (type)
|
||||
{
|
||||
case TEXT_HTML:
|
||||
result = CLASSPATH_TEXT_HTML_TEMPLATE;
|
||||
break;
|
||||
case TEXT_PLAIN:
|
||||
result = CLASSPATH_TEXT_PLAIN_TEMPLATE;
|
||||
break;
|
||||
}
|
||||
final StringBuilder templateName = new StringBuilder(DICTIONARY_TEMPLATE_PREFIX).append("-").append(type.getTypeSubtype()).append(".ftl");
|
||||
int indexOfStoreDelim = repositoryTemplatePath.indexOf(StoreRef.URI_FILLER);
|
||||
if (indexOfStoreDelim == -1)
|
||||
{
|
||||
logger.error("Bad path format, " + StoreRef.URI_FILLER + " not found");
|
||||
return result;
|
||||
}
|
||||
indexOfStoreDelim += StoreRef.URI_FILLER.length();
|
||||
int indexOfPathDelim = repositoryTemplatePath.indexOf("/", indexOfStoreDelim);
|
||||
if (indexOfPathDelim == -1)
|
||||
{
|
||||
logger.error("Bad path format, / not found");
|
||||
return result;
|
||||
}
|
||||
final String storePath = repositoryTemplatePath.substring(0, indexOfPathDelim);
|
||||
final String rootPathInStore = repositoryTemplatePath.substring(indexOfPathDelim);
|
||||
final String query = String.format("+PATH:\"%1$s/*\" +@cm\\:name:\"%2$s\"", rootPathInStore, templateName.toString());
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Using template path :" + repositoryTemplatePath + "/" + templateName);
|
||||
logger.debug("Query: " + query);
|
||||
}
|
||||
StoreRef storeRef = new StoreRef(storePath);
|
||||
ResultSet resultSet = searchService.query(storeRef, "lucene", query);
|
||||
if (resultSet == null || resultSet.length() == 0)
|
||||
{
|
||||
logger.error(String.format("IMAP message template '%1$s' does not exist in the path '%2$s'.", templateName, repositoryTemplatePath));
|
||||
return result;
|
||||
}
|
||||
result = resultSet.getNodeRef(0).toString();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds default email template model for TemplateProcessor
|
||||
*
|
||||
* @param ref NodeRef of the target content.
|
||||
* @return Map that includes template model objects.
|
||||
*/
|
||||
private Map<String, Object> createEmailTemplateModel(NodeRef ref)
|
||||
{
|
||||
Map<String, Object> model = new HashMap<String, Object>(8, 1.0f);
|
||||
TemplateNode tn = new TemplateNode(ref, serviceRegistry, null);
|
||||
model.put("document", tn);
|
||||
NodeRef parent = nodeService.getPrimaryParent(ref).getParentRef();
|
||||
model.put("space", new TemplateNode(parent, serviceRegistry, null));
|
||||
model.put("date", new Date());
|
||||
model.put("contextUrl", new String(getWebApplicationContextUrl()));
|
||||
model.put("alfTicket", new String(serviceRegistry.getAuthenticationService().getCurrentTicket()));
|
||||
return model;
|
||||
}
|
||||
|
||||
private void checkForFlaggableAspect(NodeRef nodeRef)
|
||||
{
|
||||
if (!nodeService.hasAspect(nodeRef, ImapModel.ASPECT_FLAGGABLE))
|
||||
{
|
||||
Map<QName, Serializable> aspectProperties = new HashMap<QName, Serializable>();
|
||||
nodeService.addAspect(nodeRef, ImapModel.ASPECT_FLAGGABLE, aspectProperties);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPatchApplied()
|
||||
{
|
||||
return patchApplied;
|
||||
}
|
||||
|
||||
// ----------------------Getters and Setters----------------------------
|
||||
|
||||
public NodeService getNodeService()
|
||||
{
|
||||
return nodeService;
|
||||
}
|
||||
|
||||
public void setNodeService(NodeService nodeService)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
}
|
||||
|
||||
public SearchService getSearchService()
|
||||
{
|
||||
return searchService;
|
||||
}
|
||||
|
||||
public void setSearchService(SearchService searchService)
|
||||
{
|
||||
this.searchService = searchService;
|
||||
}
|
||||
|
||||
public FileFolderService getFileFolderService()
|
||||
{
|
||||
return fileFolderService;
|
||||
}
|
||||
|
||||
public void setFileFolderService(FileFolderService fileFolderService)
|
||||
{
|
||||
this.fileFolderService = fileFolderService;
|
||||
}
|
||||
|
||||
public TemplateService getTemplateService()
|
||||
{
|
||||
return templateService;
|
||||
}
|
||||
|
||||
public void setTemplateService(TemplateService templateService)
|
||||
{
|
||||
this.templateService = templateService;
|
||||
}
|
||||
|
||||
public NamespaceService getNamespaceService()
|
||||
{
|
||||
return namespaceService;
|
||||
}
|
||||
|
||||
public void setNamespaceService(NamespaceService namespaceService)
|
||||
{
|
||||
this.namespaceService = namespaceService;
|
||||
}
|
||||
|
||||
public PermissionService getPermissionService()
|
||||
{
|
||||
return permissionService;
|
||||
}
|
||||
|
||||
public void setPermissionService(PermissionService permissionService)
|
||||
{
|
||||
this.permissionService = permissionService;
|
||||
}
|
||||
|
||||
public ConfigService getConfigService()
|
||||
{
|
||||
return configService;
|
||||
}
|
||||
|
||||
public void setConfigService(ConfigService configService)
|
||||
{
|
||||
this.configService = configService;
|
||||
}
|
||||
|
||||
public DictionaryService getDictionaryService()
|
||||
{
|
||||
return dictionaryService;
|
||||
}
|
||||
|
||||
public void setDictionaryService(DictionaryService dictionaryService)
|
||||
{
|
||||
this.dictionaryService = dictionaryService;
|
||||
}
|
||||
|
||||
public PreferenceService getPreferenceService()
|
||||
{
|
||||
return preferenceService;
|
||||
}
|
||||
|
||||
public void setPreferenceService(PreferenceService preferenceService)
|
||||
{
|
||||
this.preferenceService = preferenceService;
|
||||
}
|
||||
|
||||
public SiteService getSiteService()
|
||||
{
|
||||
return siteService;
|
||||
}
|
||||
|
||||
public void setSiteService(SiteService siteService)
|
||||
{
|
||||
this.siteService = siteService;
|
||||
}
|
||||
|
||||
public ServiceRegistry getServiceRegistry()
|
||||
{
|
||||
return serviceRegistry;
|
||||
}
|
||||
|
||||
public void setServiceRegistry(ServiceRegistry serviceRegistry)
|
||||
{
|
||||
this.serviceRegistry = serviceRegistry;
|
||||
}
|
||||
|
||||
public PatchService getPatchService()
|
||||
{
|
||||
return patchService;
|
||||
}
|
||||
|
||||
public void setPatchService(PatchService patchService)
|
||||
{
|
||||
this.patchService = patchService;
|
||||
}
|
||||
|
||||
}
|
143
source/java/org/alfresco/repo/imap/config/ImapConfigElement.java
Executable file
143
source/java/org/alfresco/repo/imap/config/ImapConfigElement.java
Executable file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.imap.config;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.config.ConfigElement;
|
||||
import org.alfresco.config.element.ConfigElementAdapter;
|
||||
|
||||
public class ImapConfigElement extends ConfigElementAdapter
|
||||
{
|
||||
private static final long serialVersionUID = -6911139959296875159L;
|
||||
|
||||
public static final String CONFIG_ELEMENT_ID = "imapConfig";
|
||||
|
||||
private Map<String, ImapConfig> imapConfigs = new LinkedHashMap<String, ImapConfig>(8, 10f);
|
||||
|
||||
public ImapConfigElement()
|
||||
{
|
||||
super(CONFIG_ELEMENT_ID);
|
||||
}
|
||||
|
||||
public ImapConfigElement(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigElement combine(ConfigElement configElement)
|
||||
{
|
||||
ImapConfigElement combined = new ImapConfigElement();
|
||||
|
||||
// add all the imapConfigs from this element
|
||||
for (ImapConfig imapConfig : getImapConfigs().values())
|
||||
{
|
||||
combined.addImapConfig(imapConfig);
|
||||
}
|
||||
|
||||
// add all the imapConfigs from the given element
|
||||
for (ImapConfig imapConfig : ((ImapConfigElement) configElement).getImapConfigs().values())
|
||||
{
|
||||
combined.addImapConfig(imapConfig);
|
||||
}
|
||||
|
||||
return combined;
|
||||
}
|
||||
|
||||
public Map<String, ImapConfig> getImapConfigs()
|
||||
{
|
||||
return imapConfigs;
|
||||
}
|
||||
|
||||
public ImapConfig getImapConfig(String name)
|
||||
{
|
||||
return imapConfigs.get(name);
|
||||
}
|
||||
|
||||
void addImapConfig(ImapConfig imapConfig)
|
||||
{
|
||||
imapConfigs.put(imapConfig.getName(), imapConfig);
|
||||
}
|
||||
|
||||
public static class ImapConfig implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 424330549937129149L;
|
||||
|
||||
private String name;
|
||||
private String mode;
|
||||
private String store;
|
||||
private String rootPath;
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getMode()
|
||||
{
|
||||
return mode;
|
||||
}
|
||||
|
||||
public void setMode(String mode)
|
||||
{
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public String getStore()
|
||||
{
|
||||
return store;
|
||||
}
|
||||
|
||||
public void setStore(String store)
|
||||
{
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
public String getRootPath()
|
||||
{
|
||||
return rootPath;
|
||||
}
|
||||
|
||||
public void setRootPath(String rootPath)
|
||||
{
|
||||
this.rootPath = rootPath;
|
||||
}
|
||||
|
||||
public static long getSerialVersionUID()
|
||||
{
|
||||
return serialVersionUID;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
81
source/java/org/alfresco/repo/imap/config/ImapElementReader.java
Executable file
81
source/java/org/alfresco/repo/imap/config/ImapElementReader.java
Executable file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.imap.config;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.alfresco.config.ConfigElement;
|
||||
import org.alfresco.config.ConfigException;
|
||||
import org.alfresco.config.xml.elementreader.ConfigElementReader;
|
||||
import org.alfresco.repo.imap.config.ImapConfigElement.ImapConfig;
|
||||
import org.dom4j.Element;
|
||||
|
||||
public class ImapElementReader implements ConfigElementReader
|
||||
{
|
||||
|
||||
private static final String ELEMENT_IMAP_CONFIG = "imapConfig";
|
||||
private static final String ELEMENT_IMAP = "imap";
|
||||
private static final String ELEMENT_STORE = "store";
|
||||
private static final String ELEMENT_ROOTPATH = "rootPath";
|
||||
|
||||
private static final String ATTR_NAME = "name";
|
||||
private static final String ATTR_MODE = "mode";
|
||||
|
||||
public ConfigElement parse(Element element)
|
||||
{
|
||||
ImapConfigElement configElement = null;
|
||||
|
||||
if (element != null)
|
||||
{
|
||||
String elementName = element.getName();
|
||||
if (elementName.equals(ELEMENT_IMAP_CONFIG) == false)
|
||||
{
|
||||
throw new ConfigException("ImapElementReader can parse '" + ELEMENT_IMAP_CONFIG + "' elements only, the element passed is '" + elementName + "'");
|
||||
}
|
||||
|
||||
configElement = new ImapConfigElement();
|
||||
|
||||
for (Iterator<Element> items = element.elementIterator(ELEMENT_IMAP); items.hasNext();)
|
||||
{
|
||||
Element item = items.next();
|
||||
|
||||
String name = item.attributeValue(ATTR_NAME);
|
||||
String mode = item.attributeValue(ATTR_MODE);
|
||||
String store = item.element(ELEMENT_STORE).getStringValue();
|
||||
String rootPath = item.element(ELEMENT_ROOTPATH).getStringValue();
|
||||
|
||||
ImapConfig imapConfig = new ImapConfig();
|
||||
imapConfig.setName(name);
|
||||
imapConfig.setMode(mode);
|
||||
imapConfig.setStore(store);
|
||||
imapConfig.setRootPath(rootPath);
|
||||
|
||||
configElement.addImapConfig(imapConfig);
|
||||
}
|
||||
}
|
||||
return configElement;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.imap.exception;
|
||||
|
||||
import com.icegreen.greenmail.store.FolderException;
|
||||
|
||||
/**
|
||||
* Thrown on an inappropriate attempt to modify a folder.
|
||||
*
|
||||
* @author Ivan Rybnikov
|
||||
*/
|
||||
public class AlfrescoImapFolderException extends FolderException
|
||||
{
|
||||
|
||||
private static final long serialVersionUID = -2721708848846740336L;
|
||||
|
||||
public final static String PERMISSION_DENIED = "Can't create folder - Permission denied";
|
||||
|
||||
public AlfrescoImapFolderException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
@@ -48,6 +48,8 @@ import org.dom4j.io.SAXReader;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
|
||||
import com.werken.saxpath.XPathReader;
|
||||
|
||||
/**
|
||||
* Search component for use by the ScriptService.
|
||||
* <p>
|
||||
@@ -215,6 +217,26 @@ public final class Search extends BaseScopableProcessorExtension
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation Xpath query
|
||||
*
|
||||
* @param query xpath query
|
||||
* @return true if xpath query valid
|
||||
*/
|
||||
public boolean isValidXpathQuery(String query)
|
||||
{
|
||||
try
|
||||
{
|
||||
XPathReader reader = new XPathReader();
|
||||
reader.parse(query);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a Lucene search
|
||||
*
|
||||
|
Reference in New Issue
Block a user