Saturday, July 19, 2014

Change SharePoint ms-core-brandingtext with JavaScript

A quick note about this task is that you can rename the SharePoint site brand name with SharePoint Management PowerShell to avoid having to use this approach. When we do not have access to PowerShell, we can do this with some JavaScript on the site’s master page. We should use SharePoint 2013 Designer for this change.

We first need to identify the master page that our site is using. We then need to find the HTML element that contains the site brand name. For SharePoint 2013, the brand name is contained in the following HTML element:

<div class="ms-core-brandingText">SharePoint</div>

On the master page, this is generated from a server side control with this mark up:

<SharePoint:DelegateControl id="ID_SuiteBarBrandingDelegate" ControlId="SuiteBarBrandingDelegate" runat="server" />

We need to just change the HTML that is rendered on the client side. We can quickly do this by scrolling to the end of the master page and entering our JavaScript snippet to change the text on that HTML element.

<sharepoint:scriptblock runat="server">
var g_Workspace = "s4-workspace";
try{
   document.querySelectorAll('div.ms-core-brandingText')[0].innerHTML = 'All About .NET';
}catch(e){}
</sharepoint:scriptblock>

The snippet is selecting all the div elements with the ms-core-brandingText class. There should only be one element with that class name. We then just set the innerHTML to the text that we need. Make the change, save the master page, and refresh the site. It should now look like this:


A drawback about this is that it needs to be done in each master page that your site is using and for each sub-site.


I hope this helps.

Saturday, July 12, 2014

TFS Build Web.config Transformation

With Visual Studio, we are able to create different build configurations with the Configuration Manager. This allows us to create different web.config files for a particular build target like Stage, Prod. The idea behind this is that each target configuration contains the correct application settings for that target environment.

When we build our projects on Visual Studio, we notice that the web.config file is transformed to contain the changes that are needed for the build target. When we use TFS Team Build, this does not happen by default.  An approach that we can take to address this is as follows:

Add a target directive only on the web project definition

For this step, we need to edit the .csproj file and add the following XML entries:

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="AfterBuild" Condition="'$(IsTFSBuild)' != ''"
<Message Text="'Message After Build: TFSBuild Status $(IsTFSBuild) - $(OutDir)\_PublishedWebsites\$(TargetName) '" />
  <TransformXml Condition="Exists('$(OutDir)\_PublishedWebsites\$(TargetName)')"
       Source="Web.config"
       Transform="Web.$(Configuration).config"
       Destination="$(OutDir)\_PublishedWebsites\$(TargetName)\Web.config"
/>

Element Name
Description
UsingTask
This element allows the MSBuild process to know in what assembly to find the TrasnformXml task. We need to note that the path is the one on the Build Server not a personal work station.

Target
The target is the action that runs the transformation process. We want this to be done AfterBuild has completed, and we only want this done when the IsTFSBuild argument is not empty. We add this argument to the TFS build definition.

Message
The message element helps us make a log entry, so that we can validate if this action is getting executed.

TransformXml
With this directive, we are executing a transformation task and merging the web.config file with another config file that is associated to the build target. For example for a release build, the $(Configuration) is set to “Release”. The result is merged and copied on the _publishedWebsites folder

*Note this can also be done as part of the build definition, but web.config is only applicable to web projects.

Save your file and unit test the change using the MSBuild application from the Visual Studio command shell. The command should look like this:

msbuild webproject.csproj /p:Configuration=Release /p:IsTFSBuild=True > out.log

On our desktop the OutDir is set to the Bin folder, so we can change the folder paths to read like this:

<TransformXml Condition="Exists('$(OutDir')"
       Source="Web.config"
       Transform Web.$(Configuration).config "
       Destination="$(OutDir)\Web.config"
/>

A merged web.config file should be copied to the Bin folder. Make sure to not include two target elements with the same name (AfterBuild). The last element always override the one that was declared before it.

Add a MSBuild Argument to the build definition

Now that the project file has been checked-in, we can test this on Team Build. First we need to add the IsTFSBuild argument to the build definition. This is done on Visual Studio. Add the parameter in the MSBuild Argument textbox as shown below:


Now, we can queue a build and take a look at the _PublishedWebSites folder. The web.config file should be merged with the configuration from the build target.  In case this did not happen, we can look at the build log file. We can look for the AfterBuild tag and find the message that was added to the project file. If this is not visible in the log file, we need to make sure that there are no typos on the changes and arguments that we are using.

Key Benefits

This process is a key component for the delivery a build and deployment automation process.

Management of configuration files for multiple environments.

Saturday, May 31, 2014

Custom Error Page 401 Access Denied when Using Windows Authentication

