Wednesday, December 22, 2010

Reset Domain Admin Password

I had to change my admin password on my development domain recently... Then promptly forgot it. How do you over come this? I thought that I would have to reinstall Windows on my DC, and redo all of my domain work. Turns out there is a hack.

First to make this solution work you need to have a local account that has admin rights to the OS. This can be the built in Administrator user that is created on install, but if you are like most people that account has been disabled. If you have no local accounts active with admin access, you are screwed. Go find some third party hackerware that will hack your domain and leave your vulnerable to any attack the hackerware wants to put on.

Anyway I always create an account that is a local admin on my servers for various reasons. This account is not named administrator and is set up according to best practices. So what do?

First you need to get out your Windows Server 2008 R2 DVD. You boot to this disk and select the repair option.
You are then given several options. You want to click on the Command Prompt option.

Things get slightly tricky here, because it is ambiguous what your drives are called. On a physical server, the C drive is most likely C, but if you are dealing with a HyperV or a VMWare server, C just might not be C. You need to find your OS dive and navigate to your Windows\System32 directory.

Here you will need to do some trickery. You need to be able to get to the command prompt from the log in screen when you get back to normal mode. So we have to do some fun stuff in order to get this to happen. So... When you look at your log on screen what do you have to work with? You have the two text boxes, the switch user button, the submit button, and (ta da!!) the Ease of Access button. We need to rename the command prompt application to the Ease of Access application, so that when we press the Ease of Access button on the log in screen the command prompt opens.

So from your command prompt in the recovery console rename the utilman.exe to utilman.exe.bak and then rename cmd.exe to utilman.exe. Reboot

When you get to the log on screen, simply click on the Ease of Access button. That will launch the command prompt. From the prompt type "user administrator NewPass123" That's it! Log in now with your new password.

Tuesday, December 14, 2010

Pivot Tables

For the last few weeks I have been building a web site that would display parent companies with the services they offered. The kicker is that I needed to differentiate if that service was provided by the parent company or some sub contracting company.

So I needed a variety of tables in the database to keep track of the data. First I needed a table to keep all of the various companies data in. This table also contained a flag to differentiate what was a parent company.
Next I needed a table to keep the services in. Then I needed a couple of look up tables that matched services to companies, and sub contracting companies with the parent companies. Lots of tables.

My requirement was to display the data in a grid that had the services run across the top, the parent companies run along the left, and a check box in cells where the company could provide the services.
At any time the services could be added to or removed, so I was left with the prospect of building the table from a set of four lists.
I first did this in C#, with several ForEach loops. Easy enough, and was fine for small amounts of data. Then word came down that another team saw the web site and wanted to use it for their parent company/sub contractors. This team had been in existence for nearly 10 years, so they had hundreds of services and thousands of companies. My C# ForEach loop solution would not scale well for this much data. The performance would be unacceptably slow. What to do? Use the power of SQL server and feed the roll up data to the data service. How?

My first problem was, how to take a table and use its rows as columns. There is no foreach loop in TSQL, so how to solve? With the release of SQL 2005 Microsoft gave us a neat little function for just such a problem. It is the PIVOT function:
SELECT <non-pivoted column>,
 [first pivoted column] AS <column name>,
 [second pivoted column] AS <column name>,
 ...
 [last pivoted column] AS <column name>
FROM
 (<SELECT query that produces the data>)
AS <alias for the source query>
PIVOT
(
 <aggregation function>(<column being aggregated>)
FOR
[<column that contains the values that will become column headers>]
IN ( [first pivoted column], [second pivoted column],
 ... [last pivoted column])
) AS <alias for the pivot table>
<optional ORDER BY clause>;

Looks worse than it is. So we start with my services table:

ServiceID

ServiceName

1

Engine

2

Fuel

3

Wheels

4

Wings

Company Table:

CompanyID

CompanyName

HoldingCompany

1

Bob's Holding Company

true

2

Jim's SubContractor

false

3

Matt's SubContractor

false

4

Bruce's Holding Company

true

Service Match Table:

ServiceMatchID

CompanyID

ServiceID

1

1

1

2

4

1

3

2

2

4

3

3

Company Match Table

CompanyMatchID

ParentCompanyID

ContractorCompanyID

1

1

2

2

4

3

Fun right? Anyway what we first need to do is craft the data table that the PIVOT function will use. What we want is to match up our Companies with our Services, while joining on the other tables to make the return human readable.

SELECT CompanyName, Company.CompanyID, ServiceName, Services.ServiceID, HoldingCompany, HoldingCompanySubContractMatch.SubContractID,
CellType =
CASE
WHEN HoldingCompany = true THEN 'M'
WHEN HoldingCompany = false THEN 'S'
END
FROM ServiceCompanyMatch
JOIN Services ON ServiceCompanyMatch.QualID = Service.ServiceID
JOIN Company ON ServiceCompanyMatch.CompanyID = Company.CompanyID
LEFT OUTER JOIN HoldingSubContractMatch ON company.CompanyID = HoldingSubContractMatch.MOAHolderID

This gives us:

CompanyName

CompanyID

ServiceName

ServiceID

HoldingCompany

SubContractID

CellType

Bob's Holding Company

1

Engine

1

true

2

M

Bruce's Holding Company

4

Engine

1

true

NULL

M

Jim's SubContractor

2

Fuel

2

false

NULL

S

Matt's SubContractor

3

Wheels

3

false

NULL

S

Now we have our data table. Now we have to make some decisions. First how do we want our pivot table to look? We want the columns to be the Services, and the rows to be the companies. So we want to pivot on the the ServiceID column, but display the CellType information.
We also want to be very careful with the Services table. These rows can change at any point, so we have to dynamically load these in to the PIVOT statement.
There is no ForEach loop in TSQL so we have to do something else to load all of the columns. To do that we need to use a little trick, called dynamic SQL. We load the function as a varchar in to an EXEC statement. Inside this varchar we load a flattened list of the rows. We use the COALESCE statement to do this. So, in pieces, here is our statement:

DECLARE @ColumnHeader varchar(MAX)
SELECT @ColumnHeader=
COALESCE(
@ColumnHeader + ',[' +Services.ServiceName+']','['+Services.ServiceName+']'
)
FROM Services

First, we declare a varchar to contain the list of the would be columns. Then we use COALESCE to put the rows in to a comma delimited string.

DECLARE @Pivot varchar(MAX)
SET @Pivot = N'
SELECT
*
FROM
(
SELECT CompanyName, Company.CompanyID, QualName, Quals.QualID, MOAHolder, MOAHolderSubContractMatch.SubContractID,
CellType = CASE
WHEN MOAHolder = 1 then ''M''
WHEN MOAHolder = 0 then ''S''
END
FROM QualCompanyMatch
JOIN Quals on QualCompanyMatch.QualID = quals.QualID
JOIN Company on QualCompanyMatch.CompanyID = Company.CompanyID
LEFT OUTER JOIN MOAHolderSubContractMatch on company.CompanyID = MOAHolderSubContractMatch.MOAHolderID
) DataTable

I then declare the varchar that will be executed in the EXEC statement, and set it to the string that follows, thus the opening tick mark after the N.
Then we grab the entire table from the SELECT statement that we figured out above. We call that result set DataTable.

PIVOT
(
MAX(CellType) FOR ServiceName
IN
(
'+@ColumnHeader+'
)
)as PivotTable'
exec (@Pivot)

Here we pivot the columns and load the column names from the varchar variable that we loaded earlier.
In the syntax of PIVOT we need to have an aggregation function over the column that contains the data we want to display in the rows. In my example, the aggregated column is a varchar, so we use the MAX aggregation function. This does nothing to a varchar, so the data remains untouched.
Next we have the FOR statement determining the column that we wish to pivot on. SQL takes the data in the rows of this column, and sets them as the columns in the new result set we are creating.
The IN statement then names the columns, we load in the @ColumnHeader vachar that we created in the first part of the statement.
Finally we name the new result set "PivotTable", and close the tick. We then run the dynamically crated command in the EXEC function.
The result is

CompanyName

CompanyID

ServiceID

HoldingCompany

SubContractID

Engine

Fuel

Wheels

Wings

Bob's Holding Company

1

1

true

2

M

NULL

NULL

NULL

Bruce's Holding Company

4

1

true

3

M

NULL

NULL

NULL

Jim's SubContractor

2

2

false

NULL

NULL

S

NULL

NULL

Matt's SubContractor

3

3

false

NULL

NULL

NULL

S

NULL

So we now have a data source that now can be called as a stored procedure or a view. It contains everything we need to display the information that we want it to.
I am still working on getting the result set to display with the data rolled up in to the parent companies. If I figure it out I will post!

Monday, November 15, 2010

Create a List Dynamically With Reflection

I wanted to do more with the Dynamic List that I put up a little while ago. I wanted a method that I could pass any type, and get a List of that type out of it. After a bit of playing around, I was able to get what I wanted using System.Reflection. Here is the code:
1:  private object GetList(object[] sourceArray, Type arrayType) {
2:        Type listType = typeof(List<>).MakeGenericType(new System.Type[] { arrayType });
3:        var list = Activator.CreateInstance(listType);
4:        MethodInfo mi = listType.GetMethod("Add");
5:        foreach (object item in sourceArray) {
6:          mi.Invoke(list, new object[] { item });
7:        }
8:        return list;
9:      }  

What I did was to first create an instance of the List. Then set the nested type to be the type of the calling type. This is all done using the Type class' MakeGenericType Method. All this does is create a List were T is the type that is passed as the parameter. I still need to instantiate this monstrosity. I do that in the next line of code, using the System.Activator class' CreateInstance method. After that I have an actual list. Now I need to populate it.
Because we don't know what types and classes we are dealing with at Design Time, we can't just call Class.Method() to make things work. I first need to find the specific method I need. To do that I call System.Reflection.Type's GetMethod method. This comes out as a System.Reflection.MethodInfo type, thus the creation of the MethodInfo variable.
From here I simply iterate through the objects in the array passed in to the method, and Add them to the List object.

There is one small issue... The return is not exactly what you would expect. It is not a List of the type you pass it, rather it is a single object, of type object. The good news is that this object can be casted as the proper List when you get back to your calling method.

For instance, here is my calling method:
1:  protected void butPeople_Click(object sender, EventArgs e) {
2:        Type peopleType = typeof(People);
3:        List<People> people = new List<People>();
4:        people = GetList(GetPeople(), peopleType) as List<People>;
5:        GridView1.DataSource = people;
6:        GridView1.DataBind();
7:        GridView1.Visible = true;
8:      }  

You can see in line 4 where I call the GetList method. I send it a custom type of People, along with an array of People. As I mentioned above, the return type of the GetList method is object, but this can be casted in to the type that we wanted it to be, as seen with the "as List" on line 4.

Monday, November 8, 2010

Network Adapter Priority Windows Server 2008 R2

Something cool that I just found out about... How does Windows choose which network to use? Does it do a network test to see which network has the best throughput? No. There may be a preference built in for wired over wireless, maybe. How do you change this? What if I have a preference for one network over another. Perhaps I want to connect via my wireless network over my wired connection. What if, I have RRAS connected to my wireless adapter, and I need that guy to be the preferred connection?
HOW??

Turns out Microsoft hid it from us, sort of... What you do is go to Network and Sharing Center and click on the change adapter settings link. From there, if you have the Desktop Experience installed, you might not have the menu bar running across the top. You need to hit the Alt button on your keyboard in order to get the menu bar to show up. From the Advanced menu option, click Advanced Settings.

What will load is a funky little window with your adapters in the top part, and the bindings associated with the adapters on the bottom. For our purposes, you can ignore the bottom part of the window. We are only concerned with the top part.
In the top part you see all of your network adapters. You will also notice two arrows on the right of the selection window. What you are seeing is the adapter connection priority. The highest priority is on the top, running to the lowest priority on the bottom.
All that you need to do to set an adapter with a higher priority is to highlight the adapter, and click the up arrow. That's it. Simple solution to a difficult problem.

Friday, November 5, 2010

Set Up Hyper-V With an Internal Network

Hyper-V is Microsoft's server virtualization product. Unlike Windows 7's Virtual PC, this product is a true vitalization suite. It allows you to create 32bit and 64bit virtual computers with any OS. This makes it very handy to create a virtual server farm for development or R&D use. Personally I use mine to host development environments for SharePoint 2010, and MOSS 2007.
If you want to create virtual servers that are available on your current network, simply attach your network card to the VM and off you go(more on this later), BUT if you want a private network separate from your public net work, i.e. you want to create a separate domain for testing and dev work, but want to keep your workstation connected to your current domain, it gets a little tricky.
I did my configuration using RRAS, not ICS. For one, RRAS is much more flexible than ICS, for another, RRAS is the professional way to do such a configuration. And I am always the professional. Like that guy in the movie Professional... Except I don't kill people... and I have friends other than a plant... and I don't have a French accent... and I am not being chased by Garry Oldman... and I don't live in New York... Other than that it is just like the movie.

Install Hyper-V
Hyper-V is a Role that you need to add to your host server. So, open your old friend Server Manager and head to the Server Roles section. Check Hyper-V and install.

Configure Networking
Now things get fun. In Hyper-V Manager, click on Virtual Network Manager. Create a new Internal network and give it a meaningful name. That name will become very important, so be sure to name it well. I called mine Hyper-V Internal Network.

What Microsoft has done with Hyper-V is to separate the physical network card completely from the OS. It then creates a virtual switch with several virtual network adapters connected to it. This is kind of complex, and you can go HERE if you want to learn more. What concerns us here is what happens with the Host's network adapters.
Open up Network Sharing Center, then click on Change Adapter Settings. You will notice that there is a new icon there. It will say something like Local Area Connection 2 or some other number. What is important is that the last line in the description, the binding information, should be the name that you gave your virtual network back in the Hyper-V manager.

Configure the Host's Virtual Network Adapter
Right click on the icon representing the Hyper-V virtual network adapter and select Properties. Double click on the v4 network settings.
Setting up the Host adapter is kind of funky compared to normal network set ups. First of all, we will not be putting in a gateway address. The virtual switch will be the gate, and if we specify a gate here, it screws everything up. In addition to this, we will not be putting in any DNS IPs either. Again, the switch will act as the DNS relay. The only thing we need to put in this area is the IP address and mask of the Host on the internal network. This address must be on a different subnet than the subnet that the host is on for the public network.
It makes sense if you think about it. Essentially, you are configuring your server to be a router. Routers need two network cards and need to have each card on a different subnet. Since you are creating a private VLAN, you will need some sort of router or switch to get you to other networks, thus the separate subnets.
So, my public network is, 192.168.107.x, I made my internal network 192.168.2.x.
To reiterate, on the HOST, the virtual adapter settings should just be the IP address and mask of the HOST in the subnet of the internal network.
In my set up my IP is 192.168.2.1, subnet 255.255.255.0.

Install RRAS
Back to the Server Manager for another role install. This time it is the Network Policies and Access Services role. Check that, then select Routing and Remote Access Service. Be sure both boxes underneath the selection are checked. Unless you are familiar with access restrictions, DO NOT install the Network Policy Server. This can mess up your installation if you don't know what you are doing. I didn't need this service, so I did not install it.

Configure RRAS
From here you will click on the new role that you installed (Network Policies and Remote Access) and right click on Routing and Remote Access. Select Configure. We are going to create a policy for NAT, so that is the guy we select. The next screen deals with which interface has the Internet. Select your public network interface and select next. Now we select the interface that we want to NAT on. Select the internal Hyper-V interface. That completes the initial configuration. We still have to configure NAT. You thought you were done with that, but you are not!

Configure NAT
Open up RRAS, then IPv4. Click on NAT. Right click on your public interface and select Properties. Click the radio button that says Public interface connected to the Internet. Check the box that said Enable NAT on this interface.
From there go to the Services and Ports tab. Check the box for the the various services that you want to use NAT for. I just want Internet, so I checked Secure Web Server(HTTPS), and Web Server(HTTP). When you check the boxes it will ask you what IP address you want to NAT from. Put in your interface IP address (for me that was 192.168.2.1). Click OK.
Done!

Install VM
From here You want to create your first Virtual Server. Set this up how ever you like it according to your needs. The network adapter that you want to use will be the name of your internal network.
When your OS is done installing, you will notice that it will have an IP address if you have enabled DHCP. This comes from the RRAS server. If you want a static, simply put in your address and mask from the internal network subnet. Your gate will be the Host interface, as will be your DNS servers.

DONE!

How To Set Up Windows Server 2008 R2 As a Kick Ass Workstation

Everybody knows that I am a Microsoft Guy... I make the majority of my money using and developing for their products. That said, they have massively screwed up at times. So badly that I have refused to work on several of their platforms (Windows Me, Windows Vista, Windows 2008). I have to admit Windows Server 2008 R2 is the bees knees. It is everything I have asked for in a modern operating system. This is R2, mind you, not Windows 2008. While a good start that OS wanted to BSOD every few moments... not fun.

Anyway, I like the OS so much that I wanted to use it as the OS on my workstation. Why? You get all of the benefits of 64bit with none of the hangups that Windows XP and Windows 7 seem to have. You get all of the features of a Windows 7 workstation, PLUS IIS 7.5, PLUS Hyper-V, PLUS RRAS, PLUS PLUS PLUS. It is awesome. In the past I have attempted to use a Server OS as my workstation OS, and I always ran in to some sort of hang up, I couldn't game on it, or It wasn't as user friendly, or it lacked some aspect of the workstation OS. Windows Server 2008 has none of those hang ups. It handles hardware, even advanced graphics cards, like a champ, and gives all of the nice to have perks that you are used to in a workstation OS. BUT... You have to work at it. There are lots of stuff you need to do to get the OS like you want it. I am going to go through what I did to get my Windows Server 2008 R2 working on my IBM Lenovo T410.

