4

I have an asp.net-mvc website where up until now there have been no entitlements as its been open to everyone. Many of the pages are detailed forms with textboxes, select dropdowns, etc

I now need to change this to make many of the existing pages "entitled" so only certain people have edit capability and everyone else sees a read only page. I am trying to figure out if I should

  1. Create a seperate view for everyone one of my existing views with forms that is just read only html on a page and redirect based on entitlements on the server side, something like

    public ActionResult OrderView()
    {
         var isEntitled = Model.IsEntitled()
         if (isEntitled)
         { 
              return("OrderEditableView", GetViewModel()); 
         }
         else 
         {
             return("OrderReadOnlyView", GetViewModel());  
         }
    

    }

or

  1. Reuse the same view and simply disable or hide the "Save" button on the screen.

on my view have

     <% if (Model.IsEntitled) { %>
           <button id="saveButton">Save Changes</button>
     <% } %>

The second option would be much quicker to implement but would be a little weird because it would look like you could edit all of the fields but just dont' see (or see a disabled) Save button on the form.

The first option seems cleaner but I would have to go and create a new view for everyone of my screens and that seems like a lot of work (over 100 views currently)

This seems like a common situation so I wanted to see if there was a best practice on dealing with this situation. Obviously I am looking for a solution given the current situation that I am in but I would also be interested if there were patterns or solution that would be considered best practice or recommended if I was starting from scratch.

leora
  • 188,729
  • 360
  • 878
  • 1,366
  • Sounds like you need to built up a server session class where you can place things like MyWeb.Session.isEntitled. – Ross Bush May 04 '14 at 04:19
  • @Irb - I have no issues calculating and determining if someone is entitled. As per my question, I am more focused on how I manage the views – leora May 04 '14 at 04:22
  • You could create a view as a partial based on editable requirements.. – Ross Bush May 04 '14 at 04:29
  • @Irb - isn't that the first option i had listed above? – leora May 04 '14 at 04:34
  • There are many patterns to do what you want. But: Do you use EditorFor helpers to render all the input controls you want to convert to passive controls? Is it ok for the Non-Admin users to see deactivated inputs (or do you require they see simple text)? Is it too much trouble to make "tweaks" or use a custom helpers whose behavior you want to alter? Are you agreeable to adding some functionality from a JS lib like Angular? – Dave Alperovich May 14 '14 at 10:29

4 Answers4

6

I would go for creating a separate partial/full views for both.

Reasons:
easy to change and maintain
validation code only in editable part
clear separation of concerns
cleaner code
may be we can reuse some of the code by abstracting/separating similar code to re usable partial views
easy control over access

Also, in real world we balance among budget,time and effort. so considering that I may use mix of these depending upon the situation.

aamir sajjad
  • 3,019
  • 1
  • 27
  • 26
3

an option is to do the switch in the view..

<% if (Model.IsEntitled) 
{ 
    Html.Partial("OrderEditablePartialView", Model)
}
else 
{
    Html.Partial("OrderReadOnlyPartialView", Model)
}
%>

another option could be to create custom Html helpers to render the elements as editable or readonly, which would give you some flexibily on how you handled each element.

crude example:

public static MvcHtmlString FormTextBox(this HtmlHelper helper, string name, string text, bool editable)
        {
            return
                new MvcHtmlString(
                    String.Format(
                        editable
                            ? "<input type='text' id='{0}' name='{0}' value='{1}'>"
                            : "<label for='{0}'>{1}</label>",
                            name, text));
        }
Mark Redman
  • 24,079
  • 20
  • 92
  • 147
  • Moving OP's suggestion from the controller to the view doesn't really change much, and OP says _"but I would have to go and create a new view for everyone of my screens and that seems like a lot of work (over 100 views currently)"_ which this doesn't solve. – CodeCaster May 10 '14 at 10:18
  • +1 for the extension method. That way, you write the view once, and it knows its personality. However, I would use the built-in EditorFor and DisplayFor templates: `public static MvcHtmlString EditOrDisplayFor( this HtmlHelper html, Expression> expression, bool isEditable) { return (isEditable) ? html.EditorFor(expression):html.DisplayFor(expression); }` – Colby Cavin May 15 '14 at 02:20
3

I'd go for the second options with a single view for both writable and readonly forms.

For the visual side, I'd implement a few things in addition to hiding the Save button:

  • Set the input fields to readonly. You can easily do this with client side Javascript.

  • Change the colors (and possibly other attributes) of the input fields to emphasize that they are readonly (e.g. remove the borders). This can be easily achieved by a style sheet and a single additional class name in the form tag.

The authorization on the server side is simple as well. Just annotate the action accordingly.

It could look like this (simplified, using Razor and jQuery):

View:

@using (Html.BeginForm("AddressController", "SaveChanges", FormMethod.Post,
        new { @class = Model.IsEntitled ? "regular" : "readonly"}))
{
   <p>
     @Html.TextboxFor(model => model.Name)
   </p>
   <p>
     @Html.TextboxFor(model => model.Address)
   </p>
   @if (Model.IsEntitled) {
     <p>
       <button id="saveButton">Save Changes</button>
     </p>
   }
}

<script type="text/javascript">
  $( document ).ready(function() {
     $('form.readonly input').attr("readonly", true);
  });
</script>

Controller:

public class AddressController : Controller {

    [Authorize(Roles = "xyz")]
    [HttpPost]
    public ActionResult SaveChanges(Address address) {
        ...
    }
}

CSS:

.readonly input {
    border: none;
}
Torino
  • 588
  • 2
  • 9
  • I think this is a great solution. Using client-side javascript in that way is perhaps not ideal but it's an excellent compromise in that it avoids having to manually make changes for every input element. – Ben Aaronson May 14 '14 at 21:42
1

The quickest solution would be some javascript that disables all input elements on pages where the user isn't entitled. How to implement this is up to you, it depends on how your views and models are structured.

This way you can reuse the edit-views without even having to edit them, but depending on the kind of user you have you can expect "site not working" calls when your forms appear disabled.

patterns or solution that would be considered best practice or recommended if I was starting from scratch.

The proper way to go would be DisplayTemplates and EditorTemplates with proper viewmodels, but from your "create a new view for everyone of my screens and that seems like a lot of work" comment I gather you're not interested in that.

So I'd go with the former. Please make sure you check the permissions in the [HttpPost] action methods, as malicious users can easily re-enable the form and post it anyway.

Community
  • 1
  • 1
CodeCaster
  • 147,647
  • 23
  • 218
  • 272