Exemplo n.º 1
0
        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
            {
                WorkflowTaskInstancePoco currentTask = _tasksService.GetTasksByNodeId(id).FirstOrDefault();

                return(Json(new
                {
                    items = currentTask != null && currentTask.TaskStatus.In(TaskStatus.PendingApproval, TaskStatus.Rejected) ?
                            _tasksService.ConvertToWorkflowTaskList(new List <WorkflowTaskInstancePoco> {
                        currentTask
                    }) :
                            new List <WorkflowTask>()
                }, ViewHelpers.CamelCase));
            }
            catch (Exception ex)
            {
                string msg = MagicStrings.ErrorGettingPendingTasksForNode.Replace("{id}", id.ToString());
                Log.Error(msg, ex);
                return(Content(HttpStatusCode.InternalServerError, ViewHelpers.ApiException(ex, msg)));
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Maps Users to the UserGroup property of a WorkflowTaskInstance
        /// </summary>
        /// <param name="wtip"></param>
        /// <param name="wip"></param>
        /// <param name="ugp"></param>
        /// <returns></returns>
        public WorkflowInstancePoco MapIt(WorkflowInstancePoco wip, WorkflowTaskInstancePoco wtip, UserGroupPoco ugp)
        {
            if (wip == null)
            {
                return(Current);
            }

            if (ugp.GroupId == wtip.GroupId)
            {
                wtip.UserGroup = ugp;
            }

            if (Current != null && Current.Guid == wip.Guid)
            {
                if (Current.TaskInstances.All(t => t.ApprovalStep != wtip.ApprovalStep))
                {
                    Current.TaskInstances.Add(wtip);
                }
                return(null);
            }

            var prev = Current;

            Current = wip;
            Current.TaskInstances.Add(wtip);

            return(prev);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Create simple html markup for an inactive workflow task.
        /// </summary>
        /// <param name="taskInstance">The task instance.</param>
        /// <param name="index"></param>
        /// <returns>HTML markup describing an active task instance.</returns>
        private static string BuildTaskSummary(WorkflowTaskInstancePoco taskInstance, int index)
        {
            var result = "";

            switch (taskInstance.Status)
            {
            case (int)TaskStatus.Approved:
            case (int)TaskStatus.Rejected:
            case (int)TaskStatus.Cancelled:

                if (taskInstance.CompletedDate != null)
                {
                    result += $"Stage {index}: {taskInstance.StatusName} by {taskInstance.ActionedByUser.Name} on {taskInstance.CompletedDate.Value.ToString("dd/MM/yy")}";
                }

                if (!string.IsNullOrEmpty(taskInstance.Comment))
                {
                    result += $"<br/>&nbsp;&nbsp;Comment: <i>{taskInstance.Comment}</i>";
                }

                break;

            case (int)TaskStatus.NotRequired:

                result += $"Stage {index}: Not required";

                break;
            }

            return(result);
        }
Exemplo n.º 4
0
        public async void Cannot_Validate_Request_When_Last_Task_Not_Pending()
        {
            Scaffold.Config();

            Guid guid = Guid.NewGuid();

            WorkflowTaskInstancePoco task = Scaffold.Task(guid, status: (int)TaskStatus.NotRequired);

            _tasksService.InsertTask(task);
            _instancesService.InsertInstance(Scaffold.Instance(guid, 1, 1089));

            bool isValid = await _previewService.Validate(1089, 0, task.Id, guid);

            Assert.False(isValid);
        }
Exemplo n.º 5
0
        public void Can_Insert_Task_And_Raise_Event()
        {
            TasksService.Created += (sender, args) =>
            {
                Assert.NotNull(args);
                Assert.IsAssignableFrom <WorkflowTaskInstancePoco>(args.Task);
            };

            int count = _service.CountPendingTasks();

            WorkflowTaskInstancePoco task = Scaffold.Task();

            _service.InsertTask(Scaffold.Task());

            Assert.Equal(count + 1, _service.CountPendingTasks());
        }
Exemplo n.º 6
0
        public void Can_Update_Task_And_Raise_Event()
        {
            const string comment = "Comment has been updated";

            TasksService.Updated += (sender, args) =>
            {
                Assert.NotNull(args);
                Assert.IsAssignableFrom <WorkflowTaskInstancePoco>(args.Task);
                Assert.Equal(comment, args.Task.Comment);
            };

            WorkflowTaskInstancePoco task = Scaffold.Task();

            _service.InsertTask(task);

            task.Comment = comment;

            _service.UpdateTask(task);
        }
Exemplo n.º 7
0
        /// <summary>
        /// 
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public WorkflowInstancePoco MapIt(WorkflowInstancePoco a, WorkflowTaskInstancePoco b)
        {
            if (a == null)
            {
                return Current;
            }

            if (Current != null && a.Guid == b.WorkflowInstanceGuid)
            {
                Current.TaskInstances.Add(b);
                return null;
            }

            var prev = Current;
            Current = a;
            Current.TaskInstances = new List<WorkflowTaskInstancePoco>() { b };

            return prev;
        }
Exemplo n.º 8
0
        /// <summary>
        /// Delete from /app_plugins/workflow/preview
        /// </summary>
        /// <param name="nodeId"></param>
        /// <param name="userId"></param>
        /// <param name="taskId"></param>
        /// <param name="guid"></param>
        public async Task <bool> Validate(int nodeId, int userId, int taskId, Guid guid)
        {
            List <WorkflowTaskInstancePoco> taskInstances = _tasksService.GetTasksByNodeId(nodeId);

            if (!taskInstances.Any() || taskInstances.Last().TaskStatus == TaskStatus.Cancelled)
            {
                return(false);
            }

            // only interested in last active task
            WorkflowTaskInstancePoco activeTask = taskInstances.OrderBy(t => t.Id).LastOrDefault(t => t.TaskStatus.In(TaskStatus.PendingApproval, TaskStatus.Rejected));

            if (activeTask == null)
            {
                return(false);
            }

            UserGroupPoco group = await _groupService.GetPopulatedUserGroupAsync(activeTask.GroupId);

            // only valid if the task belongs to the current workflow, and the user is in the current group, and the task id is correct
            return(activeTask.WorkflowInstanceGuid == guid && group.Users.Any(u => u.UserId == userId) && activeTask.Id == taskId);
        }
Exemplo n.º 9
0
        public override void Up()
        {
            //Don't exeucte if the column is already there
            ColumnInfo[] columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();

            if (columns.Any(x => x.TableName.InvariantEquals("WorkflowInstance") && x.ColumnName.InvariantEquals("CompletedDate")))
            {
                return;
            }

            // column doesn't exist, add it and populate the completed date for any existing instances

            Create.Column("CompletedDate").OnTable("WorkflowInstance").AsDateTime().Nullable();

            // once the column has been added, check for any instances where status is not active, find the last task, and set complete date to match
            // this only impacts on charting, but allows more complete history as instances didn't previously store a completion date

            List <WorkflowInstancePoco> instances = InstancesService.GetAll()
                                                    .Where(x => x.Status == (int)WorkflowStatus.Approved || x.Status == (int)WorkflowStatus.Cancelled)
                                                    .ToList();

            if (!instances.Any())
            {
                return;
            }

            foreach (WorkflowInstancePoco instance in instances)
            {
                if (!instance.TaskInstances.Any())
                {
                    continue;
                }

                WorkflowTaskInstancePoco finalTask = instance.TaskInstances.OrderBy(x => x.Id).Last();

                instance.CompletedDate = finalTask.CompletedDate;
                Context.Database.Update(instance);
            }
        }
Exemplo n.º 10
0
        public async void Can_Validate_Request()
        {
            Scaffold.Config();

            Guid guid = Guid.NewGuid();

            WorkflowTaskInstancePoco task = Scaffold.Task(guid);

            _tasksService.InsertTask(task);
            _instancesService.InsertInstance(Scaffold.Instance(guid, 1, 1089));

            // is valid when the user is in the group responsible for the task with the given id
            // and the task belongs to the given instance by guid
            // and both the task and instance are related to the given node id
            bool isValid = await _previewService.Validate(1089, 0, task.Id, guid);

            Assert.True(isValid);

            isValid = await _previewService.Validate(1089, 99, 6456, guid);

            Assert.False(isValid);
        }
Exemplo n.º 11
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="poco"></param>
 public void UpdateTask(WorkflowTaskInstancePoco poco)
 {
     _database.Update(poco);
 }
Exemplo n.º 12
0
        /// <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();
            WorkflowTaskInstancePoco finalTask = null;

            bool?doSend = settings.SendNotifications;

            if (doSend != true)
            {
                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;
            }

            try
            {
                string docTitle = instance.Node.Name;
                string docUrl   = UrlHelpers.GetFullyQualifiedContentEditorUrl(instance.NodeId);

                WorkflowTaskInstancePoco[] 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.Count();

                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;
                }

                if (finalTask == null)
                {
                    Log.Error("No valid task found for email notifications");
                    return;
                }

                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, 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, 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
                {
                    Subject    = BuildEmailSubject(emailType, instance),
                    IsBodyHtml = true,
                };


                if (!string.IsNullOrEmpty(systemEmailAddress))
                {
                    msg.From = new MailAddress(systemEmailAddress);
                }

                // 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, instance.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);
            }
        }
