Skip Ribbon Commands
Skip to main content
Nik Charlebois > Nik Charlebois - Blog
Nik Charlebois' Blog
January 10
SharePoint 2010 Opens All Documents in Read-Only Mode

Updates. The problem as been fixed. See resolution

Problem Description

Every user running Windows 7/Office 2010 can only open documents in Read-Only mode, and can't edit them. Windows XP/Office 2007 users however can edit them just fine.

Background Information

Since the week-end, users started to complain that they can no longer edit documents from SharePoint. It seems that every time a user running Windows 7/Office 2010 tries to edit a document, the office application opens it in Read-only mode, giving them the following mention in the title bar:

If they try to edit and save it, it forces them to save it in another location. Users running Windows XP/Office 2007 never noticed any issues, and can still edit the document normally. If users checkout the document remotely (not choosing Local Draft Folder) before trying to edit it, they get a notification bar at the top of the document saying that the file is locked by another users, which is not the case:

If I check it out using the local draft folder, I am able to open the document in edit mode, but when time comes to check the changes back in, it complaints that there were errors uploading the files back into the environment:

We had a release over the week-end that was simply adding some custom web parts assembly files to the GAC, and adding a new Binding entry for a WCF service connection. I know what you guys are thinking, there's just too much of a coincidence here for that release not to have caused the problem, and I agree 100% with you. But why would a basic out-of-the-box feature such as editing a file be broken by the addition of a simple WCF binding entry? I tried rolling back the changes in the web.config, but the problem still persists.