Install Desktop Experience
First things first... You look at Windows Server 2008 after install, and it looks like well... It looks like a server OS. Where is all of the pretty??? We want themes, and that cool graphical active program selector thing, and rotating background images, and stuff!!! We also want the ability to download and use Active X components as well as other WebDAV and whizbang web stuff. We get all of that by going in to the Server Manager and installing the Desktop Experience feature. It is easy, just check the box.
After installing the Desktop Experience we have all of the stuff right? Nope... That would be too easy. If you want Microsoft's whole new Windows 7 "glass" stuff, they call it "Aero", you need to open up the Services area in the Server Manager and find the Themes service.
After that, simply download the Windows 7 theme of your choice, and you are off to the races... So to speak...

Configure Wireless
A very annoying aspect of installing Windows Server 2008 on a laptop is that you have to jump through hoops to get your wireless network card to work. I must admit that I wasted a lot of time on this issue. I kept thinking that the OS had a problem with the driver. Nope, it liked the driver just fine, you have to install the Wireless LAN Service in order for it to work. Back to the Server Manager and open up the Features area and add the Wireless LAN Service Feature. If you card is installed correctly, immediately after installation is complete, you will see the OS looking for wireless networks.

Configure IE and Web Client Service
IE on Windows Server 2008 R2 is a pain in the neck. Even if you change your security and privacy options, it still complains to you that you are not as secure as you should be. There is a fix for that! You need to change the policy that is complaining.
First turn off IE Enhanced Security by going in to Server Manager. On the very first page is the Server Summary. On that page there is a link that says Configure IE ESC. It is on the right hand side in a box under the Security Information section. Set what you want for your IE annoyances just for users or just for admins. I set both to off. After that you are free to change your security and privacy settings to however you like them. Depending on your settings in IE, you may get a banner warning telling you that your security configuration has left your computer vulnerable. I hate that... So, let's get that changed.

Disable Security Warnings in IE
You will need to open up the Group Policy Editor (gpedit.msc, from run in the start menu, or somewhere else). From there go to:
Local Computer Policy
Computer Configuration
Administrative Templates
Windows Components
Internet Explorer
Double-click on “Turn off the Security Settings Check feature” and set it to Enabled. Done!

Monday, November 1, 2010

Dynamic List

I needed to create a method that would take any type, and return a List of that type. The catch was I didn't know what type T would be. How to get that done?

Like this:
1:  private object CreateList(Type type){
  
2:   System.Collections.IList lst = (System.Collections.IList)Activator.CreateInstance((typeof(List<>).MakeGenericType(type)));
  
3:  return lst;
  
4:  }  

Your calling code will need to cast the return correctly, but that one line will get you your dynamic typed list.

Thursday, October 28, 2010

SharePoint Explorer View, Windows 7, and the WebDAV Redirecter

Nothing is more frustrating than having something work on one computer, then not working on another... Some of this is to be expected, especially when upgrading Operating Systems, but with server based products, you expect most clients to act the same way. They are, after all, connecting to a program on another computer.

My frustration was the Explorer View in SharePoint. This view is a great tool for uploading lots and lots of files to a document library. It is also a great tool to show users that are used to having their documents on a file share. This view gives them a nice warm fuzzy that things aren't changing that much for them.

What I found was that in Windows XP IE 8, I could open the Explorer View just fine, however when I walked over to my Windows 7 IE 8 box, no dice. What the...

What I found was this. Microsoft changed how they handled various functions that are allowed to access to the Operating System. WebDAV being just one such function. It is important to note that SharePoint has its own WebDAV functions separate from IIS' WebDAV. More on that later.
In Windows 7 the WebDAV Redirecter Service must be turned on in order for SharePoint WebDAV to have access to Windows Explorer. If the service is not on, you either get an error saying that your browser doesn't support Explorer View, or, most frustrating of all, nothing happens. Flip on the Web Client Service, and you should be good to go.
Yeah, I realize that the service name is Web Client Service, and that the process is called WebDAV Redirecter. I don't know why. Microsoft is funny like that.

So, say you are setting up your server for a new SharePoint implementation, and you want to use the Explorer View for your document libraries. What do you do? Ignore WebDAV. Seriously. On both IIS 6 and IIS 7/7.5 servers, do not enable or install the WebDAV features. Windows 2003 will work regardless of it being enabled, but installing it on Windows Server 2008 will break Explorer View. So it is just good practice to leave WebDAV off of the box entirely. Why enable something that has to be patched, updated, and monitored if you are not using it?

Friday, October 22, 2010

Presence in SPGridView MOSS 2007

A company I work for will be incorporating Windows Communicator with their VOIP and IM system.
We also have a web based Corporate Directory application.
Wouldn't it be nice, someone thought, if we could put the Windows Communicator Presence Indicator in to our Corporate Directory?
The Corporate Directory is currently a SharePoint Data View Web part. Adding Presence would require custom development.
Off I go.

