I'm creating a workflow tool that will be used on our company intranet. Users are authenticated using Windows Authentication and I've set up a custom RoleProvider that maps each user to a pair 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 are able to create a request that then flows up the chain to Development and so on:
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 the Request header record and the first History item:
Request 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 further stage of the request will need to be handled by different users in the chain. A small subset of the process is:
- User creates Request [Analytics, User]
- Manager authorises Request [Analytics, Manager]
- Developer processes Request [Development, User]
Currently I have a single CreateHistory() method that handles each stage of the process. The status of the new History item is pulled up 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 itself will render a different partial form depending on the Status. My intention was that I could use a single generic CreateHistory method for each of the stages in the process, using the Status as a reference to determine which partial View to render.
Now, the problem comes in rendering and restricting available actions in the View. My CreateHistory View is becoming bloated with If statements to determine the availability of actions depending on the Request's current Status:
@* 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>
Making the right actions appear at the right time is the easy part, but it feels like a clumsy approach and I'm not sure how I would manage permissions in this way. So my question is:
Should I create a separate method for every stage of the process in the RequestController, even if this results in a lot of very similar methods?
An example would be:
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 rendering the appropriate buttons in the View? I only want a set of valid actions appear as controls.
Sorry for the long post, any help would be greatly appreciated.