As it seems to be the case with all of my problems, I am not able to reproduce the problem in our development environment. It made me wonder if a Group Policy may have changed lately. However accessing our SharePoint environment using a PC connected via VPN (that doesn't get any GPO applied), exposes the same behavior. I have also asked our infrastructure folks, and they assured me that no updates had been pushed to the front-end, the database, or any of the client machines in about 3 weeks.

Another strange thing to note is that before this problem started happening, users reported that opening the latest version of a document gave them an error in the office application saying that they weren't able to find the document. They were however able to open previous versions without any issues. That error eventually disappeared by itself, and the read-only issue started happening.

Please note that the problem described above happens for every file no matter where they reside on the server (different web, different site collections).

Summary

Observations:

  • Office documents open in read-only mode for windows 7/office 2010.
  • Windows XP/Office 2007 users don't have any problems.
  • Checking out a document remotely before editing gives an error saying the document is locked by another user.
  • Checking out a document locally gives you an "Upload Failed" error when trying to check your changes back in.
  • Windows 7/Office 2010 don't have the problem in our development environment.

Things I've tried without success:

  • Repairing Office 2010;
  • Downloading the document locally, and re-uploading it;
  • Sacrificing a goat;

 

Resolution

The fix for this problem was to get rid of the serviceDebug and serviceMetadata entries in the web application's web.config:

<behaviors>
<serviceBehaviors>
<behavior>
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors> 

December 01
Delete All Items in a List – The « Ribbonnized Way »

Summary

This article describes how to create a new tab in the SharePoint ribbon that will be displayed on every list having a base type of "Generic List". The ribbon extension will contain a button that will delete all items in a list by calling a Javascript that uses the SharePoint Client Object Model.

The Story

I've discussed this many times before, I'm a big believer in logging errors from custom SharePoint modules into custom lists. Given, this approach has a certain performance impact on the server, but the benefits you get out of it are worth taking the hit. You can run custom reports on the items, identify trends, be proactive by setting alerts, etc. However, where it really becomes a problem is when you don't have a good strategy in place to decide how frequently items need to be deleted from the list. I'm sure all of you have experienced some frustration with the way SharePoint handles item deletion, only allowing you to delete the items display in the current view. If you have paging enabled, you may be looking at having to repeat the operation on several pages of items. Off course you can go and create a view that would display all items in the list, but that will often result in very slow operations, or in you exceeding the view throttling.

What we need is an easy way to go and delete all items in a list, something that would be as simple as pressing a button to make all items vanish. Off course we could go and code a nice visual web part, have it deployed at the farm level and then plug in on a page somewhere to do our biding. I'm not interested in that. That's the easy and cheap way of doing it. No, what I want is something that would appear on all my lists, and would allow me to clean all items inside of it with a simple click on my mouse. What I need is a new ribbon extension!

The nice thing about Ribbon extensions is that they can easily be Sandboxed, meaning we can deploy them in the cloud, which is always a bonus. So let's do that, create a new ribbon extension that will display a nice button allowing us to wipe any list clean of its items, and have it deployed onto our SharePoint Online Cloud, in my case Office 365. The content of this blog post was Demo #4 of my Techdays Canada session in both Toronto and Montreal in Fall of 2011.

The Logic Behind the Module

First off, let's build the script that will allow us to achieve what we are looking for. Because the ribbon runs on the client's side, we will develop the script using the ECMAScript SharePoint Client Object Model. Please don't let the name scare you, it's only Javascript. The script I built does the following, it starts off by getting the list currently selected, or in our case displayed on screen. It then queries the list using a CAML query that does the following, return every item that has an ID that is greater or equal to 1, which always returns all items. We then execute the Asynchronous query and jump onto the following method, which loops through all elements and adds their ID in a new array. We then loop through the array, and delete items from the list based on their IDs. The reason for this is that while looping inside of a collection, it is not permitted to alter its size, meaning that we cannot add or delete items when looping through the enumerator.

The second step is to register this script some places where it will be made accessible by all lists. To the Master Page! I'm lazy so I simply added the javascript to the end of the Master Page's Head tag. As a best practice I would recommend you create a separate .js file and have it linked from your master page, but since we're only demoing this, copying its content in the Master page directly will do.

The last but none the least step is to go and create our ribbon extension. The extension definition specifies a new tab that will have a single group of button, with a single row, having a single large button. The button will be set to us the out of the box Warning icon. Please make sure you point the icon to a file that will likely exist in every farm instance if your solution is aimed at the cloud. The button will call our newly developed javascript method and will be displayed on item of type ID = 100, which is the ID of type "Generic List" For a complete list of all possible type IDs we can use, please refer to the following Microsoft MSDN article: http://msdn.microsoft.com/en-us/library/ms415091.aspx .

How To

The following section gives the step by step approach one should follow to get this demo working.

What you will need

  1. The following Javascript content:

     

    <script type="text/javascript">

    var context = null;

    var web = null;

    function deleteAllItems()

    {

    var okToContinue = confirm("Are you sure you want to delete all items in this list?");

    if(okToContinue)

    {

    context = new SP.ClientContext.get_current();

    web = context.get_web();

    context.load(web);

    var ID = SP.ListOperation.Selection.getSelectedList();

    curList = web.get_lists().getById(ID);

    context.load(curList);

     

    var camlQuery = new SP.CamlQuery();

    camlQuery.set_viewXml("<View><Query><Where><Geq><FieldRef Name='ID' /><Value Type='Number'>1</Value></Geq></Where></Query></View>");

    this.collListItem = curList.getItems(camlQuery);

    context.load(collListItem);

     

    context.executeQueryAsync(Function.createDelegate

    (this, this.onSuccessGetList, Function.createDelegate,

    this, this.onFailure));

    }

    }

    function onSuccessGetList(sender, args)

    {

    var listItemEnum = collListItem.getEnumerator();

    var counter = 0;

    var itemArr = new Array();

    while(listItemEnum.moveNext())

    {

    var listItem = listItemEnum.get_current();

    itemArr[counter] = listItem.get_item("ID");

    counter++;

    }

     

    for(var id in itemArr)

    {

    var spItem = curList.getItemById(itemArr[id]);

    spItem.deleteObject();

    }

     

    context.executeQueryAsync(Function.createDelegate

    (this, this.onSuccessClean, Function.createDelegate,

    this, this.onFailure));

    }

     

    function onSuccessClean(sender, args)

    {

    alert("All items deleted!");

    window.location = window.location.href;

    }

     

    function onFailure(sender, args)

    {

    alert("Error");

    }

    </script>

     

  2. The following Ribbon Extension XML definition:

    <?xml version="1.0" encoding="utf-8"?>

    <Elements xmlns="http://schemas.microsoft.com/sharepoint/">

    <CustomAction

    Id="Techdays.Ribbon.Action"

    Location="CommandUI.Ribbon"

    RegistrationId="100"

    RegistrationType="List">

    <CommandUIExtension>

    <CommandUIDefinitions>

    <CommandUIDefinition Location="Ribbon.Tabs._children">

    <Tab Id="Techdays.Ribbon.Tab"

    Sequence="550"

    Description="Techdays Demo Tab"

    Title="Techdays" CssClass="TechdaysTabGroup">

    <Scaling

    Id="Techdays.Ribbon.Tab.Scaling">

    <MaxSize Id="Techdays.Ribbon.Tab.MaxSize" GroupId="Techdays.Ribbon.Tab.Group" Size="OneLarge"/>

    <Scale Id="Techdays.Ribbon.Tab.Scaling" GroupId="Techdays.Ribbon.Tab.Group" Size="OneLarge"/>

    </Scaling>

    <Groups Id="Techdays.Ribbon.Tab.Groups">

    <Group Id="Techdays.Ribbon.Tab.Group" Description="Techdays Ribbon Custom Group" Title="Techdays"

    Sequence="20"

    Template="Techdays.Ribbon.Tab.Template" >

    <Controls Id ="Techdays.Ribbon.Tab.Controls">

    <Button

    Id="Techdays.Ribbon.Tab.Group.TestButton"

    Command="DeleteAllItems"

    Sequence="10"

    Description="Delete All Items"

    LabelText="Clean all items in List"

    TemplateAlias="TechdaysTemplateAlias"

    Image32by32="/_layouts/images/warning32by32.gif"

    />

    </Controls>

    </Group>

    </Groups>

    </Tab>

    </CommandUIDefinition>

    <CommandUIDefinition Location="Ribbon.Templates._children">

    <GroupTemplate Id="Techdays.Ribbon.Tab.Template">

    <Layout Title="OneLarge" LayoutTitle="OneLarge">

    <Section Alignment="Top" Type="OneRow">

    <Row>

    <ControlRef DisplayMode="Large" TemplateAlias="TechdaysTemplateAlias"/>

    </Row>

    </Section>

    </Layout>

    </GroupTemplate>

    </CommandUIDefinition>

    </CommandUIDefinitions>

    <CommandUIHandlers>

    <CommandUIHandler Command="DeleteAllItems" CommandAction="javascript:deleteAllItems();" />

    </CommandUIHandlers>

    </CommandUIExtension>

    </CustomAction>

    </Elements>

     

Steps to Follow

  1. Copy the Javascript content above in your master page, just before the closing </head> tag

  2. Save your changes back to the server;
  3. Launch Visual Studio and create a new Empty SharePoint Project, naming it whatever you feel like

  4. When prompted, make sure you chose to use a Sandboxed Solution, and not a farm one

  5. In the newly created project, add a new Empty Element

  6. In the newly created item, inside of elements.xml, copy and paste the ribbon extension content above.
  7. Build your solution;
  8. Grab the .wsp file produced and deploy on your site via the solutions gallery
  9. Activate the solution

  10. Navigate to any Custom List, you should now see your new ribbon extension under the "Techdays" tab

     

The full solution file can be downloaded at http://nikcharlebois.sharepointspace.com/Solutions/DeleteAllItems.wsp . Enjoy!

 

Nik,

Onboard of Train 59 back from Techdays

November 26
When Troubleshooting Also Means Breaking Stuff

Plot

The following blog post described how I spent nearly 3 days trying to solve an issue I had with the Office Web Apps not wanting to work for Word and PowerPoint files.

Story

I decided it was time to upgrade our SharePoint 2010 farm to Service Pack 1. While we were at it, I've also decided to roll out the Office Web Apps in order for our organization to leverage the Office Document Thumbnail feature in the Enterprise Search result page. The installation went smoothly, but the Office Web Apps gave us errors whenever we were trying to view a Word of PowerPoint file in Browser. Excel files were displayed without any problems, only .docx and pptx files weren't working. Whenever we tried to open them, SharePoint would throw an exception with only "File Not Found" as a message. The ULS logs did not give me any more help, displaying the following error:

Cannot complete this action. Please try again.<nativehr>0x80004005</nativehr><nativestack></nativestack>

 

The exact same error was reproduced on our Production server copies in our Development, Integration, and Test environments. However, it was working fine on my developer's machine, which led me to believe that a configuration setting on our production server was the source of the problem. I went and make sure that all the Services in Central Admin were correctly started, that the Service Applications were correctly started, and that the Office Web Apps feature was activated on my site collection. Still, nothing was working, and the same error persisted

I then went ahead and try to match every settings and permissions from my dev box onto our copy of the production server. I made sure the account running the app pool for the Word Viewing service was local admin, and part of the WSS_ADMIN_WPG local group. I compared every single setting for both app pools in IIS and both were the same. It seemed like no matter what I tried, I couldn't get the other server to work like the other one.

Resolution

Then this idea popped up in my head, what if instead of trying to make the server that had the error work like the other one, I tried to break the one that worked so that it gets the same error as the other one. I decided to start off by copying the faulty server's web.config file onto the working server. There it was! On first attempt, I managed to get the working server to throw the same "File Not Found Error", I had a culprit. After downloading WinMerge to compare the 2 web.config, I quickly identified the line that was different from the two machines:

<add name="PointFire2010" type="PointFire2010.PFHttpModule, PointFire2010, Version=1.0.0.0, Culture=neutral, PublicKeyToken=aa723fd590a9b1d4" />

An entry for a custom component we use to do our translation, and handle other multilingual operations on our site. Removing that entry automatically solved the Office Web Apps problem, and documents started opening in the Browser!

Acknowledgements

Thanks to Todd Klindt (@ToddKlindt) for helping me out on his own time with this issue!

References

Official OWA Forum Thread - http://social.technet.microsoft.com/Forums/en-US/officewebappssetup/thread/2ba3e50d-9f9d-465a-a60c-4ec8237acbfd?prof=required

Todd's Blog - http://toddklindt.com/blog

October 27
Techdays Canada

I know, I know, my blog has been pretty quiet for the last little while. I was keeping busy getting my mojo ready for Techdays Canada. As some of you may know, I've been presenting my "Developing SharePoint solutions for Office 365" in Toronto last Wednesday, October 26th. I will also be presenting the exact same session in Montreal on November 29th, as well as at the end of February 2012. The Microsoft team put together one of the best even I was given the chance to attend. We had well over 50 different sessions and labs available to the attendees. I received a lot of great questions from the audience while being in the expert hall. It is always interesting to see people from so many different area of expertise all come together to push the limit of their knowledge even further.

As for my session, it had a total of five demos in it. The source code for all of them will be made available on my blog, at the end of November when the Techdays are officially over. So please keep posted.

September 27
Performance Benchmarks for Adding List Items in SharePoint

As some of you may already know, SharePoint 2010 introduced its set of new methods in the Server side API. One of these new methods is the SPList.AddItem method on lists that is adding a new List item. In prior versions of the API, we had to call the SPList.Items.Add method, to add the SPListItem to the collection of list items. Surprisingly enough, this method is not deprecated in the 2010 version, so we are left with two methods that seem to be doing exactly the same thing. Without any further indication from Microsoft as to what method is preferable to use in what circumstance, I set on my own, and decided to burn some tires, and run benchmarks on both methods.

For my tests, I created two Generic lists inside of an empty Team Site on my test machine. I then created an executable that would do two things in parallel. It would start off by creating 5,000 items in the first list using the old SPList.Items.Add() method, and once finished, it would go and create 5,000 in the second list using the SPList.AddItem() method. I executed the test application 15 times, for a total of 75,000 items in each list, taking note of the time it took for each run on each list. Table 1 illustrates the results obtained from the test runs. All times are in seconds.

# of Items

Old Method

New Method

5,000

337

327

10,000

363

503

15,000

416

425

20,000

395

511

25,000

410

542

30,000

347

379

35,000

363

384

40,000

364

374

45,000

410

378

50,000

536

433

55,000

463

443

60,000

447

457

65,000

476

440

70,000

448

446

75,000

458

457

 

 

 

 

 

 

 

 

 

 

Table 1 – Data Points for 75,000 items

Put on a graph, we can clearly see that as we increase the number of items, the two methods produce similar results, but that the old method still wins over the new one for list with a relatively small number of items. My results don't really prove anything, and I can't make recommendation as to what method should be used in what scenario, but I can at least say that both methods seem to be performing in a similar fashion under 100,000 items.

 

Graph 1 – Data Points for the two methods

I will still get in touch with someone on the product team to try to get more information/guidance on this topic. Stay tuned for another blog post.

September 12
Techdays Canada 2011 – I’m in…almost

It's now official, two of my sessions made it up Microsoft's short list of sessions for its upcoming Techdays tour. One of them is about developing SharePoint solutions for Office 365, and the other one is my now world famous Visio Services Overview session. Making it onto the short list doesn't however ensure me from being picked up for the final "roaster", for that I need votes from the community (my mom can only vote so often). If you'd like to support me and vote for my sessions, you can do so by filling in the survey at https://microsoft.qualtrics.com/SE/?SID=SV_eKwC55qai5wajcw. Thank you all in advance for encouraging me,

 

Sincerely yours,

 

Nik

August 21
UIVersion and Partial Visual Upgrade

My current assignment is fairly simple, upgrade our department's SharePoint 2007 environment to SharePoint 2010 by reducing as much as possible the impact on the end users. Now that may sound simple at first, but our clients just went through another upgrade less than a year and a half ago when we changed from SharePoint 2003 to 2007, and even if the interface stayed pretty much the same, lots of complaints and confusion were generated by this upgrade. As part of our 2010 upgrade, I made an executive decision to keep the 2007 layouts on the different sites and workspace. We are doing a db attach migration, meaning, that by default all existing sites and webs will keep their 2007 look. Now, I already hear people calling me names, and throwing virtual rocks at me. I know this is probably not the ideal situation, but I'm hoping this will be temporary, and that eventually our clients will be ready to make the move to the 21st century.

For your information, SharePoint 2010 ships with two master pages in the [14 Hive]/TEMPLATE/GLOBAL. The v4.master one represents the 2010 look, and the default.master the 2007 one. Every SPWeb object in the SharePoint API has a property exposed named UIVersion. This value can either be 3 or 4, and this is what determines what version of the master page the current web will be using. We have then modified our code so that every new web that gets created, automatically gets assigned with the UIVersion = 3.

As you have probably already guessed, keeping a master page from a previous generation of product is most likely to be created problems down the road. If you are ever planning on doing a migration and on keeping the 2007 look, know this, many new features offered by the 2010 version of the product will only be made available if you do a visual upgrade on your sites, meaning that you need to use the UIVersion = 4 before being able to leverage these new features. It is not enough to just upgrade the backend bits. One of these features is the edit in datasheet feature. Some of you may know that there exists a bug in the way the edit in datasheet feature works in SharePoint 2007. As described in my blog post found here, the edit in datasheet option of SharePoint 2007 doesn't play well when trying to edit lists having multiple content types assigned to them. Keeping the UIVersion = 3 will still present this problem. However, as soon as you do a visual upgrade on your web, the problem goes away.

Here's a simply PowerShell script excerpt I produced that allows you to revert a single web to the previous 2007 UI:

$web = get-spweb <url of web>

$web.UIVersion = 3

$web.Update()

Visual upgrades are performed on an entire site collection at once. This option is available from the site collection settings page as shown in the following screenshot:

This may be good if you want to be able to convert everything to a 2010 look, but what if you want to do a gradual "Visual Upgrade" and upgrade each web one at a time? Well, that option is available to you out-of-the-box, but requires you to run 3 little lines of PowerShell code. The SPWeb object has a property named UIVersionConfigurationEnabled, that owhen set to true, allow you to do a visual upgrade that targets a single web within a site collection. The following PowerShell snippet shows you exactly how to do it:

$web = get-spweb <url of web>

$web.UIVersionConfigurationEnabled = $true;

$web.Update();

Once the script has been executed, the option to do a Partial Visual Upgrade can be found under the Title, description, and icon menu. If SharePoint 2010 recognizes that the current web is using a previous version of the User Interface (UI), it will automatically add a new section at the bottom of the administrative page letting you to either upgrade to the new interface, or to previous what the current web would look like with the new interface:

This is an excellent tip for anyone who wants to gradually update their farm to use the 2010 interface, without converting Site Collections all at once. I know for my part, the "Preview the updated user interface" option will be very useful when time will come to present the users with what the new interface will look like, and hopefully get them to buy-in it.

July 27
Creating Pinned Sites Jump Lists for SharePoint 2010

This idea came to me after attending the HTML 5 Boot camp in Ottawa back in April 2011. The boot camp introduced me to a new concept in Internet Explorer 9, called Pinned Sites. Basically, this feature allows web developers to build web sites that can interact with the Desktop when users pin the site to their taskbar. Sites can be pinned in Windows 7 and Windows Server 2008 R2 by dragging the site's tab from internet explorer down onto the taskbar. You can read more about pinned site on the following MSDN site:

http://msdn.microsoft.com/en-us/library/gg131029(v=vs.85).aspx

Controlling the behavior of the pinned sites is really easy to do from any html page. However it gets a little trickier when doing from inside of SharePoint. For example, one needs to modify the Master Page's <head> section to be able to specify a custom color for the Navigation buttons (back and forward arrows). Note however, that if you site is using a custom Favorite Icon (favicon.ico, normally located at http://<server>/_layouts/images/favicon.ico), the navigation button will adjust their colors to match the icon's.

Back in early June, while chatting with Todd Klindt (@ToddKlindt) during one of his "world-famous" Netcasts, he asked the chat room if anybody had any suggestions on how he could make his blog more attractive and user friendly. One of my suggestions to him was to leverage the pinned site's features of IE. Todd had a couple of key blog posts he wanted to promote, so Jump Lists inside of a Pinned Site would work perfectly.

I then decided to venture on my own, and developed a very simple sandboxed web part that would allow non-developers folks, like Todd, to easily leverage some of the Pinned Site functionality. The web part allows users to specify a Category for their Jump List, and to enter up to 5 items in it. The following Blog post describes how to deploy and use the custom web part. Since my web part is Sandboxed, you can simply upload it in the Site Collection's Solution Gallery and have it activated from there.

By activating the solution on your site, you will automatically get a new web part named "Pinned Site Generator".

Simply drag and drop it onto the page you want users to pin to their desktop. Once the web part is placed on a page, go edit its properties. Under the Miscellaneous header, you will find 11 custom properties that you can fill in. The very first one is the Category Name. This is a required field if you want your jumplist to be visible at all from your pinned site. This value will show up as the heading to your list. Logical values for this field could be anything like "Articles, Sections, News, etc". The other fields are paired together, and they include a place to specify the name of the item, and the url it will point to. If we take back the example stated above, where Todd is looking to promote specific Blog articles, then the name of the item could be "Loopback Check", and the url value would point to Todd's blog post at: http://toddklindt.com/loopback .

Assuming the values entered above, here is what the final result of the pinned site's jumplist will look like:

The solution (.wsp) is available to download from my company's Office 365 site at the following location:

http://www.ignitesoft.ca/Documents/PinnedSiteGenerator.wsp

Now, I'm sure you guys will agree with me, the value added by creating pinned sites isn't that great, but you can be sure that the next version of IE will continue to build and improve on this front. So please, don't be pinless, and start creating dynamic Pinned SharePoint sites.

 

 

June 07
Comparing Administrative Options for SharePoint 2010

The following exercise was undertaken in preparation for my upcoming session at the Federal SharePoint Technical User Group session on PowerShell. I wanted to prove to my audience just how fast and better PowerShell was compared to all the other Administrative options for SharePoint. I decided to go ahead and do a fairly quick and sample test, and calculate benchmarks out of it for each option.

 

The tests consisted in creating 5 site collections (each under a distinct Web Application), and have 15 new Webs created under each of them, for a total of 75 webs, and 5 site collections. The following blog article describes the benchmarks for each of the following options:

  1. STSAdm
  2. Object Model
  3. PowerShell
  4. Web Services

 

STSAdm

Alright, so we all know STSAdm. It's been around for ages, since the original SharePoint Team Sites product in the early 2000. I went ahead and created the following batch file that implicitly called the STSAdm executable for every command:

prompt $p$g $t -

cd "C:\Program Files\Common Files\Microsoft Shared\Web server Extensions\14\BIN\"

 

stsadm -o createsite -url "http://spdemo:82/sites/SiteColl1" -ownerlogin "litware\Nik.Charlebois" -owneremail "Nik.Charlebois@Litware.com"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl1/Web1"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl1/Web2"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl1/Web3"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl1/Web4"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl1/Web5"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl1/Web6"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl1/Web7"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl1/Web8"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl1/Web9"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl1/Web10"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl1/Web11"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl1/Web12"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl1/Web13"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl1/Web14"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl1/Web15"

 

stsadm -o createsite -url "http://spdemo:82/sites/SiteColl2" -ownerlogin "litware\Nik.Charlebois" -owneremail "Nik.Charlebois@Litware.com"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl2/Web1"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl2/Web2"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl2/Web3"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl2/Web4"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl2/Web5"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl2/Web6"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl2/Web7"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl2/Web8"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl2/Web9"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl2/Web10"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl2/Web11"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl2/Web12"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl2/Web13"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl2/Web14"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl2/Web15"

 

stsadm -o createsite -url "http://spdemo:82/sites/SiteColl3" -ownerlogin "litware\Nik.Charlebois" -owneremail "Nik.Charlebois@Litware.com"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl3/Web1"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl3/Web2"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl3/Web3"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl3/Web4"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl3/Web5"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl3/Web6"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl3/Web7"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl3/Web8"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl3/Web9"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl3/Web10"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl3/Web11"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl3/Web12"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl3/Web13"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl3/Web14"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl3/Web15"

 

stsadm -o createsite -url "http://spdemo:82/sites/SiteColl4" -ownerlogin "litware\Nik.Charlebois" -owneremail "Nik.Charlebois@Litware.com"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl4/Web1"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl4/Web2"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl4/Web3"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl4/Web4"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl4/Web5"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl4/Web6"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl4/Web7"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl4/Web8"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl4/Web9"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl4/Web10"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl4/Web11"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl4/Web12"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl4/Web13"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl4/Web14"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl4/Web15"

 

stsadm -o createsite -url "http://spdemo:82/sites/SiteColl5" -ownerlogin "litware\Nik.Charlebois" -owneremail "Nik.Charlebois@Litware.com"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl5/Web1"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl5/Web2"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl5/Web3"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl5/Web4"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl5/Web5"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl5/Web6"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl5/Web7"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl5/Web8"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl5/Web9"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl5/Web10"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl5/Web11"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl5/Web12"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl5/Web13"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl5/Web14"

stsadm -o createweb -url "http://spdemo:82/sites/SiteColl5/Web15"

 

Running this script took a total of 14 minutes and 36 seconds (14:26).

Object Model

The Object Model on the other hand, allows one to write recursive loops in a more friendly way than writing them in command line script would have been. I created a new console application in C#, added a reference to the Microsoft.SharePoint v14 assembly, and made elevated calls to the proper methods. Here's the code for the method I used:

static void Main(string[] args)

{

SPSecurity.RunWithElevatedPrivileges(delegate(){

 

DateTime time = DateTime.Now;

SPWebApplication webAPP = SPWebApplication.Lookup(new Uri("http://spdemo:83"));

for (int i = 1; i <= 5; i++)

{

try

{

using (SPSite site = webAPP.Sites.Add("http://spdemo:83/sites/SiteColl" + i.ToString(), @"Litware\Nik.Charlebois", "nik.Charlebois@Litware.com"))

{

Console.Out.WriteLine("Site Collection #" + i.ToString() + " Created");

for (int j = 1; j <= 15; j++)

{

using (SPWeb web = site.AllWebs.Add("Web" + j.ToString()))

{

Console.Out.WriteLine("\tWeb #" + j.ToString() + " Created");

}

}

}

}

catch { }

}

Console.Out.WriteLine("\r\nIt took " + (DateTime.Now.Subtract(time).TotalSeconds).ToString() + " seconds to execute");

});

}

 

Executing the operation using the Object Model took a total of 1,063 seconds so 17 minutes total.

PowerShell

The third option, which really was the focus of my little exercise, is PowerShell. IT people have been able to use PowerShell to manage SharePoint since the 2007 version, and even if I've never played with it, it could be done for SharePoint 2003. PowerShell is such an amazing tool. I find it finally gets rid of that Gap between developers and IT Pros. Administrators now have to start learning the artifacts of the SharePoint ecosystem, and finally get to start speaking the same language as developers (SPWebs, SPSite, etc). Here's my PowerShell code for executing the operations. Please note that if you start it directly from the SharePoint PowerShell Management Shell, you do no need to include the first line to add the SharePoint Snapin.

Add-PSSnapin Microsoft.SharePoint.PowerShell

$time = Get-Date

$spWebApp = Get-SPWebApplication http://spdemo:81;

 

for($i = 1; $i -le 5; $i++)

{

    $url =" http://spdemo:81/sites/SiteColl$i";

    new-spsite $url -OwnerAlias "Litware\Administrator" -OwnerEmail "Administrator@Litware.com";

    Write-Host "Site Collection #$i Created";

    for($j = 1; $j -le 15; $j++)

    {

        $url = " http://spdemo:81/sites/SiteColl$i/Web$j";

        

        new-spweb $url;

        Write-Host "Web #$j Created";

    }

}

$now = Get-Date

$benchmark = $now.Subtract($time)

Write-Host $benchmark.TotalSeconds;

 

Just look at how simple the code is, and just how close to the Object Model code it looks. I wasn't surprised to see that PowerShell was the fastest of all tests, scoring a 296 seconds or 5 minutes to execute.

Web Services

Just to be fair, I had to go and evaluate all possible options, including this last one, the Web Service API. SharePoint out of the box comes with a wide range of different Web Services of all kinds of flavor. To execute this test, I had to make calls to 2 distinct web services. The first one, being the Central Admin service, which allows us to create new Site collections. By default, this web service is located at http://<servername>/_vti_adm/admin.asmx. The second web service, is the one that would allow me to create Webs under the newly created Site Collection. This one is located at http://<servername>/_vti_bin/sites.asmx. To test this API, I went ahead and created a new Console App in C#, and called the following method:

static void Main(string[] args)

{

DateTime begin = DateTime.Now;

CentralAdmin.Admin proxy = new CentralAdmin.Admin();

SitesWS.Sites sitesWS = new SitesWS.Sites();

sitesWS.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;

proxy.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;

 

for (int i = 1; i <= 5; i++)

{

proxy.CreateSite("http://spdemo:84/sites/SiteColl" + i.ToString(), "SiteColl" + i.ToString(), "", 1033, "STS#1", "Litware\\Administrator", "Nik", "Administrator@Litware.com","","");

Console.WriteLine("Site Collection #" + i.ToString() + " Created...");

for (int j = 1; j <= 15; j++)

{

sitesWS.Url = "http://spdemo:84/sites/SiteColl" + i.ToString() + "/_vti_bin/sites.asmx";

try

{

sitesWS.CreateWeb("Web" + j.ToString(), "Web #" + j.ToString(), "", "STS", 1033, true, 1033, true, 1033, true, true, true, true, true, true, true);

}

catch { }

Console.Out.WriteLine("\tWeb #" + j.ToString() + " Created...");

}

}

Console.WriteLine("Completed in " + DateTime.Now.Subtract(begin).TotalSeconds);

}

 

In theory, I was expecting this test to be the slowest one of them all. I was very surprised to see that it scored a respectable 13 minutes and 30 seconds. Therefore beating both STSAdm and the Object Model has the fastest method of creating batch sites. Please note, however that this was executed on the SharePoint server itself, so there was no delay sending the Soap Envelops over the wire.

 

Summary

Here's a table to summarize my findings. PowerShell, as expected is the undisputed King of Management tools for SharePoint!

Method Name

Time of Execution (in minutes)

STSAdm

15

Object Model

17

PowerShell

5

Web Services

13.5

 

May 29
Official SharePoint Podcast

Hi guys, 

It is now official, I have officially started my French SharePoint Podcast serie. The first episode which will soon be made available on itunes, and the zune market place, can be downloaded directly from my current site at http://nikcharlebois.sharepointspace.com/Podcasts/RSS.xml. In this first episode I discuss several SharePoint problems and challenges I've encountered over the past couple of weeks​. Episodes will be released on an ad-hoc basis. There probably won't be one next week as I will be down in Boston Massachussett for the SharePoint Technical Podcast.

Nik

1 - 10Next
 

 About this blog

 
Nik
Welcome to my SharePoint blog where I make sure you are getting the latest details about things I uncovered during my SharePoint journeys.
 

 Follow me on Twitter