Modify a SharePoint 2010 (probably 2007 as well) Wiki page with PowerShell


This turned out to be much harder than I expected, so I thought it would be worth sharing.

First, you ask, why Web Services? Simple, I wanted this script to work from anywhere. with any other methods of accessing SharePoint you need to be on the server for it to work, so Web Services it is.

First step, getting a handle on your SharePoint server. PowerShell has a cmdlet for that!

New-WebServiceProxy

$proxy = New-WebServiceProxy ‘http://localhost/_vti_bin/lists.asmx’ -UseDefaultCredential

I just used local host for testing, but it works with any server and you have to specify the site, which in this case I used the default root site. I also passed my logged in creds but you can specify creds if need be.

NOTE: I found that the URL didnt always stay as it was suppose to so verify that and update it if need be.

If you want to modify/add/delete data you’ll likely want to use the List Web Service, but remember there are others (http://msdn.microsoft.com/en-us/library/ms479390(office.12).aspx )

Now that we have this List Web Service, how do we work with it? You can poke around a bit with PowerShell


$proxy | get-member

Whole lot of things there, but since its a Web Service, we want to look at methods (list of members here)

There are a handful of Get methods, but the GetListItems seems to be what we are looking for. If we check the signature we see that a few things are required.

  • ListName
  • ViewName
  • Query
  • ViewFields
  • RowLimit
  • QueryOptions

Lot of info just to get a list item so lets start to tackle this.

ListName – Simple one, a string of the list we want to work with, in my case its “myWiki” but, as per the docs, its better to use the GUID (get in to that later).

ViewName – This is a string/GUID of the view we want to use, but its optional, so I found it was easy enough to leave blank for my needs.

Query – An XML node of CAML that defines what to return.

ViewFields – An XML node of more CAML to define what views we want to see (this one is important).

RowLimit – Pretty obvious, the default (all) is what we want.

QueryOptions – An XML node of CAML for the query options

Not too bad once we start to look at it, but whats this CAML?

CAML – Collaborative Application Markup Language, its just XML with named tags (same idea as HTML)

I dont know much about CAML, and I spent a good deal of time digging, but what I found is that there is a tool for it!

http://www.u2u.be/Tools/wincamlquerybuilder/CamlQueryBuilder.aspx

This tool was a HUGE help. I’m not going to go in to details because it was fairly easy to use, and when you see the CAML I provide it will make more sense.

Now that we have a method and a way to build the queries, lets get off and running.

Using the U2U Caml builder (tweaking it a bit) I came up with this to get back the one record I’d like to modify.


<mylistitemrequest>
<Query>
<Where>
<Eq>
<FieldRef ID="ID" />
<Value Type="Counter">5</Value>
</Eq>
</Where>
</Query>
<ViewFields>
<FieldRef Name='WikiField'/>
</ViewFields>
<QueryOptions>
<IncludeMandatoryColumns>FALSE</IncludeMandatoryColumns>
</QueryOptions>
</mylistitemrequest>

You’ll notice there are a few key words there that stick out, Query, ViewFields and QueryOptions which nicely match up with our method arguments!


$str = @'
<mylistitemrequest><Query><Where><Eq><FieldRef ID="ID" />
<Value Type="Counter">5</Value></Eq></Where>
</Query><ViewFields><FieldRef Name='WikiField'/></ViewFields><QueryOptions>
<IncludeMandatoryColumns>FALSE</IncludeMandatoryColumns></QueryOptions></mylistitemrequest>
'@
($xml = [xml]"").LoadXml($str)

$query = $xml.SelectSingleNode('//Query')
$view = $xml.SelectSingleNode('//ViewFields')
$options = $xml.SelectSingleNode('//QueryOptions')

$wiki = $proxy.GetListItems("myWiki","",$query,$view,"",$options,"")

So we took that XML document object and ripped out the nodes and feed the nodes in to the method and stored its results in to $wiki.

Running Get-Member against our $wiki will show us that we have an XML document that we can dig in to.

$wiki.data will show us that there is one item in there, just what we were looking for.

$wiki.data.row will show us the fields of a wiki, the one we really care about is the content of that wiki which is ows_wikifield

$wiki.data.row.ows_wikifield

Some pretty basic HTML in there, in my case I get back this


<div><table id="layoutsTable" style="width:100%"><tbody><tr style="vertical-align:top"><t
d style="width:100%"><div style="width:100%"><div><p>here</p>
<p>is some</p>
<p>sample data</p>
<p>i want to add</p>
<p>after this</p></div></div></td></tr></tbody></table>
<span id="layoutsData" style="display:none">false,false,1</span></div>

Now lets tack on some text to the end of our wiki


$content = $wiki.data.row.ows_WikiField
$content = $content -replace "<p>after this</p>", "<p>after this</p><p>yay more data</p>"

We’ve updated the text, but now we have to inject that back in to our Wiki, which involves a new method and more CAML. I’m not going to bother explaining it because if you’ve followed whats above, the update will make sense.


$updatestr = @"
<Batch OnError="Continue">
<Method ID='1' Cmd='Update'>
<Field Name='ID'>3</Field>
<Field Name='WikiField'>
test
</Field></Method></Batch>
"@

($xml= [xml]"").loadXml($updatestr)
$xml.batch.Method.Field[1]."#text" = $content

$proxy.UpdateListItems("myWiki",$xml)

There is one part that should be making you scratch your head, and let me tell you, it took a while for me to figure out.

Why did i put “test” in the XML wikifield?

well, come to find out, if you dump HTML in there, it looks an aweful lot like XML. If you dont put anything then the #text field isnt generated, so i put “test” in there and then used the XML members to inject the actual content I wanted updated.

Was a bit of a trick figuring this out, so I hope it helps!

About these ads

About jrich

I am the Solutions Architect for Apex Learning in Seattle WA. I've been working with computers since I was 13. Started programming when I was 14. Had my first IT job as tech support at an ISP at the age of 15 and became a network admin at the age of 17. Since then I've worked at a variety of small to mid size companies supporting, maintaining and developing all aspects of IT. Mostly working with Windows based networks but have recently been working with Solaris system as well. I created this blog mostly as a place for me to take my own notes, but also share things that I had a hard time finding the info for.

Posted on November 14, 2011, in SharePoint, WMF (Powershell/WinRM) and tagged , , , . Bookmark the permalink. 5 Comments.

  1. ($xml= [xml]$updatestr).batch.Method.Field[1].”#text” = $content

  2. another way to do this to avoid the text replacement is doing this.

    $updatestr = @"
    <Batch OnError="Continue">
    <Method ID='1' Cmd='Update'>
    <Field Name='ID'>3</Field>
    <Field Name='WikiField'>
    $([web.httputility]::htmlencode($content))
    </Field></Method></Batch>
    "@
    

    this will encode the HTML for the wiki in to non html like text (ie < is replaced &lt etc)

  3. I made a script which basically does the same, but doesn’t require the webservices. If executed on the server (but remote powershell probably works too), you can just use the object model instead: http://blog.repsaj.nl/?p=434.

    • Thats very true, and WAY easier, but i couldnt get it to execute correctly. it keep dropping my remote session. i tried some crazy things to get that to work consistantly without luck.

      i’ve noticed this with working with two different remote sharepoint installs and also with IIS as well. basically the commands time out i think. there is a way around this with session properties, but, to keep it simple and allow it to work on all setups i went with the web services.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 115 other followers

%d bloggers like this: