All posts by knarfalingus

TYPESCRIPT PLUNGE

My plunge into TypeScript is to convert an existing proprietary ASP.NET Control Class Library, that uses C# on the server side, and Javascript on the client side from Javascript to TypeScript.   Although this library is in production use daily, there hasn’t been significant development on it  since it was first created back in the 2006-2007 timeframe.  As such, it is packed with older style Javascript.   There is also zero jQuery usage.  After the conversion, I expect another developer at my company may be able to modify it! (safely!).  The amount of code is not insignificant, there are about 20,500 total lines of Javascript spread across several files. Although I won’t post any of this closed source code here, I will put up some pseudo code that illustrates what I encounter.

The next step is to get TypeScript to work in a class library project, by default it does not.  This can be achieved be editing the project file in a text editor and changing the below from

  <Import Project="$(MSBuildBinPath)Microsoft.CSharp.targets" />
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  -->

to

  <Import Project="$(MSBuildBinPath)Microsoft.CSharp.targets" />
  <Import Project="$(MSBuildExtensionsPath32)MicrosoftVisualStudiov$(VisualStudioVersion)TypeScriptMicrosoft.TypeScript.targets" />
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  -->

HOSTING WORDPRESS ON IIS, YOU WON’T BELIEVE WHAT YOU DON’T KNOW [PICS] [NSFW]

Ok, so link bait headlines are the order of the day

One tricky issue I had when setting up this website is that the domain itself (knarfalingus.com) is not the main domain of the site.   This is a shared hosting account, so on disk,  what I will call the ‘primary’ domain points to the root folder ‘’ , and ‘knarfalingus.com’ is a domain pointer that points to the same IP address.  I wanted ‘knarfalingus.com’ to point to a subfolder. The way I have it set up, this domain is hosted on disk in a subdirectory of the root, ‘knarfalingus.com’

In order to achieve this, various rewrite rules were put in play.  I’ll just refer to the two excellent articles I used when I first set up the site for details, but they should cover most of what is needed to handle the situation.  The web.config containing these rules go in the root folder.

http://weblogs.asp.net/owscott/archive/2010/01/26/iis-url-rewrite-hosting-multiple-domains-under-one-site.aspx

http://weblogs.asp.net/owscott/archive/2010/05/26/url-rewrite-multiple-domains-under-one-site-part-ii.aspx

For the starting point of this post, this is the web.config for the case when the you have a domain pointer “yourdomain.com” and you want to host it on disk in a same named subfolder of the root i.e. ‘yourdomain.com’

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
		<rewrite>
			<rules>
        <rule name="yourdomain.com" enabled="true" stopProcessing="true">
          <match url=".*" />
          <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
            <add input="{HTTP_HOST}" pattern="^(www.)?yourdomain.com$" />
            <add input="{PATH_INFO}" pattern="^/yourdomain.com($|/)" negate="true" />
          </conditions>
          <action type="Rewrite" url="/yourdomain.com/{R:0}" />
        </rule>
      </rules>
			<outboundRules>
				<rule name="Outgoing - URL paths" preCondition=".aspx pages only" enabled="true">
					<match filterByTags="A" pattern="^(?:yourdomain|(.*//[_a-zA-Z0-9-.]*)?/yourdomain.com)(.*)" />
					<action type="Rewrite" value="{R:1}{R:2}" />
				</rule>
				<rule name="response_location URL">
					<match serverVariable="RESPONSE_LOCATION" pattern="^(?:yourdomain|(.*//[_a-zA-Z0-9-.]*)?/yourdomain.com)(.*)" />
					<action type="Rewrite" value="{R:1}{R:2}" />
				</rule>
				<rule name="response_location querystring">
					<match serverVariable="RESPONSE_LOCATION" pattern="(.*)%2fyourdomain.com(.*)" />
					<action type="Rewrite" value="{R:1}{R:2}" />
				</rule>
				<preConditions>
				   <preCondition name=".aspx pages only">
					   <add input="{SCRIPT_NAME}" pattern=".aspx$" />
				   </preCondition>
				</preConditions>
			</outboundRules>
		</rewrite>
    </system.webServer>
</configuration>

Although this was satisfactory, I recently wanted to add permalinks to my WordPress site.  But doing so resulted in nasty 404 errors in IIS.   Following the advice here would probably work on most sites but not on mine due to the special setup. 

First, I wanted to have a URL pattern that was useful in terms of SEO and also resistant to change.  For example, in the WordPress settings, one of the built-in Permalink Types is ‘Day and Name’ which looks like so:

http://www.yourdomain.com/2014/05/26/sample-post/

Where ‘sample post’ is the post slug (which was helpfully hidden for me in WordPress when editing posts, this can be made visible via Screen Options).

To me this not a very useful format.  The date is included which might be helpful on a more time sensitive blog like a news site, but to me is meaningless. Worse still the entire format is based on the premise that your title slug never changes – which you may want to change at some later date. 

If in the future you change your scheme to

http://www.yourdomain.com/sample-post/2014/05/26/

you can rest assured you have broken all existing links to your posts!

How to solve these issues and improve SEO?  For the SEO part, I’d want the title first in the URL, as it is the most specific information.  After that I’d want the category for some more keywords. Finally, add the unique post_id to the URL.  My URL scheme is therefore as follows

image

Here are the parts of the scheme (see Permalinks for syntax)

  • /blog/ – the reason for this is explained below
  • %postname% – this is next – this is the most ‘specific’ information about the post, and I put it first in the URL for that reason.
  • %category% – this is next, less specific, but useful keywords.  If you use a category hiearchy you will get multiple keywords separated by ‘/’
  • %post_id% – this is last.   It is meaningless in terms of SEO so it can be last, and as long as I commit to keeping %post_id% last in any URL scheme I come up in the future, any existing links out on other sites should continue to work (EDIT:not with WordPress permalinks but by using IIS Rewrite, see below)

To demonstrate this, examine the following links, they will all lead back to this page, due to the non-varying placement of %post_id%

http://www.knarfalingus.com/blog/hosting-wordpress-on-iis-you-wont-believe-what-you-dont-know-pics-nsfw/wordpress/80/

http://www.knarfalingus.com/blog/still/works/wordpress/80/

http://www.knarfalingus.com/blog/still/works/now/80/

And finally this one here – which won’t work with the WordPress Permalink scheme, which expects three leading directories in the path to the %post_id%. It does work now as I address the issue below when discussing the /blog/ prefix.

http://www.knarfalingus.com/blog/80/

/blog/ prefix

The reason for this is simple, as I stated earlier I have the pre-existing rules which make the domain pointer appear as a standalone domain, but this has a consequence, the rules in the web.config in the root folder actually control the rewrites.  In order to squeeze in a rule for the blog without interfering with anything else, I chose to have a rule that handles only URLs with a specific prefix, in this case ‘/blog/’ (actually it matches /blog followed by anything, /blog1/, /blogxyz/ but for my purposes, good enough). This rewrites those URLs to index.php

        <rule name="yourdomain.com wordpress" enabled="true" stopProcessing="true">
          <match url=".*" />
          <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
            <add input="{HTTP_HOST}" pattern="^(www.)?yourdomain.com$" />
            <add input="{PATH_INFO}" matchType="Pattern" pattern="^/blog/.*$" ignoreCase="true" negate="false" />
          </conditions>
          <action type="Rewrite" url="/yourdomain.com/index.php/{R:0}" appendQueryString="false" />
        </rule>

Now above I mentioned http://www.knarfalingus.com/blog/80/ doesnt redirect to the page – but we can fix that by changing our newly added rule to:

        <rule name="yourdomain.com wordpress" enabled="true" stopProcessing="true">
          <match url=".*" />
          <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                        <add input="{HTTP_HOST}" pattern="^(www.)?yourdomain.com$" />
                        <add input="{PATH_INFO}" matchType="Pattern" pattern="^/blog[w-_/.]*/(d+)/?$" ignoreCase="true" negate="false" />
          </conditions>
          <action type="Rewrite" url="/yourdomain.com/?p={C:1}" appendQueryString="false" />
        </rule>

This rule captures all URLs for yourdomain.com that start with /blog and end with a number. That number is rewritten to what is the default PermaLink URL in wordpress (/?p=%post_id%).

Now URL’s like this work, note the ID is at the end of the URL:

http://www.knarfalingus.com/blog/94935/this/is/a/really/4434/long/URL-dont-you-think.aspx/342534235/test/tes/90/yada-yada-yada/the_answer_is_42/why/are/you/still/reading/this/test/test/80/

I also added a trailing ? to the RegEx to make the trailing slash optional

http://www.knarfalingus.com/blog/94935/this/is/a/really/4434/long/URL-dont-you-think.aspx/342534235/test/tes/90/yada-yada-yada/the_answer_is_42/why/are/you/still/reading/this/test/test/80

Wrapping up, many WordPress themes have a 404 page,  But I did not see it, I saw an IIS 404 page. The issue is IIS is handling the 404, instead we need to let it flow through to PHP/Wordpress. The solution is to add the following to web.config

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
    <httpErrors existingResponse="PassThrough" />
    .....

Tossing in a little security, we can block others from loading our content in frames (I know, not very common anymore), by using the X-Frame-Options header and specifying SAMEORIGIN.


<?xml version="1.0" encoding="UTF-8"?>
<configuration>
	<system.webServer>
		<httpProtocol>
			<customHeaders>
				<add name="X-Frame-Options" value="sameorigin" />
			</customHeaders>
		</httpProtocol>

