diff --git a/config/alfresco/subsystems/email/InboundSMTP/inboundSMTP-context.xml b/config/alfresco/subsystems/email/InboundSMTP/inboundSMTP-context.xml index cdb4cb1ace..38634f3a8f 100755 --- a/config/alfresco/subsystems/email/InboundSMTP/inboundSMTP-context.xml +++ b/config/alfresco/subsystems/email/InboundSMTP/inboundSMTP-context.xml @@ -125,7 +125,12 @@ + class="org.alfresco.email.server.handler.FolderEmailMessageHandler" > + + + ${email.handler.folder.overwriteDuplicates} + + props = new HashMap(); + props.put(ContentModel.PROP_USERNAME, TEST_USER); + props.put(ContentModel.PROP_EMAIL, TEST_EMAIL); + person = personService.createPerson(props); + } + nodeService.setProperty(person, ContentModel.PROP_EMAIL, TEST_EMAIL); + Set auths = authorityService.getContainedAuthorities(null, "GROUP_EMAIL_CONTRIBUTORS", true); + if(!auths.contains(TEST_USER)) + { + authorityService.addAuthority("GROUP_EMAIL_CONTRIBUTORS", TEST_USER); + } + + String companyHomePathInStore = "/app:company_home"; + String storePath = "workspace://SpacesStore"; + StoreRef storeRef = new StoreRef(storePath); + NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); + List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false); + NodeRef companyHomeNodeRef = nodeRefs.get(0); + assertNotNull("company home is null", companyHomeNodeRef); + String companyHomeDBID = ((Long)nodeService.getProperty(companyHomeNodeRef, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + String testUserDBID = ((Long)nodeService.getProperty(person, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + NodeRef testUserHomeFolder = (NodeRef)nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER); + assertNotNull("testUserHomeFolder is null", testUserHomeFolder); + String testUserHomeDBID = ((Long)nodeService.getProperty(testUserHomeFolder, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + + // Clean up old messages in test folder + List assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + for(ChildAssociationRef assoc : assocs) + { + nodeService.deleteNode(assoc.getChildRef()); + } + + /** + * Send From the test user TEST_EMAIL to the test user's home + */ + String from = TEST_EMAIL; + String to = testUserHomeDBID; + String content = "hello world"; + + Session sess = Session.getDefaultInstance(new Properties()); + assertNotNull("sess is null", sess); + SMTPMessage msg = new SMTPMessage(sess); + InternetAddress[] toa = { new InternetAddress(to) }; + + msg.setFrom(new InternetAddress(TEST_EMAIL)); + msg.setRecipients(Message.RecipientType.TO, toa); + msg.setSubject(TEST_SUBJECT); + msg.setContent(content, "text/plain"); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + msg.writeTo(bos); + InputStream is = new StringInputStream(bos.toString()); + assertNotNull("is is null", is); + + SubethaEmailMessage m = new SubethaEmailMessage(is); + + /** + * Turn on overwriteDuplicates + */ + logger.debug("Step 1: turn on Overwite Duplicates"); + folderEmailMessageHandler.setOverwriteDuplicates(true); + + emailService.importMessage(m); + assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + assertEquals("assocs not 1", 1, assocs.size()); + assertEquals("name of link not as expected", assocs.get(0).getQName(), QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, TEST_SUBJECT)); + emailService.importMessage(m); + assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + assertEquals("assocs not 1", 1, assocs.size()); + + /** + * Turn off overwrite Duplicates + */ + logger.debug("Step 2: turn off Overwite Duplicates"); + folderEmailMessageHandler.setOverwriteDuplicates(false); + emailService.importMessage(m); + assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + assertEquals("assocs not 2", 2, assocs.size()); + emailService.importMessage(m); + assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + assertEquals("assocs not 3", 3, assocs.size()); + + /** + * Check assoc rename with long names. So truncation and rename need to work together. + */ + logger.debug("Step 3: turn off Overwite Duplicates with long subject name"); + msg.setSubject(TEST_LONG_SUBJECT); + ByteArrayOutputStream bos2 = new ByteArrayOutputStream(); + msg.writeTo(bos2); + is = new StringInputStream(bos2.toString()); + assertNotNull("is is null", is); + m = new SubethaEmailMessage(is); + + folderEmailMessageHandler.setOverwriteDuplicates(false); + emailService.importMessage(m); + assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + assertEquals("assocs not 4", 4, assocs.size()); + emailService.importMessage(m); + assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + assertEquals("assocs not 5", 5, assocs.size()); + } + } diff --git a/source/java/org/alfresco/email/server/handler/AbstractEmailMessageHandler.java b/source/java/org/alfresco/email/server/handler/AbstractEmailMessageHandler.java index ddc9b4cfba..50cdd57fc4 100644 --- a/source/java/org/alfresco/email/server/handler/AbstractEmailMessageHandler.java +++ b/source/java/org/alfresco/email/server/handler/AbstractEmailMessageHandler.java @@ -24,6 +24,7 @@ import java.io.InputStream; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.alfresco.error.AlfrescoRuntimeException; @@ -125,43 +126,43 @@ public abstract class AbstractEmailMessageHandler implements EmailMessageHandler this.mimetypeService = mimetypeService; } - /** - * @param to Email address which user part specifies node-dbid - * @return Referance to requested node. - * @throws InvalidArgumentException The exception is thrown if input string has incorrect format or empty. - */ - protected NodeRef getTargetNode(String to) throws InvalidArgumentException - { - if (to == null || to.length() == 0) - { - throw new InvalidArgumentException("Input string has to contain email address."); - } - String[] parts = to.split("@"); - if (parts.length != 2) - { - throw new InvalidArgumentException("Incorrect email address format."); - } - try - { - Long dbId = Long.parseLong(parts[0]); - return nodeService.getNodeRef(dbId); - } - catch (NumberFormatException e) - { - return null; - } - } +// /** +// * @param to Email address which user part specifies node-dbid +// * @return Referance to requested node. +// * @throws InvalidArgumentException The exception is thrown if input string has incorrect format or empty. +// */ +// protected NodeRef getTargetNode(String to) throws InvalidArgumentException +// { +// if (to == null || to.length() == 0) +// { +// throw new InvalidArgumentException("Input string has to contain email address."); +// } +// String[] parts = to.split("@"); +// if (parts.length != 2) +// { +// throw new InvalidArgumentException("Incorrect email address format."); +// } +// try +// { +// Long dbId = Long.parseLong(parts[0]); +// return nodeService.getNodeRef(dbId); +// } +// catch (NumberFormatException e) +// { +// return null; +// } +// } - /** - * Write the content to the node - * - * @param nodeRef Target node - * @param content Content - */ - protected void writeContent(NodeRef nodeRef, String content) - { - writeContent(nodeRef, content, MimetypeMap.MIMETYPE_TEXT_PLAIN); - } +// /** +// * Write the content to the node as MIMETYPE TEXT PLAIN. +// * +// * @param nodeRef Target node +// * @param content Content +// */ +// protected void writeContent(NodeRef nodeRef, String content) +// { +// writeContent(nodeRef, content, MimetypeMap.MIMETYPE_TEXT_PLAIN); +// } /** * Write the string as content to the node. @@ -189,7 +190,7 @@ public abstract class AbstractEmailMessageHandler implements EmailMessageHandler * @param nodeRef Target node. * @param content Content stream. * @param mimetype MIME content type. - * @param encoding Encoding. Can be null for non text based content. + * @param encoding Encoding. Can be null for text based content, n which case the best guess. */ protected void writeContent(NodeRef nodeRef, InputStream content, String mimetype, String encoding) { @@ -266,47 +267,82 @@ public abstract class AbstractEmailMessageHandler implements EmailMessageHandler * @param nodeService Alfresco Node Service * @param parent Parent node * @param name Name of the new node + * @param overwrite if true then overwrite an existing node with the same name. if false the name is changed to make it unique. * @param assocType Association type that should be set between parent node and the new one. * @return Reference to created node */ - protected NodeRef addContentNode(NodeService nodeService, NodeRef parent, String name, QName assocType) + protected NodeRef addContentNode(NodeService nodeService, NodeRef parent, String name, QName assocType, boolean overwrite) { - NodeRef childNodeRef = nodeService.getChildByName(parent, assocType, name); - if (childNodeRef != null) + NodeRef childNodeRef = null; + + String workingName = name; + + for(int counter = 0; counter < 10000; counter++) { - // The node is present already. Make sure the name case is correct - nodeService.setProperty(childNodeRef, ContentModel.PROP_NAME, name); - } - else - { - Map contentProps = new HashMap(); - contentProps.put(ContentModel.PROP_NAME, name); + QName safeQName = QName.createQNameWithValidLocalName(NamespaceService.CONTENT_MODEL_1_0_URI, workingName); + + List childNodeRefs = nodeService.getChildAssocs(parent, ContentModel.ASSOC_CONTAINS, safeQName); + + if (childNodeRefs.size() > 0) + { + if(overwrite) + { + childNodeRef=childNodeRefs.get(0).getChildRef(); + + // Node already exists + // The node is present already. Make sure the name case is correct + nodeService.setProperty(childNodeRef, ContentModel.PROP_NAME, name); + return childNodeRef; + } - QName assocName = QName.createQNameWithValidLocalName(NamespaceService.CONTENT_MODEL_1_0_URI, name); + // Need to work out a new safe name. + + String postFix = " (" + counter + ")"; - ChildAssociationRef associationRef = nodeService.createNode( + if(name.length() + postFix.length() > QName.MAX_LENGTH ) + { + workingName = name.substring(0, QName.MAX_LENGTH-postFix.length()) + postFix; + // Need to truncate name + } + else + { + workingName = name + postFix; + } + } + else + { + // Here if child node ref does not already exist + Map contentProps = new HashMap(); + contentProps.put(ContentModel.PROP_NAME, workingName); + + ChildAssociationRef associationRef = nodeService.createNode( parent, assocType, - assocName, + safeQName, ContentModel.TYPE_CONTENT, contentProps); - childNodeRef = associationRef.getChildRef(); + childNodeRef = associationRef.getChildRef(); + + return childNodeRef; + } } - return childNodeRef; + throw new AlfrescoRuntimeException("Unable to add new file"); } /** * Add new node into Alfresco repository with specified parameters. - * Node content isn't added. New node will be created with ContentModel.ASSOC_CONTAINS association with parent. + * Node content isn't added. + * + * New node will be created with ContentModel.ASSOC_CONTAINS association with parent. * * @param nodeService Alfresco Node Service * @param parent Parent node * @param name Name of the new node * @return Reference to created node */ - protected NodeRef addContentNode(NodeService nodeService, NodeRef parent, String name) + protected NodeRef addContentNode(NodeService nodeService, NodeRef parent, String name, boolean overwrite) { - return addContentNode(nodeService, parent, name, ContentModel.ASSOC_CONTAINS); + return addContentNode(nodeService, parent, name, ContentModel.ASSOC_CONTAINS, overwrite); } /** @@ -320,14 +356,13 @@ public abstract class AbstractEmailMessageHandler implements EmailMessageHandler */ protected NodeRef addAttachment(NodeService nodeService, NodeRef folder, NodeRef mainContentNode, String fileName) { - fileName = getAppropriateNodeName(folder, fileName, ContentModel.ASSOC_CONTAINS); if (log.isDebugEnabled()) { log.debug("Adding attachment node (name=" + fileName + ")."); } - NodeRef attachmentNode = addContentNode(nodeService, folder, fileName); + NodeRef attachmentNode = addContentNode(nodeService, folder, fileName, false); // Add attached aspect nodeService.addAspect(mainContentNode, ContentModel.ASPECT_ATTACHABLE, null); @@ -354,6 +389,7 @@ public abstract class AbstractEmailMessageHandler implements EmailMessageHandler if (nodeService.getChildByName(parent, assocType, name) != null) { name = name + "(1)"; + while (nodeService.getChildByName(parent, assocType, name) != null) { diff --git a/source/java/org/alfresco/email/server/handler/AbstractForumEmailMessageHandler.java b/source/java/org/alfresco/email/server/handler/AbstractForumEmailMessageHandler.java index b3f8082616..4e1c36dfdf 100644 --- a/source/java/org/alfresco/email/server/handler/AbstractForumEmailMessageHandler.java +++ b/source/java/org/alfresco/email/server/handler/AbstractForumEmailMessageHandler.java @@ -29,6 +29,7 @@ import java.util.Map; import org.alfresco.model.ApplicationModel; import org.alfresco.model.ContentModel; import org.alfresco.model.ForumModel; +import org.alfresco.repo.content.MimetypeMap; import org.alfresco.service.cmr.email.EmailMessage; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; @@ -92,7 +93,7 @@ public abstract class AbstractForumEmailMessageHandler extends AbstractEmailMess } else { - writeContent(postNodeRef, ""); + writeContent(postNodeRef, "", MimetypeMap.MIMETYPE_TEXT_PLAIN); } addEmailedAspect(postNodeRef, message); diff --git a/source/java/org/alfresco/email/server/handler/FolderEmailMessageHandler.java b/source/java/org/alfresco/email/server/handler/FolderEmailMessageHandler.java index 2c27dcce41..8710e45497 100644 --- a/source/java/org/alfresco/email/server/handler/FolderEmailMessageHandler.java +++ b/source/java/org/alfresco/email/server/handler/FolderEmailMessageHandler.java @@ -55,6 +55,8 @@ public class FolderEmailMessageHandler extends AbstractEmailMessageHandler private static final String ERR_MAIL_READ_ERROR = "email.server.err.mail_read_error"; private static final Log log = LogFactory.getLog(FolderEmailMessageHandler.class); + + private boolean overwriteDuplicates = false; /** * {@inheritDoc} @@ -108,7 +110,7 @@ public class FolderEmailMessageHandler extends AbstractEmailMessageHandler // Create main content node NodeRef contentNodeRef; - contentNodeRef = addContentNode(getNodeService(), spaceNodeRef, messageSubject); + contentNodeRef = addContentNode(getNodeService(), spaceNodeRef, messageSubject, overwriteDuplicates); // Add titled aspect addTitledAspect(contentNodeRef, messageSubject, message.getFrom()); // Add emailed aspect @@ -182,4 +184,18 @@ public class FolderEmailMessageHandler extends AbstractEmailMessageHandler log.debug("Titled aspect has been added."); } } + + /** + * Set the behaviour to be done on detecting a new message with the same subject. + * @param overwriteDuplicates + */ + public void setOverwriteDuplicates(boolean overwriteDuplicates) + { + this.overwriteDuplicates = overwriteDuplicates; + } + + public boolean isOverwriteDuplicates() + { + return overwriteDuplicates; + } }