Friday, October 16, 2015

Genenrate full Url (absolute Url) using C# in ASP.NET MVC

A quick way to generate full Url (absolute Url) in ASP.NET MVC:

Url.Action("Details", "Product", new { id = p.ProductID}, Request.Url.Scheme)


If without id, simply use null in the parameter.

Url.Action("Details", "Product", null, Request.Url.Scheme)

Wednesday, October 7, 2015

Stop screensaver autolock

A quick VBScript could be useful in keeping computer active without screensaver automatically taking over. For security policies, most companies have automatic screensaver or session lock in place based on user inactivity. For those of us who need to keep the system monitor PC on all the time, this script could be useful. It basically sends escape key stroke once a minute for the duration of time you specify.

Dim shell, minutes, minuteCount
minutes = 300

Set shell = CreateObject("WScript.Shell")
minuteCount = 0
For minuteCount = 0 To minutes
  shell.SendKeys "{ESC}"
  minuteCount = minuteCount + 1
  WScript.Sleep(60000) 
Next

Just don't forget to lock the computer when you step away from the computer.
To stop the script, close "WScript.exe" in Task Manager or logout from Windows.

Sunday, October 4, 2015

Custom Error Page in ASP.NET MVC

Setting up custom error page for 400 (bad request), 401 (unauthorized), 403 (forbidden), 404 (not found), 500 (internal server error) seemed like a web.config task as simple as specifying
    
<system.web>
    <customErrors mode="On" redirectMode="ResponseRedirect"  >
      <error statusCode="400" redirect="~/CustomError/BadRequest"/>
      <error statusCode="401" redirect="~/CustomError/Unauthorized" />
      <error statusCode="404" redirect="~/CustomError/NotFound"  />
      <error statusCode="403" redirect="~/CustomError/Forbidden"/>
      <error statusCode="500" redirect="~/CustomError/InternalServerError"/>
    </customErrors>
</system.web>
However, the above may not allow our custom error pages to handle certain cases.

For example, the default IIS 404 error page, not my custom error page, will handle 404 for url that is not understood by my MVC application's routing rule (i.e., http://localhost/MyApp/a/b/c/d/e/f).

It may be easier to use <httpErrors> under <system.webServer> rather than <customErrors> in <system .web>. The above can be converted to below. Note that we cannot use ~/ in the path attribute. <system.webServer> is IIS-level override for customError handling in this case. So remove the above and try something like the following.

<system.webServer>
  <httpErrors errorMode="Custom" existingResponse="Replace">      
    <remove statusCode="400" />
    <remove statusCode="401" />
    <remove statusCode="403" />
    <remove statusCode="404" />
    <remove statusCode="500" />
    <error statusCode="400" path="/MyApp/CustomError/BadRequest" responseMode="ExecuteURL"/>
    <error statusCode="401" path="/MyApp/CustomError/Unauthorized" responseMode="ExecuteURL"/>
    <error statusCode="403" path="/MyApp/CustomError/Forbidden" responseMode="ExecuteURL"/> 
    <error statusCode="404" path="/MyApp/CustomError/NotFound" responseMode="ExecuteURL"/>
    <error statusCode="500" path="/MyApp/CustomError/InternalServerError" responseMode="ExecuteURL"/>
  </httpErrors>
</system.webServer>


Below is the Controller


public class CustomErrorController : Controller
{
 public ActionResult BadRequest()
 {
  Response.StatusCode = 400;
  ViewBag.ErrorCode = "400";
  ViewBag.ErrorTitle = "400 Error - Bad Request";
  ViewBag.ErrorMessage = 
   "The resource you tried to access appears to be incomplete.\n" + 
   "Please check your URL and try again.";
  return View("Error");
 }
 public ActionResult Unauthorized()
 {
  Response.StatusCode = 401;
  ViewBag.ErrorCode = "401";
  ViewBag.ErrorTitle = "401 Error - Unauthorized";
  ViewBag.ErrorMessage = 
   "The resource you tried to access appears to require a user permission.\n" + 
   "Please re-launch the browser and try again.";
  return View("Error");
 }
 public ActionResult Forbidden()
 {
  Response.StatusCode = 403;
  ViewBag.ErrorCode = "403";
  ViewBag.ErrorTitle = "403 Error - Forbidden: Access is denied";
  ViewBag.ErrorMessage = 
   "The resource you tried to access is not allowed. \n" + 
   "Please check your URL and try again.";
  return View("Error");
 }
 public ActionResult NotFound()
 {
  Response.StatusCode = 404;
  ViewBag.ErrorCode = "404";
  ViewBag.ErrorTitle = "404 Error - Resource Not Found";
  ViewBag.ErrorMessage = 
   "The resource you tried to access was not found.\n" + 
   "Please check your URL and try again.";
  return View("Error");
 }
 public ActionResult InternalServerError()
 {
  Response.StatusCode = 500;
  ViewBag.ErrorCode = "500";
  ViewBag.ErrorTitle = "500 Error - Internal Server Error";
  ViewBag.ErrorMessage = 
   "An error occurred while system tried to process your request.\n" + 
   "Try again, and if problem persists contact system administrator.";
  return View("Error");
 }
}


