/// <summary>Hit when the user wants to merge their changes with the concurrent task.</summary> /// <param name="sender">btnConcurrencyMergeYes</param> /// <param name="e">RoutedEventArgs</param> private void btnConcurrencyMergeYes_Click(object sender, RoutedEventArgs e) { try { e.Handled = true; //Get the client. ImportExportClient client = ((dynamic)sender).Tag as ImportExportClient; if (client != null) { //Switch screens again... this.display_SetOverlayWindow(this.panelSaving, System.Windows.Visibility.Visible); this.display_SetOverlayWindow(this.panelError, System.Windows.Visibility.Hidden); this.barSavingIncident.Value--; //Re-launch the saving.. RemoteIncident incMerged = StaticFuncs.MergeWithConcurrency(this.save_GetFromFields(), this._Incident, this._IncidentConcurrent); this._clientNumSaving++; client.Incident_UpdateAsync(incMerged, this._clientNum++); } } catch (Exception ex) { Logger.LogMessage(ex, "btnConcurrencyMergeYes_Click()"); MessageBox.Show(StaticFuncs.getCultureResource.GetString("app_General_UnexpectedError"), StaticFuncs.getCultureResource.GetString("app_General_ApplicationShortName"), MessageBoxButton.OK, MessageBoxImage.Error); } }
/// <summary>Hit if we hit a concurrency issue, and have to comapre values.</summary> /// <param name="sender">ImportExportClient</param> /// <param name="e">Incident_RetrieveByIdCompletedEventArgs</param> private void clientSave_Incident_RetrieveByIdCompleted(object sender, Incident_RetrieveByIdCompletedEventArgs e) { try { const string METHOD = CLASS + "clientSave_Incident_RetrieveByIdCompleted()"; Logger.LogTrace(METHOD + " Enter: " + this._clientNumSaving.ToString() + " running."); ImportExportClient client = (sender as ImportExportClient); this._clientNumSaving--; this.barSavingIncident.Value++; if (!e.Cancelled) { if (e.Error == null) { //We got new information here. Let's see if it can be merged. bool canBeMerged = this.save_CheckIfConcurrencyCanBeMerged(e.Result); this._IncidentConcurrent = e.Result; if (canBeMerged) { this.gridLoadingError.Visibility = System.Windows.Visibility.Collapsed; this.gridSavingConcurrencyMerge.Visibility = System.Windows.Visibility.Visible; this.gridSavingConcurrencyNoMerge.Visibility = System.Windows.Visibility.Collapsed; this.display_SetOverlayWindow(this.panelSaving, System.Windows.Visibility.Hidden); this.display_SetOverlayWindow(this.panelError, System.Windows.Visibility.Visible); //Save the client to the 'Merge' button. this.btnConcurrencyMergeYes.Tag = sender; } else { //TODO: Display error message here, tell users they must refresh their data. this.gridLoadingError.Visibility = System.Windows.Visibility.Collapsed; this.gridSavingConcurrencyMerge.Visibility = System.Windows.Visibility.Collapsed; this.gridSavingConcurrencyNoMerge.Visibility = System.Windows.Visibility.Visible; this.display_SetOverlayWindow(this.panelSaving, System.Windows.Visibility.Hidden); this.display_SetOverlayWindow(this.panelError, System.Windows.Visibility.Visible); } } else { //We even errored on retrieving information. Somethin's really wrong here. //Display error. Logger.LogMessage(e.Error, "Getting updated Concurrency Incident"); } } Logger.LogTrace(METHOD + " Exit"); } catch (Exception ex) { Logger.LogMessage(ex, "clientSave_Incident_RetrieveByIdCompleted()"); MessageBox.Show(StaticFuncs.getCultureResource.GetString("app_General_UnexpectedError"), StaticFuncs.getCultureResource.GetString("app_General_ApplicationShortName"), MessageBoxButton.OK, MessageBoxImage.Error); } }
/// <summary>Hit when the user wants to save the task.</summary> /// <param name="sender">The save button.</param> /// <param name="e">RoutedEventArgs</param> private void btnSave_Click(object sender, RoutedEventArgs e) { if (e != null) { e.Handled = true; } try { this.barSavingIncident.Value = -5; this.barSavingIncident.Maximum = 0; this.barSavingIncident.Minimum = -5; if (this._isFieldChanged || this._isWkfChanged || this._isResChanged || this._isDescChanged) { //Clear highlights. this.workflow_ClearAllRequiredHighlights(); //Set working flag. this.IsSaving = true; //Get the new values from the form.. RemoteIncident newIncident = this.save_GetFromFields(); if (newIncident != null && this.workflow_CheckRequiredFields()) { //Create a client, and save task and resolution.. ImportExportClient clientSave = StaticFuncs.CreateClient(((SpiraProject)this._ArtifactDetails.ArtifactParentProject.ArtifactTag).ServerURL.ToString()); clientSave.Connection_Authenticate2Completed += clientSave_Connection_Authenticate2Completed; clientSave.Connection_ConnectToProjectCompleted += clientSave_Connection_ConnectToProjectCompleted; clientSave.Incident_UpdateCompleted += clientSave_Incident_UpdateCompleted; clientSave.Incident_AddCommentsCompleted += clientSave_Incident_AddCommentsCompleted; clientSave.Connection_DisconnectCompleted += clientSave_Connection_DisconnectCompleted; //Fire off the connection. this._clientNumSaving = 1; clientSave.Connection_Authenticate2Async(((SpiraProject)this._ArtifactDetails.ArtifactParentProject.ArtifactTag).UserName, ((SpiraProject)this._ArtifactDetails.ArtifactParentProject.ArtifactTag).UserPass, StaticFuncs.getCultureResource.GetString("app_ReportName"), this._clientNum++); } else { //Display message saying that some required fields aren't filled out. MessageBox.Show(StaticFuncs.getCultureResource.GetString("app_General_RequiredFieldsMessage"), StaticFuncs.getCultureResource.GetString("app_General_RequiredFields"), MessageBoxButton.OK, MessageBoxImage.Error); } } } catch (Exception ex) { Logger.LogMessage(ex, "btnSave_Click()"); MessageBox.Show(StaticFuncs.getCultureResource.GetString("app_General_UnexpectedError"), StaticFuncs.getCultureResource.GetString("app_General_ApplicationShortName"), MessageBoxButton.OK, MessageBoxImage.Error); } if (this._clientNumSaving == 0) { this.IsSaving = false; } }
public static RemoteIncident MergeWithConcurrency(RemoteIncident userSaved, RemoteIncident original, RemoteIncident serverModded) { //If the field was not changed by the user (tskUserSaved == tskOriginal), then use the tskConcurrent. (Assuming that the // tskConcurrent has a possible updated value. //Otherwise, use the tskUserSaved value. RemoteIncident retIncident = new RemoteIncident(); try { retIncident.ActualEffort = ((userSaved.ActualEffort == original.ActualEffort) ? serverModded.ActualEffort : userSaved.ActualEffort); retIncident.ClosedDate = ((userSaved.ClosedDate == original.ClosedDate) ? serverModded.ClosedDate : userSaved.ClosedDate); retIncident.CreationDate = original.CreationDate; string strDescUser = StaticFuncs.StripTagsCharArray(userSaved.Description); string strDescOrig = StaticFuncs.StripTagsCharArray(original.Description); retIncident.Description = ((strDescOrig.TrimEquals(strDescOrig)) ? serverModded.Description : userSaved.Description); retIncident.DetectedReleaseId = ((userSaved.DetectedReleaseId == original.DetectedReleaseId) ? serverModded.DetectedReleaseId : userSaved.DetectedReleaseId); retIncident.EstimatedEffort = ((userSaved.EstimatedEffort == original.EstimatedEffort) ? serverModded.EstimatedEffort : userSaved.EstimatedEffort); retIncident.IncidentId = original.IncidentId; retIncident.IncidentStatusId = ((userSaved.IncidentStatusId == original.IncidentStatusId) ? serverModded.IncidentStatusId : userSaved.IncidentStatusId); retIncident.IncidentTypeId = ((userSaved.IncidentTypeId == original.IncidentTypeId) ? serverModded.IncidentTypeId : userSaved.IncidentTypeId); retIncident.LastUpdateDate = serverModded.LastUpdateDate; retIncident.Name = ((userSaved.Name.TrimEquals(original.Name)) ? serverModded.Name : userSaved.Name); retIncident.OpenerId = ((userSaved.OpenerId == original.OpenerId) ? serverModded.OpenerId : userSaved.OpenerId); retIncident.OwnerId = ((userSaved.OwnerId == original.OwnerId) ? serverModded.OwnerId : userSaved.OwnerId); retIncident.PriorityId = ((userSaved.PriorityId == original.PriorityId) ? serverModded.PriorityId : userSaved.PriorityId); retIncident.ProjectId = original.ProjectId; retIncident.RemainingEffort = ((userSaved.RemainingEffort == original.RemainingEffort) ? serverModded.RemainingEffort : userSaved.RemainingEffort); retIncident.ResolvedReleaseId = ((userSaved.ResolvedReleaseId == original.ResolvedReleaseId) ? serverModded.ResolvedReleaseId : userSaved.ResolvedReleaseId); retIncident.SeverityId = ((userSaved.SeverityId == original.SeverityId) ? serverModded.SeverityId : userSaved.SeverityId); retIncident.StartDate = ((userSaved.StartDate == original.StartDate) ? serverModded.StartDate : userSaved.StartDate); retIncident.TestRunStepId = ((userSaved.TestRunStepId == original.TestRunStepId) ? serverModded.TestRunStepId : userSaved.TestRunStepId); retIncident.VerifiedReleaseId = ((userSaved.VerifiedReleaseId == original.VerifiedReleaseId) ? serverModded.VerifiedReleaseId : userSaved.VerifiedReleaseId); //Custom Properties retIncident.CustomProperties = MergeCustomFieldsWithConcurrency(userSaved, original, serverModded); } catch (Exception ex) { Logger.LogMessage(ex, "clientSave_Connection_ConnectToProjectCompleted()"); MessageBox.Show(StaticFuncs.getCultureResource.GetString("app_General_UnexpectedError"), StaticFuncs.getCultureResource.GetString("app_General_ApplicationShortName"), MessageBoxButton.OK, MessageBoxImage.Error); retIncident = null; } //Return our new task. return(retIncident); }
/// <summary>Returns whether the given Concurrent Incident can be safely merged with the user's values.</summary> /// <param name="moddedTask">The concurrent task.</param> private bool save_CheckIfConcurrencyCanBeMerged(RemoteIncident moddedIncident) { bool retValue = false; try { //Get current values.. RemoteIncident userIncident = this.save_GetFromFields(); retValue = userIncident.CanBeMergedWith(this._Incident, moddedIncident); } catch (Exception ex) { Logger.LogMessage(ex, "save_CheckIfConcurrencyCanBeMerged()"); MessageBox.Show(StaticFuncs.getCultureResource.GetString("app_General_UnexpectedError"), StaticFuncs.getCultureResource.GetString("app_General_ApplicationShortName"), MessageBoxButton.OK, MessageBoxImage.Error); retValue = false; } return(retValue); }
/// <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="originalInc">The original artifact before any edits.</param> /// <param name="serverModdedInc">The new artifact form the server - edited by someone else.</param> /// <param name="userEnteredInc">The modified artifact from our user.</param> /// <returns>True if fields can be merged. False otherwise.</returns> public static bool CanBeMergedWith(this RemoteIncident userEnteredInc, RemoteIncident originalInc, RemoteIncident serverModdedInc) { bool retValue = false; if (userEnteredInc != null && serverModdedInc != null) { //Okay, check all fields. We want to see if a user-changed field (userTask) was also // changed by someone else. If it was, we return false (they cannot be merged). Otherwise, // we return true (they can be merged). //So we check the user-entered field against the original field. If they are different, // check the original field against the concurrent field. If they are different, return // false. Otherwise to both if's, return true. //We just loop through all available fields. The fielNum here has no reference to workflow // field ID, _WorkflowFields is just used to get the count of fields to check against. int fieldNum = 1; if (userEnteredInc.ActualEffort != originalInc.ActualEffort) { retValue = (originalInc.ActualEffort == serverModdedInc.ActualEffort); } if (!retValue && userEnteredInc.ClosedDate != originalInc.ClosedDate) { retValue = (originalInc.ClosedDate == serverModdedInc.ClosedDate); } if (!retValue && userEnteredInc.CreationDate != originalInc.CreationDate) { retValue = (originalInc.CreationDate == serverModdedInc.CreationDate); } if (!retValue) { if (StaticFuncs.StripTagsCharArray(userEnteredInc.Description).ToLowerInvariant().Trim() != StaticFuncs.StripTagsCharArray(originalInc.Description).ToLowerInvariant().Trim()) { retValue = (StaticFuncs.StripTagsCharArray(originalInc.Description).ToLowerInvariant().Trim() == StaticFuncs.StripTagsCharArray(serverModdedInc.Description).ToLowerInvariant().Trim()); } } if (!retValue && userEnteredInc.DetectedReleaseId != originalInc.DetectedReleaseId) { retValue = (originalInc.DetectedReleaseId == serverModdedInc.DetectedReleaseId); } if (!retValue && userEnteredInc.EstimatedEffort != originalInc.EstimatedEffort) { retValue = (originalInc.EstimatedEffort == serverModdedInc.EstimatedEffort); } if (!retValue && userEnteredInc.IncidentStatusId != originalInc.IncidentStatusId) { retValue = (originalInc.IncidentStatusId == serverModdedInc.IncidentStatusId); } if (!retValue && userEnteredInc.IncidentTypeId != originalInc.IncidentTypeId) { retValue = (originalInc.IncidentTypeId == serverModdedInc.IncidentTypeId); } if (!retValue && userEnteredInc.Name.TrimEquals(originalInc.Name)) { retValue = (originalInc.Name.TrimEquals(serverModdedInc.Name)); } if (!retValue && userEnteredInc.OpenerId != originalInc.OpenerId) { retValue = (originalInc.OpenerId == serverModdedInc.OpenerId); } if (!retValue && userEnteredInc.OwnerId != originalInc.OwnerId) { retValue = (originalInc.OwnerId == serverModdedInc.OwnerId); } if (!retValue && userEnteredInc.PriorityId != originalInc.PriorityId) { retValue = (originalInc.PriorityId == serverModdedInc.PriorityId); } if (!retValue && userEnteredInc.RemainingEffort != originalInc.RemainingEffort) { retValue = (originalInc.RemainingEffort == serverModdedInc.RemainingEffort); } if (!retValue && userEnteredInc.ResolvedReleaseId != originalInc.ResolvedReleaseId) { retValue = (originalInc.ResolvedReleaseId == serverModdedInc.ResolvedReleaseId); } if (!retValue && userEnteredInc.SeverityId != originalInc.SeverityId) { retValue = (originalInc.SeverityId == serverModdedInc.SeverityId); } if (!retValue && userEnteredInc.StartDate != originalInc.StartDate) { retValue = (originalInc.StartDate == serverModdedInc.StartDate); } if (!retValue && userEnteredInc.TestRunStepId != originalInc.TestRunStepId) { retValue = (originalInc.TestRunStepId == serverModdedInc.TestRunStepId); } if (!retValue && userEnteredInc.VerifiedReleaseId != originalInc.VerifiedReleaseId) { retValue = (originalInc.VerifiedReleaseId == serverModdedInc.VerifiedReleaseId); } //Check custom fields. if (!retValue) { retValue = CanCustomPropertiesBeMergedWith(userEnteredInc, originalInc, serverModdedInc); } } return(retValue); }
static void Main(string[] args) { _options = new Settings(); if (Parser.Default.ParseArguments(args, _options)) { //Make sure that the Spira string is a URL. Uri serverUrl = null; string servicePath = _options.SpiraServer + ((_options.SpiraServer.EndsWith("/") ? "" : "/")) + "Services/v5_0/SoapService.svc"; if (!Uri.TryCreate(servicePath, UriKind.Absolute, out serverUrl)) { //Throw error: URL given not a URL. return; } //See if we have a mappings file specified, if so, make sure it exists List <ArtifactMapping> artifactMappings = null; string customPropertyField = null; if (!String.IsNullOrWhiteSpace(_options.SpiraMappingsFile)) { if (!File.Exists(_options.SpiraMappingsFile)) { //Throw error: Bad path. ConsoleLog(LogLevelEnum.Normal, "Cannot access the mapping file, please check the location and try again!"); Environment.Exit(-1); } artifactMappings = new List <ArtifactMapping>(); //Read in the lines, the first column should contain: //Filename,ArtifactTypeId,Custom_03 //where the number in the third column is the name of the custom property that the IDs will be using using (StreamReader streamReader = File.OpenText(_options.SpiraMappingsFile)) { string firstLine = streamReader.ReadLine(); string[] headings = firstLine.Split(','); //See if we have a match on the custom property number if (headings.Length > 2 && !String.IsNullOrWhiteSpace(headings[2])) { customPropertyField = headings[2].Trim(); //Now read in the rows of mappings while (!streamReader.EndOfStream) { string mappingLine = streamReader.ReadLine(); ArtifactMapping artifactMapping = new ArtifactMapping(); string[] components = mappingLine.Split(','); artifactMapping.Filename = components[0]; artifactMapping.ArtifactTypeId = Int32.Parse(components[1]); artifactMapping.ExternalKey = components[2]; artifactMappings.Add(artifactMapping); } streamReader.Close(); } } } //Make sure the path given is a real path.. try { Directory.GetCreationTime(_options.ImportPath); } catch { //Throw error: Bad path. ConsoleLog(LogLevelEnum.Normal, "Cannot access the import path, please check the location and try again!"); Environment.Exit(-1); } //Tell user we're operating. ConsoleLog(LogLevelEnum.Normal, "Importing files in " + _options.ImportPath); //Now run through connecting procedures. ConsoleLog(LogLevelEnum.Verbose, "Connecting to Spira server..."); SoapServiceClient client = CreateClient_Spira5(serverUrl); client.Open(); if (client.Connection_Authenticate2(_options.SpiraLogin, _options.SpiraPass, "DocumentImporter")) { ConsoleLog(LogLevelEnum.Verbose, "Selecting Spira project..."); var Projects = client.Project_Retrieve(); RemoteProject proj = Projects.FirstOrDefault(p => p.ProjectId == _options.SpiraProject); if (proj != null) { //Connect to the project. if (client.Connection_ConnectToProject((int)_options.SpiraProject)) { ConsoleLog(LogLevelEnum.Normal, "Uploading files..."); //Now let's get a list of all the files.. SearchOption opt = ((_options.PathRecursive) ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); List <string> files = Directory.EnumerateFiles(_options.ImportPath, _options.ImportFilter, opt).ToList(); //Loop through each file and upload it. ConsoleLog(LogLevelEnum.Verbose, "Files:"); foreach (string file in files) { string conLog = Path.GetFileName(file); try { //Get the file details.. FileInfo fileInf = new FileInfo(file); string size = ""; if (fileInf.Length >= 1000000000) { size = (fileInf.Length / 1000000000).ToString() + "Gb"; } else if (fileInf.Length >= 1000000) { size = (fileInf.Length / 1000000).ToString() + "Mb"; } else if (fileInf.Length >= 1000) { size = (fileInf.Length / 1000).ToString() + "Kb"; } else { size = fileInf.Length.ToString() + "b"; } conLog += " (" + size + ")"; //Generate the RemoteDocument object. RemoteDocument newFile = new RemoteDocument(); newFile.AttachmentTypeId = 1; newFile.FilenameOrUrl = Path.GetFileName(file); newFile.Size = (int)fileInf.Length; newFile.UploadDate = DateTime.UtcNow; //Now we see if have mapped artifact ArtifactMapping mappedArtifact = artifactMappings.FirstOrDefault(m => m.Filename.ToLowerInvariant() == newFile.FilenameOrUrl.ToLowerInvariant()); if (mappedArtifact != null && !String.IsNullOrEmpty(customPropertyField)) { //We have to lookup the artifact, currently only incidents are supported if (mappedArtifact.ArtifactTypeId == 3) { //Retrieve the incident RemoteSort sort = new RemoteSort(); sort.PropertyName = "IncidentId"; sort.SortAscending = true; List <RemoteFilter> filters = new List <RemoteFilter>(); RemoteFilter filter = new RemoteFilter(); filter.PropertyName = customPropertyField; filter.StringValue = mappedArtifact.ExternalKey; filters.Add(filter); RemoteIncident remoteIncident = client.Incident_Retrieve(filters, sort, 1, 1).FirstOrDefault(); if (remoteIncident != null) { RemoteLinkedArtifact remoteLinkedArtifact = new SpiraService.RemoteLinkedArtifact(); remoteLinkedArtifact.ArtifactTypeId = mappedArtifact.ArtifactTypeId; remoteLinkedArtifact.ArtifactId = remoteIncident.IncidentId.Value; newFile.AttachedArtifacts = new List <RemoteLinkedArtifact>(); newFile.AttachedArtifacts.Add(remoteLinkedArtifact); } } else { ConsoleLog(LogLevelEnum.Normal, "Warning: Only incident mapped artifacts currently supported, so ignoring the mapped artifacts of type: " + mappedArtifact.ArtifactTypeId); } } //Read the file contents. (Into memory! Beware, large files!) byte[] fileContents = null; fileContents = File.ReadAllBytes(file); ConsoleLog(LogLevelEnum.Verbose, conLog); if (fileContents != null && fileContents.Length > 1) { newFile.AttachmentId = client.Document_AddFile(newFile, fileContents).AttachmentId.Value; } else { throw new FileEmptyException(); } } catch (Exception ex) { conLog += " - Error. (" + ex.GetType().ToString() + ")"; ConsoleLog(LogLevelEnum.Normal, conLog); } } } else { ConsoleLog(LogLevelEnum.Normal, "Cannot connect to project. Verify your Project Role."); Environment.Exit(-1); } } else { ConsoleLog(LogLevelEnum.Normal, "Cannot connect to project. Project #" + _options.SpiraProject.ToString() + " does not exist."); Environment.Exit(-1); } } else { ConsoleLog(LogLevelEnum.Normal, "Cannot log in. Check username and password."); Environment.Exit(-1); } } else { Environment.Exit(-1); } }
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."); } }
/// <summary>Copies over our values from the form into an Incident object.</summary> /// <returns>A new RemoteIncident, or Null if error.</returns> private RemoteIncident save_GetFromFields() { const string METHOD = CLASS + "save_GetFromFields()"; RemoteIncident retIncident = null; try { retIncident = new RemoteIncident(); //*Fixed fields.. retIncident.IncidentId = this._Incident.IncidentId; retIncident.ProjectId = this._Incident.ProjectId; if (this._Incident.CreationDate.HasValue) { retIncident.CreationDate = this._Incident.CreationDate.Value.ToUniversalTime(); } retIncident.LastUpdateDate = this._Incident.LastUpdateDate.ToUniversalTime(); retIncident.ConcurrencyDate = this._Incident.ConcurrencyDate; //*Standard fields.. retIncident.Name = this.cntrlIncidentName.Text.Trim(); retIncident.IncidentTypeId = ((RemoteIncidentType)this.cntrlType.SelectedItem).IncidentTypeId; retIncident.IncidentStatusId = ((this._IncSelectedStatus.HasValue) ? this._IncSelectedStatus.Value : this._IncCurrentStatus.Value); retIncident.OpenerId = ((RemoteUser)this.cntrlDetectedBy.SelectedItem).UserId; retIncident.OwnerId = ((RemoteUser)this.cntrlOwnedBy.SelectedItem).UserId; retIncident.PriorityId = ((RemoteIncidentPriority)this.cntrlPriority.SelectedItem).PriorityId; retIncident.SeverityId = ((RemoteIncidentSeverity)this.cntrlSeverity.SelectedItem).SeverityId; retIncident.DetectedReleaseId = ((RemoteRelease)this.cntrlDetectedIn.SelectedItem).ReleaseId; retIncident.ResolvedReleaseId = ((RemoteRelease)this.cntrlResolvedIn.SelectedItem).ReleaseId; retIncident.VerifiedReleaseId = ((RemoteRelease)this.cntrlVerifiedIn.SelectedItem).ReleaseId; if (this._isDescChanged) { retIncident.Description = this.cntrlDescription.HTMLText; } else { retIncident.Description = this._Incident.Description; } //*Schedule fields.. if (this.cntrlStartDate.SelectedDate.HasValue) { retIncident.StartDate = this.cntrlStartDate.SelectedDate.Value.ToUniversalTime(); } if (this.cntrlEndDate.SelectedDate.HasValue) { retIncident.ClosedDate = this.cntrlEndDate.SelectedDate.Value.ToUniversalTime(); } retIncident.EstimatedEffort = StaticFuncs.GetMinutesFromValues(this.cntrlEstEffortH.Text, this.cntrlEstEffortM.Text); retIncident.ActualEffort = StaticFuncs.GetMinutesFromValues(this.cntrlActEffortH.Text, this.cntrlActEffortM.Text); retIncident.RemainingEffort = StaticFuncs.GetMinutesFromValues(this.cntrlRemEffortH.Text, this.cntrlRemEffortM.Text); //Custom Properties retIncident.CustomProperties = this.cntCustomProps.GetCustomProperties(); } catch (Exception ex) { Logger.LogMessage(ex, METHOD); MessageBox.Show(StaticFuncs.getCultureResource.GetString("app_General_UnexpectedError"), StaticFuncs.getCultureResource.GetString("app_General_ApplicationShortName"), MessageBoxButton.OK, MessageBoxImage.Error); retIncident = null; } //Return return(retIncident); }
/// <summary>Hit when we're finished connecting to the project.</summary> /// <param name="sender">ImportExportClient</param> /// <param name="e">Connection_ConnectToProjectCompletedEventArgs</param> private void clientSave_Connection_ConnectToProjectCompleted(object sender, Connection_ConnectToProjectCompletedEventArgs e) { try { const string METHOD = CLASS + "clientSave_Connection_ConnectToProjectCompleted()"; Logger.LogTrace(METHOD + " Enter: " + this._clientNumSaving.ToString() + " running."); ImportExportClient client = (sender as ImportExportClient); this._clientNumSaving--; this.barSavingIncident.Value++; if (!e.Cancelled) { if (e.Error == null) { if (e.Result) { //Get the new RemoteIncident RemoteIncident newIncident = this.save_GetFromFields(); if (newIncident != null) { //Fire off our update calls. this._clientNumSaving++; client.Incident_UpdateAsync(newIncident, this._clientNum++); } else { //TODO: Show Error. //Cancel calls. this._clientNumSaving++; client.Connection_DisconnectAsync(this._clientNum++); } } else { //TODO: Show Error. //Cancel calls. this._clientNumSaving++; client.Connection_DisconnectAsync(this._clientNum++); } } else { //TODO: Show Error. //Cancel calls. this._clientNumSaving++; client.Connection_DisconnectAsync(this._clientNum++); } } //See if it's okay to reload. this.save_CheckIfOkayToLoad(); Logger.LogTrace(METHOD + " Exit: " + this._clientNumSaving.ToString() + " left."); } catch (Exception ex) { Logger.LogMessage(ex, "clientSave_Connection_ConnectToProjectCompleted()"); MessageBox.Show(StaticFuncs.getCultureResource.GetString("app_General_UnexpectedError"), StaticFuncs.getCultureResource.GetString("app_General_ApplicationShortName"), MessageBoxButton.OK, MessageBoxImage.Error); } }
/// <summary>Hit when the user clicks to save the incident.</summary> /// <param name="sender">The save button.</param> /// <param name="e">Event Args</param> private void _cntrlSave_Click(object sender, RoutedEventArgs e) { try { if (this._isFieldChanged) { string badFields = ""; if (this.workflow_CheckFieldValues(out badFields)) { this.isInSaveMode = true; // Update the form to show we're saving. this.panelNone.Visibility = Visibility.Collapsed; this.panelWarning.Visibility = Visibility.Visible; this.msgWrnMessage.Text = "Saving incident..."; this.panelContents.IsEnabled = false; RemoteIncident newIncident = new RemoteIncident(); //Copy over our base fields.. newIncident.Name = this.cntrlIncidentName.Text; newIncident.IncidentTypeId = ((RemoteIncidentType)this.cntrlType.SelectedItem).IncidentTypeId.Value; if (this.cntrlStatus.SelectedItem.GetType() == typeof(RemoteWorkflowIncidentTransition)) { newIncident.IncidentStatusId = ((RemoteWorkflowIncidentTransition)this.cntrlStatus.SelectedItem).IncidentStatusID_Output; } else if (this.cntrlStatus.SelectedItem.GetType() == typeof(RemoteIncidentStatus)) { newIncident.IncidentStatusId = ((RemoteIncidentStatus)this.cntrlStatus.SelectedItem).IncidentStatusId.Value; } newIncident.OpenerId = ((this.cntrlDetectedBy.SelectedItem.GetType() == typeof(RemoteUser)) ? ((RemoteUser)this.cntrlDetectedBy.SelectedItem).UserId.Value : -1); newIncident.OwnerId = ((this.cntrlOwnedBy.SelectedItem.GetType() == typeof(RemoteUser)) ? ((RemoteUser)this.cntrlOwnedBy.SelectedItem).UserId.Value : new int?()); newIncident.PriorityId = ((this.cntrlPriority.SelectedItem.GetType() == typeof(RemoteIncidentPriority)) ? ((RemoteIncidentPriority)this.cntrlPriority.SelectedItem).PriorityId : new int?()); newIncident.SeverityId = ((this.cntrlSeverity.SelectedItem.GetType() == typeof(RemoteIncidentSeverity)) ? ((RemoteIncidentSeverity)this.cntrlSeverity.SelectedItem).SeverityId : new int?()); newIncident.DetectedReleaseId = ((this.cntrlDetectedIn.SelectedItem.GetType() == typeof(RemoteRelease)) ? ((RemoteRelease)this.cntrlDetectedIn.SelectedItem).ReleaseId : new int?()); newIncident.ResolvedReleaseId = ((this.cntrlResolvedIn.SelectedItem.GetType() == typeof(RemoteRelease)) ? ((RemoteRelease)this.cntrlResolvedIn.SelectedItem).ReleaseId : new int?()); newIncident.VerifiedReleaseId = ((this.cntrlVerifiedIn.SelectedItem.GetType() == typeof(RemoteRelease)) ? ((RemoteRelease)this.cntrlVerifiedIn.SelectedItem).ReleaseId : new int?()); if (this._isDescChanged) { newIncident.Description = this.cntrlDescription.HTMLText; } else { newIncident.Description = this._Incident.Description; } //Now custom properties. //TODO: Custom Property fields. (Needs API update.) newIncident.List01 = this._Incident.List01; newIncident.List02 = this._Incident.List02; newIncident.List03 = this._Incident.List03; newIncident.List04 = this._Incident.List04; newIncident.List05 = this._Incident.List05; newIncident.List06 = this._Incident.List06; newIncident.List07 = this._Incident.List07; newIncident.List08 = this._Incident.List08; newIncident.List09 = this._Incident.List09; newIncident.List10 = this._Incident.List10; newIncident.Text01 = this._Incident.Text01; newIncident.Text02 = this._Incident.Text02; newIncident.Text03 = this._Incident.Text03; newIncident.Text04 = this._Incident.Text04; newIncident.Text05 = this._Incident.Text05; newIncident.Text06 = this._Incident.Text06; newIncident.Text07 = this._Incident.Text07; newIncident.Text08 = this._Incident.Text08; newIncident.Text09 = this._Incident.Text09; newIncident.Text10 = this._Incident.Text10; //Schedule fields. newIncident.StartDate = this.cntrlStartDate.SelectedDate; newIncident.ClosedDate = this.cntrlEndDate.SelectedDate; newIncident.CompletionPercent = ((string.IsNullOrEmpty(this.cntrlPerComplete.Text)) ? 0 : int.Parse(this.cntrlPerComplete.Text)); int?EstH = ((string.IsNullOrEmpty(this.cntrlEstEffortH.Text)) ? new int?() : int.Parse(this.cntrlEstEffortH.Text)); int?EstM = ((string.IsNullOrEmpty(this.cntrlEstEffortM.Text)) ? new int?() : int.Parse(this.cntrlEstEffortM.Text)); newIncident.EstimatedEffort = ((!EstH.HasValue && !EstM.HasValue) ? new int?() : (((!EstH.HasValue) ? 0 : EstH.Value * 60) + ((!EstM.HasValue) ? 0 : EstM.Value))); int?ActH = ((string.IsNullOrEmpty(this.cntrlActEffortH.Text)) ? new int?() : int.Parse(this.cntrlActEffortH.Text)); int?ActM = ((string.IsNullOrEmpty(this.cntrlActEffortM.Text)) ? new int?() : int.Parse(this.cntrlActEffortM.Text)); newIncident.ActualEffort = ((!ActH.HasValue && !ActM.HasValue) ? new int?() : (((!ActH.HasValue) ? 0 : ActH.Value * 60) + ((!ActM.HasValue) ? 0 : ActM.Value))); //Now the set fields. newIncident.IncidentId = this._Incident.IncidentId; newIncident.LastUpdateDate = this._Incident.LastUpdateDate; newIncident.CreationDate = this._Incident.CreationDate; newIncident.ProjectId = this._Incident.ProjectId; //Add a resoution. RemoteIncidentResolution newRes = new RemoteIncidentResolution(); if (this._isResChanged) { newRes.CreationDate = DateTime.Now; newRes.CreatorId = this._Project.UserID; newRes.IncidentId = newIncident.IncidentId.Value; newRes.Resolution = this.cntrlResolution.HTMLText; } this._NumRunning++; this._Client.Incident_UpdateAsync(newIncident, this._NumAsync++); if (this._isResChanged) { this._NumRunning++; this._Client.Incident_AddResolutionsAsync(new RemoteIncidentResolution[] { newRes }, this._NumAsync++); } } else { string errMsg = ""; if (badFields.Split(';').Length > 3) { errMsg = "You must fill out all required fields before saving."; } else { errMsg = "The "; foreach (string fieldName in badFields.Split(';')) { errMsg += fieldName + ", "; } errMsg = errMsg.Trim(' ').Trim(','); errMsg += " field" + ((badFields.Split(';').Length > 1) ? "s" : ""); errMsg += " " + ((badFields.Split(';').Length > 1) ? "are" : "is"); errMsg += " required before saving."; } this.msgErrMessage.Text = errMsg; this.panelError.Visibility = Visibility.Visible; this.panelWarning.Visibility = Visibility.Collapsed; this.panelInfo.Visibility = Visibility.Collapsed; this.panelNone.Visibility = Visibility.Collapsed; } } } catch (Exception ex) { Connect.logEventMessage("wpfDetailsIncident::_cntrlSave_Click", ex, System.Diagnostics.EventLogEntryType.Error); } }