The first issue I ran in to was that there is some debate on how to get Presence information in to SharePoint (MOSS) 2007, some blogs say to use the IMNRC java script, while others say to use the imnmark image with a IMNImageOnClick java script event.
Confused? Me too. The problem is that Microsoft left the old way to do Presence in to the new version of SharePoint, so what would work in 2003:


<span><img border=\"0\" height=\"12\" src=\"/_layouts/images/imnhdr.gif\" onload=\"IMNRC('"you@domain.com"')\" ShowOfflinePawn=\"1\" alt=\"\" id=\"user_presence_icon\"/></span>


will work in 2007, but the way Microsoft does Presence in 2007 is with the anchor and image tags:


<a href="javascript:" onclick="IMNImageOnClick();return false;" class="ms-imnlink"><img title="" alt="No presence information" name="imnmark" border="0" valign="middle" height="12" width="12" src="/_layouts/images/blank.gif" sip="you@domain.com" id="imn1,type=sip" ></a></span>


Great! It works for just one user, with their email address hard coded. The problem is that I have lots of users, and everything is stored in a database.
Easy!, you say, Just use a GridView and put that stuff in there!! Sounds good, but SharePoint always has a way of messing with us. The SharePoint version of a GridView is the SPGridView. It is just like a GridView, only harder. In SharePoint you do nearly everything in the code. It sucks. Personally, I like the ability to use the Design tap of Visual Studio it lets me know what I have going. So, I tend to use User Controls so that I can see what is going on. Only that does not really apply here.
Anyway, typically what you would do in ASP.NET programming would be to drop a GridView on a page and set up your fields. Because we are using some HTML you will need to create some template fields. Generally looks like this:


<asp:GridView ID="GridView1" runat="server">
<Columns>
<asp:TemplateField>
<ItemTemplate>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>


No problem! Except that we are in SharePoint and we have to do all of that in code...

This is where it really gets sticky. You will notice that the Template Field is called as a control. That means that it is a part separate from the GridView. That makes your life in SharePoint difficult.

As Oscar Medina details in his awesome blog post SPGridView - Using a custom TemplateField to add a Checkbox Column you can declare a Template Field inside your SPGridView, but you will need to create a class for your Item Templates.

A class that inherits from ITemplate, and you will need to override the InstantiateIn method in order to stick your stuff in.

During my very frustrating trial and error period, I found that the best way to get the Presence stuff to work is to use the new 2007 method, but convert all of that HTML in to ASP.NET controls. The ITemplate object likes those better. So what I ended up doing was creating two Literal Controls for the span tags, a Hyperlink Control for the anchor, and an Image control for the image. I used the Attributes.Add for all of the extra stuff in the anchor and the image tags that is not supported in the normal control.
So your code from above now becomes:


LiteralControl lc = new LiteralControl();
lc.ID = "lcSpan1";
lc.Text = "";


HyperLink hypLink = new HyperLink();
hypLink.Attributes.Add("onclick", "IMNImageOnClick();return false;");
hypLink.CssClass = "ms-imnlink";
hypLink.NavigateUrl = "javascript:";


System.Web.UI.WebControls.Image imgPresence = new Image();
imgPresence.Attributes.Add("name", "imnmark");
imgPresence.Attributes.Add("id", "imn1,type=sip");
imgPresence.ImageUrl = "/_layouts/images/blank.gif";
imgPresence.Attributes.Add("sip", "you@domain.com");
imgPresence.ID = "imgPresence";


LiteralControl lc1 = new LiteralControl();
lc1.ID = "lcSpan2";
lc1.Text = "
";


Sweet huh? Again, it works great for hard coding a single user to a Presence indicator. We don't want that. We want something we can stick in to a SPGridView to display a whole bunch of people. Here we go.

First you need to set up your data source. I like to use DataTables.


DataTable DT = new DataTable("Data");
DT.Columns.Add("Name");
DT.Columns.Add("Position");
DT.Columns.Add("Presence");
DataRow row = DT.NewRow();
row["Name"] = "Natto Ninja";
row["Position"] = "Goofball";
row["Presence"] = "nattoNinja@domain.com";
DT.Rows.Add(row);

row = DT.NewRow();
row["Name"] = "Megan Fox";
row["Position"] = "Megga Hottie";
row["Presence"] = "iamhot@domain.com";
DT.Rows.Add(row);

row = DT.NewRow();
row["Name"] = "Al Sharpton";
row["Position"] = "King of the Goofballs";
row["Presence"] = "KOG@domain.com";
DT.Rows.Add(row);


DataTables are fun. Now we need to create our SPGridView. There are a couple of different ways to do this, if you are using a control, you can use Ted Pattison's approach or you can just create it in the code. Calling it in the code makes it tougher for you to place on the page, but Pattison's approach requires you to create a code behind (not a code beside, very important distinction. If you don't know the difference you need to read Pattison's article). At any rate you have to create the grid.


