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:
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:
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:
Click here to download the source code.