Navigation

Friday 11 January 2008

The Definitive Guide to Windows Supported Memory

In support of my blog on Development and Virtualisation, below is a guide to the maximum supported memory configuration for the current range of Windows Operation Systems.

Windows Server 2008 (32-bit)

OS

Maximum Physical Memory

Hot Memory Support

Add

Replace

Web Edition

4GB

No

No

Standard

4GB

No

No

Enterprise

64GB

Yes

No

Datacenter

64GB

Yes

Yes

 

Windows Server 2008 (x64)

OS

Maximum Physical Memory

Hot Memory Support

Add

Replace

Web Edition

32GB

No

No

Standard

32GB

No

No

Enterprise

2048GB

Yes

No

Datacenter

2048GB

Yes

Yes

Itanium

2048*

Yes

Yes

* Uses IA64 RAM

 

Windows Server 2003 (32-bit)

OS

Maximum Physical Memory

Web Edition

2GB

Standard SP2

4GB

Standard R2

4GB

Enterprise SP2

64GB

Enterprise R2

64GB

Datacenter SP2

128GB*

Datacenter R2

128GB*

* Requires Intel PAE

 

Windows Server 2003 (x64)

OS

Maximum Physical Memory

Standard

32GB

Enterprise

2048GB

Datacenter

2048GB

 

Windows Vista (32-bit)

OS

Maximum Physical Memory

Home Basic

4GB

Home Premium

4GB

Business

4GB

Enterprise

4GB

Ultimate

4GB

 

Windows Vista (x64)

OS

Maximum Physical Memory

Home Basic

8GB

Home Premium

16GB

Business

128GB

Enterprise

128GB

Ultimate

128GB

 

Windows XP (32-bit)

OS

Maximum Physical Memory

Home

4GB

Professional

4GB

Media Center

4GB

 

Windows XP (x64)

OS

Maximum Physical Memory

Professional

128GB

 



Understanding Content Type IDs

This is a topic that is confusing to many developers, and this post aims to clear up some of the issues around Content Type IDs, how they are structured and how you can extend them.

Content Types are re-usable definitions for meta-data that can be stored in Lists and Libraries in SharePoint. I won't go into them in too much detail, you can find a reasonable amount of information about Content Types here and Content Type IDs here on MSDN.