SPGridView grdSPGrid = new SPGridView();
//Createyourfields
SPBoundFieldfldName=newSPBoundField();
fldName.HeaderText="Name";
fldName.DataField="Name";
grdSPGrid.Columns.Add(fldName);

SPBoundFieldfldPosition=newSPBoundField();
fldPosition.HeaderText="Position";
fldPosition.DataField="Position";
grdSPGrid.Columns.Add(fldPosition);

TemplateFieldfldPresence=newTemplateField();
fldPresence.HeaderText="Presence";

//Call your class that creates your Item Template
fldPresence.ItemTemplate=newGridViewTemplate("Presence");

grdSPGrid.Columns.Add(fldPresence);

//Call the method that inserts your row information in to your image control
grdSPGrid.RowDataBound+=newGridViewRowEventHandler(grdSPGrid_RowDataBound);

//Bind the the DataTable to the SPGridView
grdSPGrid.DataSource=DT.DefaultView;
grdSPGrid.DataBind();


You will notice the place where I call a class to get my Item Template going. It is in that class where I create my objects that will become my Presence indicator.
Here is that class:


public class GridViewTemplate : ITemplate
{

private string columnName;

public GridViewTemplate(string colname)
{

columnName = colname;
}

public void InstantiateIn(System.Web.UI.Control container)
{

LiteralControl lc = new LiteralControl();
lc.ID = "lcSpan1";
lc.Text = "";
container.Controls.Add(lc);

HyperLink hypLink = new HyperLink();
hypLink.Attributes.Add("onclick", "IMNImageOnClick();return false;");
hypLink.CssClass = "ms-imnlink";
hypLink.NavigateUrl = "javascript:";
container.Controls.Add(hypLink);

System.Web.UI.WebControls.Image imgPresence = new Image();
imgPresence.Attributes.Add("name", "imnmark");
imgPresence.Attributes.Add("id", "imn1,type=sip");
imgPresence.ImageUrl = "/_layouts/images/blank.gif";
imgPresence.ID = "imgPresence";
container.Controls.Add(imgPresence);

LiteralControl lc1 = new LiteralControl();
lc1.ID = "lcSpan2";
lc1.Text = "
";
container.Controls.Add(lc1);


}

}


That builds our template field. Now for the last little piece. There has to be a way to insert the data from the DataTable in to a variable that we can set in to the image control.
To do that, we need to go in during the data binding of the SPGridView. Which is why I have this line in the code that creates the grid:


grdSPGrid.RowDataBound+=newGridViewRowEventHandler(grdSPGrid_RowDataBound);


That guy calls this method in the same class:


void grdSPGrid_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
string row = "";
//Find the row with the data
DataRowView rowView = (DataRowView)e.Row.DataItem;
row = rowView["Presence"].ToString();
//Create a new image control and cast it as the control in the grid
System.Web.UI.WebControls.Image imgPresence =
(System.Web.UI.WebControls.Image)e.Row.FindControl("imgPresence");
imgPresence.Attributes.Add("sip", row);

}


}


That is all there is to it! It took me nearly a week to figure all of this stuff out. It was a lot of fun finding the right events, and creating the proper event handlers and stuff.

In case you are confused, the code that creates the SPGridView and the code that does the special binding all should be in the same class. The class that creates the ItemTemplate can be in a separate class file, or just as another class in the same class file as your other class.
Hope this helps some people.

Many thanks to Oscar Medina and Ted Pattison. Their posts really helped be out, and I surely would not have gotten my grid to work without them.

Thursday, October 7, 2010

SharePoint Template Gotchas

I am having to do a lot of simple web part movement and site design stuff lately and I ran across some issues that threw me...

In the past, I have created a site and configured the basic web parts that I wanted set up the primary pages generically and configured everything the way I wanted to see it anytime I created a new sub site. I would then go to the Site Settings and to Look and Feel then save the site as a template. Anytime I wanted my generic site look, I would simply create a new subsite with this template. Easy peasy.

This time I was working on a Publishing Site that would need several sub sites that would all have the same basic properties, lists, and web part views. I created the first site, clicked on up the Site Settings, looked over at Look and Feel and... Nothing. No Save Site as a Template link. What the...?? I stated going through the site features and even in to Central Admin to see if I had all of the Farm Features active that I needed to be active. No Dice.

It turns out that Publishing Sites can not be saved as templates. Bummer.

