20 November 2010

Installing Tomcat onto Ubuntu Apache2

This post describes how to install Tomcat 6 on an Ubuntu 10.10 server and connect it up with the Apache2 server using the jk module. Although there are plenty of posts about this, none gave a full list of instructions; this is what I aim to provide - let me know if you have any corrections.

I assume that you have Apache2 installed.

You will need to do most of the following actions at a root shell prompt. Get this from your login using: sudu su - You will need to edit various text files. You can do this using vi at the shell prompt. However I usually do this on my local computer using my preferred editor, transferring files to and fro using FTP.

Installing Tomcat

This command gets Tomcat 6 - and Java if need be:
aptitude install tomcat6

I'm not sure if that gets all the Tomcat files, so enter this to get them all:
apt-get install tomcat6 tomcat6-admin tomcat6-common tomcat6-user tomcat6-docs tomcat6-examples

On my system, Java was installed to here: /usr/lib/jvm/default-java/

You now need to set up Java environment variables in text script file /etc/environment
Add this to the PATH variable: /usr/lib/jvm/default-java/bin after a colon separator. Add these lines:


You will now need to reboot so these changes are used.

Tomcat should now be running. You can start, stop or restart it as follows:

/etc/init.d/tomcat6 stop
/etc/init.d/tomcat6 status
/etc/init.d/tomcat6 restart

or more easily like this:

service tomcat6 restart

Tomcat should now be running on port 8080 at your host, eg http://www.example.com:8080/ If you go there, you should see an It works page. To test it further, I put a JSP file in here: /var/lib/tomcat6/webapps/ROOT/ and saw that it ran OK. I then put a servlet WAR file in /var/lib/tomcat6/webapps/. This was soon expanded automatically by Tomcat and I was able to navigate to the servlet directory (still on the 8080 port).

You may find it useful to run the manager and host-manager Tomcat applications. To enable these you need to edit the Tomcat configuration files in /etc/tomcat6/. Edit tomcat-users.xml to include the following:

<role rolename="manager"/>
<role rolename="admin"/>
<user name="admin" password="secret_password" roles="manager,admin"/>

You should now be able to access these apps at /manager/html and /host-manager/html. You will need to enter your credentials; possibly a couple of times on first access.

The Tomcat logs are at /var/log/tomcat6/. By default Tomcat creates a different log file for each day. Keep an eye on these, and the disk space they consume.

Connecting Tomcat to Apache

Hopefully you already know these commands to test your Apache configuration and restart Apache. You'll need these later.

apache2ctl configtest
apache2ctl restart

First, tell Tomcat to listen out for connections from Apache. Edit /etc/tomcat6/server.xml to uncomment this line:

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

Restart Tomcat.

Install the jk module that acts as the connector between Apache and Tomcat:
apt-get install libapache2-mod-jk

You should see a file called jk.load in /etc/apache2/mods-available/. I edited this to contain:

LoadModule jk_module /usr/lib/apache2/modules/mod_jk.so
JkWorkersFile /etc/libapache2-mod-jk/workers.properties
JkLogFile /var/log/apache2/mod_jk.log
JkShmFile /var/log/apache2/jk-runtime-status
JkLogLevel info

You can also add JkMount instructions here, but I found that I needed to put them in each VirtualHost file in /etc/apache2/sites-available/. Eg in my default VirtualHost file I added:

JkMount /*.jsp ajp13_worker
JkMount /manager ajp13_worker
JkMount /manager/* ajp13_worker
JkMount /host-manager ajp13_worker
JkMount /host-manager/* ajp13_worker

An alternative is to define JkMount commands in jk.load and add a JkMountCopy command to copy these settings to all virtual hosts.

You now need to edit the jk worker file /etc/libapache2-mod-jk/workers.properties. Make sure it has code as follows, ie to tell jk to use the Tomcat connection we defined earlier:


Restart apache.

I was now able to access the Tomcat manager here: http://www.example.com/manager/html.

Edit your other VirtualHost files to expose any other apps that you need.

You'll find the Apache and jk logs in here: /var/log/apache2/. As above, keep an eye on these files and the disk space they use.

If you see "missing uri map" in log file mod_jk.log then you need to define JkMount in your VirtualHost.

As a final task, if you wish, disable the Tomcat 8080 port by editing /etc/tomcat6/server.xml and then restart Tomcat.

21 September 2010

Windows 7 system drive letters

I've previously blogged about Wandering drive letters in Windows 7. I've now a little more hard-won knowledge of what's going on.

Be very careful if you resize a Windows 7 system partition when the partition letter is not C. In fact, don't do it!

I am revisiting this issue because I ran into a problem. Our family shared laptop came with Vista. However this started playing up showing a DOS box regularly and Windows Update wouldn't install a particular patch. So I decided to install Windows 7 on drive D. This worked fine with W7 running with its system drive as D:.

Recently, drive D: started getting full so I decided to resize the partitions with the great Partition Wizard. This seemed to go OK. However, Windows 7 started playing up very badly, showing DwmHintDxUpdate error messages and worse. I eventually tracked the problem down to the fact that Windows 7 had decided that drive D: would now be called drive C: which is a serious problem as Windows and other software will store many full file locations (in the registry and elsewhere). If W7 thinks a file should be on drive D when in fact it is on drive C, then there's going to be serious problems. It's amazing that it booted at all.

The post by MarcusOS7 here explained what was going on, ie that Windows 7 tries to rename its system partition to drive C if the drive is resized, ie when the drive identifier changes. This is a serious flaw in Windows 7 and must be fixed.

I had hoped that with this information, I would be able to mend my W7 system. Even though I could just run regedit, I did not know what magic to do to change the system drive letter.

Eventually I decided to reinstall Windows 7. I saved the data over to an external USB drive. For this installation I wanted to be sure that W7 would install itself on drive C. Previously I must have done the installation when running the Vista system which somehow prevented W7 from using the C drive. This time, I was careful to do the install after booting into the Windows DVD. This trick ensured that W7 marked its install partition as drive C (even though it was not on the first partition).

In fact: before doing this, I re-partitioned the system so I had 3 partitions in the laptop, one each for Vista and W7, and one as a data drive. This process also helped me clear the previous W7 installation which was quite stubborn to remove, permission-wise. I eventually had to clear the partition by reformatting the drive.

Back in the new W7 system, I was able to restore the desired user data from the backup. And then I had to set up W7 again from scratch for each user, and installing all my development tools. As usual one of my first tasks is to turn off various system sounds, particularly the annoying Start Navigation click...

25 August 2010

Drupal staging site security

Suppose you have a Drupal staging site where you are preparing to go live or testing new features. You could install this on a separate domain that you have. Here's how to redirect casual users to your live site, while giving those in the know easy entry.

The crucial trick is to use a Session variable to indicate an authorised user. All Drupal access is via the root index.php file (except use of static files). index.php is amended to redirect users who do not have the session variable set correctly. Another secret file eg password.php, is used to let you get into the site by setting the session variable.

In the following example, www.example.com is your live domain and www.example.info is the staging server. The following code is on the staging server.

In index.php add this code after the line that contains drupal_bootstrap...

if( $_SESSION['password']!='asecret')
header('Location: http://www.example.com/');

Create a secret file in the root directory eg password.php with content like this:


require_once './includes/bootstrap.inc';

if( $_SESSION['password']=='asecret')
header('Location: http://www.example.info/');

$pwd = trim($_POST['pwd']);
if( get_magic_quotes_gpc())
$pwd = stripslashes($pwd);
if( $pwd=='asecret')
$_SESSION['password'] = $pwd;
header('Location: http://www.example.info/');


<form method="post">

<input type="text" name="pwd" />
<input type="submit" value="Go" />



23 April 2010

Tool to find maps KML Lat/Lng

Use this tool if you want to find a Google Maps KML GLatLng location, eg to use within your own code: http://www.phdcc.com/GoogleLatLng.htm Either drag the initial marker or enter a search term. The latitude and longitude values are listed below, to 6 decimal places of accuracy.

The map is centred on the UK, as this is where I will want to use it most. It uses the Google AJAX Search API to work out UK postcodes well. Look at the source to see how it works.

This is based on the code by cmarshall at http://www.webmasterworld.com/xml/3542700.htm

19 April 2010

DNN5 User soft-delete issues

In DotNetNuke DNN5 when you delete a user that login is no longer removed from the system. Instead they are soft-deleted, ie a new IsDeleted flag column is set in the UserPortals table. (Note that IsDeleted in the Users table is *not* set - is this ever set?)

In DNN5, code that calls DotNetNuke.Entities.Users.UserController.GetUser() etc will return a UserInfo object even if the user is deleted. Therefore you may have to check the UserInfo.IsDeleted property every time you get a user.

I would have not have implemented it this way. I'd keep the DNN4 functionality and have extra API calls to find deleted users. I wonder: does the DNN core code always check IsDeleted now?

Anyway, UserInfo.IsDeleted is not available in DNN4. As I do not want different versions of my code for DNN4 and DNN5, I have written this isUserDeleted() static method that uses reflection to detect if the IsDeleted property is available and if so calls it. It returns true if the UserInfo is null. After that, for DNN4 it always returns false.

public static bool isUserDeleted(UserInfo ui)
if (ui == null) return true;
try {
Type tUserInfo = ui.GetType();

PropertyInfo piIsDeleted = tUserInfo.GetProperty("IsDeleted");
if (piIsDeleted != null)
bool IsDeleted = (bool)piIsDeleted.GetValue(ui, null);
return IsDeleted;
catch (Exception) { }
return false;

10 April 2010

Freegle Find a Group mashup

This blog describes how I've used one JavaScript script to power several different incarnations of a Google Maps tool to help people Find a Freegle Group. Usage instructions for the Find a Group tool are on the Freegle wiki. You can also see the Find a Group gadger lower down on this page on the right.

This is primarily a technical article - for usage instructions, see the above link.

Freegle Find a Group screenshotFreegle is a network of local re-use groups in the UK. Group members give away and receive any unwanted items for free. This helps conserve the world's resources, clears clutter and helps others.

The Find a Group tool shows a UK map with pinpoint markers for each Freegle group. You can move around and zoom in as normal - clicking on a marker shows an information bubble - clicking on the link in the bubble takes you to the group web site.

Find a Group also has a search box. The tool uses Google to search for that location in the UK. If found, the tool zooms to that location and adds a marker - you are invited to look for the nearest Freegle group marker. The nearest groups are listed below

Source data

The green group markers come from this KML file - maintained by another Freegle member. This is the latest data so the tool should always be up to date.

Actually, that's not the whole truth. The tool needs to load the KML file into memory so it can search for groups. Loading the above Google Maps KML link doesn't work because of cross-site scripting security (as I understand it). Therefore the code loads a separate snapshot of the KML file from a URL that it can access.

There's a plan to resolve this issue by having the primary KML file on the iLoveFreegle.org servers. That way, my code would only need to reference one file. And the plan is to have this file generated directly from the main Freegle groups database, so it is always up to date. (Currently the Google Maps KML file has to be updated by hand.)

Mashup variants

(P) Full web page

The Find a Group tool occupies a full web page here: maps.ilovefreegle.org.

(M) Google Mapplet

The Find a Group tool appears in the Google mapplets directory here. When added to My Maps it shows filling the browser window.

(I) Iframe

The Iframe version of the Find a Group tool is designed to fit into a small window within another page. The usage instructions describe the various configuration parameters.

You can see the iframe in action at the Penrith and Eden District Freegle group and its associated helper web site.

(G) Google gadget

The Find a Group tool is packaged as a Google Gadget, defined here. You can therefore add it to iGoogle, add it to a blog, and get the code to put on a web site. The gadget has similar options to the iframe.

The gadget has a Content type of url, with the main gadget code here. The gadget options are passed as URL QueryString parameters.

(F) Facebook application

The Find a Group tool is being developed as a simple Facebook iframe application.

Common Page Layout

Each of the above tools has HTML which has the same page elements:

  • Map div called "map_canvas"
  • Noscript block
  • Div called "jsBlock" that wraps the search form
  • Input text box called "search"
  • Submit button that has an onclick handler "return doSearch();" that always returns false
  • Information div called "idInfo"
  • Debug div called "idDebug" that shows version info by default


The tool has a noscripts block that has a link to the main site groups list - this is shown if the browser has no JavaScript or it is turned off. However Chrome doesn't show this text if JavaScript is turned off - sounds like a bug to me.

The "jsBlock" search form div is not displayed by default; the JavaScript makes this block visible. This avoids confusion if JavaScript is not available.

The search text box has an accesskey of "4" defined. For the larger variants, a label is defined for this text box.

The tool HTML is hopefully valid XHTML and CSS.

API and api keys

The Google Maps/Mapplets API is used in the JavaScript. There are various differences depending on whether it is a mapplet or not. Therefore one of the JavaScript initialise() parameters is a mapplet boolean; this is stored in a global for later use throughout the code.

To use the Google Maps API, each site needs an API key. All these tools run on the maps.iLoveFreegle.org domain so the iLoveFreegle.org API key is used. The mapplet code does not need an API key.

JavaScript structure

The JavaScript is pretty straightforward procedural code, with some globals, an initialise() function and a function doSearch() that is called when a user does a search. The JavaScript code is here: http://maps.ilovefreegle.org/freegle_maps.js.

initialise() function

This takes parameter specifying whether big map controls are wanted, and the mapplet boolean.

  • If not a mapplet, check that the browser is Google Maps compatible
  • Get references to the "idInfo" and "idDebug" page elements - add the js version to idDebug
  • If the "jsBlock" page element is present, change its display style to block to make it show.
  • Get the map object and set the map height if it has been specified in a parameter.
  • Get the z SearchZoom integer parameter for later use

  • Set the map to be the approx centre of the UK at a suitable scale
  • Use GGeoXml to load the primary KML file and add it as an overlay - the code cannot access the contents of the overlay, so...
  • Initiate a separate load of the (secondary) KML for processing, either using _IG_FetchXmlContent() or GDownloadUrl()
  • Finally set the focus to the search box if required

processKML() function

This parses the received KML XML file and stores info about each Group placemark object in the Groups array.

Note that the KML file stores each coordinate the wrong way round, ie longitude, latitude then height. The GLatLng.fromUrlValue() function needs just the first two values but reversed.

Once the KML is loaded, it sees if a search has been requested and does it.

doSearch() function

The search function does a Google GClientGeocoder search and zooms in to show the result. The search also lists the nearest groups, calculated using GLatLng.distanceFrom().

doSearch() uses showAddressResult(), SearchGroups() and createMarker().

Helper functions

String.prototype.trim, PageQuery, queryString() and isInteger() were obtained from code on the Internet. I added queryString2() helper function.

I have updated PageQuery to cope with URL parameters with key=value pairs where "=value" is missing.

19 March 2010

800700C1 KB977165 and verclsid

Our Vista laptop has developed 3 problems recently, which are probably all related. Can anyone help?

Windows Update keeps on trying to install KB977165, sometimes successfully, but it keeps re-appearing.

Clicking on Check for Updates now fails with error 800700C1.

It also often shows an error with a DOS box title C:\Windows\system32\verclsid.exe and a message 16 bit MS-DOS Subsystem saying The NTVDM CPU has encountered an illegal instruction.

We did have some software for 2 HP printers installed. I've uninstalled one of them as this was suggested as being a problem.

Any other ideas pls?

09 March 2010

Crystaltech to 3essentials DNN copy

PHDCC Director John Cant describes the process of transferring a DNN site from one shared host to another, at this stage simply as a backup using a different/private domain.

The site, ourdnnsite.com is being copied from Newtek (Crystaltech) hosting to 3Essentials with domain ourdnnsite.info. Both support services are very helpful but it currently seems that 3essentials runs faster than Crystaltech, probably because of a faster database server.

The existing site runs only one portal but this contains many users and many files in /Portals/0/. Maintaining the UserIDs is crucial as much custom information in the database is keyed off this value.

Enable ASP.NET 3.5 at the 3essentials site.

First, place a holding page at ourdnnsite.info, eg index.html, that will be shown instead of Default.aspx for people accessing the root domain URL.

Make sure to truncate transaction log prior to the upgrade, clear the eventlog and maybe even sitelog and search tables, if those are exaggeratingly large.

The 3essentials site currently runs SQL Server 2005/2003. They need a .BAK backup file so that the database copy can be restored correctly.

Do a database backup on the Crystaltech site and ask for the entire site to be zipped, to get these files:

  • ourdnnsitedb_1002050754.bak
  • ourdnnsite.zip

Load these to the 3essentials site ourdnnsite.info /private/

Submit a 3essentials site support request, asking for:

  • the database to be restored from /private/ourdnnsitedb_1002050754.bak
  • the site files to be unzipped from /private/ourdnnsite.zip
  • the site file permissions to be set correctly:
    "I have corrected this issue, this involves giving the httpdocs directory IWPD User - Modify permissions which you are not able to do via Plesk."

Check the Web.Config file at the 3essentials site:

  • set the new database connection string twice
  • check the ObjectQualifier
  • set CustomErrors to 'Off' to see the errors
  • ensure the machinekey values are the same as the original values from the Crystaltech site
  • set AutoUpgrade off

On the database:

  • Update the portal alias, eg:
    UPDATE [DNN_PortalAlias] SET
    HTTPAlias='www.ourdnnsite.info' WHERE PortalAliasID=0

Access the site by visiting Default.aspx

If any errors are visible, keep deleting the sections of Web.Config that cause errors.

If you cannot log in, use Chris' technique, described here. To access the DNN database, log in using myLittleAdmin here: http://db1.3essentials.com/mla/

To prevent unauthorised access to the ourdnnsite.info site, add to the Default.aspx file a check that a cookie is set. Provide a means of setting the cookie using another private page.

There are a couple of other additions that we usually make:

  • As well as creating an app_offline.htm file, we use the Cookie technique in Default.aspx to ensure that the site is offline during maintenance, redirecting to an offline page.
  • In the skin, we warn people who are online that the site is about to go down; this also helps us see if anyone is online now.

More details on these Default.aspx techniques later if you wish.

24 February 2010

.NET app using Jet on x64

If you are trying to use an Access MDB file using the Microsoft Jet OleDb driver in .NET or ASP.NET, you must set your platform target to x86 (32 bit).

If you are running on a 64 bit system and your assembly target is Any CPU or x64 then you will see this exception:
The 'Microsoft.Jet.OLEDB.4.0' provider is not registered on the local machine.

The Microsoft ACE.OLEDB driver for MDB and ACCDB files works OK whichever platform is targeted. However this driver is not installed by default on user computers unlike the Jet driver.

This fix can be used to make existing code work with Jet/MDB, eg the Cassini server.

Useful blog links:

22 February 2010

System.String hidden UTF8 BOM

In .NET, a string (System.String) can contain an initial UTF-8 Byte Order Mark (BOM) which might not be seen in ordinary processing but is present when converted to a character array or into an encoding byte array.

For example, a text file might be saved in UTF8 format with UTF-8 Byte Order Mark bytes at the start, ie 0xEF 0xBB 0xBF. You might receive this file in ASP.NET using a FileUpload control, or read it directly in a Forms .NET app in C#:

byte[] FileBytes = File.ReadAllBytes(path);
string content = Encoding.UTF8.GetString(FileBytes);

If the file contains these 7 bytes (in hex) EF BB BF 44 65 61 72 then content will superficially contain the single word "Dear", eg as seen in the debugger, and content.StartsWith("Dear") will return true.

However, content.Length is 5 and content.ToCharArray() will return an array with 5 elements, the first being set to 0xFEFF. Similarly, Encoding.UTF8.GetBytes(content) will return the same 7 bytes as was used in the first place.

(Note that that has nothing to do with the encoderShouldEmitUTF8Identifier optional parameter for the UTF8Encoding constructor.)

As this hidden extra character can be misleading, I have written the following snippet that detects the presence of the UTF8 Byte Order Mark preamble and ignores it if present:

byte[] FileBytes = File.ReadAllBytes(path);
int StartPoint = 0;
int Count = FileBytes.Length;
if ( Count>= 3 && FileBytes[0] == 0xEF && FileBytes[1] == 0xBB && FileBytes[2] == 0xBF)
StartPoint += 3;
Count -= 3;
content = Encoding.UTF8.GetString(FileBytes, StartPoint, Count);

PS The code could not doubt be improved using Encoding.GetPreamble()

07 February 2010

Clearing nested CSS floats

Here's a XHTML/CSS technique to ensure you only clear the desired floats and get the right background areas for your DIV.

The code examples are here: http://www.phdcc.com/CSS_ClearingFloats.htm - look at the source code for full details.

My starting point is to have a DIV set with floats left and right. Within the middle unfloated DIV, I have another DIV set with floats and right. I want to clear just the middle floats, not the outer ones.

The crucial trick is to add CSS style 'overflow:hidden' to create a Block Formatting Context. Any CSS style 'clear:both' then only applies to the 'nearest'/current Block Formatting Context.

An associated problem was the fact that the background of the middle unfloated DIV goes wide to the border of the current Block Formatting Context. Adding CSS style 'overflow:hidden' to create a new context constrains the background to the expected area.

Using 'overflow:hidden' feels slightly naff: shouldn't there be an explicit CSS style to define a Block Formatting Context, instead of using something that does this as a side effect? Also, you may be concerned about what overflow is being hidden - as long as you have the default width:auto and/or height:auto set, you will not be hiding anything.

05 February 2010

Adding Access key support to DotNetNuke

This page explains how to add "access key" support to a DNN5 web site.
Access keys provide keyboard short-cuts to improve web accessibility, as described here - which also lists the UK government recommendations:

I have implemented access keys using the accesskey attribute rather than the
Role Access Model

How it works for a user

For standard users, each browser uses different modifier keys and acts minorly differently when pressed. Consider accesskey '1'. In Windows Internet Explorer, pressing Alt+1 selects the corresponding link - you must press Enter to go to that link. In Firefox, pressing Alt+Shift+1 goes to the associated link immediately.

These accesskey shortcuts take precedence over standard Window commands, so if you define an accesskey 'f' it will be acted on rather than show the File menu in Windows.

In Internet Explorer, the link is not selected if the link has a style of display:none.

Search accesskey

Most of the access keys can be defined in your DNN skin using extra HTML that is not normally seen. However, accesskey 4 takes you straight to the Search text box. To make this happen, I had to amend admin/Skins/search.ascx to add AccessKey="4" to the txtSearchNew asp:TextBox.

While there, I took the opportunity to add a label for this box, as recommended for all fields to improve accessibility. This label is present but not normally displayed:
<asp:Label AssociatedControlID="txtSearchNew" style="display:none;" runat="server">Search:</asp:Label>

Main accesskeys

The main site access keys can be defined using code like this at the top of your skin, which I adapted from another web site:

<ul id="skips">
<li><a href="#content" accesskey="s">Skip to page content, accesskey=s</a></li>
<li><asp:HyperLink ID="SkipToHome" NavigateUrl="~/" AccessKey="1" runat="server">Skip to Home page, accesskey=1</asp:HyperLink></li>

CSS (below) can then be used to make sure that this information is not normally seen, but each link become visible when it has the focus or is active. The list-style is set to none to avoid bullet points etc. When not in focus, the links have width and height 0; when in focus these are set to auto. Tweak the other settings as you wish.

Finally, don't forget to provide an anchor within your skin for the skip to navigation link:
<a id="content" name="content"></a>

CSS code:
ul#skips { list-style: none; }
#skips li { list-style: none; display: inline; }
#skips a
color: white; width: 0; height: 0; overflow: hidden; z-index: 1000;
position: absolute; top: 15px; left: 100px;
#skips a:active, #skips a:focus
color: red; border: 1px solid red;
width: auto; height: auto;
display: block; overflow: visible;
padding: 3px;

01 February 2010

DNN local install with SQL Server

The post summarises the steps required to set up DotNetNuke 5 (DNN5) locally on Windows using the full Microsoft SQL Server using SQL Server authentication, ie not the Express version. Getting the Logins and Users right is the crucial trick that I want to remember.

Click on each image to see it larger.

  1. In Microsoft SQL Server Management Studio, connect to your local server. Right-click on Databases on the left. Select "New database". In the following screen, enter a database name, eg "DNN522pen" and click OK without changing anything else.

  2. Now, find the server Security and right-click on Logins and select "New Login...":

  3. Enter a login name such as "dnn522pen", select "SQL Server Authentication" and enter the password. Untick "Enforce password policy" if you wish. Do not set the Default database. Click OK.

  4. Open up your database tree on the left: open "dnn522pen" then Security then Users. Right-click and select "New user...":

  5. In the new user dialog, enter a User name such as "dnn522pen" and the Login name you used before eg "dnn522pen". Tick "db_owner" twice below and click OK:

  6. Next, create a new folder on disk somewhere, eg in directory "D:\dnn522pen\". Unzip the DotNetNuke community installation file in there DotNetNuke_Community_05.02.02_Install.zip.

  7. Open up Internet Information Services (IIS 7) Manager. Expand the menu on the left to open Sites and Default Web Site. Click on "Add Application..." on the right.

  8. Enter a suitable alias and physical path, eg "dnn522pen" and "D:\dnn522pen\":

  9. In IIS Manager, select the new alias on the left, select Content View and click "Browse" to start the DNN installation:

  10. After a delay you should see the first DNN install screen. Choose custom:

  11. Test file permissions.

  12. Now, set up your database connection:

    • First, click on "SQL Server 2005/2008 Database" and wait for the screen to refresh

    • Enter "(local)" for the Server

    • Enter the database name, eg "dnn522pen"

    • Uncheck "Integrated Security" and wait for the screen to refresh

    • Enter the User ID and password

    • Keep "Run as db Owner" checked

    • If desired, enter a database table name qualifier, eg "DNN_"

    • Click on "Test Database Connection"

    • If OK, click on Next

  13. Proceed with the rest of the DNN installation as normal.

15 January 2010

Importing Identity column data into SQL Server

Updated 23/1/10:
I am in the process of porting some (DNN module) tables from one Microsoft SQL Server database to another. For various reasons, I want the primary key identity column values to remain the same. This is on a shared SQL server, so we don't have admin access, eg to the command line and file system.

The SQL Server Import and Export wizard transfers the data nicely and will preserve the identity values if you do it correctly.

There are two crucial tricks:
- set up the destination table(s) with the correct primary keys etc before the copy
- in the wizard, click Edit Mapping and tick Enable Identity Insert.

This is alternative technique which should no longer be needed:

I first export the original data to a temporary database. This creates the tables but but does not create primary keys, set default values etc. However, the copy process does preserve the original primary key identity column values.

By installing the DNN module on the destination system, the tables are created correctly there. However, a wizard import into these tables (even with "Enable identity insert" set) does not preserve the key values.

The trick I found is to upload the data into a new table eg "Form2", and then use the following code to copy the data into the correct "Form" table. The trick is to use the "set identity_insert" statement.
set identity_insert DNN_Form on;

insert into DNN_Form ([FormID],[FormName])
select * from DNN_Form2;

set identity_insert DNN_Form off;

You must list all the required columns in the insert statement.
Only one table can have identity_insert on at a time.

Finally, delete the "Form2" table.

You can check the current insert identity value before and after as follows:

14 January 2010

Wandering drive letters in Windows

My main computer's motherboard died - long live the new computer! Thankfully, all the data on the two existing drives were OK. A hastily bought 3.5 inch USB drive caddy worked a treat, keeping me going on the laptop.

The new computer was pre-installed with Windows 7 Home Premium on drive C with a separate partition on drive D for data. I have an MSDN subscription and I wanted to set up various versions of Windows multi-booting on the same computer. I have two different systems that I want to run normally, one with Visual Studio 2008 and the other with various older bits of software. I also want to have some different versions of Windows for software testing, ideally Windows 7, Vista and XP, both in x86 and x64 versions.

OK - I know I can run a virtual PC, but I want to use two of these systems regularly at top speed. I haven't tried virtual PC yet, so I decided to stick with what I knew, which is the HyperOs multibooter, though I had to upgrade to the latest version.

The first task was to repartition the disk, to give more partitions, each with a Windows installation, as well as big photo/video partition. I found that there were already two extra partitions, a (Packard Bell?) Recovery Partition and a small System Reserved partition set up by Windows 7, ie 4 Primary partitions in total. The W7 Disk Management tool wanted to convert my disk to Dynamic Disks to give me more partitions, but these cannot be used for booting, so a swift exit was called for.

HyperOS mentioned the Acronis partitioner but this wasn't playing - not sure if it was Windows 7 or the size of the disk. I did have a copy of gparted on CD, but this hadn't worked for me before. Eventually I found Partition Wizard www.partitionwizard.com which has worked brilliantly. I got a free commercial licence for this.

Using Partition Wizard, I deleted (empty) drive D and made quite a few other Logical partitions. Partition Wizard is clever enough to know that it cannot do changes in some circumstances, and so does it on reboot. Partition Wizard can also resize partitions, moving data if need be. So far that's worked fine. (Check your computer power settings don't shut your disk down at an awkward moment.)

Having done that, I could now copy all my precious data onto the new hard disk from the drive caddy.

Anyway, I've now done various Windows installations, some by installing from DVD from Windows, but mostly by installing from DVD at reboot, ie choosing DVD at boot up from the BIOS boot menu - and choosing Custom Setup to choose the install partition. The problem that I have found is that the drive letters that Windows uses and sees seems to change a lot. The original W7 system is on drive C on the "first" partition. I have another W7 on the fifth partition which thinks of itself as being on drive M - fine. However, most other W7 and Vista installations think of themselves as being drive C, even though they are on partition 4, 7 or 8. When I have rebooted in one of these systems, the drive letters are assigned in a fairly random way. The DVD drive is usually drive E but not always.

In Windows Disk Management you change the drive letters - for some drives at least. But there's limited scope for what you can change to. Partition Wizard can do this, and is probably more successful. However there appears to be no way of persuading a Windows on partition 4 (that thinks it is at drive C) to think of itself as being drive L for example.

All these wandering drive letters might not be problem. However my software development stuff and business data has always been carefully set up (for various reasons) to be stored on both drives C and D. I don't tend to put data in "My Documents", "Documents", "Pictures", etc because these are (usually) stored in different locations for each version of Windows. Anyway, I have persuaded partition 2 to be drive D in all the installations so far. However it was a problem that partition 1 kept wandering all over the shop. My solution was to move all my crucial data from drive C onto a bigger drive D. Ok - fairly simple in itself, but I'm still having to work out what dependencies there are in all my scripts.

Another complication in this process was that Windows XP was dying during installation (with a BSOD). This turned out to be because the SATA drives were being accessed using AHCI. Changing the BIOS to use the SATA setting "Native IDE" got the XP installation to work. However I did not want to leave this setting as is, so I change it whenever I want to switch to XP. XP also doesn't recognise many of the motherboard peripherals, eg Ethernet, so the installation is not very useful. The option to press F6 during installation would let me install a suitable driver, but (a) I don't have the driver and (b) the system doesn't have a floppy; it does look as though new motherboard has a floppy interface, but there's no connector soldered in there!

I was also able to add an IDE/PATA cable and drive to the system to connect my old drives, but the installation still did not work if I was in AHCI SATA mode.

I've still many applications to configure and systems to set up, but I'm getting there.