Configure IIS to use a certain user account to work

The problem I faced with with Drupal installation – I could not give right permissions to the upload folder to store files as required.
I had the solution for IIS6, but IIS7 have different user set-up, so new solution was required.
After some fiddling about, I stumbled across this solution:

  1. Create a normal Windows user for your Drupal install. Create a password too.
  2. Create an application pool for your Drupal install. Set the application pool’s identity to your windows user: in IIS7 double click the new application pool and then Advanced Settings -> Process Model -> Identity
  3. Set your website to use that application pool
  4. In web-site go to Authentication -> Anonymous Authentication -> Edit -> Set to Application pool identity:
  5. Give (or deny) the Windows user full control to the sites\default directory

So, what all this does is give the Drupal install privilege isolation. It can only write files in the sites\default directory, but not your settings.php. It can still create needed cache file, uploads, etc.

Unable to set break point for debugging in Visual Studio

Getting “The breakpoint will not currently be hit. No symbols have been loaded for this document.” in Visual studio whenever you try to debug?
There are a million solutions out there and every time it is a different issue.

Here is one way of sorting things out in VS2012:

Go to Tools -> Options -> Debugging -> Symbols.
Hit Empty Symbol Cache and select All Modules Unless Excluded.

Possibly will need to restart VS. And should work.

MVC3: Form submitted only by GET method, without specifying controller and action

Sometimes in ASP.Net MVC you need to submit a form that will always go by GET method. But standard BeginForm() function does not have an override that does not requires specifying Controller and Action where you would like to submit the form back to. And sometimes you don’t want to specify where the form to go to, cause the same form can go into multiple controllers (think of a partial view). And default BeginForm() submits only by POST. So there is no default way to say I want to submit form by POST to wherever the form has came from.

I had to poke through MVC source code to figure out how to do what I want, and keep all the parameters and not mess up any paths. Here you go:

public static MvcForm BeginForm(this HtmlHelper htmlHelper, FormMethod method)
{
    var rd = htmlHelper.ViewContext.HttpContext.Request.RequestContext.RouteData;
    var currentAction = rd.GetRequiredString("action");
    var currentController = rd.GetRequiredString("controller");

    return htmlHelper.BeginForm(currentAction, currentController, rd.Values, method);
}

Read Only User in MVC3 with minimal changes

I had a task of creating a read-only user role in our MVC application. And given the limited time for the task I had to do that with minimal changes to the domain/architecture.

I have started with creating “Readonly” user role. Hopefully I don’t need to explain how to do that.

This is not the most elegant solution to the problem, but it kinda does the job. It block read-only user from using the POST actions, also if available substitutes Edit views with Details views (presuming this is CRUD application). Also we add javascript to Edit pages where we don’t have Details page.