SharePoint being SharePoint, many times you can do things you are not supposed to do if you know how to use the forms and other features, I decided to poke around to see if I could get things to work the way I wanted to. The first thing I found was that if I deactivated the Publishing features, I could save the site as a template, create a new site, then activate the Publishing features at that time... This would do what I wanted... kind of. I wanted a better solution, so I tried a trick that I learned to do when I had a number of web parts that I wanted to delete from a page, to grab the actual template save application page URL and append that on to my publishing site URL to see if I could get it to save the template as I wanted it.

The URL of the save as a template application page is: _layouts/savetmpl.aspx. So my site URL: http://MOSS2007/TemplateSite became http://MOSS2007/TemplateSite/_layouts/savetmpl.aspx
To my wonder and surprise the application page popped up and I was able to save the site as a template. I created a new site using this template, and it worked like a champ.
Turns out my buddy Shane Young already had this figured out way back when. Go figure...

Wednesday, August 18, 2010

Web Sites, Databases and Kerberos

For some reason the documentation on how to set up Kerberos in your environment seems to be very sketchy out there. So I will write out what you need to do to get things to work.
In my example we will think of a system where we have a user, a web application server, and a database server. In our situation the database server is set up so that the user is granted execute access to the stored procedures called by the web application.
  1. ALL servers that you will be using need to be trusted for delegation in AD. No exceptions. If any servers involved are not trusted for delegation, the chain fails and the user credential will not propagate to the next hop. That means that all web servers, all database servers, and whatever application servers you have in your environment need to be trusted, regardless of the next steps.
  2. All users involved need to be trusted for delegation. This includes the end users, and any service accounts that may be running your application pools.
  3. If you are using a DNS alias for your web site a Service Principal Name needs to be created for that alias. This is where things get tricky.
    • If there is only one web server in your environment you would register the DNS alias with that server. You would then use the Network Account as the ID to run your application pool under.
      • setspn -a HTTP\MyDNSName WEBSERVER
    • If you have a web farm, the IP that owns the DNS alias exists on some other device, so what server do you register your SPN under? You need a domain account to register your DNS SPN. If you are using IIS 6, you will need to use this account that the DNS SPN is registered under as the ID that your application pool uses for its identity. If you are using IIS 7 and up, you do not need to use this account, but any account the application pool uses as its identity will be need to be trusted for delegation.
      • setspn -a HTTP\MyDNSName MyDomain\ServiceAccount
  4. Any database that is used will need to be registered with a SPN as well. If you are using a named instance as your server, you will need to register the instance as ServerName:PortNumber. Again, you will need a domain user, trusted for delegation, to register your database instances under. This account does not necessarily need to be the account you installed SQL under, but it does need to be a valid domain account.
    • setspn –a MSSQLSvc/ServerName:PortNumber MyDomain\SQLServiceAccount
  5. In IIS, the web site must be set to Integrated Windows Authentication. You still need to get the user credential from the user, this is how it is obtained. No other type of authentication will work, it must be Windows Authentication.
    • If your application is a .NET application, you will want to make sure that your authentication tag in your web.config is set to Windows.
      In IIS 7 and up, just by clicking Windows Authentication will update your web.config file, but in IIS 6 you will have to make sure it is there.
    • If your app is not a .NET app, IIS will handle the authentication part for you, however the next step might be tricky...
  6. Next, you will need to tell your application to impersonate the user that is logging on to the system. In IIS 7 and up that is easy, you simply check the box in the Authentication section that says ASP.NET Impersonation, and you are done. In IIS 6 you will need to add an identity tag to your web config, then set impersonation equal to true (<identity impersonate=true />)
    • If your app is not a .NET application, and you are not using IIS 7 and up, you will need to follow whatever steps your platform uses to impersonate users.

  7. Finally, you will need to write your connection string so that it uses integrated windows authentication. For a MS SQL server that is Integrated Security=SSPI. That is all you need do, no user name, no password.
    • If you want to use Integrated Security, but instead of the end user, you wanted to use the application pool identity, all you would need to do is uncheck ASP.NET Impersonation option (IIS 7 and higher), or remove the >identity /> tag (IIS 6) from the web.config. The IIS impersonation credential hierarchy is user, app pool, web site anonymous account.

I hope this helps. I have found that where I tend to make mistakes is making sure my applications are set to Windows Security, and the Database instances have SPNs.

First Post

This blog is mainly so I don't forget stuff... I create cool fixes or find something out just to document it at a company, only to loose that information later on when I leave that company. I don't want that to happen anymore, thus the blog.
I will attempt to phrase my posts so that others can read and use my Ramblings, but don't expect them to make much sense.
If you want to leave a comment, you will have to follow my blog, I get too much spam by leaving it open. I will attempt to respond to every posting. The only stupid question is the one not asked.