private static List <RemoteArtifactCustomProperty> MergeCustomFieldsWithConcurrency(RemoteArtifact userSaved, RemoteArtifact original, RemoteArtifact serverModded) { List <RemoteArtifactCustomProperty> retList = new List <RemoteArtifactCustomProperty>(); //First get all the ones our user entered in. foreach (RemoteArtifactCustomProperty prop in userSaved.CustomProperties) { if (((original.CustomProperties.Count(cp => cp.PropertyNumber == prop.PropertyNumber) == 0) && //If the property does not exist in the original serverModded.CustomProperties.Count(cp => cp.PropertyNumber == prop.PropertyNumber) == 0)) // or the concurrent artifact. { retList.Add(prop); } else //There is either an original one or a concurrent one, { //If the user one is different than the original, use the user one. // Otherwise, use the one from the concurrent. bool useUser = false; RemoteArtifactCustomProperty orig = original.CustomProperties.SingleOrDefault(cp => cp.PropertyNumber == prop.PropertyNumber); if (orig != null) { RemoteArtifactCustomProperty concur = serverModded.CustomProperties.SingleOrDefault(cp => cp.PropertyNumber == prop.PropertyNumber); switch (prop.Definition.CustomPropertyTypeId) { case 1: // String (String) case 9: // URL (String) if (prop.StringValue.Equals(orig.StringValue)) { retList.Add(concur); } else { retList.Add(prop); } break; case 2: // Integer (Int) case 6: // List (Int) case 8: // User (Int) if (prop.IntegerValue.Equals(orig.IntegerValue)) { retList.Add(concur); } else { retList.Add(prop); } break; case 3: // Decimal (Decimal) if (prop.DecimalValue.Equals(orig.DecimalValue)) { retList.Add(concur); } else { retList.Add(prop); } break; case 4: // Boolean (Bool) if (prop.BooleanValue.Equals(orig.BooleanValue)) { retList.Add(concur); } else { retList.Add(prop); } break; case 5: // DateTime (DateTime) if (prop.DateTimeValue.Equals(orig.DateTimeValue)) { retList.Add(concur); } else { retList.Add(prop); } break; case 7: // Multilist (List<int>) prop.IntegerListValue.Sort(); orig.IntegerListValue.Sort(); if (prop.IntegerListValue.SequenceEqual(orig.IntegerListValue)) { retList.Add(concur); } else { retList.Add(prop); } break; } } else { useUser = true; } } } return(retList); }
/// <summary>Checks the given properties on the artifact the user is modifying against the given artifact to report wether the fields can be merged or not.</summary> /// <param name="original">The original artifact before any edits.</param> /// <param name="serverModded">The new artifact form the server - edited by someone else.</param> /// <param name="userEntered">The modified artifact from our user.</param> /// <returns>True if fields can be merged. False otherwise.</returns> private static bool CanCustomPropertiesBeMergedWith(this RemoteArtifact userEntered, RemoteArtifact original, RemoteArtifact serverModded) { bool retValue = false; //Loop through custom fields.. foreach (RemoteArtifactCustomProperty newProp in userEntered.CustomProperties) { //See if the user changed this property. RemoteArtifactCustomProperty origProp = original.CustomProperties.FirstOrDefault(cp => cp.PropertyNumber == newProp.PropertyNumber); bool userChanged = false; bool otherChanged = false; if (origProp == null) { userChanged = true; } else { //Pull the original value.. switch (newProp.Definition.CustomPropertyTypeId) { case 1: // String (String) case 9: // URL (String) userChanged = (newProp.StringValue.Equals(origProp.StringValue)); break; case 2: // Integer (Int) case 6: // List (Int) case 8: // User (Int) userChanged = (newProp.IntegerValue.Equals(origProp.IntegerValue)); break; case 3: // Decimal (Decimal) userChanged = (newProp.DecimalValue.Equals(origProp.DecimalValue)); break; case 4: // Boolean (Bool) userChanged = (newProp.BooleanValue.Equals(origProp.BooleanValue)); break; case 5: // DateTime (DateTime) userChanged = (newProp.DateTimeValue.Equals(origProp.DateTimeValue)); break; case 7: // Multilist (List<int>) newProp.IntegerListValue.Sort(); origProp.IntegerListValue.Sort(); userChanged = (newProp.IntegerListValue.SequenceEqual(origProp.IntegerListValue)); break; } } //If the user changed the field, check to see if the remote user also changed the field. // If so, then we can't merge. if (userChanged) { RemoteArtifactCustomProperty remProp = serverModded.CustomProperties.FirstOrDefault(cp => cp.PropertyNumber == newProp.PropertyNumber); if (remProp == null) { //If the original was null, (unset) and this one isn't, we don't care at checking values. if (origProp != null) { otherChanged = true; } } else { //Pull the original value.. switch (remProp.Definition.CustomPropertyTypeId) { case 1: // String (String) case 9: // URL (String) otherChanged = (remProp.StringValue.Equals(origProp.StringValue)); break; case 2: // Integer (Int) case 6: // List (Int) case 8: // User (Int) otherChanged = (remProp.IntegerValue.Equals(origProp.IntegerValue)); break; case 3: // Decimal (Decimal) otherChanged = (remProp.DecimalValue.Equals(origProp.DecimalValue)); break; case 4: // Boolean (Bool) otherChanged = (remProp.BooleanValue.Equals(origProp.BooleanValue)); break; case 5: // DateTime (DateTime) otherChanged = (remProp.DateTimeValue.Equals(origProp.DateTimeValue)); break; case 7: // Multilist (List<int>) remProp.IntegerListValue.Sort(); origProp.IntegerListValue.Sort(); otherChanged = (remProp.IntegerListValue.SequenceEqual(origProp.IntegerListValue)); break; } } } //Now set our final value. if (userChanged && otherChanged) { retValue = false; //Escape early.. break; } else { retValue = true; } } return(retValue); }
/// <summary> /// Sets a custom property value on an artifact, even if it doesn't have an entry yet, can handle the various types /// </summary> /// <param name="remoteArtifact">The artifact we're setting the properties on</param> /// <param name="propertyNumber">The position number (1-30) of the custom property</param> /// <param name="propertyValue">The typed property value</param> /// <returns>True if any changes were made</returns> public static bool SetCustomPropertyValue <T>(RemoteArtifact remoteArtifact, int propertyNumber, T propertyValue, bool changesMade = false) { //First see if we have any custom properties at all for this artifact, if not, create a collection List <RemoteArtifactCustomProperty> artifactCustomProperties; if (remoteArtifact.CustomProperties == null) { artifactCustomProperties = new List <RemoteArtifactCustomProperty>(); } else { artifactCustomProperties = remoteArtifact.CustomProperties.ToList(); } //Now see if we have a matching property already in the list RemoteArtifactCustomProperty artifactCustomProperty = artifactCustomProperties.FirstOrDefault(c => c.PropertyNumber == propertyNumber); if (artifactCustomProperty == null) { artifactCustomProperty = new RemoteArtifactCustomProperty(); artifactCustomProperty.PropertyNumber = propertyNumber; artifactCustomProperties.Add(artifactCustomProperty); } //Set the value that matches this type if (typeof(T) == typeof(String)) { string newValue = ((T)propertyValue as String); if (artifactCustomProperty.StringValue != newValue) { artifactCustomProperty.StringValue = newValue; changesMade = true; } } if (typeof(T) == typeof(Int32) || typeof(T) == typeof(Nullable <Int32>)) { int?newValue = ((T)propertyValue as Int32?); if (artifactCustomProperty.IntegerValue != newValue) { artifactCustomProperty.IntegerValue = newValue; changesMade = true; } } if (typeof(T) == typeof(Boolean) || typeof(T) == typeof(Nullable <Boolean>)) { bool?newValue = ((T)propertyValue as bool?); if (artifactCustomProperty.BooleanValue != newValue) { artifactCustomProperty.BooleanValue = newValue; changesMade = true; } } if (typeof(T) == typeof(DateTime) || typeof(T) == typeof(Nullable <DateTime>)) { DateTime?newValue = ((T)propertyValue as DateTime?); if (artifactCustomProperty.DateTimeValue != newValue) { artifactCustomProperty.DateTimeValue = newValue; changesMade = true; } } if (typeof(T) == typeof(Decimal) || typeof(T) == typeof(Nullable <Decimal>)) { decimal?newValue = ((T)propertyValue as Decimal?); if (artifactCustomProperty.DecimalValue != newValue) { artifactCustomProperty.DecimalValue = newValue; changesMade = true; } } if (typeof(T) == typeof(Int32[])) { int[] newValue = ((T)propertyValue as Int32[]); if (newValue == null || newValue.Length == 0) { if (artifactCustomProperty.IntegerListValue != null && artifactCustomProperty.IntegerListValue.Length > 0) { artifactCustomProperty.IntegerListValue = null; changesMade = true; } } else if (artifactCustomProperty.IntegerListValue == null || artifactCustomProperty.IntegerListValue.Length == 0) { artifactCustomProperty.IntegerListValue = newValue; changesMade = true; } else if (newValue.Any(v => !artifactCustomProperty.IntegerListValue.Contains(v)) || artifactCustomProperty.IntegerListValue.Any(v => !newValue.Contains(v))) { artifactCustomProperty.IntegerListValue = newValue; changesMade = true; } } if (typeof(T) == typeof(List <Int32>)) { List <Int32> intList = (List <Int32>)((T)propertyValue as List <Int32>); if (intList == null || intList.Count == 0) { if (artifactCustomProperty.IntegerListValue != null && artifactCustomProperty.IntegerListValue.Length > 0) { artifactCustomProperty.IntegerListValue = null; changesMade = true; } } else if (artifactCustomProperty.IntegerListValue == null || artifactCustomProperty.IntegerListValue.Length == 0) { artifactCustomProperty.IntegerListValue = intList.ToArray(); changesMade = true; } else if (intList.Any(v => !artifactCustomProperty.IntegerListValue.Contains(v)) || artifactCustomProperty.IntegerListValue.Any(v => !intList.Contains(v))) { artifactCustomProperty.IntegerListValue = intList.ToArray(); changesMade = true; } } //Finally we need to update the artifact's array remoteArtifact.CustomProperties = artifactCustomProperties.ToArray(); return(changesMade); }
/// <summary> /// Sets a custom property value on an artifact, even if it doesn't have an entry yet, can handle the various types /// </summary> /// <param name="remoteArtifact">The artifact we're setting the properties on</param> /// <param name="propertyNumber">The position number (1-30) of the custom property</param> /// <param name="propertyValue">The typed property value</param> public static void SetCustomPropertyValue <T>(RemoteArtifact remoteArtifact, int propertyNumber, T propertyValue) { //First see if we have any custom properties at all for this artifact, if not, create a collection List <RemoteArtifactCustomProperty> artifactCustomProperties; if (remoteArtifact.CustomProperties == null) { artifactCustomProperties = new List <RemoteArtifactCustomProperty>(); } else { artifactCustomProperties = remoteArtifact.CustomProperties.ToList(); } //Now see if we have a matching property already in the list RemoteArtifactCustomProperty artifactCustomProperty = artifactCustomProperties.FirstOrDefault(c => c.PropertyNumber == propertyNumber); if (artifactCustomProperty == null) { artifactCustomProperty = new RemoteArtifactCustomProperty(); artifactCustomProperty.PropertyNumber = propertyNumber; artifactCustomProperties.Add(artifactCustomProperty); } //Set the value that matches this type if (typeof(T) == typeof(String)) { artifactCustomProperty.StringValue = ((T)propertyValue as String); } if (typeof(T) == typeof(Int32) || typeof(T) == typeof(Nullable <Int32>)) { artifactCustomProperty.IntegerValue = ((T)propertyValue as Int32?); } if (typeof(T) == typeof(Boolean) || typeof(T) == typeof(Nullable <Boolean>)) { artifactCustomProperty.BooleanValue = ((T)propertyValue as bool?); } if (typeof(T) == typeof(DateTime) || typeof(T) == typeof(Nullable <DateTime>)) { artifactCustomProperty.DateTimeValue = ((T)propertyValue as DateTime?); } if (typeof(T) == typeof(Decimal) || typeof(T) == typeof(Nullable <Decimal>)) { artifactCustomProperty.DecimalValue = ((T)propertyValue as Decimal?); } if (typeof(T) == typeof(Int32[])) { artifactCustomProperty.IntegerListValue = ((T)propertyValue as Int32[]); } if (typeof(T) == typeof(List <Int32>)) { List <Int32> intList = (List <Int32>)((T)propertyValue as List <Int32>); if (intList == null || intList.Count == 0) { artifactCustomProperty.IntegerListValue = null; } else { artifactCustomProperty.IntegerListValue = intList.ToArray(); } } //Finally we need to update the artifact's array remoteArtifact.CustomProperties = artifactCustomProperties.ToArray(); }
private void processSpiraAccount(Pop3Client clientPOP3, AccountDetails account, ApplicationSystem appServer) { const string METHOD = CLASS + "processSpiraAccount()"; this._eventLog.EntryLog(METHOD); //Create the application client and connect to our project and get users.. SoapServiceClient clientAppl = (SoapServiceClient)this.CreateApplicationClient(appServer, account); //Get users in the project.. List <RemoteProjectUser> spiraUsers = clientAppl.Project_RetrieveUserMembership(); //Get the known message IDs.. List <string> seenUIDs = this.readMessageIDsForAccount(account.AccountID.Value); //Get new emails from the client. List <Message> newMsgs = this.popGetNewMessages(clientPOP3, account, seenUIDs); //Get all projects.. List <RemoteProject> spiraProjs = clientAppl.Project_Retrieve(); //Loop through each email. foreach (Message msg in newMsgs) { this._eventLog.WriteTrace(METHOD, "Starting on message " + msg.MessageLogID + "..."); //Make sure we have a from address, otherwise skip (Delivery Returned messages have no FROM address) //First see if the message should be skipped. (Keywords, Headers, or Email Addresses) if (msg.Headers != null && msg.Headers.From != null && !String.IsNullOrWhiteSpace(msg.Headers.From.Address) && msg.Headers.From.Address.ToLowerInvariant().Trim() != account.AccountEmail.ToLowerInvariant().Trim()) { string filterMsg; if (this.doesMessageClearFilters(msg, out filterMsg)) { //First see if there's a header we can get the artifact ID from.. ArtifactTypeEnum artType = ArtifactTypeEnum.None; int artId = -1; if (msg.Headers.UnknownHeaders[Common.MESSAGEHEADER_SPIRA_ARTIFACT] != null && rgxArtifactToken.IsMatch(msg.Headers.UnknownHeaders[Common.MESSAGEHEADER_SPIRA_ARTIFACT])) { //Get the art type and the id.. Match matches = rgxArtifactToken.Match(msg.Headers.UnknownHeaders[Common.MESSAGEHEADER_SPIRA_ARTIFACT]); this.retrieveArtTypeAndId(matches.Groups["type"].Value, matches.Groups["id"].Value, out artType, out artId); } if (artId == -1 || artType == ArtifactTypeEnum.None) { if (rgxArtifactToken.IsMatch(msg.Headers.Subject)) { //Get the art type and the id.. Match matches = rgxArtifactToken.Match(msg.Headers.Subject); this.retrieveArtTypeAndId(matches.Groups["type"].Value, matches.Groups["id"].Value, out artType, out artId); } } //Change projects, if necessary, and if we're able to.. try { int projNum = clientAppl.System_GetProjectIdForArtifact((int)artType, artId); if (projNum != 0) { bool succNewProject = clientAppl.Connection_ConnectToProject(projNum); if (!succNewProject) { //Couldn't connect to the project this item belongs to. Throw an error. this._eventLog.WriteMessage("Message " + msg.MessageLogID + " contains information for a project [PR:" + projNum.ToString() + "] that the client could not connect to. Skipping.", System.Diagnostics.EventLogEntryType.Information); artType = ArtifactTypeEnum.Skip; artId = 0; } } } catch (Exception ex) { this._eventLog.WriteMessage(METHOD, ex, "Message " + msg.MessageLogID + " - while trying to retrieve project number from artifact. Skipping."); } //If we have a match, find the user.. if (artId > 0 && artType != ArtifactTypeEnum.None) { //Detect if more than one user is found.. int userCnt = spiraUsers.Where(su => su.EmailAddress.ToLowerInvariant() == msg.Headers.From.MailAddress.Address.ToLowerInvariant()).Count(); if (userCnt == 1) { RemoteProjectUser selUser = spiraUsers.Where(su => su.EmailAddress.ToLowerInvariant() == msg.Headers.From.MailAddress.Address.ToLowerInvariant()).Single(); //See if the item exists in the server.. RemoteArtifact remArt = null; try { switch (artType) { case ArtifactTypeEnum.Requirement: remArt = clientAppl.Requirement_RetrieveById(artId); break; case ArtifactTypeEnum.Test_Case: remArt = clientAppl.TestCase_RetrieveById(artId); break; case ArtifactTypeEnum.Incident: remArt = clientAppl.Incident_RetrieveById(artId); break; case ArtifactTypeEnum.Release: remArt = clientAppl.Release_RetrieveById(artId); break; case ArtifactTypeEnum.Task: remArt = clientAppl.Task_RetrieveById(artId); break; case ArtifactTypeEnum.Test_Set: remArt = clientAppl.TestSet_RetrieveById(artId); break; } if (remArt == null) { throw new Exception("Artifact did not exist: " + artType.ToString() + " #" + artId.ToString()); } } catch (Exception ex) { this._eventLog.WriteMessage(METHOD, ex, "For message " + msg.MessageLogID + ", referenced artifact did not exist."); continue; } try { //The artifact exists, let's add a comment.. RemoteComment comment = new RemoteComment(); comment.ArtifactId = artId; comment.CreationDate = DateTime.UtcNow; comment.UserId = selUser.UserId; comment.Text = this.getTextFromMessage(msg, true, true); switch (artType) { case ArtifactTypeEnum.Requirement: comment = clientAppl.Requirement_CreateComment(comment); break; case ArtifactTypeEnum.Test_Case: comment = clientAppl.TestCase_CreateComment(comment); break; case ArtifactTypeEnum.Incident: comment = clientAppl.Incident_AddComments(new List <RemoteComment>() { comment }).FirstOrDefault(); break; case ArtifactTypeEnum.Release: comment = clientAppl.Release_CreateComment(comment); break; case ArtifactTypeEnum.Task: comment = clientAppl.Task_CreateComment(comment); break; case ArtifactTypeEnum.Test_Set: comment = clientAppl.TestSet_CreateComment(comment); break; } if (comment != null && comment.CommentId.HasValue) { //Now check for attachments try { foreach (MessagePart attach in msg.FindAllAttachments().Where(aa => aa.IsMultiPart == false && aa.IsText == false)) { //Add the file.. RemoteLinkedArtifact artifactLink = new RemoteLinkedArtifact(); artifactLink.ArtifactId = comment.ArtifactId; artifactLink.ArtifactTypeId = (int)artType; RemoteDocument newDoc = new RemoteDocument(); newDoc.AttachedArtifacts = new List <RemoteLinkedArtifact>() { artifactLink }; newDoc.AttachmentTypeId = 1; newDoc.AuthorId = selUser.UserId; newDoc.FilenameOrUrl = attach.FileName; newDoc.UploadDate = DateTime.UtcNow; //Check for string overrun and add extension if necessary. if (newDoc.FilenameOrUrl.Length > 250) { newDoc.FilenameOrUrl = newDoc.FilenameOrUrl.Substring(0, 250); } if (string.IsNullOrWhiteSpace(Path.GetExtension(newDoc.FilenameOrUrl)) && attach.ContentType != null) { string tempFileExtension = Utils.GetExtensionFromMimeType(attach.ContentType.MediaType); if (!string.IsNullOrWhiteSpace(tempFileExtension)) { newDoc.FilenameOrUrl += "." + tempFileExtension; } } //Call the function to upload the file to Spira newDoc = clientAppl.Document_AddFile(newDoc, attach.Body); //Log it. this._eventLog.WriteMessage("Attachment #" + newDoc.AttachmentId.ToSafeString() + " created from file '" + attach.FileName + "' for Artifact " + artType + " #:" + comment.ArtifactId + " from message " + msg.MessageLogID + ".", System.Diagnostics.EventLogEntryType.Information); } } catch (Exception ex) { //Log and continue this._eventLog.WriteMessage(METHOD, ex, "Saving attachment for message " + msg.MessageLogID); } } } catch (Exception ex) { this._eventLog.WriteMessage(METHOD, ex, "While trying to save message '" + msg.MessageLogID + "' as a new comment."); continue; } //If we get this far, mark the message as processed. try { //Add it to our list.. seenUIDs.Add(msg.MessageUID); if (account.RemoveFromServer) { clientPOP3.DeleteMessage(msg.MessageIndex); } } catch (Exception ex) { this._eventLog.WriteMessage(METHOD, ex, "Trying to delete message " + msg.MessageLogID + " from server."); } } else { string msgLog = ""; if (userCnt == 0) { msgLog = "Message " + msg.MessageLogID + " was sent from a user not a mamber of the specified project. Not importing unknown users."; } else if (userCnt > 1) { msgLog = "Message " + msg.MessageLogID + " was sent from a user that did not have a unique email address. Cannot import to avoid selecting the wrong user."; } this._eventLog.WriteMessage(msgLog, System.Diagnostics.EventLogEntryType.Information); } } else { if (artType != ArtifactTypeEnum.Skip) { int userCnt = spiraUsers.Where(su => su.EmailAddress.ToLowerInvariant() == msg.Headers.From.MailAddress.Address.ToLowerInvariant()).Count(); if (userCnt == 1) { RemoteProjectUser selUser = spiraUsers.Where(su => su.EmailAddress.ToLowerInvariant() == msg.Headers.From.MailAddress.Address.ToLowerInvariant()).Single(); //Create a new Incident.. RemoteIncident newIncident = new RemoteIncident(); newIncident.CreationDate = DateTime.Now; newIncident.Description = this.getTextFromMessage(msg, true, false); newIncident.Name = msg.Headers.Subject; newIncident.OpenerId = selUser.UserId; //Check regex other projects if (account.UseRegexToMatch) { Regex regProj1 = new Regex(@"\[PR[\:\-\s](\d*?)\]", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); if (regProj1.IsMatch(newIncident.Description) || regProj1.IsMatch(newIncident.Name)) { //Someone used the PR tag in the email body, we'll use it, if possible. string matchNum = ""; if (regProj1.IsMatch(newIncident.Name)) { matchNum = regProj1.Matches(newIncident.Name)[0].Groups[1].Value; } else if (regProj1.IsMatch(newIncident.Description)) { matchNum = regProj1.Matches(newIncident.Description)[0].Groups[1].Value; } else { this._eventLog.WriteMessage("ERROR: At least one RegEx returned IsMatch, but none contained match.", System.Diagnostics.EventLogEntryType.Information); continue; } int projNum = 0; if (int.TryParse(matchNum, out projNum)) { //We had a number, let's see if it's a valid product. RemoteProject proj = spiraProjs.FirstOrDefault(prd => prd.ProjectId == projNum); if (proj != null) { //Connect to project.. if (clientAppl.Connection_ConnectToProject(proj.ProjectId.Value)) { newIncident.ProjectId = proj.ProjectId.Value; this._eventLog.WriteMessage("Message " + msg.MessageLogID + " changed to Project '" + proj.Name + "' due to having [PR:xx] tag.", System.Diagnostics.EventLogEntryType.Information); } else { this._eventLog.WriteMessage("Message " + msg.MessageLogID + " contained project token for project '" + proj.Name + "', but email import has no access to that project. Using default.", System.Diagnostics.EventLogEntryType.Information); } } else { this._eventLog.WriteMessage("Message '" + msg.MessageLogID + "' contained token for project " + projNum.ToString() + " but project was inaccessible. Using default.", System.Diagnostics.EventLogEntryType.Information); } } } else { foreach (RemoteProject prod in spiraProjs) { Regex regProd3 = new Regex(@"\b" + prod.Name + @"\b", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); if (regProd3.IsMatch(newIncident.Description) || regProd3.IsMatch(newIncident.Name)) { newIncident.ProjectId = prod.ProjectId.Value; this._eventLog.WriteMessage("Message " + msg.MessageLogID + " changed to Product '" + prod.Name + "' due to having Product name '" + prod.Name + "'", System.Diagnostics.EventLogEntryType.Information); break; } } } } //Now save the Incident.. try { newIncident = clientAppl.Incident_Create(newIncident); if (newIncident.IncidentId.HasValue) { //Now check for attachments try { foreach (MessagePart attach in msg.FindAllAttachments().Where(aa => aa.IsMultiPart == false && aa.IsText == false)) { //Add the file.. RemoteLinkedArtifact artifactLink = new RemoteLinkedArtifact(); artifactLink.ArtifactId = newIncident.IncidentId.Value; artifactLink.ArtifactTypeId = 3; /* Incident */ RemoteDocument newDoc = new RemoteDocument(); newDoc.AttachedArtifacts = new List <RemoteLinkedArtifact>() { artifactLink }; newDoc.AttachmentTypeId = 1; newDoc.AuthorId = selUser.UserId; newDoc.FilenameOrUrl = attach.FileName; newDoc.UploadDate = DateTime.UtcNow; //Check for string overrun and add extension if necessary. if (newDoc.FilenameOrUrl.Length > 250) { newDoc.FilenameOrUrl = newDoc.FilenameOrUrl.Substring(0, 250); } if (string.IsNullOrWhiteSpace(Path.GetExtension(newDoc.FilenameOrUrl)) && attach.ContentType != null) { string tempFileExtension = Utils.GetExtensionFromMimeType(attach.ContentType.MediaType); if (!string.IsNullOrWhiteSpace(tempFileExtension)) { newDoc.FilenameOrUrl += "." + tempFileExtension; } } //Call the function to upload the file to Spira newDoc = clientAppl.Document_AddFile(newDoc, attach.Body); //Log it. this._eventLog.WriteMessage("Attachment #" + newDoc.AttachmentId.ToSafeString() + " created from file '" + attach.FileName + "' for Incident IN:" + newIncident.IncidentId.Value + " from message " + msg.MessageLogID + ".", System.Diagnostics.EventLogEntryType.Information); } } catch (Exception ex) { //Log and continue this._eventLog.WriteMessage(METHOD, ex, "Saving attachment for message " + msg.MessageLogID); } } //If we get this far, mark the message as processed. try { //Add it to our list.. seenUIDs.Add(msg.MessageUID); if (account.RemoveFromServer) { clientPOP3.DeleteMessage(msg.MessageIndex); } } catch (Exception ex) { this._eventLog.WriteMessage(METHOD, ex, "Trying to delete message " + msg.MessageLogID + " from server."); } } catch (Exception ex) { this._eventLog.WriteMessage(METHOD, ex, "While trying to save message '" + msg.MessageLogID + "' as an incident."); } } else { string msgLog = ""; if (userCnt == 0) { msgLog = "Message " + msg.MessageLogID + " was sent from a user not a mamber of the specified project. Not importing unknown users."; } else if (userCnt > 1) { msgLog = "Message " + msg.MessageLogID + " was sent from a user that did not have a unique email address. Cannot import to avoid selecting the wrong user."; } this._eventLog.WriteMessage(msgLog, System.Diagnostics.EventLogEntryType.Information); } } else { } } } else { //Log it.. this._eventLog.WriteMessage("Message " + msg.MessageLogID + " on the server did not pass filters:" + Environment.NewLine + "Subject: " + msg.Headers.Subject + Environment.NewLine + "Reasons: " + Environment.NewLine + filterMsg, System.Diagnostics.EventLogEntryType.Warning); } } else { //Log it.. this._eventLog.WriteMessage("Message " + msg.MessageLogID + " had no From address or was from the import account --" + Environment.NewLine + "Subject: " + msg.Headers.Subject + Environment.NewLine + "Msg UID: " + msg.Headers.MessageId + Environment.NewLine + "For reason: From Email address same as importing account, or was null.", System.Diagnostics.EventLogEntryType.Warning); } } //Save the seen message IDs.. this.saveMessageIDsForAccount(account.AccountID.Value, seenUIDs); //Disconnect client and POP3.. try { clientPOP3.Disconnect(); clientAppl.Connection_Disconnect(); } catch (Exception ex) { this._eventLog.WriteMessage(METHOD, ex, "Trying to close connections."); } }