I am creating a workflow tool that will be used on our corporate intranet. Users authenticate using Windows authentication, and I created a custom RoleProvider that maps each user to a couple of roles.
One role indicates their seniority (guest, user, senior user, manager, etc.), and the other indicates their role / department (analytics, development, testing, etc.). Users in Analytics can create a query, which then translates the chain into "Development", etc.:
Models
public class Request { public int ID { get; set; } ... public virtual ICollection<History> History { get; set; } ... } public class History { public int ID { get; set; } ... public virtual Request Request { get; set; } public Status Status { get; set; } ... }
In the controller, I have a Create () method that will create a request header record and the first history element:
Query controller
public class RequestController : BaseController { [HttpPost] [ValidateAntiForgeryToken] public ActionResult Create (RequestViewModel rvm) { Request request = rvm.Request if(ModelState.IsValid) { ... History history = new History { Request = request, Status = Status.RequestCreated, ... }; db.RequestHistories.Add(history); db.Requests.Add(request); ... } } }
Each subsequent stage of the request must be processed by different users in the chain. A small subset of the process:
- User makes a request [Analytics, User]
- Manager resolves request [Analytics, Manager]
- Developer Processes Request [Development, User]
Currently, I have one CreateHistory () method that processes every step of the process. The state of the new History element is deduced from the view:
// GET: Requests/CreateHistory public ActionResult CreateHistory(Status status) { History history = new History(); history.Status = status; return View(history); } // POST: Requests/CreateHistory [HttpPost] [ValidateAntiForgeryToken] public ActionResult CreateHistory(int id, History history) { if(ModelState.IsValid) { history.Request = db.Requests.Find(id); ... db.RequestHistories.Add(history); } }
The CreateHistory View will display a different partial form depending on the state. My intention was that I could use one common CreateHistory method for each step of the process, using Status as a reference to determine which partial view to display.
Now the problem is rendering and limiting the available actions in the view. My CreateHistory View gets bloated with If statements to determine if actions are available depending on the current current state:
@* Available user actions *@ <ul class="dropdown-menu" role="menu"> @* Analyst has option to withdraw a request *@ <li>@Html.ActionLink("Withdraw", "CreateHistory", new { id = Model.Change.ID, status = Status.Withdrawn }, null)</li> @* Request manager approval if not already received *@ <li>...</li> @* If user is in Development and the Request is authorised by Analytics Manager *@ <li>...</li> ... </ul>
Creating the right actions at the right time is the easy part, but it seems like a clumsy approach, and I'm not sure how I will manage permissions this way. So my question is:
Should I create a separate method for each step of the process in the RequestController, even if this leads to many very similar methods?
Example:
public ActionResult RequestApproval(int id) { ... } [MyAuthoriseAttribute(Roles = "Analytics, User")] [HttpPost] [ValidateAntiForgeryToken] public ActionResult RequestApproval(int id, History history) { ... } public ActionResult Approve (int id) { ... } [MyAuthoriseAttribute(Roles = "Analytics, Manager")] [HttpPost] [ValidateAntiForgeryToken] public ActionResult Approve (int id, History history) { ... }
If so, how do I handle the display of the corresponding buttons in the view? I want a set of valid actions to be displayed as controls.
Sorry for the long post, any help would be greatly appreciated.