ASP.NET provides us with the ability to add custom pages for HTTP errors via Web.config CustomErrors tag.  This however does not work when we try to handle the 401 error under Windows authentication.  The reason for this is that this error (401) is raised during the Authorization request event on the HttpApplication process pipeline (see below), and Custom error settings are processed during the Action Method invocation (ASP.NET Handler.ProcessRequest event).

HTTP Process Pipeline
HTTP 401 Error browser Interaction
Http Process Pipeline




The common interaction between the browser and the server is as follows:

The browser sends request with no authentication tokens.

The server responds with a 401.2 HTTP error.

The browser send authentication tokens if the user is already logged. If the user is not, the browser shows a login dialog.

Even if the user is logged on, but he does not have the required role for this access, the server returns a 401.2 error and displays the Access Denied Page.

To provide a custom page for the Access Denied error, we should implement the following:
  • Use httpErrors Configuration settings
  • Allow Anonymous Access to content
  • Allow Anonymous Access to controller actions

Use httpErrors Configuration settings

<configuration>
<system.webServer>
  <httpErrors errorMode="Custom" xdt:Transform="Insert">
    <remove statusCode="401" />
    <error statusCode="401" path="/Error/NotAuthorized" responseMode="ExecuteURL" />
  </httpErrors>
</system.webServer>
</configuration>

*Note that this setting only works when the application is running under IIS. This setting can be added to the Web.Release.config file, so when the deployment is done these settings are merged into the Web.config file. This is done with the help of the xdt:Transform=insert attribute.

With this setting, we are basically telling IIS that we want to use our own custom page when the 401 error is raised. Note that since the machine.config already has this setting defined, we need to first remove the entry. We can then add the entry with our own directives. In this case, we are doing a server execute in the response mode which requires a relative path to our page or route. There is also support to redirect to an absolute Url. For this case, we are using a controller action named Error.NotAuthorized that is relative to the application.

Allow anonymous access to content

Since our custom error page may need to download images and other resources, we need to add some settings in our web.config to indicate that these resources should be unprotected. This is important because even if we redirect to a custom view, the images and bundled resources would also raise a 401 error, and the page may not look as the rest of the application. To allow access to other content, we can add the following settings to our web.config file.

Location Setting
Paths that should be allowed

<location path="Error">
    <system.web>
      <authorization>
        <allow users ="*" />
      </authorization>
    </system.web>
  </location>


Images

Error
The route for our custom error page
Bundles
This is the path use to download bundled resources.
Content
For CSS files
Favicon.ico
Favorite icon

*Note add one location setting per path
*Use fiddler to get an idea of the resources that are downloaded


This setting allows all users to have access to the path defined by the location path attribute. We should note that if we are using a particular folder or route for our custom view, this also should be allowed to all users.


Allow Anonymous Access to controller action

When using a controller to provide the custom error page, we must allow anonymous access to the action that should process this error.  This can be done as follows:

[Authorize]
public class ErrorController : Controller
{
//
// GET: /Error/
public ActionResult Index()
{
return View("Error");
}

//GET: /Error/NotAuthorized
[AllowAnonymous]
public ActionResult NotAuthorized()
{
return View("NotAuthorized", "NoChromeLayout");
}

}
The AllowAnonymous attribute allows non-authenticated users to have access to this request. Our view and layout provide only content which does not required authentication.

How about using Application EndRequest handler?

Another approach often use is to implement a redirect on the end request handler (global.asx.cs) as follows:

 protected void Application_EndRequest()
{
// If the user is not authorized redirect to error page
if (Response.StatusCode == 401)
{
Response.ClearContent();
              Response.RedirectToRoute("NotAuthorized ");
}
}

The problem I find with this approach is that it can lead to endless redirect scenario. The end request event is raised multiple times during the request life cycle of a page. The page can download more  content like images, CSS, JavaScript files each one sending a request thus raising an EndRequest event. If one of those resources is not properly configured, the status code would also be 401 and another redirect would be initiated.

Conclusion


The HttpError custom page configuration should be less intrusive approach to add custom error pages to our application as it is a configuration task instead of an implementation concern.

Saturday, May 17, 2014

Visual Studio 2012 does not support SSIS projects

If you are trying to open a SSIS (.dtproj) with Visual Studio 2012m and you are getting this error message:

Unsupported

This version of Visual Studio does not have the following project types installed or does not support them. You can still open these projects in the version of Visual Studio in which they were originally created.

As you may already know, this means that this project type is not configured on your installation of Visual Studio, but for some installing the support for BI projects is a bit tricky. We can address this by following these steps:

Download the Microsoft SQL Server Data Tools 2012


Run and install the data tools. If this is not installed, the next step will failed to install as well.