The final config is now as follows:


<?xml version="1.0" encoding="UTF-8"?>
<configuration>
	<system.webServer>
		<httpProtocol>
			<customHeaders>
				<add name="X-Frame-Options" value="sameorigin" />
			</customHeaders>
		</httpProtocol>
		<httpErrors existingResponse="PassThrough" />		
		<rewrite>
			<rules>
        <rule name="yourdomain.com wordpress" enabled="true" stopProcessing="true">
          <match url=".*" />
          <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                        <add input="{HTTP_HOST}" pattern="^(www.)?yourdomain.com$" />
                        <add input="{PATH_INFO}" matchType="Pattern" pattern="^/blog[w-_/.]*/(d+)/?$" ignoreCase="true" negate="false" />
          </conditions>
          <action type="Rewrite" url="/yourdomain.com/?p={C:1}" appendQueryString="false" />
        </rule>
        <rule name="yourdomain.com" enabled="true" stopProcessing="true">
          <match url=".*" />
          <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                        <add input="{HTTP_HOST}" pattern="^(www.)?yourdomain.com$" />
                        <add input="{PATH_INFO}" pattern="^/yourdomain.com($|/)" negate="true" />
          </conditions>
          <action type="Rewrite" url="/yourdomain.com/{R:0}" />
        </rule>
      </rules>
			<outboundRules>
				<rule name="Outgoing - URL paths" preCondition=".aspx pages only" enabled="true">
					<match filterByTags="A" pattern="^(?:yourdomain|(.*//[_a-zA-Z0-9-.]*)?/yourdomain.com)(.*)" />
					<action type="Rewrite" value="{R:1}{R:2}" />
				</rule>
				<rule name="response_location URL">
					<match serverVariable="RESPONSE_LOCATION" pattern="^(?:yourdomain|(.*//[_a-zA-Z0-9-.]*)?/yourdomain.com)(.*)" />
					<action type="Rewrite" value="{R:1}{R:2}" />
				</rule>
				<rule name="response_location querystring">
					<match serverVariable="RESPONSE_LOCATION" pattern="(.*)%2fyourdomain.com(.*)" />
					<action type="Rewrite" value="{R:1}{R:2}" />
				</rule>
				<preConditions>
				   <preCondition name=".aspx pages only">
					   <add input="{SCRIPT_NAME}" pattern=".aspx$" />
				   </preCondition>
				</preConditions>
			</outboundRules>
		</rewrite>
    </system.webServer>
</configuration>

UPDATE : after posting I noticed the categories, date, comments, feed, author links (side menu) and pages didn’t work. This seemed to be the combination of PermaLinks plus the rules I started with, not the recent rule additions. I basically exclude rewriting any subdirectories and any .php files to index.php – this seems to be working. All the permalinks seem to be working as well without any logic for the /blog prefix.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <!-- {EA385C80-7BD5-46DC-8E47-F992B0514618}  -->
  <system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="X-Frame-Options" value="sameorigin" />
      </customHeaders>
    </httpProtocol>
    <httpErrors existingResponse="PassThrough" />
    <rewrite>
      <rules>
              <!-- permalink = /blog/%postname%/%category%/%post_id%/ -->
                <clear />
                <rule name="wordpress grab trailing post_id" enabled="true" stopProcessing="true">
                  <match url=".*" />
                  <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                    <add input="{HTTP_HOST}" pattern="^(www.)?yourdomain.com$" />
                    <add input="{PATH_INFO}" matchType="Pattern" pattern="^/blog[w-_/.]*/(d+)/?$" ignoreCase="true" negate="false" />
                  </conditions>
                  <action type="Rewrite" url="/yourdomain.com/?p={C:1}" appendQueryString="false"/>
                </rule>
                <rule name="wordpresss urls" enabled="true" stopProcessing="false">
                  <match url=".*" />
                  <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                    <add input="{HTTP_HOST}" pattern="^(www.)?yourdomain.com$" />
                    <add input="{PATH_INFO}" pattern="^/wp-includes/.*$" negate="true" />
                    <add input="{PATH_INFO}" pattern="^/wp-content/.*$" negate="true" />
                    <add input="{PATH_INFO}" pattern="^/wp-admin/.*$" negate="true" />
                    <add input="{REQUEST_FILENAME}" pattern="^.*.php$" negate="true" />
                  </conditions>
                  <action type="Rewrite" url="/index.php/{R:0}"   />
                </rule>
                <rule name="yourdomain.com" enabled="true" stopProcessing="false">
                    <match url=".*" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                        <add input="{HTTP_HOST}" pattern="^(www.)?yourdomain.com$" />
                        <add input="{PATH_INFO}" pattern="^/yourdomain.com($|/)" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="/yourdomain.com/{R:0}" />
                </rule>
      </rules>
      <outboundRules>
        <rule name="Outgoing - URL paths" preCondition=".aspx pages only" enabled="true">
          <match filterByTags="A" pattern="^(?:yourdomain|(.*//[_a-zA-Z0-9-.]*)?/yourdomain.com)(.*)" />
          <action type="Rewrite" value="{R:1}{R:2}" />
        </rule>
        <rule name="response_location URL">
          <match serverVariable="RESPONSE_LOCATION" pattern="^(?:yourdomain|(.*//[_a-zA-Z0-9-.]*)?/yourdomain.com)(.*)" />
          <action type="Rewrite" value="{R:1}{R:2}" />
        </rule>
        <rule name="response_location querystring">
          <match serverVariable="RESPONSE_LOCATION" pattern="(.*)%2fyourdomain.com(.*)" />
          <action type="Rewrite" value="{R:1}{R:2}" />
        </rule>
        <preConditions>
          <preCondition name=".aspx pages only">
            <add input="{SCRIPT_NAME}" pattern=".aspx$" />
          </preCondition>
        </preConditions>
      </outboundRules>
    </rewrite>
  </system.webServer>