Content Type IDs
As you should know, Content Types are based around inheritence (and if you didn't know that, then you should have followed the links above more carefully :P). Each content type inherits from a parent content type (apart from System which is the root of all Content Types). A child content type will automatically have the same site columns that the parent has.
You can see this structure in the "out of the box" (ootb) Content Type IDs:

0x = System
0x01 = Item (inherits from System)
0x0101 = Document (inherits from Item)
0x0120 = Folder (inherits from Item)

Basically you have a {ParentID}{ChildID} structure.
This goes for all of the ootb Content Types (and you can find a list of them all in the WSS 3.0 SDK, downloadable free from Microsoft).

Custom Content Types
If you want to customise Content Types then you are looking at a slightly different ball game. For some reason (don't ask me) you cannot use the same {ParentID}{ChildID} structure. Instead you have to use a {ParentID}{00}{GUID} structure.

So lets say you wanted to create a Custom Content Type that inherits from Document and call it "Demo Document". You would create the ID as follows:

0x010100763A775BFF7849a0A65FE1F57F56CC33 = "Demo Document"

Now lets say you want to inherit from "Demo Document" and create a further child, say "Demo Sales Document", you would create the following:

0x010100763A775BFF7849a0A65FE1F57F56CC3301 = "Demo Sales Document"
0x010100763A775BFF7849a0A65FE1F57F56CC3302 = "Demo Sales Document"

Note that here we already have a custom Content Type ID, so we can just go back to {ParentID}{ChildID} again.

So each "child"  of a custom content type simply adds a unique 2-digit  id on the end of the Content Type ID. If you wanted more child types you would add "02", "03" and so on.

And you can go one step further, if you wanted to create another "child" section, for example:

0x010100763A775BFF7849a0A65FE1F57F56CC3301 = "Demo Sales Document" (inherits from "Demo Document")
0x010100763A775BFF7849a0A65FE1F57F56CC330101 = "Demo Sales Order Form" (inherits from "Demo Sales Document")
0x010100763A775BFF7849a0A65FE1F57F56CC330102 = "Demo Sales Invoice Document" (inherits from "Demo Sales Document")

So from this you can create simple and effective "Parent - Child" relationships throughout your Content Types which allow you to manage your meta-data quickly and effectively.

In the above structure, if you want to add a custom column to all custom documents? You just modify "Demo Documents" and it will also update all of the child content types too!

Closing Thoughts ...
One final thing to be careful of. If you are using CAML (Collaborative Application Markup Language) then you need to be aware of the following:
Modifications to a Content Type applied through a CAML based feature will NOT update child content types!!!!!
If you want to make sure that child content types are updated then you either have to make the change through Site Settings --> Content Type Gallery, or make your changes through managed code (such as C# using the SharePoint API).
Both of those give you the option of updating all child Content Types .. but CAML does NOT (unless I am mistaken, please let me know if I am! :))

MSDN Conference .. SharePoint for Developers ... been and gone

Well it was quite a decent SharePoint event, and full as usual (but then I've not been to an MSDN event in London that wasn't "sold-out").
It was mainly an "Introduction" to WSS 3.0 and MOSS 2007 with a bit of code thrown in to keep the developers happy, and to whet their appetite so that they go looking for some of the more technical details out there.

If you are looking for the basics, some general pointers on where to get started and some information on some of the things you can do in SharePoint then this is a great place to start. If you are already an experienced SharePoint developer then don't expect to get much out of this.

If you wanted to see the event slides (and a video of the event from a previous conference) then you can get them all from the MSDN events page.


Thursday 10 January 2008

General Ramblings on Development Environments ...

This is mainly a post in response to lots of posts I've seen on the MSDN forums asking the same question. "What should I use for my development environment"?
So I figured I would share some of the experience that I've had in this area.

The key word here is virtualisation, virtualisation, virtualisation! If you are serious about SharePoint Development then you simply HAVE to virtualise your environment. The reason for this is because of several key issues that SharePoint Development throws up:

1. The Object Model Issue - Windows Server IS required
This is a fundamental issue with any development work that utilises the SharePoint Object Model (a.k.a. The SharePoint API). It will ONLY run on the same physical machine that SharePoint has been installed on, and you can ONLY access content and sites that are running on that box.
Now this creates a number of initial stumbling blocks for developers. Most developers out there would be (and really should be) using XP or Vista for their workstation (which is quite right .. XP and Vista are the two latest Workstation Operating Systems .. you certainly shouldn't be running Server as your host OS). But, you cannot execute any SharePoint code on either XP or Vista, because you need SharePoint which can only be installed on a Server OS.

Now, you could go down the root of setting up remote debugging and basically writing and compiling your code on the workstation, deploying to the server and debugging from there by attaching to the processes.
However this means that you not only have to spend time setting up remote debugging but this can also lead to security issues if your Workstation and SharePoint servers run in different domains.

Just as a side note, our Network Infrastructure Manager would never let us do development work on the live company domain. Mainly because we would be creating dozens of test accounts and creating dozens of web servers and potentially "vulnerable" working environments. It is much safer, easier and secure to setup a separate "development domain" .. you know that you can do pretty much anything you like without annoying the IT Support department where you work ;)

2. The "12" folder.
This is a physical folder on the hard disk of the SharePoint Server and it contains the XML manifest files for all of your Features, Site Definitions (onet.xml) list definitions (schema.xml), your user controls (_controltemplates), your Site Pages (_layouts) and any other physical files and resources that you want to be globally available. The main issue here is that there is only one physical folder on the server. If you modify any of the files here then you will be modifying them for ALL web applications and ALL site collections that are running on that machine.
So if you are expecting to be doing any development which changes these files (which you most likely will if you are doing any more than just selecting options through Site Settings) then you really need to have multiple server environments to develop in.
Even if you are only working on a single project, you will still need several environments {Development | Testing | Staging | Production}

3. Global Assembly Cache
This is pretty much the same as point #2. There is only one Global Assembly Cache, and if you are creating, installing and testing dozens of different web parts (for different projects) then you are going to start getting a messy GAC. Also, any GAC references will probably require web.config changes, and this might also get messy if you are trying to write software for different environments.

4. Developing in a Team .. and debugging processes
For any software development company, this is a big one. No two users can debug the same process at the same time.
Now with standard ASP.Net development this was never a problem. Visual Studio installed it's own web server and you could run, debug and test your code on your workstation ... but with SharePoint this is not possible. You need a Server OS to run SharePoint code, and you won't be (or shouldn't be) running Server on your workstation.

So this instantly raises problems for debugging code. Ideally each developer should have their own SharePoint environment so that they can happily attach and debug processes without interfering with anyone else.

(we've had teams of 2 people where this has caused problems ... for larger teams it just gets worse!)


So .. in short, it is far easier to have multiple environments, each one with their own clean installs of WSS 3.0 / MOSS 2007 and their own clean GAC and filing systems.
You should have one environment for each Developer.
You should also have a clean environment for your Test | Staging | Production installs too!

So what about setting this up? Well Virtual Server (or the workstation version, Virtual PC) holds the answers. You can create "virtual machines" that can be copied from machine to machine and run from within virtually any host OS.

Now this is the bit that really has to be done right from the start.
Create a "base build" Virtual Machine (for example, with Windows Server 2003, Visual Studio 2005 / 2008 (for debugging)).
Do NOT attach the machine to a domain (leave it as a Workgroup .. the reason for this will become apparent later).
Install SharePoint onto that machine as a "Stand Along Install" (so that SQL Server and all the trappings go with it), along with the Visual Studio extensions for SharePoint and Workflow.

That should give you your base image
(for those wondering, our own base images are roughly 6GB in size when first created. Obviously the more work you do on them, and the more software you install, the bigger they get).

The "base image" should basically consist of a VHD (virtual hard disk) and the VMC (Virtual Machine Configuration) and it's the VHD that we want to use here, there and everywhere.

Basically .. Copy and Paste this file and you can use it to create multiple machines.
This is the good bit .. you don't need to spend 2-3 hours installing and configuring a new server every time you want a new build. Just take a copy of the VHD and off you go (so .. what .. 10-15 minutes?)

Each developer can take a copy onto their local workstation. They can use that VHD as their SharePoint development environment. As it is running locally they should have access to their local disk drives from the virtual machine, so they can copy files back and forth from their workstation to the virtual "server" environment.
If you reach the end of a project, or you've been playing about and messed up the machine you are working on, just take a new VHD copy and start again! it's that simple! And each time you start over you can guarantee that you are working on a clean, vanilla install of MOSS.

You can even run your Test / Staging environments in the same way. Just run multiple Virtual Machine environments using Virtual Server. If you are running several projects at the same time, create copies of the VHD for each project (you will only realistically be limited by disk space and memory!)

Now for the hardware
The developer workstations are going to need some pretty hefty kit if they are going to run multiple virtualised machines. These Virtual Machines are running Windows Server 2003, SQL Server 2005, SharePoint (either WSS 3.0 or MOSS 2007), Visual Studio, and problem some other tools along the way.
So they will need at least 1GB of RAM to runn smoothly.

So for the Dev Workstation, my recommendation:
Dual or Quad Core CPU (if you want to have enough resources to run multiple Virtual Machines as well as your host OS, then you need this)
minimum 2GB of RAM (and preferably 4GB .. with 2GB "speedboost" stick if you are running Vista!) Basically the more the better ... each VM will chew up 1GB each .. once you run 2-3 machines you run out of RAM real quick (and your Host OS is going to need around 1-2GB just for itself!)
Large (250GB+) Hard Disk. This is for the VHD storage. Once you start installing, running and playing about with the VHDs it is not uncommon for them to be around 10-15GB in size each. If you are running 4 projects, and take a couple of backups of each VHD then thats over 150GB of disk space gone already. Besides .. hard disks are dead cheap so why not go for a HUUUGE one :)

Anyways ... got to dash off to that conference now so thats all for today! Tune in next time and I will be talking more about development environments, this time the applications used (e.g. SPD, VSS, VS2005, VS2008 .. etc)

How to create HTML from InfoPath Text Data

Note - This was done with InfoPath 2003. The principals should be the same for InfoPath 2007 but I haven't tested it yet.
Also  .. PLEASE don't confuse this post with Form Server . This is an isolated and independent code example, which I developed mainly to create PDFs (the PDF creator I used allows input of formatted XHTML to generate the PDF).
 

I wrestled this for quite some time. InfoPath has several little nuances that you need to be aware of:

    • When you create a Text Box, the text formatting is stored using HTML. This includes fonts, colours, styles, tables and Images.
    • Line Breaks .. however ... are NOT stored using break tags. For some reason (ask the InfoPath development team?) line breaks inside text boxes are stored using a Unicode line-break character!
    • Images are also embedded as data into the text. It took some rummaging to eventually find (on the InfoPath Team Blog) that they are encoded as Base64 binary arrays!

    So .. on to the code!

    I will assume that the InfoPath data is being loaded from an SPFile object in SharePoint (i.e. from a Form Library). You can however load it from whatever you like. If you want to get the data from a file then you can use standard .Net practices to load a file from the filing system into a MemoryStream.

    The following Code Snippet will allow you to read out the string values stored in Text Boxes from the XML structure of the XML File itself.

    Code Snippet

    public void LoadSPFile(Microsoft.SharePoint.SPFile xmlFile)

    {

    try

    {

    if (xmlFile != null)

    {

    // grab the binary file data into a byte array

    byte[] fileBytes = xmlFile.OpenBinary();

     

    // create an XML Document to grab the required information

    XmlDocument xmlDoc = new XmlDocument();

     

    // parse those bytes into a memory stream

    using (MemoryStream fileStream = new MemoryStream(fileBytes))

    {

    // load in the XML form data

    xmlDoc.Load(fileStream);

    }

     

    #region InfoPath - Create NameSpaceManager

    // the "my" prefix is defined in the Root Element,

    // so we can retrieve it from there

    XmlNode root = xmlDoc.DocumentElement;

    string infoPathNsUri = root.GetNamespaceOfPrefix("my");

    string infoPathNsPrefix = "my";

    // to use the namespace in the XPath queries we must first

    // load the InfoPath URI into a Name Space Manager.

    XmlNamespaceManager nsMgr = new XmlNamespaceManager(xmlDoc.NameTable);

    nsMgr.AddNamespace(infoPathNsPrefix, infoPathNsUri);

    #endregion

     

    // we can now do "NameSpace Aware" XPath Queries

    #region Load the properties for the object using XPath queries

     

    // get document text from a TextBox called txtTextBox

    string strTextBoxValue = xmlDoc.SelectSingleNode("//my:txtTextBox", nsMgr).InnerXml;

     

    // get all TextBox nodes called "txtRepeatText"

    // from a Repeating Section called "SectionsRepeater"

    XmlNodeList sectionNodes = xmlDoc.SelectNodes("//my:SectionsRepeater/my:txtRepeatText", nsMgr);

     

     

     

    }

    catch (Exception ex)

    {

    throw new Exception("Error in LoadSPFile() - " + ex.Message, ex);

    }

    }

     

    Now note that InfoPath forms use XML NameSpaces. That means your XPath queries won't work unless you load the namespace into a NameSpaceManager and use that to select the nodes

 

Having done that, we should now have one (or more) string values which contain the Text Box "data".

As previously mentioned .. this "data" will be XHTML formatted. But Line Breaks are NOT.

 

So .. to remove Line Breaks you have to replace those Unicode Characters with a < br/ > tags.

 

Code Snippet

// remove this Unicode character that InfoPath uses for line breaks

// (yes .. I know it's annoying!)

strHtml = strHtml.Replace("�", "");

 

 

Now .. to get the correct character please copy and paste into your code. Otherwise you can use the XML Serialised view when stepping through your code, and you can copy and paste directly from there while developing.

NOTE - adding this will force you to save your Source Code files in unicode format. Visual Studio should prompt you to do that after adding this character.

Now, the code snippet we have so far is NOT fully XHTML formatted. we need to add the Namespace headers into HTML < head > tags and format the rest of the file. So we need to add the following to our string:

< html xmlns:xsf2="http://schemas.microsoft.com/office/infopath/2006/solutionDefinition/extensions" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xdEnvironment="http://schemas.microsoft.com/office/infopath/2006/xslt/environment" xmlns:xdUser="http://schemas.microsoft.com/office/infopath/2006/xslt/User" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2007-03-28T10:00:14" xmlns:xd="http://schemas.microsoft.com/office/infopath/2003" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:xdExtension="http://schemas.microsoft.com/office/infopath/2003/xslt/extension" xmlns:xdXDocument="http://schemas.microsoft.com/office/infopath/2003/xslt/xDocument" xmlns:xdSolution="http://schemas.microsoft.com/office/infopath/2003/xslt/solution" xmlns:xdFormatting="http://schemas.microsoft.com/office/infopath/2003/xslt/formatting" xmlns:xdImage="http://schemas.microsoft.com/office/infopath/2003/xslt/xImage" xmlns:xdUtil="http://schemas.microsoft.com/office/infopath/2003/xslt/Util" xmlns:xdMath="http://schemas.microsoft.com/office/infopath/2003/xslt/Math" xmlns:xdDate="http://schemas.microsoft.com/office/infopath/2003/xslt/Date" xmlns:sig="http://www.w3.org/2000/09/xmldsig#" xmlns:xdSignatureProperties="http://schemas.microsoft.com/office/infopath/2003/SignatureProperties" xmlns:ipApp="http://schemas.microsoft.com/office/infopath/2006/XPathExtension/ipApp">
  < head >
    < meta http-equiv="Content-Type" content="text/html" >
    < /meta >
  < /head >
< body >

<-- STRING DATA GOES HERE

< /body >

< /html >

 
Once we've done that, we almost have a fully valid XHTML structured file.

Now, we have to sort out the images.

In order to find all of the IMG tags we need to do an XPath query on the XHTML. However, this again means we need to declare an XHTML namespace for the XPath queries.

  Code Snippet

XmlDocument xmlDoc = new XmlDocument();

xmlDoc.LoadXml(strHtml);

// Create NameSpaceManager

XmlNode root = xmlDoc.DocumentElement;

string infoPathNsUri = "http://www.w3.org/1999/xhtml";

string infoPathNsPrefix = "XHTML";

// to use the namespace in the XPath we must first

// load the InfoPath URI into a Name Space Manager.

XmlNamespaceManager nsMgr = new XmlNamespaceManager(xmlDoc.NameTable);

nsMgr.AddNamespace(infoPathNsPrefix, infoPathNsUri);

// we can now do "NameSpace Aware" XPath Queries

 
Once we have our namespace manager we can find all of the "IMG" tags in the Body section.

 

Code Snippet

XmlNode bodyNode = xmlDoc.SelectSingleNode("html/body");

XmlNodeList imgNodes = bodyNode.SelectNodes(@"//XHTML:img", nsMgr);

 

// store filenames in here to be removed later

ArrayList arrFilesList = new ArrayList();

 

foreach (XmlNode imgNode in imgNodes)

{

string strImgData = imgNode.Attributes["xd:inline"].InnerText;

byte[] image = Convert.FromBase64String(strImgData);

MemoryStream memStr = new MemoryStream();

memStr.Write(image, 0, image.Length);

System.Drawing.Bitmap img = (System.Drawing.Bitmap)System.Drawing.Bitmap.FromStream(memStr);

 

// Create GUID filename for image

Guid imgGuid = Guid.NewGuid();

string imgFileName = @"C:\Temp\" + imgGuid.ToString() + ".jpg";

 

if (Directory.Exists(@"C:\Temp") == false)

{

Directory.CreateDirectory(@"C:\Temp");

}

 

// store the filename

arrFilesList.Add(imgFileName);

 

// save the image, and reset the HTML source

img.Save(imgFileName);

imgNode.Attributes["src"].Value = imgFileName;

 

#region Remove the "xd:inline" attribute

XmlAttribute imgInlineAttribute = imgNode.Attributes["xd:inline"];

if (imgInlineAttribute != null)

{

imgNode.Attributes.Remove(imgInlineAttribute);

}

#endregion

 

}

 

// get the newly formatted HTML and put it back in our string

strHtml = xmlDoc.InnerXml;

 

 

Now note that for tracking purposes we have created each "image" as a jpg file, with a GUID as the filename. We've used a GUID to make sure that they don't have any file conflicts.

 

Once you've finished with your HTML you can clear up those files by calling:

 

Code Snippet

foreach(string strFileName in arrFilesList)

{

System.IO.File.Delete(strFileName);

}

 

So .. now you should have formatted HTML. You can post this to the screen, or you can save it to a file (note the image file references would need to remain for the file to work though ... you could save the images and re-point them quite easily though.



Wednesday 9 January 2008

MSDN Conference .. SharePoint for Developers

Well, going to the MSDN conference "SharePoint for Developers" tomorrow, at Microsoft's Victoria offices in London.


I've even managed to arrange a meeting with the 2 presenting speakers :) (one of whom is a member of the Microsoft UK Developer Team).

Going to see if I can get any insider information on SharePoint from them, hope so.

Looking forward to the conference in any case (it's the first SharePoint development one I've seen this year) .. also hope the coffee isn't too strong like it was last time ;)


My first blog entry

Well, this is my first blog article, I've finally joined the mass ranks of bloggers out there.

This blog will (hopefully) contain my thoughts, experiences and occassionally the odd code example from my experience of working with SharePoint (both WSS 3.0 and MOSS 2007).

So .. as they say ... watch this space (or is it "spaces"?)