Skip to main content

Weblog Ton Stegeman [MVP]

Go Search
Home
  

ODC 2008
If you have a question or suggestion, please contact me through Windows Live Messenger.
My status: .

If I am not online, please send me an e-mail.
Weblog Ton Stegeman [MVP] > Posts > SharePoint RPC part 2 - creating a subsite
SharePoint RPC part 2 - creating a subsite

In a previous article I showed how you can create MOSS publishing pages using SharePoint's FP-RPC. In this post I will describe how you can create a sub site using RPC. In this case we're using a combination of Frontpage RPC and WSS RPC. The first step is to create the site using RPC. After this, the site needs to be provisioned. In this step we will use WSS RPC to apply the site template.

I am running the sample code below in a WinForms application. In the UI I can enter the URL for the parent site, the new url of the sub site, and the name of the site template. The UI looks like this:

image

You will find the code in this zip file. This zip also contains the sample code for the first article about RPC.

Step 1 - Create the site (FP RPC)

In the code snippet below you will find the code to create the site. After initializing the variables, the rpcString is encoded (in GetRPCCall) before posting the RPC command using a WebClient.

private string CreateSite(string siteUrl, string parentUrl, string newUrl)
{
    string method = "create+service";
    string service = string.Format("/{0}/{1}", parentUrl, newUrl);
    string target = "_vti_bin/_vti_adm/admin.dll";
    string path = string.Format("{0}/{1}/", siteUrl, parentUrl);

    string rpcString = "method={0}&service_name={1}";

    // Get the RPC call data
    rpcString = String.Format(rpcString, method, service);
    byte[] callData = GetRPCCall(rpcString);
    string url = path + target;

    // Send the request.
    byte[] result = null;
    using (WebClient client = new WebClient())
    {
        client.Credentials = System.Net.CredentialCache.DefaultCredentials;
        client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
        client.Headers.Add("X-Vermeer-Content-Type", "application/x-www-form-urlencoded");
        result = client.UploadData(url, "POST", callData);
    }

    // Return the result.
    return System.Text.Encoding.UTF8.GetString(result);
}

After running this code succesfully, you will get a HTML response that looks like this (it just shows the first few lines):

<html>
<head>
    <title>vermeer RPC packet</title>
</head>
\n<body>\n<p>
    method=create service:12.0.0.6219\n<p>
        message=successfully created service '/News/Demo1'\n<p>
            service=\n<ul>
                \n<li>service_name=/News/Demo1\n<li>meta_info=\n<ul>
                    \n<li>vti_defaultlanguage\n<li>SW|en-us\n<li>vti_servercharsets\n<li>VX|windows-1257

When you navigate to your new site, it will look like this:

image

So we now have a site, but the template still needs to be applied.

Step 2 - Apply the template (WSS RPC)

First we initialize our variables and get a valid RequestDigest. This is a value that is used for security validation. The SharePoint object model contains a function to get a valid requestdigest for the Central Administration pages, but I didn't get it to work using that. Instead I request the parent site of our new site in code and extract the ID from the HTML that is returned by SharePoint.

string target = "_vti_bin/owssvr.dll";
string path = string.Format("{0}/{1}/{2}/", siteUrl, parentUrl, newUrl);
string rpcString = string.Empty;

// Get valid RequestDigest by getting the homepage of the parent site and parsing the HTML.
string digest = GetRequestDigest(siteUrl, parentUrl);

This digest string looks like this:
0x926E57EB23D64181BE56EA7C614FFDCD79206C697E290C588231A5359C6CA9975065D871616C185AC635A2BD5960D33B8EAFB00A3E7E8BDF279D365DBB31A5D8,17 Jul 2008 18:44:23 -0000

Here is the code to get a valid RequestDigest:

private string GetRequestDigest(string siteUrl, string parentUrl)
{
    string search = "id=\"__REQUESTDIGEST\"";
    string url = string.Format("{0}/{1}", siteUrl, parentUrl);

    using (WebClient client = new WebClient())
    {
        client.Credentials = System.Net.CredentialCache.DefaultCredentials;
        string digest = client.DownloadString(url);
        int p = digest.ToLower().IndexOf(search.ToLower());
        if (p > 0)
        {
            digest = digest.Substring(p+search.Length);
            digest = digest.Substring(digest.IndexOf("\"") + 1);
            digest = digest.Substring(0, digest.IndexOf("\""));
            return digest;
        }
    }
    return string.Empty;
}

I am not extremely happy about this implementation. If you have a better suggestion, please let me know.
If you do not pass a valid value to the WSS RPC call, your call will fail and you will get this WebException:

{"The remote server returned an error: (404) Not Found."}

Next thing to do is initialize the parameters for our RPC call, encode them and turn them into a byte array:

rpcString += string.Format("__REQUESTDIGEST={0}&", digest);
rpcString += "Cmd=DisplayPost&PostBody=";
rpcString += "<Method ID='Text'>";
rpcString +="<SetVar Name='Cmd'>SiteProvision</SetVar>";
rpcString +="<SetVar Name='CreateLists'>True</SetVar>";
rpcString += "<SetVar Name='SiteTemplate'>STS#0</SetVar>";
rpcString += string.Format("<SetVar Name='__REQUESTDIGEST'>{0}</SetVar>", digest);
rpcString += "</Method>";

// Get the RPC call data
byte[] callData = System.Text.Encoding.UTF8.GetBytes(rpcString);
byte[] data = new byte[callData.Length];
callData.CopyTo(data, 0);

Last thing to do is setup a new WebClient that will handle our request and return the result:

string url = path + target;

// Send the request.
byte[] result = null;
using (WebClient client = new WebClient())
{
    client.Credentials = System.Net.CredentialCache.DefaultCredentials;
    client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");

    result = client.UploadData(url, "POST", data);
}

// Return the result.
return System.Text.Encoding.UTF8.GetString(result);

The result of our second step looks (if succesfull) like this:

<Result ID="Text" Code="0">
</Result>

And when we now navigate to the site, we will have a normal SharePoint site:

image

Click here to download the source code.

Comments

Fantastic post!

Great post Ton!

-iwkid
at 7/18/2008 8:23 AM

Just a small mistake in the source code

in the ProvisionSite()

the Template STS#0 is harcoded and should be replace by the argument "string template"
at 10/13/2009 3:02 AM

GetRequestDigest 401 error

in the GetRequestDigest()

I get a 401 unauthorized error.

I use this in WSS 3.0. After debugging with Fiddler I saw that the response was a Object Moved page, redirecting to the url + default.aspx.

So I changed this:

string digest = client.DownloadString(url);

into this:

string digest = client.DownloadString(url + "/default.aspx");

and it worked like a charm.

Ron
at 3/21/2010 5:15 AM

Add Comment

Items on this list require content approval. Your submission will not appear in public views until approved by someone with proper rights. More information on content approval.

Title


Body *


Your city *


Type the name of the city you live in (making it easier to handle spam...)

CurrentDate *

Select the current date (see if this gives me fewer spam...)
Attachments

 Links

  SharePoint Object on CodePlex
  Screencast introducing SharePoint Objects
  Content by Type and Filter Web Parts on CodePlex
  Archive
  Archive (Calendar)

 My Latest Blog Posts

Scripting SharePoint 2007 setup: choices and conceptsUse SHIFT+ENTER to open the menu (new window).
Adventures in Visual Studio 2010: Migrate the Content By Type web part to SharePoint 2010Use SHIFT+ENTER to open the menu (new window).
Register SharePoint themes by using a featureUse SHIFT+ENTER to open the menu (new window).
SharePoint 2010 development on Windows 2008 Server R2 – Getting StartedUse SHIFT+ENTER to open the menu (new window).
New release SharePoint Objects: features and groupsUse SHIFT+ENTER to open the menu (new window).
Constructing the url to the SharePoint Edit Permissions pageUse SHIFT+ENTER to open the menu (new window).
Screencast: introduction to SharePoint ObjectsUse SHIFT+ENTER to open the menu (new window).
SharePoint 2007 and Reporting ServicesUse SHIFT+ENTER to open the menu (new window).
SharePoint Objects – Insight in usage of your SharePoint artifactsUse SHIFT+ENTER to open the menu (new window).
SharePoint 2007 Custom list schema and the Content Query Web PartUse SHIFT+ENTER to open the menu (new window).
SharePoint 2010 Silverlight Client Object Model – ExecuteQuery vs ExecuteQueryAsyncUse SHIFT+ENTER to open the menu (new window).
SharePoint 2010, the Client Object Models and Bing MapsUse SHIFT+ENTER to open the menu (new window).
Having fun with SharePoint 2010, Silverlight 3 and Bing MapsUse SHIFT+ENTER to open the menu (new window).
Connecting TFS 2010 projects to SharePoint sitesUse SHIFT+ENTER to open the menu (new window).
Adding a database to the SharePoint database Server using SPDatabaseUse SHIFT+ENTER to open the menu (new window).