/// <summary> /// This method gives a fully qualified url and can be used without an HTTPContext /// </summary> /// <param name="partialUrl">The partial url to fully qualify eg /images/calendar-icon.png</param> /// <returns>The fully qualified web site url</returns> public static string GetFullyQualifiedSiteUrl(string partialUrl) { WorkflowSettingsPoco settings = SettingsService.GetSettings(); string editUrl = settings.EditUrl; HttpRequest request = HttpContext.Current.Request; if (editUrl.HasNoValue()) { if (request.ApplicationPath != null) { editUrl = request.Url.Scheme + "://" + request.Url.Authority + request.ApplicationPath.TrimEnd('/') + "/"; } } if (editUrl.HasNoValue()) { return(string.Empty); } bool valid = Uri.TryCreate(editUrl, UriKind.Absolute, out Uri uriResult) && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps); // if result is false, the settings value has no scheme, so prepend from the current request, or fallback to https if (!valid) { editUrl = (request.ApplicationPath.HasValue() ? request.Url.Scheme : Uri.UriSchemeHttps) + "://" + editUrl; } var baseUrl = new Uri(editUrl); return((new Uri(baseUrl, partialUrl)).ToString()); }
public void Returns_New_Settings_If_No_Settings_Exist() { WorkflowSettingsPoco settings = _repo.GetSettings(); // email will be populated automatically Assert.NotNull(settings); Assert.NotNull(settings.Email); }
public void Returns_Settings_If_Settings_Exist() { Scaffold.Config(); WorkflowSettingsPoco settings = _repo.GetSettings(); Assert.NotNull(settings); Assert.Equal("12", settings.DefaultApprover); }
public Emailer() { _settings = new SettingsService().GetSettings(); _tasksService = new TasksService(); _groupService = new GroupService(); _utility = new Utility(); }
public void Can_Get_Settings() { WorkflowSettingsPoco settings = _service.GetSettings(); Assert.NotNull(settings); Assert.False(settings.LockIfActive); Assert.Equal(1, settings.Id); Assert.Equal(0, settings.FlowType); }
protected WorkflowProcess() { _configService = new ConfigService(); _groupService = new GroupService(); _instancesService = new InstancesService(); _settingsService = new SettingsService(); _tasksService = new TasksService(); _settings = _settingsService.GetSettings(); }
protected WorkflowProcess() { _configService = new ConfigService(); _groupService = new GroupService(); _instancesService = new InstancesService(); _settingsService = new SettingsService(); _tasksService = new TasksService(); _notifications = new Notifications(); _utility = new Utility(); _settings = _settingsService.GetSettings(); }
public IHttpActionResult Save(WorkflowSettingsPoco model) { try { DatabaseContext.Database.Update(model); return(Ok("Settings updated")); } catch (Exception ex) { return(Content(HttpStatusCode.InternalServerError, ViewHelpers.ApiException(ex))); } }
protected WorkflowProcess() { _configService = new ConfigService(); _groupService = new GroupService(); _instancesService = new InstancesService(); _settingsService = new SettingsService(); _tasksService = new TasksService(); _emailer = new Emailer(); _utility = new Utility(); _settings = _settingsService.GetSettings(); }
public void Can_Update_Settings() { WorkflowSettingsPoco settings = _repo.GetSettings(); string defaultApprover = settings.DefaultApprover; settings.DefaultApprover = "15"; _repo.UpdateSettings(settings); settings = _repo.GetSettings(); Assert.NotEqual(defaultApprover, settings.DefaultApprover); }
public IHttpActionResult Save(WorkflowSettingsPoco model) { try { DatabaseContext.Database.Update(model); return(Ok("Settings updated")); } catch (Exception ex) { const string error = "Could not save settings"; Log.Error(error, ex); return(Content(HttpStatusCode.InternalServerError, ViewHelpers.ApiException(ex, error))); } }
public IHttpActionResult Save(WorkflowSettingsPoco model) { try { _settingsService.UpdateSettings(model); return(Ok(Constants.SettingsUpdated)); } catch (Exception ex) { const string error = Constants.SettingsNotUpdated; Log.Error(error, ex); return(Content(HttpStatusCode.InternalServerError, ViewHelpers.ApiException(ex, error))); } }
public IHttpActionResult GetNodePendingTasks(int id) { // id will be 0 when creating a new page - id is assigned after save if (id == 0) { return(Json(new { settings = false, noFlow = false }, ViewHelpers.CamelCase)); } try { WorkflowSettingsPoco settings = _settingsService.GetSettings(); bool hasFlow = _configService.HasFlow(id); if (null == settings || !hasFlow) { return(Json(new { settings = settings == null, noFlow = !hasFlow }, ViewHelpers.CamelCase)); } List <WorkflowTaskInstancePoco> taskInstances = _tasksService.GetTasksByNodeId(id); if (!taskInstances.Any() || taskInstances.Last().TaskStatus == TaskStatus.Cancelled) { return(Json(new { total = 0 }, ViewHelpers.CamelCase)); } taskInstances = taskInstances.Where(t => t.TaskStatus.In(TaskStatus.PendingApproval, TaskStatus.Rejected)).ToList(); return(Json(new { items = taskInstances.Any() ? _tasksService.ConvertToWorkflowTaskList(taskInstances) : new List <WorkflowTask>(), total = taskInstances.Count }, ViewHelpers.CamelCase)); } catch (Exception ex) { string msg = $"Error getting pending tasks for node {id}"; Log.Error(msg, ex); return(Content(HttpStatusCode.InternalServerError, ViewHelpers.ApiException(ex, msg))); } }
public void Can_Update_Settings() { WorkflowSettingsPoco settings = _service.GetSettings(); const string editUrl = "not.a.real.domain.com"; settings.FlowType = 1; settings.SendNotifications = !settings.SendNotifications; settings.EditUrl = editUrl; _service.UpdateSettings(settings); settings = _service.GetSettings(); Assert.Equal(editUrl, settings.EditUrl); Assert.Equal(1, settings.FlowType); }
public async void Can_Update_Settings() { var model = new WorkflowSettingsPoco { DefaultApprover = "12", EditUrl = "some.url.com", Email = "*****@*****.**", ExcludeNodes = "", FlowType = 1, SendNotifications = true, SiteUrl = "site.url.com" }; string result = await _settingsController.Save(model).GetContent(); Assert.Equal(MagicStrings.SettingsUpdated, result); }
/// <summary> /// Get the current workflow settings, or persist an empty instance if none exist /// </summary> /// <returns>A object of type <see cref="WorkflowSettingsPoco"/> representing the current settings</returns> public WorkflowSettingsPoco GetSettings() { var wsp = new WorkflowSettingsPoco(); List <WorkflowSettingsPoco> settings = _database.Fetch <WorkflowSettingsPoco>(SqlQueries.GetSettings); if (settings.Any()) { wsp = settings.First(); } else { _database.Insert(wsp); } if (string.IsNullOrEmpty(wsp.Email)) { wsp.Email = UmbracoConfig.For.UmbracoSettings().Content.NotificationEmailAddress; } return(wsp); }
public IHttpActionResult GetNodePendingTasks(int id) { try { WorkflowSettingsPoco settings = Pr.GetSettings(); bool hasFlow = Pr.HasFlow(id); if (null == settings || !hasFlow) { return(Json(new { settings = settings == null, noFlow = !hasFlow }, ViewHelpers.CamelCase)); } List <WorkflowTaskInstancePoco> taskInstances = Pr.TasksByNode(id); if (taskInstances.Last().TaskStatus == TaskStatus.Cancelled) { return(Json(new { total = 0 }, ViewHelpers.CamelCase)); } taskInstances = taskInstances.Where(t => t.TaskStatus.In(TaskStatus.PendingApproval, TaskStatus.Rejected)).ToList(); return(Json(new { items = taskInstances.Any() ? taskInstances.ToWorkflowTaskList() : new List <WorkflowTask>(), total = taskInstances.Count }, ViewHelpers.CamelCase)); } catch (Exception ex) { string msg = $"Error getting pending tasks for node {id}"; Log.Error(msg, ex); return(Content(HttpStatusCode.InternalServerError, ViewHelpers.ApiException(ex, msg))); } }
/// <summary> /// /// </summary> /// <returns></returns> public WorkflowSettingsPoco GetSettings() { var wsp = new WorkflowSettingsPoco(); var db = GetDb(); var settings = db.Fetch <WorkflowSettingsPoco>("SELECT * FROM WorkflowSettings"); if (settings.Any()) { wsp = settings.First(); } else { db.Insert(wsp); } if (string.IsNullOrEmpty(wsp.Email)) { wsp.Email = UmbracoConfig.For.UmbracoSettings().Content.NotificationEmailAddress; } return(wsp); }
/// <summary> /// This method gives a fully qualified Back Office Edit url /// </summary> /// <param name="partialUrl">The partial url to fully qualify</param> /// <returns>The fully qualified Back Office edit url</returns> private static string GetFullyQualifiedEditUrl(string partialUrl) { WorkflowSettingsPoco settings = SettingsService.GetSettings(); string editUrl = settings.EditUrl; if (editUrl.HasNoValue()) { HttpRequest request = HttpContext.Current.Request; if (request.ApplicationPath != null) { editUrl = request.Url.Scheme + "://" + request.Url.Authority + request.ApplicationPath.TrimEnd('/') + "/"; } } if (editUrl.HasNoValue()) { return(string.Empty); } var baseUrl = new Uri(editUrl.StartsWith("http") ? editUrl : $"http://{editUrl}"); return(new Uri(baseUrl, partialUrl).ToString()); }
/// <summary> /// Update the workflow settings /// </summary> /// <param name="settings"></param> /// <returns></returns> public void UpdateSettings(WorkflowSettingsPoco settings) { _repo.UpdateSettings(settings); }
public void UpdateSettings(WorkflowSettingsPoco settings) { _database.Update(settings); }
/// <summary> /// Sends an email notification out for the workflow process /// </summary> /// <param name="instance"></param> /// <param name="emailType">the type of email to be sent</param> public async void Send(WorkflowInstancePoco instance, EmailType emailType) { WorkflowSettingsPoco settings = _settingsService.GetSettings(); if (!settings.SendNotifications) { return; } if (!instance.TaskInstances.Any()) { instance.TaskInstances = _tasksService.GetTasksWithGroupByInstanceGuid(instance.Guid); } if (!instance.TaskInstances.Any()) { Log.Error($"Notifications not sent - no tasks exist for instance { instance.Id }"); return; } WorkflowTaskPoco finalTask = null; try { string docTitle = instance.Node.Name; string docUrl = UrlHelpers.GetFullyQualifiedContentEditorUrl(instance.NodeId); WorkflowTaskPoco[] flowTasks = instance.TaskInstances.OrderBy(t => t.ApprovalStep).ToArray(); // always take get the emails for all previous users, sometimes they will be discarded later // easier to just grab em all, rather than doing so conditionally List <string> emailsForAllTaskUsers = new List <string>(); // in the loop, also store the last task to a variable, and keep the populated group var taskIndex = 0; int taskCount = flowTasks.Length; foreach (WorkflowTaskPoco task in flowTasks) { taskIndex += 1; UserGroupPoco group = await _groupService.GetPopulatedUserGroupAsync(task.GroupId); if (group == null) { continue; } emailsForAllTaskUsers.AddRange(group.PreferredEmailAddresses()); if (taskIndex != taskCount) { continue; } finalTask = task; finalTask.UserGroup = group; } if (finalTask == null) { Log.Error("No valid task found for email notifications"); return; } List <string> to = new List <string>(); var body = ""; string typeDescription = instance.WorkflowType.Description(instance.ScheduledDate); string typeDescriptionPast = instance.WorkflowType.DescriptionPastTense(instance.ScheduledDate); switch (emailType) { case EmailType.ApprovalRequest: to = finalTask.UserGroup.PreferredEmailAddresses(); body = string.Format(EmailApprovalRequestString, to.Count > 1 ? "Umbraco user" : finalTask.UserGroup.Name, docUrl, docTitle, instance.AuthorComment, instance.AuthorUser.Name, typeDescription, string.Empty); break; case EmailType.ApprovalRejection: to = emailsForAllTaskUsers; to.Add(instance.AuthorUser.Email); body = string.Format(EmailRejectedString, "Umbraco user", docUrl, docTitle, finalTask.Comment, finalTask.ActionedByUser.Name, typeDescription.ToLower()); break; case EmailType.ApprovedAndCompleted: to = emailsForAllTaskUsers; to.Add(instance.AuthorUser.Email); //Notify web admins to.Add(settings.Email); if (instance.WorkflowType == WorkflowType.Publish) { IPublishedContent n = _utility.GetPublishedContent(instance.NodeId); docUrl = UrlHelpers.GetFullyQualifiedSiteUrl(n.Url); } body = string.Format(EmailApprovedString, "Umbraco user", docUrl, docTitle, typeDescriptionPast.ToLower()) + "<br/>"; body += instance.BuildProcessSummary(); break; case EmailType.ApprovedAndCompletedForScheduler: to = emailsForAllTaskUsers; to.Add(instance.AuthorUser.Email); body = string.Format(EmailApprovedString, "Umbraco user", docUrl, docTitle, typeDescriptionPast.ToLower()) + "<br/>"; body += instance.BuildProcessSummary(); break; case EmailType.WorkflowCancelled: to = emailsForAllTaskUsers; // include the initiator email to.Add(instance.AuthorUser.Email); body = string.Format(EmailCancelledString, "Umbraco user", typeDescription, docUrl, docTitle, finalTask.ActionedByUser.Name, finalTask.Comment); break; case EmailType.SchedulerActionCancelled: break; default: throw new ArgumentOutOfRangeException(nameof(emailType), emailType, null); } if (!to.Any()) { return; } var client = new SmtpClient(); var msg = new MailMessage { Subject = $"{emailType.ToString().ToTitleCase()} - {instance.Node.Name} ({typeDescription})", IsBodyHtml = true, }; if (settings.Email.HasValue()) { msg.From = new MailAddress(settings.Email); } // if offline is permitted, email group members individually as we need the user id in the url if (emailType == EmailType.ApprovalRequest && finalTask.UserGroup.OfflineApproval) { foreach (User2UserGroupPoco user in finalTask.UserGroup.Users) { string offlineString = string.Format(EmailOfflineApprovalString, settings.SiteUrl, instance.NodeId, user.UserId, finalTask.Id, instance.Guid); body = string.Format(EmailApprovalRequestString, user.User.Name, docUrl, docTitle, instance.AuthorComment, instance.AuthorUser.Name, typeDescription, offlineString); msg.To.Clear(); msg.To.Add(user.User.Email); msg.Body = string.Format(EmailBody, msg.Subject, body); client.Send(msg); } } else { msg.To.Add(string.Join(",", to.Distinct())); msg.Body = string.Format(EmailBody, msg.Subject, body); client.Send(msg); } Log.Info($"Email notifications sent for task { finalTask.Id }, to { msg.To }"); } catch (Exception e) { Log.Error($"Error sending notifications for task { finalTask.Id }", e); } }
public SettingsEventArgs(WorkflowSettingsPoco settings) { Settings = settings; }
/// <summary> /// Get the workflow settings /// </summary> /// <returns></returns> public WorkflowSettingsPoco GetSettings() { WorkflowSettingsPoco settings = _repo.GetSettings(); return(settings); }
/// <summary> /// Update the workflow settings /// </summary> /// <param name="settings"></param> /// <returns></returns> public void UpdateSettings(WorkflowSettingsPoco settings) { _repo.UpdateSettings(settings); Updated?.Invoke(this, new SettingsEventArgs(settings)); }
/// <summary> /// Sends an email notification out for the workflow process /// </summary> /// <param name="instance"></param> /// <param name="emailType">the type of email to be sent</param> public async void Send(WorkflowInstancePoco instance, EmailType emailType) { WorkflowSettingsPoco settings = _settingsService.GetSettings(); bool?doSend = settings.SendNotifications; if (doSend != true) { return; } if (!instance.TaskInstances.Any()) { instance.TaskInstances = _tasksService.GetTasksWithGroupByInstanceGuid(instance.Guid); } try { string docTitle = instance.Node.Name; string docUrl = UrlHelpers.GetFullyQualifiedContentEditorUrl(instance.NodeId); IOrderedEnumerable <WorkflowTaskInstancePoco> flowTasks = instance.TaskInstances.OrderBy(t => t.ApprovalStep); // always take get the emails for all previous users, sometimes they will be discarded later // easier to just grab em all, rather than doing so conditionally List <string> emailsForAllTaskUsers = new List <string>(); // in the loop, also store the last task to a variable, and keep the populated group var taskIndex = 0; int taskCount = flowTasks.Count(); WorkflowTaskInstancePoco finalTask = null; foreach (WorkflowTaskInstancePoco task in flowTasks) { taskIndex += 1; UserGroupPoco group = await _groupService.GetPopulatedUserGroupAsync(task.GroupId); if (group == null) { continue; } emailsForAllTaskUsers.AddRange(group.PreferredEmailAddresses()); if (taskIndex != taskCount) { continue; } finalTask = task; finalTask.UserGroup = group; } List <string> to = new List <string>(); string systemEmailAddress = settings.Email; var body = ""; switch (emailType) { case EmailType.ApprovalRequest: to = finalTask.UserGroup.PreferredEmailAddresses(); body = string.Format(EmailApprovalRequestString, to.Count > 1 ? "Umbraco user" : finalTask.UserGroup.Name, docUrl, docTitle, instance.AuthorComment, instance.AuthorUser.Name, instance.TypeDescription); break; case EmailType.ApprovalRejection: to = emailsForAllTaskUsers; to.Add(instance.AuthorUser.Email); body = string.Format(EmailRejectedString, "Umbraco user", docUrl, docTitle, finalTask.Comment, finalTask.ActionedByUser.Name, instance.TypeDescription.ToLower()); break; case EmailType.ApprovedAndCompleted: to = emailsForAllTaskUsers; to.Add(instance.AuthorUser.Email); //Notify web admins to.Add(systemEmailAddress); if (instance.WorkflowType == WorkflowType.Publish) { IPublishedContent n = _utility.GetPublishedContent(instance.NodeId); docUrl = UrlHelpers.GetFullyQualifiedSiteUrl(n.Url); } body = string.Format(EmailApprovedString, "Umbraco user", docUrl, docTitle, instance.TypeDescriptionPastTense.ToLower()) + "<br/>"; body += BuildProcessSummary(instance); break; case EmailType.ApprovedAndCompletedForScheduler: to = emailsForAllTaskUsers; to.Add(instance.AuthorUser.Email); body = string.Format(EmailApprovedString, "Umbraco user", docUrl, docTitle, instance.TypeDescriptionPastTense.ToLower()) + "<br/>"; body += BuildProcessSummary(instance); break; case EmailType.WorkflowCancelled: to = emailsForAllTaskUsers; // include the initiator email to.Add(instance.AuthorUser.Email); body = string.Format(EmailCancelledString, "Umbraco user", instance.TypeDescription, docUrl, docTitle, finalTask.ActionedByUser.Name, finalTask.Comment); break; case EmailType.SchedulerActionCancelled: break; default: throw new ArgumentOutOfRangeException(nameof(emailType), emailType, null); } if (!to.Any()) { return; } var client = new SmtpClient(); var msg = new MailMessage(); if (!string.IsNullOrEmpty(systemEmailAddress)) { msg.From = new MailAddress(systemEmailAddress); } string subject = BuildEmailSubject(emailType, instance); msg.To.Add(string.Join(",", to.Distinct())); msg.Subject = subject; msg.Body = $"<!DOCTYPE HTML SYSTEM><html><head><title>{subject}</title></head><body><font face=\"verdana\" size=\"2\">{body}</font></body></html>"; msg.IsBodyHtml = true; client.Send(msg); } catch (Exception e) { Log.Error("Error sending notifications", e); } }