Then I created a global filter:


    public class ReadOnlyFilter : IActionFilter 
    {
        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var currentUser = HttpContext.Current.User;
            if (currentUser == null || !currentUser.Identity.IsAuthenticated)
                return; // user is not logged in yet. Give them a break, please! Maybe they just trying to login. Not even touching your precious controllers yet!


            if (!currentUser.IsInRole("Readonly")) 
                return; // user is not read-only. Nothing to see here, move on!


            // Presume User is read-only from now on************************************

            var readOnlyAttribute = GetForReadOnlyUserAttribute(filterContext);

            // If DisableUserInput applied to the action, turn on JS for disabling the input fields on the view
            if (readOnlyAttribute != null && readOnlyAttribute.DisableInputFields)
                DisableInputOnForm(filterContext);

            // if AllowPost Attribute applied to the action, ignore the rest of this filter and get on with this
            if (readOnlyAttribute != null && readOnlyAttribute.AllowPost == true)
                return;

            // if action is of type post - deny
            if (filterContext.HttpContext.Request.HttpMethod.ToLower() == "post" )
            {
                ReadOnlyPage(filterContext);
                return;
            }

            // if action is "Create" - deny access
            if (filterContext.ActionDescriptor.ActionName.ToLower() == "create")
            {
                ReadOnlyPage(filterContext);
                return;
            }

            // if action is edit - check if Details action exits -> redirect to it.
            if (filterContext.ActionDescriptor.ActionName.ToLower() == "edit")
            {
                // find a method named Details that has exactly the same set of parameters as the request.
                var detailsAction = GetActionMethod(filterContext, "Details");

                // if "Details" action exist, we redirect the user into that action.
                if (detailsAction != null)
                {
                    // (in)sanity check. Also Resharper told me this could be null.
                    if (filterContext.HttpContext.Request.Url == null) return;

                    // simply replace the "Edit" with "Details" in a url. This is DIRTY, just the way I like my women: http://goo.gl/wrKuI
                    var currentUrl = filterContext.HttpContext.Request.Url.ToString();
                    var newUrl = currentUrl.Replace("Edit", "Details");

                    // and redirect to the new url.
                    filterContext.Result = new RedirectResult(newUrl);
                }
                else
                {
                    DisableInputOnForm(filterContext);
                }
            }
        }



        /// 
        /// Redirect user to read-only message page
        /// 
        /// 
        private void ReadOnlyPage(ActionExecutingContext filterContext)
        {
            filterContext.Result = new RedirectResult("~/ReadOnlyAccess");
        }


        /// 
        /// Activate javascript to disable all input fields.
        /// 
        private void DisableInputOnForm(ActionExecutingContext filterContext)
        {
            filterContext.Controller.ViewBag.DisableAllInputElements = true;
        }


        /// 
        /// If ForReadOnlyUserAttribute is applied for a method, return that attribute object.
        /// If actionName provided, work with that action in the controller. Otherwise work with currently executed action.
        /// 
        private ForReadOnlyUserAttribute GetForReadOnlyUserAttribute(ActionExecutingContext filterContext, String actionName = null)
        {
            MethodInfo method = GetActionMethod(filterContext, actionName);
            if (method == null) return null;

            var readOnlyAttribute = (ForReadOnlyUserAttribute)Attribute.GetCustomAttribute(method, typeof(ForReadOnlyUserAttribute));

            return readOnlyAttribute;
        }


        /// 
        /// Return MethodInfo for the executed action on the controller.
        /// If actionNameString is provided we return MethodName for the provided name.
        /// If no actionNameString is provided, we return the currently executed Action.
        /// 
        private MethodInfo GetActionMethod(ActionExecutingContext filterContext, String actionNameString = null)
        {
            var controllerType = filterContext.Controller.GetType();

            // get all the parameters from the original request. Copy Types of parameters in an array. 
            var paramTypes = filterContext.ActionParameters
                .Values
                .Where(p => p != null)
                .Select(o => o.GetType())
                .ToArray();

            var actionName = actionNameString ?? filterContext.ActionDescriptor.ActionName;

            // find a method that has exactly the same set of parameters as the request.
            var detailsAction = controllerType.GetMethod(actionName, paramTypes);

            return detailsAction;
        }


        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            // 1960: "I have a great idea! lets have every person in the country carry a radio tracking beacon!" 
            //  "That'll never fly!"  
            //
            // 2012: "I can has TWO iphones??"
        }
    }

That class is registered in Global.asax.cs:

        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new ReadOnlyFilter());
        }

And you need ForReadOnlyUserAttribute.cs:

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false) ]
    public class ForReadOnlyUserAttribute : Attribute
    {
        public bool DisableInputFields { get; set; }
        public bool AllowPost { get; set; }
    }

And in your global Layout page you need to add following block of code for javascript. Taking you already have jQuery in your project:

    @{  // if we are working with read-only user and Edit page does not have Details alternative, we show Edit page, but with all the controls disabled.
        var disable = (bool?) ViewBag.DisableAllInputElements;  // this is set in ReadOnlyFilter
        if (disable.HasValue && disable.Value == true)
        {
            //script from asawyer: http://stackoverflow.com/a/12477283/809357
            
        }
    }