And the View

@{
    ViewBag.Title = ViewBag.Title;
}

<div style="text-align: center;">
    <div style="font-size: 10em; color:#ccc; font-weight: bold; ">
        @ViewBag.ErrorCode
    </div>

    <h2>
        @ViewBag.ErrorTitle
    </h2>

    <h3 style="white-space: pre-wrap; word-wrap: break-word;">@ViewBag.ErrorMessage</h3>

    <h4>
        <a href="javascript:void(0);" onclick="javascript: history.go(-1);">Back</a>
    </h4>
</div>


And this is the custom 404 page.


Friday, October 2, 2015

Format <td> to handle linefeed and wrap long text in a table

When a table cell needs to properly display texts with linefeed and really long text with no whitespace (i.e., long URL), try the following styles.
<table style="table-layout: fixed">
  <tbody>
    <tr>
      <td>Some Links</td>
      <td style="white-space: pre-wrap; word-wrap: break-word">
        ...
      </td>
    </tr>
  </tbody>
</table>


Below is a problematic example of table cell display. The long URL text, which has no whitespace character stretches the table's width beyond the specified table width of 400px.
<table style="width: 400px; border: 1px solid white; color: white">
  <tbody>
    <tr>
      <td style="width: 100px; border: 1px solid white; vertical-align: top; ">Some Links</td>
      <td style="border: 1px solid white;" >Link Example: 
        https://www.google.com/maps/@33.7338729,-117.8530829,3a,75y,304h,90t/data=!3m7!1e1!3m5!1suAg9o6LKzX3</td">
    </tr>
  </tbody>
</table>
Some Links Link Example: https://www.google.com/maps/@33.7338729,-117.8530829,3a,75y,304h,90t/data=!3m7!1e1!3m5!1suAg9o6LKzX3



Below is better way to display the content with proper line-feed in order to keep the specified table width of 400px;
<table style="width: 400px; border: 1px solid white; color: white; table-layout: fixed;">
  <tbody>
    <tr>
      <td style="width: 100px; border: 1px solid white; vertical-align: top; ">Some Links</td>
        <td style="border: 1px solid white; 
          white-space: pre-wrap; word-wrap: break-word;" >Link Example:
https://www.google.com/maps/@33.7338729,-117.8530829,3a,75y,304h,90t/data=!3m7!1e1!3m5!1suAg9o6LKzX3</td>
    </tr>
  </tbody>
</table>

Some Links Link Example: https://www.google.com/maps/@33.7338729,-117.8530829,3a,75y,304h,90t/data=!3m7!1e1!3m5!1suAg9o6LKzX3



Monday, September 21, 2015

Set up local email server

Quick way to set up a local email server for development purpose:


  1. Download and Install hMailServer.
  2. After installation, create a test domain, "mail.local".
  3. Run notepad.exe as local admin. 
  4. Open "C:\Windows\System32\Drivers\Etc\host" file.
  5. Add new entry 127.0.0.1  mail.local. Save and close notepad.exe.
  6. Test #5 by pinging mail.local.
  7. In hMailServer console, create a new account admin@mail.local with password under the domain, "mail.local".
  8. Go to Settings --> Protocols --> SMTP.
    Click "Delivery of e-mail" tab. Enter 0 for "Number of retries".
  9. Go to Settings --> Advanced.
    Enter mail.local as the default domain.
  10. Go to Settings --> Advanced --> AutoBan.
    Disable AutoBan.
  11. Go to Settings --> Advanced --> IP Ranges.
    Remove "Internet" and keep "My Computer (127.0.0.1 ~ 127.0.0.1)".
  12. Download and Install Mozilla Thunderbird.
  13. Set up existing account with name = "admin", email = "admin@mail.local" and password from #7.
  14. Test the local email server by sending emails.
  15. Sometimes, the default SMTP port 25 is blocked even to the local PC itself due to network policy. You can change the SMTP port to something else, i.e., 125 at
    Settings --> Advanced --> TCP/IP Ports ---> 0.0.0.0 / 25 / SMTP.
    Change the TCP/IP Port to another available port here.
  16. In order to disable SMTP authentication to avoid SMTP 530 error (authentication is required), go to Settings --> Advanced --> IP Ranges --> Internet --> Require SMTP Authentication. Uncheck "Local to local e-mail addresses", "Local to external e-mail addresses", and "External to local e-mail addresses". Leave check on "External to external e-mail addresses".

Wednesday, September 16, 2015

Saturday, September 12, 2015

SharePoint 2013 + Windows Server 2012 R2 Pre-Requisite Install (Part 2)