Exemplo n.º 13
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="poco"></param>
 public void InsertTask(WorkflowTaskInstancePoco poco)
 {
     _database.Insert(poco);
 }
Exemplo n.º 14
0
        /// <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);
            }
        }
Exemplo n.º 15
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public WorkflowTask GetTask(int id)
        {
            WorkflowTaskInstancePoco task = _tasksRepo.Get(id);

            return(ConvertToWorkflowTaskList(task.AsEnumerableOfOne().ToList()).FirstOrDefault());
        }
Exemplo n.º 16
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="poco"></param>
 public void InsertTask(WorkflowTaskInstancePoco poco)
 {
     _tasksRepo.InsertTask(poco);
     Created?.Invoke(this, new TaskEventArgs(poco));
 }
Exemplo n.º 17
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="poco"></param>
 /// <returns></returns>
 public void UpdateTask(WorkflowTaskInstancePoco poco)
 {
     _tasksRepo.UpdateTask(poco);
 }
Exemplo n.º 18
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="poco"></param>
 /// <returns></returns>
 public void UpdateTask(WorkflowTaskInstancePoco poco)
 {
     _tasksRepo.UpdateTask(poco);
     Updated?.Invoke(this, new TaskEventArgs(poco));
 }
Exemplo n.º 19
0
 public TaskEventArgs(WorkflowTaskInstancePoco task)
 {
     Task = task;
 }
Exemplo n.º 20
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="poco"></param>
 public void InsertTask(WorkflowTaskInstancePoco poco)
 {
     _tasksRepo.InsertTask(poco);
 }