To disable input forms on the pages, add [ForReadOnlyUser(DisableInputFields = true)] attribute on the controller action:

        [ForReadOnlyUser(DisableInputFields = true)]
        public virtual ViewResult Details(int id)
        {
            var model = _personRepository.Find(id);
            return View(model);
        }

To allow POST actions add [ForReadOnlyUser(AllowPost = true)] to actions where you take POST submit.
Beware – this allows unauthorised users to do POST actions. That was fine in my case, but might not be acceptable for you, so change the filter code a bit.

And that seems to be it.

Generate QR Barcode in ASP.Net MVC

I had a task of creating a QR barcode and displaying it on a web-page from ASP.Net MVC system. There are a lot of free web-services for generating a qr-codes for you, ( like http://qrcode.kaywa.com/ ) But this time I did not want to use a service for various reasons. I had to get the barcode generated on my server. (Just accept it as default!)

I have found many .Net libraries that generate a qr-barcodes, but for some reason I did like QrCode.Net. Probably because it is hosted on codeplex and I keep my open-source projects there as well.

I have downloaded their Dll file, added it as a reference to my MVC project:

In my MVC project I have added a new action that returned a FileStremResult:

using Gma.QrCodeNet.Encoding;
using Gma.QrCodeNet.Encoding.Windows.Controls;

public virtual FileResult BarcodeImage(String barcodeText)
{
    // generating a barcode here. Code is taken from QrCode.Net library
    QrEncoder qrEncoder = new QrEncoder(ErrorCorrectionLevel.H);
    QrCode qrCode = new QrCode();
    qrEncoder.TryEncode(barcodeText, out qrCode);
    Renderer renderer = new Renderer(5, Brushes.Black, Brushes.White);

    // write to file if required for auditing
    //renderer.CreateImageFile(qrCode.Matrix, String.Format(@"d:\tmp\{0}.png", barcodeText), ImageFormat.Png);

    Stream memoryStream = new MemoryStream();
    renderer.WriteToStream(qrCode.Matrix, memoryStream, ImageFormat.Png);

    // very important to reset memory stream to a starting position, otherwise you would get 0 bytes returned
    memoryStream.Position = 0;

    var resultStream = new FileStreamResult(memoryStream, "image/png");
    resultStream.FileDownloadName = String.Format("{0}.png", barcodeText);

    return resultStream;
}

UPDATE 23 Dec 2013: Since this article was written, the API of QrCode.Net has changed slightly and the code above does no longer compile. So see the new code:

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Web.Mvc;
using Gma.QrCodeNet.Encoding;
using Gma.QrCodeNet.Encoding.Windows.Render;

namespace SampleBarCodeMvc.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult BarcodeImage(String barcodeText)
        {
            // generating a barcode here. Code is taken from QrCode.Net library
            QrEncoder qrEncoder = new QrEncoder(ErrorCorrectionLevel.H);
            QrCode qrCode = new QrCode();
            qrEncoder.TryEncode(barcodeText, out qrCode);
            GraphicsRenderer renderer = new GraphicsRenderer(new FixedModuleSize(4, QuietZoneModules.Four), Brushes.Black, Brushes.White);

            Stream memoryStream = new MemoryStream();
            renderer.WriteToStream(qrCode.Matrix, ImageFormat.Png, memoryStream);

            // very important to reset memory stream to a starting position, otherwise you would get 0 bytes returned
            memoryStream.Position = 0;

            var resultStream = new FileStreamResult(memoryStream, "image/png");
            resultStream.FileDownloadName = String.Format("{0}.png", barcodeText);

            return resultStream;
        }
    }
}

Then in one of the views I place this image tag:

  <img src="/Controller/BarcodeImage?barcodeText=Hello"/>

Where Controller is a name of your controller where you placed the action. And that will give you an image with a barcode and word “Hello” encoded in it.

I have quickly created a sample solution in Visual Studio 2013 (you should be able to open that in VS2012):
SampleBarCodeMvc – this is to show a working example.

Enjoy!

Update: QrCode.Net is now available via nuget package and can be installed either via Nuget inside of your Visual Studio or in VS-console: Install-Package QrCode.Net