Download the Microsoft SQL Server Data Tools – Business Intelligence for Visual Studio 2012


When you run this file,  we need to make sure to select perform a new installation of SQL Server 2012. We should note that this option does NOT install a new instance of SQL Server. It only installs the features. We can then select the Data tools feature and complete the installation.

After the installation is complete, all the SSIS project templates should be available on Visual Studio 2012, and the unsupported error should go away.


I hope this helps.

Sunday, April 27, 2014

JavaScript DateDiff Extension Methods

When working with Date types in JavaScript, we often have the need to do date difference calculations. An approach to provide this functionality would be to extend the Date type with functions that can handle the calculations. So let’s talk about how to create these extension methods.

We can create extension methods on any JavaScript object by using the Object.prototype property. This property allows us to associate dynamic properties and functions to an object. This is what we use to associate the following functions to any Date instance.

Extension methods:

/*
Date type Extension Methods
Source: ozkary.blogspot.com
Date: 4/27/2014
*/
//Month difference
Date.prototype.monthDiff = function (dtBefore) {
      var yr = (this.getFullYear() - dt.getFullYear()) * 12;
      var mn = ((this.getMonth() + 1) - (dt.getMonth() + 1));
      return yr + mn;
}

//year difference
Date.prototype.yearDiff = function (dtBefore) {
      var yr = (this.getFullYear() - dt.getFullYear());
      return yr;
}

//date format
Date.prototype.toShortDate = function () {
      var dt = (this.getMonth() + 1) + '/' + this.getDate() + '/' + this.getFullYear();
      return dt;
}

Function name
Description
monthDiff (date)
This extension method expects a date type as a parameter. It uses native functions to calculate the years and months difference between itself (this) and the date parameter.
yearDiff (date)
This extension method expects a date type as a parameter. It uses native functions to calculate the year difference between itself (this) and the date parameter
toShortDate
This extension method uses the native functions to extract the date parts (month, day, year) and return the short date format (mm/dd/yyyy)

Usage:

Now that we have our new functions available, we can work on showing a way to use them. We can do that by comparing a couple of date ranges to display the difference in months and years. In our examples, we use the most recent date as the extended object, and pass the before date as the parameter. This allows us to get positive values for the date difference calculation.

var date1 = new Date('2/16/2014');
var date2 = new Date('09/01/2009');
$('#dates1').text(date1.toShortDate() + ' - ' + date2.toShortDate());
//get the date difference
var m = date1.monthDiff(date2);
var y = date1.yearDiff(date2);
$('#m1').text(m);
$('#y1').text(y);

//get the date difference
date1 = new Date('3/28/2014');
date2 = new Date('12/28/1996');
$('#dates2').text(date1.toShortDate() + ' - ' + date2.toShortDate());
m = date1.monthDiff(date2);
y = date1.yearDiff(date2);
$('#m2').text(m);
$('#y2').text(y);

This script performs the following steps:
  • Create Date instances
  • Display the short date format (mm/dd/yyyy)
  • Calculate the months and year differences

The result should look as shows below. We can also use this JSFiddle sample to see it in action.


Thanks for following and keep an eye for additional entries.

Sunday, April 20, 2014

Format Currency with JavaScript and CSS

When working with currency, there is a need to format the data with commas, decimals, currency sign and an indicator for negative amounts. Let’s take a look at one approach that can help us format the data once it is rendered on the user interface.

We start by taking a look at a page that displays several numeric fields with no styles.



As we can see, these numbers are hard to read. It will be much better to just add commas and a different font for negative values. We can do this by using a CSS selector and select all the elements that we want to format.

$('* div').each(function () {   
    var item = $(this).text();
    var num = Number(item).toLocaleString('en');

    if (Number(item) < 0) {
        num = num.replace('-', '');
        $(this).addClass('negMoney');
    } else {
        $(this).addClass('enMoney');
    }

    $(this).text(num);
});

The approach here is to use the Number.toLocaleString function to format the data with commas. This provides the ability to eliminate the use of regular expression to parse the data. Now we need to add the currency sign and use a red font for negative amounts. We do this by applying these CSS classes.

.enMoney::before {
    content:"$";
}
.negMoney {
    color:red;
}
div.negMoney::before {
    content:'($';
}
div.negMoney::after {
    content:')';
}

The script adds the class name to the element. We use the enMoney and negMoney classes to provide the font style. To add the currency sign and parentheses (negative values only), we use the CSS pseudo-element ::before and ::after to apply the special currency format.  The result looks like this:




This looks much better now, and it is a simple approach that uses the browser capabilities, JavaScript and some CSS to get the formatting done.

Use this JSFiddle to play with the script: Format Currency