</configuration>

LEARN TYPESCRIPT IN 3 MINUTES WITH THIS ONE SIMPLE TRICK.

Ha ha,  sorry had to post a link bait title after reading this post by Scott Hanselman a few moments ago.  Anyway, I finally decided to take the plunge and dive into TypeScript after the recent 1.0 release.  

Since this is my first post in a planned series, I’ll cover the basics on getting TypeScript set up and also the tools I am using with Visual Studio 2013 to assist in development.  I’m actually two weeks into the plunge and way behind on posts so I’ll try not cover too much in a post.

To get Visual Studio 2013 in shape to do TypeScript development do the following.

Step 1 : Get Visual Studio 2013 Update 2 – this includes TypeScript 1.0.1, and TypeScript support is now officially part of VS 2013.

Step 2 : Get Web Essentials 2013 for VS 2013 Update 2 – admittedly this is buggy for me right now on some computers/projects, it seems to be crashing VS 2013 very frequently on save in those cases, so I keep it disabled most of the time right now and enable only when setting up script minification and doing actual builds.

Step 3 : Get a File Nesting extension like this one.  This makes things much more manageable in solution explorer, I use it to nest related JavaScript (.js) files under TypeScript (.ts) files,  minified JavaScript (.min.js, created by Web Essentials in my case) and also source map (.map) files under the JavaScript  files.  

When I have TypeScript file in my project, the TypeScript compiler automatically “trans-piles” the file to JavaScript on save.  However, I need to explicitly include the .js file in my project OR just select show all files in solution explorer and use the nesting tool to nest it appropriately, this has the side effect of including the file.  Then right click the .js file, select the WebEssentials menu and choose minify JavaScript file.   This is a one time setting so that on every save of the JavaScript file, WebEssentials will take the further step of generating minified JavaScript files.  After I include the files, I also nest them .  The end result for a TypeScript file  ‘Widget2.ts’ is as below :


image

My next post will cover the start of the ‘plunge’

Oops! Google Chrome could not find localhost

I am currently running a Windows 7, 64-bit machine and had noticed lately that I could not access localhost using Google Chrome, but never really took the time to look into it. Finally I decided to dig a bit deeper. After some searching I stumbled on this link regarding the HOSTS file, which, not being my problem, prompted me to take a peek at it. Below are the relevant lines :

# localhost name resolution is handled within DNS itself.
#	127.0.0.1       localhost
#	::1             localhost

Dargh!

Uncommenting the IPv4 entry solved my problem

# localhost name resolution is handled within DNS itself.
127.0.0.1       localhost
#	::1             localhost

I had noticed that comment in the distant past but did not think much about it. My fix here may yield future problems so one should keep that in mind, the reasons why and a discussion regarding why this change was made to the HOSTS file can be found here.

KB 949080

Recently, after some Microsoft .NET framework updates were applied to our SQL Server, one of our SQL Agent Jobs failed, with the error

System.IO.FileLoadException: Could not load file or
assembly 'System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
or one of its dependencies. Assembly in host store has a different signature
than assembly in GAC. (Exception from HRESULT: 0x80131050)
See Microsoft Knowledge Base article 949080 for more information.</blockquote>

Upon further investigation, and reading the KB article, the solution turned out to be running the following ALTER ASSEMBLY command in the affected database.

ALTER ASSEMBLY [System.DirectoryServices]
FROM 'C:WindowsMicrosoft.NETFrameworkv2.0.50727System.DirectoryServices.dll'

References :

KB 949080
ALTER ASSEMBLY