ASP.Net MVC: Autosave drop-down with notification

I did spend quite a time figuring out jQuery verbs to do just what I needed. So I’ll share this with my blog and possible readers. On my view I had a model written out, some other stuff, but here I’m only interested in DropDown and JavaScript bits:

@Html.DropDownListFor(m => model.tagId, new SelectList(ViewBag.Tags, "Value", "Text", model.tagId), new {@class = "document-category", data_id = model.modelId})

where

ViewBag.Tags was populated in controller like this:

ViewBag.Tags = _tagRepository.All
    .OrderBy(t => t.Name)
    .Select(t => new { Text = t.Name, Value = t.TagId.ToString() })
    .OrderBy(t => t.Text)
    .ToList();

Back to the view. Added JavaScript like this one:

$(document).on("change", ".document-category", function () {
    var select = $(this);
    $.ajax({
        type: "POST",
        url: "@(Url.Action("Action", "Controller"))",
        data: { id: select.data("id"), 
        tagId: $("option:selected", select).val()},
        error: function () {
            select.after("Error occurred");
        },
        success: function (data) {
            if (data.Success === true) {
                select.after(function () {
                    return $('<div> Saved </div>').delay(1000).fadeOut(1000);
                });
            } else {
                select.after("Error occurred");
            }
        }
    });
});

The very first line can be replaced by $(".document-category").change(function(){ but this time I had to deal with other java-script library that was stripping-bare my stuff. On the other end of the ajax request you need to put controller action:

[HttpPost]
public virtual JsonResult Action(int id, int? tagId)
{
        //blah, do some update stuff
    return Json(new {Success = true, Message = "Updated"});
}

Disable unobtrusive validation on per-element basis in ASP.Net MVC

Sometimes you just don’t want a validation on your form elements, but the framework just adds the validation automatically, cause it is too clever. To disable the validation on one element, pass html parameter data_val=false to your controller and JavaScript will ignore the that field. Example:

@Html.DropDownList("documentId", new SelectList(ViewBag.MetaTags, "Value", "Text"), new {data_val=false})

Convert Due Date to human readable format

Sometimes you need to show date in a “Due in X-days” format, not just a date. This will convert date to a string with granularity: Today, tomorrow, yesterday, days, weeks, months.

public static class HumanTime
{
    public static string DueDate(DateTime dueDate) {
        return DueDate(dueDate, DateTime.Now);
    }

    public static string DueDate(DateTime dueDate, DateTime dateNow) {
        DateTime nowDate = dateNow;

        TimeSpan ts = nowDate - dueDate;

        if (dueDate.Date == DateTime.Today)
        {
            return "due today";
        }

        if (dueDate.Date == DateTime.Now.AddDays(1).Date)
        {
            return "due tomorrow";
        }

        if (dueDate.Date == DateTime.Now.AddDays(-1).Date)
        {
            return "was due yesterday";
        }


        int totalDays = (int)Math.Round(ts.TotalDays);
        if (Math.Abs(totalDays) &lt; 7)
        {
            return DueStringFormat(totalDays, "day");
        }

        int weeks = (int)Math.Round( ts.TotalDays / 7 );
        if (Math.Abs(weeks) &lt; 5 && Math.Abs(totalDays)&lt;30 )
        {
            return DueStringFormat(weeks, "week");
        }

        int months = (int)Math.Round( ts.TotalDays / 30 );
        return DueStringFormat(months, "month");
    }


    public static string DueStringFormat(int count, string unit)
    {
        string format = "{0} {1}";
        if (count &lt; 0)
        {
            format = "due in {0} {1}";
        } 
        else
        {
            format = "{0} {1} overdue";
        }

        return String.Format(format, Math.Abs(count), unit.Quantify(count));
    }


    /// Adds "s" suffix to a word if there are more than one thing involved.
    public static string Quantify(this String singular, int count)
    {
        int abs = Math.Abs(count);
        if (abs > 1)
            return singular+"s";
        else 
            return singular;
    }
}