Convert Windows Line Endings to Unix
From time to time I create projects that need to be supported by both Windows and *NIX systems. Line endings are always a pain and although GIT has helped this a lot there are still issues at times that you must deal with manually.
Here is a quick way to output a project and convert line endings all in one. This is not difficult but probably won’t make much sense unless you’ve messed with .cmd or .bat files in the past.
First grab dos2unix here. You’ll need to select the correct binary for your platform either 32bit or 64bit. As of today 6.0.3 is the current version.
Save the below to “your_project_name.bat”
@ECHO off
setlocal EnableDelayedExpansion
SET source=D:\Path\To\Your\Source
SET dest=D:\Path\To\Your\Destination
SET d2upath=D:\dos2unix\Folder\Path
CALL publish.bat -r -s %source% -d %dest%
cd %dest%
ECHO Converting files to unix line endings…
SET exts=*.json, *.js
FOR %%f in (%exts%) DO (
CALL %d2upath%\dos2unix %%f
)
ECHO Finished publishing.
Save the following to “publish.bat”
@ECHO off
setlocal EnableDelayedExpansion
REM Set npm path, default build directory various variables.
SET execDir=%~dp0
SET sourceDir=%CD%
SET destDir=
SET lastArg=0
SET sourceIdx=0
SET destIdx=0
SET ctr=0
SET empty=n
REM No args passed use defaults goto dependency check.
IF [%~1]==[] GOTO :HELP
IF /i %~1==-h GOTO :HELP
REM Loop over args and assign them.
:SETARGS
IF [%~1]==[] GOTO :DIREXISTS
IF /i %~1==-s (
SET /a sourceIdx=%ctr%+1
)
IF /i %~1==-d (
SET /a destIdx=%ctr%+1
)
IF /i %~1==-r (
SET empty=y
)
IF %ctr%==%sourceIdx% (
IF %lastArg%==-s SET sourceDir=%~1
)
IF %ctr%==%destIdx% (
IF %lastArg%==-d SET destDir=%~1
)
SET lastArg=%~1
SET /a ctr+=1
SHIFT & GOTO :SETARGS
:DIREXISTS
IF EXIST %destDir% GOTO :PUBLISH
SET /p create=”Directory “%destDir%” does not exist, would you like to create it (y or n)?”
IF %create%==y (
ECHO Creating directory…
MKDIR %destDir%
GOTO :PUBLISH
)
ECHO ——————————————————-
ECHO Nothing to do, no output directory exiting…
GOTO :END
:PUBLISH
ECHO PUBLISHING: %sourceDir% to %destDir%
ECHO ——————————————————-
IF %empty%==y (
ECHO Removing files and subfolders.
RD %destDir% /s /q
ECHO:
)
MKDIR %destDir%
ECHO Publishing please wait…
ECHO:
IF EXIST pubignore.txt (
XCOPY /s /y /EXCLUDE:pubignore.txt %sourceDir%\* %destDir%
) ELSE (
XCOPY /s /y %sourceDir%\* %destDir%
)
GOTO :END
:HELP
ECHO:
ECHO HELP: Publish Script Help.
ECHO ——————————————————-
ECHO -s (specify source, default is current directory.)
ECHO -d (specify destination directory [required].)
ECHO -r (remove directory and re-create)
GOTO :END
:END
ECHO:
ECHO FINISHED
Create a text file with the name “pubignore.txt”
In this file you can put in files you want it to ignore when it copies the files from your source to your destination that you created in “your_project_name.bat”. This uses the standard xcopy EXCLUDE formatting.
NOTE: in the “your_project_name.bat” you can specify file extension types that you want dos2unix to convert also do this where it says “SET exts=*.json, *.js”
Created the files now what?
So once you’ve created all three files you have a couple options. You can put them all in the same folder as your project and then cd D:\Your\Project\Path. Where again “D” is the drive your project is in, then simply run “your_project_name.bat”.
The other option is to create a “Scripts” folder somewhere on your machine and then dump all but pubignore.txt (it needs to go in the project folder itself) in that folder. Lastly in your environment variables for your path include the path to the scripts folder. You can do this using the link I provided above or through the command line. Just Google it if you don’t quite get it. Once you do that you can just run “your_project_name.bat” from anywhere and it will do its job. This is in fact what I do and often call it from Visual Studio on build.
DOWNLOAD dos2unix
Xipframe MVC Framework
Want to check out a cool NodeJs based framework? Checkout Xipframe! Not only does it build out many of the mundane tasks of building a web application for you but it also can enable LESS and AngularJS helpers for you as well. Tired of managing CSS and JavaScript file references? Xipframe does that for you too.
Check it out here.
Download it via NPM here.
Kendo UI MVC Grid Extention
Kendo UI MVC Grid Extention
Didn’t like how the MVC Destroy() method worked so I wired up my own. This is a simple function to help you get the row’s data when you fire off the custom event.
Essential “e” is the event that is fired from the custom command event in your Kendo UI grid. It takes the target and turns it into a JQuery object. It grabs the uid of the row and then matches it with the uid in the data. Its very quick and works nicely. Sure there are other ways to skin this cat but this seemed to be very smooth. Seems to me there should be a JQuery data object for the row but all I saw was the uid hence this function below. Anyway thought I’d share.
function getRowData(e) { var target, grid, uid, row, data; target = $(e.target); uid = target.parent().parent().data().uid; grid = $('#Grid').data('kendoGrid'); row = grid._data; for (var i = 0; i < row.length; i++) { if (row[i].uid == uid) { data = row[i]; } } return data; }
What you’ll get is your data for the row as an object so you can simple do:
// note below assumes you have a custom column command that on .click() is attached to the “deleteRow” function “e” is the event that the click event will pass in.
function deleteRow(e){ var data = getRowData(e); var name = data.full_name // where full_name is a column in your grid. alert('Are you sure you want to delete ' + name); // TODO: wire up your delete event passing in an id or something $.post('/your/path', {}, function (result) { if(result.deleted){ // load your UI or whatever } }); }
The getRowData function you can copy/paste but the above “deleteRow” is just an example you’ll need to modify to your needs.
MVC3 Shared Hosting on GoDaddy
As of this post GoDaddy Shared Hosting does not directly support MVC3. Have no fear though you just need to add reference to a handful of libraries to make it all work. Additionally you’ll need to set these libraries to copy to local. Simply click the following references listed and set them from false to true as the images below. Note the post Phil Haack has additional details however the list posted there is missing a couple references follow his post but add the additional libraries. Again this may change once GoDaddy updates their shared hosting environment, so this may not be needed by the time your read this. It its not working well you know what to do then…
- Microsoft.Web.Infrastructure
- System.Web.Helpers
- System.Web.Mvc
- System.Web.Razor
- System.Web.WebPages
- System.Web.WebPages.Deployment
- System.Web.WebPages.Razor
- system.Web.Abstractions
- System.Web.Routing
MVC Data Annotations and Custom Attributes
So you’ve found out that MVC supports Editor Templates. About 10 min. later you think to yourself hey how can I leverage this to automatically set my textbox width or maxlength and so on. This is a simple example using just a couple classes.
Create Abstract Class Inherit Attribute:
public abstract class MetadataAttribute : Attribute { public abstract void Process(ModelMetadata metaData); }
This simply creates a void to process any attributes we set to inherit our class MetadataAttribute. The next piece of code will make sense of this.
Custom Attribute Class:
public class OptionalAttributes : MetadataAttribute { public string style { get; set; } public override void Process(ModelMetadata metaData) { metaData.AdditionalValues.Add("style", this.style); } }
All we are doing here is creating a public property to store our Attribute value. In this case a simple style (you would probably do something much more relevant to your project this is just for demonstration purposes) Attribute is created. We then must implement our Process as this is inheriting MetadataAttribute.
Adding Attribute to View Model
[OptionalAttributes(style = "width: 150px")] public string Email { get; set; }
Here we reference our OptionalAttributes Class and expose our style, nothing exciting here.
Wiring it All Up:
public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider { protected override ModelMetadata CreateMetadata( IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) { var metaData = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName); attributes.OfType<MetadataAttribute>().ToList().ForEach(x => x.Process(metaData)); return metaData; } }
This is the magic that makes it all work. Here we create our Metadata Provider inheriting from DataAnnotationsModelMetadataProvider. So basically any Attribute that is of type MetadataAttribute will be processed hence our first abstract class we created.
Adding our Provider to Global.asax:
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); ModelMetadataProviders.Current = new CustomModelMetadataProvider(); }
Pretty straight forward right. Since we inherit DataAnnotationsModelMetadataProvider we can simply set the ModelMetadataProviders.Current to our CustomModelMetadataProvider;
Making it Happen in Our View:
@model String @if (!ViewData.ModelMetadata.AdditionalValues.ContainsKey("style")) { @Html.TextBoxFor(model => model) } else { var width = ViewData.ModelMetadata.AdditionalValues["style"]; @Html.TextBoxFor(model => model, new { style = width }) }
O.K so a couple things here. First off what you see is an EDITOR TEMPLATE partial view (you could use the above syntax in your view as well however your model syntax would be more like model => model.Email where Email is the name of your property in your model). Not the strongly typed view itself. Please note that. If you notice the model is of type String. You can make templates for DateTime or whatever you like. In this case we are describing a simple string. So what is happening here. Well with a simple if else we check to see if the Metadata Key is null. If it has a value we get the value for that key in this case the key is called “style” and then apply it to the style Attribute for our textbox. Now any property in our view model that has a valid value for our Optional Attribute style will be used. No more styling adding css whatever to our view it will all automatically wire up. Can’t stress enough this is NOT a real world implementation but rather just giving you the idea. For instance you should incorporate checking for nulls and correct types but you already know that.
If you really want to be slick create an HtmlHelper to further extend this and do away with the if else statement and handle it all in your HtmlHelper.
No code source on this one. Straight forward just copy from the above, be sure to include your usings or imports of namespaces and you’ll be good.
MVC Paging and Sorting
If you’re new to MVC this is undoubtably something you’ll face at some point. You’ve already been searching Google and found a few examples, heard about MVCContrib , WebGrid and Telerik. All of which are great solutions. However you want the manual control, don’t want to use tables or you just like to stay up late and bang keys on the keyboard. If that is the case this should at the very least get you on the right track. As with anything I post feel free to critique. It can only help. There are always alternate ways to skin a cat. This is no exception. Be advised as often with code I post I’ve significantly scaled this down to make it easy to follow striping out the application specific stuff.
So let’s get to it…
The first thing you’ll need to do is create a class that will handle your paging and sorting. This was expanded on from Scott Gu’s PaginatedList.cs class which didn’t support sorting but we’ll get to that.
PaginatedList.cs (abbreviated)
Declare a few properties:
public int PageIndex { get; private set; } // Index of your current page. public string SortExpression { get; private set; } // The expression to sort on ex: "FullName DESC". public string SortColumn { get; private set; } // The column you are sorting on ex: "FullName". public string SortDirection { get; private set; } // The direction to sort ex: "DESC" or "ASC". public int PageDisplayIndex { get; private set; } // This is the page number to display. Show 1-5 instead of 0-4. public int PageSize { get; private set; } // The amount of records per page. public int TotalCount { get; private set; } // The total count of records. public int TotalPages { get; private set; } // The total number of pages. TotalCount / PageSize public int PagerSize { get; private set; } // The size of the pager. How many page numbers to show. public int PagerStart { get; private set; } // The starting page in the pager numbers. public int PagerEnd { get; private set; } // The ending page of the pager numbers. public List<int> Pages { get; private set; } // The list of pager page numbers to display.
Let’s create the paginated list by passing a few variables from our controller (we’ll get to that shortly) .
public PaginatedList(IQueryable<T> source, string sortExpression, int pageIndex, int pageSize, int pagerSize) { //Populate variables. PageIndex = pageIndex; SortExpression = sortExpression; PageDisplayIndex = pageIndex; PageSize = pageSize; PagerSize = pagerSize; //Store the current Sort Column and Direction. //There should always be a value but just in case. This is for providing the current sort expression in our Model if (sortExpression != null && !string.IsNullOrEmpty(sortExpression)) { string[] curSort = sortExpression.Split(' '); // Split so we can get the col and direction sep. SortColumn = curSort[0]; //This could be null so let's check first. if (curSort.Length > 1) { SortDirection = curSort[1]; } } //Get the record count and the total pages. TotalCount = source.Count(); TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize); //Build out the list of pages available. BuildPagerPages(); // See below for this method. Creates the page numbers for the pager. //Finally Sort the data and select the page size. this.AddRange(source.OrderBy(sortExpression) .Skip((PageIndex - 1) * PageSize) .Take(PageSize)); }
Now we need a simple method to build out a list of the page numbers to display. Down load source, there are a couple other properties like “HasNextPage” or “HasPreviousPage” that you’ll need to check in your view or Html Helper to verify the link should be displayed.
/// <summary> /// Creates list of page numbers for pager. /// </summary> private void BuildPagerPages() { PagerStart = Math.Max(1, (PageIndex) - PagerSize / 2); // Start page of pager. PagerEnd = Math.Min(TotalPages, PageIndex + (PagerSize / 2)); // End page of pager. //Iterate through the total pages to make page selectors. Pages = new List<int>(); for (var i = PagerStart; i <= PagerEnd; i++) { Pages.Add(i); // Add page number to list. } }
DynamicOrderBy.cs (abbreviated)
Grabbed this in part from StackOverflow and Adam Anderson’s blog. It’s a great piece of code. I won’t bore you It is included in the source below but checkout the links for further details. Essentially though this class allows us to do our Linq OrderBy with ease.
Let’s Wire Up Our Controller
Probably the easiest part of the whole process. All we are doing here is grabbing some values from our route grabbing our data from our repository or where ever and then create our new instance of our PaginatedList class.
public ActionResult Index(string sort, int? page ) { // Define the page and pager defaults. const int pageSize = 10; const int pagerSize = 10; // You could also set this as an optional value // and set the default that way too. if (sort == null || string.IsNullOrEmpty(sort)) { sort = "FullName ASC"; } var accounts = repository.GetList(); // This is simply getting your data. That’s up to you, in this case a repository. // Note the page number default there. It is 1 instead of 0. This gets changed in our PaginatedList // There are a couple ways you could do this but the point is to have page numbers 1-5 instead of 0-5. var paginatedAccounts = new PaginatedList<Account>(accounts, sort, page ?? 1, pageSize, pagerSize); return View("Index", paginatedAccounts); }
Setup Routing
The simplest way to do this is of course in your Global.asax. Or you may have some other custom mechanisim to handle your routes as I do. But for this will show you the Global.asax method for brevity. This is very straight forward. Simply create a new route like below. Essentially you’d have something like “Clients/{sort}/{page}”.
routes.MapRoute( "ClientList", // Route name "Clients/{sort}/{page}", // URL with parameters new { controller = "Account", action = "Index" } // Parameter defaults );
Last Piece of Puzzle the View and Helper
Updating the view is very easy. The below is abbreviated showing only the Previous button but you could easily add Next, First and Last if you wanted. So there are a couple ways you could do this. You could use JQuery and build out the url or you can use a Helper. There may be reasons why you might use one over the other but I’m going to show the Helper method. Remember in our PaginatedList class we had some properties called SortColumn, SortExpression and SortDirection right? Well now we need them. You could just pass the SortExpression only if you wanted and then split it up in your class however I chose to expose the expression and also the col and exp separately also. So in our link we simply need to pass in the current or last sort details plus the newly selected details. This is simply for one reason and that is if the column name passed is the same as the previous we need simply to sort the opposite way. If the column is a different one or no column exists we just sort ascending. Simple right. May see complicated but isn’t just read the comments it all makes sense.
public static MvcHtmlString SortActionLink(this HtmlHelper helper, string text, string actionName, string controller, string curColumn, string curDirection, string tag, object routeValues, object htmlAttributes) { string sortResult, sortCol, sortExpression, sortDir, tagType; tagType = "a"; sortExpression = "{0} {1}"; //Just because I don't want to write + ' ' +...ohh wait I just did. LOL. RouteValueDictionary routeDict = new RouteValueDictionary(routeValues); //Check for null. If this is the first time the page is loaded // if so we don't need to update the sort values. if (routeDict["sort"] != null) { sortCol = routeDict["sort"].ToString(); if (string.Equals(curColumn, sortCol, StringComparison.CurrentCultureIgnoreCase)) { //We're sorting the same column //Check the direction and then supply the opposite. switch (curDirection) { case "ASC": sortDir = "DESC"; break; case "DESC": sortDir = "ASC"; break; default: sortDir = "ASC"; break; } sortResult = string.Format(sortExpression, sortCol, sortDir); } else { //This is a new column pass the default. //No need to append Sort Direction. //If no sort direction is supplied ASC is assumed. sortResult = sortCol; } //Update the Route data. routeDict["sort"] = sortResult; //helper.ViewContext.RequestContext.RouteData.Values["sort"] = sortResult; } var urlHelper = new UrlHelper(helper.ViewContext.RequestContext); var url = urlHelper.Action(actionName, controller, routeDict); if (tag != null) tagType = tag; TagBuilder build = new TagBuilder(tagType); build.MergeAttribute("href", url); build.InnerHtml = text; build.MergeAttributes(new RouteValueDictionary(htmlAttributes)); return MvcHtmlString.Create(build.ToString(TagRenderMode.Normal)); }
…and the razor markup to make it all work. You are passing in the name for the link, then the action, then the controller. Then we pass in our current values of sort column and direction, then our Route Values which we modified based on the current sort values and finally any html attributes if needed.
@Html.SortActionLink("Full Name", "Index", "Client", Model.SortColumn, Model.SortDirection, new { sort = "FullName", page = Model.PageDisplayIndex }, null)
Wrap Up
To wrap up in your source you will notice that the First, Next etc links can be shown or hidden by a simple if statement that checks the HasPreviousPage bool value that we exposed in our PaginatedList class. Again see source for how to do this, if you don’t already.
Hope this helps you out,
C
Download Source