When running Setup.exe from SharePoint 2013 install media, we would experience the feared "Prerequisite Missing error message" like below.


There are 10 files to download and install with some care to pass this check successfully. In fact, it is a little bit tricky to perform this pre-requisite installation successfully the first time.


  1. On the SharePoint server, create a folder, "C:\pre".
  2. Download the 10 files below and save them to "C:\pre" folder.
    1. Windows Management Framework 3.0
      http://www.microsoft.com/en-us/download/details.aspx?id=34595
    2. SQL Server 2008 R2 SP1 Native Client
      http://download.microsoft.com/download/9/1/3/9138773A-505D-43E2-AC08-9A77E1E0490B/1033/x64/sqlncli.msi
    3. Microsoft WCF Data Serivces 5.0
      http://download.microsoft.com/download/8/F/9/8F93DBBD-896B-4760-AC81-646F61363A6D/WcfDataServices.exe
    4. Microsoft WCF Data Services 5.6 (To avoid overwrite, rename this download to WcfDataServices56.exe)
      http://download.microsoft.com/download/1/C/A/1CAA41C7-88B9-42D6-9E11-3C655656DAB1/WcfDataServices.exe
    5. Microsoft Information Protection and Control Client
      http://download.microsoft.com/download/9/1/D/91DA8796-BE1D-46AF-8489-663AB7811517/setup_msipc_x64.msi
    6. Microsoft Sync Framework Runtime version 1.0 SP1 (64-bit)
      http://download.microsoft.com/download/E/0/0/E0060D8F-2354-4871-9596-DC78538799CC/Synchronization.msi
    7. Windows Identity Extensions
      http://download.microsoft.com/download/0/1/D/01D06854-CA0C-46F1-ADBA-EBF86010DCC6/r2/MicrosoftIdentityExtensions-64.msi
    8. Windows Identity Foundation (KB974405)
      http://download.microsoft.com/download/D/7/2/D72FD747-69B6-40B7-875B-C2B40A6B2BDD/Windows6.1-KB974405-x64.msu 

      Windows Identity Foundation has been integrated into .NET 4.5. No need to install it.
    9. Windows Server AppFabric
      http://download.microsoft.com/download/A/6/7/A678AB47-496B-4907-B3D4-0A2D280A13C0/WindowsServerAppFabricSetup_x64.exe
    10. Windows Server AppFabric Cumulative Update 1 (KB2671763)
      http://download.microsoft.com/download/7/B/5/7B51D8D1-20FD-4BF0-87C7-4714F5A1C313/AppFabric1.1-RTM-KB2671763-x64-ENU.exe
  3. We are supposed to install prerequisite files via "PrerequisiteInstaller.exe" with specific switches for each downloaded file. In my case, this didn't work too well. You can try the following Powershell scripts, but if you have problems, there is a manual way to install the prerequisite files. I'll go over it in #4.

    PowerShell Script to run prerequisite install altogether:.
    $path = "C:\pre"
    
    Start-Process "E:\PrerequisiteInstaller.exe" –ArgumentList "/SQLNCli:$Path\sqlncli.msi /IDFX:$Path\Windows6.1-KB974405-x64.msu /IDFX11:$Path\MicrosoftIdentityExtensions-64.msi /Sync:$Path\Synchronization.msi /AppFabric:$Path\ WindowsServerAppFabricSetup_x64.exe /KB2671763:$Path\AppFabric1.1-RTM-KB2671763-x64-ENU.exe /MSIPCClient:$Path\setup_msipc_x64.msi /WCFDataServices:$Path\WcfDataServices.exe /WCFDataServices56:$Path\WcfDataServices56.exe"
    
    
    Prerequisite Files (Beware of the last one: AppFabric)

  4. It turns out that all prerequisite files can be installed manually via "Shift + Right Click" and "Run as Administrator", except for the two AppFabric install files.

    "WindowsServerAppFabricSetup_x64.exe"
    "AppFabric1.1-RTM-KB2671763-x64-ENU.exe"

    To install AppFabric, make sure to uninstall AppFabric from "Add Remove Programs" window from previous failed attempt if any.  Reboot the server if uninstall was performed and run the following in Command Prompt (in admin mode).
    C:\Pre\WindowsServerAppFabricSetup_x64.exe /i CacheClient,CachingService,CacheAdmin /gac
    
    

    (Basically, the above switches are what the "PrerequisiteInstaller.exe" tried to implement when the setup file was run under PrerequisiteInstaller.exe)
  5. Let it run for a couple of minutes. Check with TaskManager and "Add/Remove Programs" to confirm AppFabric 1.1's installation. Then reboot the server, and then run "AppFabric1.1-RTM-KB2671763-x64-ENU.exe" with "Run as Admin". This should be the end of SharePoint 2013's prerequisite installation, and you  should be able to proceed with the SharePoint 2013 setup.