CHUVASH.eu

CHunky Universe of Vigourous Astonishing SHarepoint :)

Tag Archives: SPList

PowerShell: Copy an entire document library from SharePoint 2007 to disk

For a while ago I needed to copy all files from a document library within a SharePoint 2007 site to the hard drive. So I didn’t need to copy files from SharePoint to SharePoint so I couldn’t use the stsadm -o export command or Chris O’Brien’s nice SharePoint Content Deployment Wizard. I came across the SPIEFolder application which should work with SharePoint 2007 and 2010. It has a site on codeplex: spiefolder.codeplex.com, but neither the binary nor the source code can be downloaded from there. After some searching I found the binary in the author’s skydrive. The fact that the source code was not available seemed as an disanvantage because I could not know what code was run. Nevertheless I tried it out and it didn’t work:

spiefolder -o export -url "http://dev/Documents" -directory c:\tolle\Documents –recursive

I got the following error:

The Web application at http://dev/Documents could not be found. Verify that you have typed the URL correctly. If the URL should be serving existing content, the system administrator may need to add a new request URL mapping to the intended application.

So I wrote my own code to copy the documents. To write a console application feels so yesterdayish, so it is written in PowerShell. Even if there are no PowerShell snapins for SharePoint 2007, you have access to the entire Server Object Model, the only thing you have to do is to load the SharePoint assembly:

[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")

Then you can instantiate all SharePoint objects like in C#, but in a PowerShell way:

$site = new-Object Microsoft.SharePoint.SPSite("http://dev")
$web = $site.OpenWeb()

You can even download a module for emulating cmdlets: Get-SPWeb, Get-SPWebApplication and Get-SPFarm, written by Natalia Tsymbalenko (sharing-the-experience.blogspot.com) to get started or just to find some inspiration.

I have created a ps1-script which only does one thing – it copies an entire document library to disk. Much of inspiration to structure the script comes from “Delete-SPListItems” (sharepointryan.com).

Here it is: Pull-Documents.ps1

<#
.Synopsis
    Use Pull-Documents to copy the entire document library to disk
.Description
    This script iterates recursively over all directories and files in a document library and writes binary data to the disk
    The structure is kept as in the Document library
    It is mainly written for SharePoint 2007, but it works even in SharePoint 2010
.Example
    Pull-Document -Url http://dev -Library "Shared Documents"
.Notes
    Name: Pull-Documents.ps1
    Author: Anatoly Mironov
    Last Edit: 2012-12-03
    Keywords: SPList, Documents, Files, SPDocumentLibrary
.Links
    https://sharepointkunskap.wordpress.com
    http://www.bool.se
.Inputs
    None
.Outputs
    None
#Requires -Version 1.0
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)][System.String]$Url = $(Read-Host -prompt "Web Url"),
[Parameter(Mandatory=$true)][System.String]$Library = $(Read-Host -prompt "Document Library")
)
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")

$site = new-object microsoft.sharepoint.spsite($Url)
$web = $site.OpenWeb()
$site.Dispose()

$folder = $web.GetFolder($Library)
$folder # must output it otherwise "doesn't exist" in 2007

if(!$folder.Exists){
    Write-Error "The document library cannot be found"
    $web.Dispose()
    return
}

$directory = $pwd.Path

$rootDirectory = Join-Path $pwd $folder.Name

if (Test-Path $rootDirectory) {
    Write-Error "The folder $Library in the current directory already exists, please remove it"
    $web.Dispose()
    return
}

#progress variables
$global:counter = 0
$global:total = 0
#recursively count all files to pull
function count($folder) {
    if ($folder.Name -ne "Forms") {
        $global:total += $folder.Files.Count
        $folder.SubFolders | Foreach { count $_ }
    }
}
write "counting files, please wait..."
count $folder
write "files count $global:total"

function progress($path) {
    $global:counter++
    $percent = $global:counter / $global:total * 100
    write-progress -activity "Pulling documents from $Library" -status $path -PercentComplete $percent
}

#Write file to disk
function Save ($file, $directory) {
    $data = $file.OpenBinary()
    $path = Join-Path $directory $file.Name
    progress $path
    [System.IO.File]::WriteAllBytes($path, $data)
}

#Forms folder doesn't need to be copied
$formsDirectory = Join-Path $rootDirectory "Forms"

function Pull($folder, [string]$directory) {
    $directory = Join-Path $directory $folder.Name
    if ($directory -eq $formsDirectory) {
        return
    }
    mkdir $directory | out-null

    $folder.Files | Foreach { Save $_ $directory }

    $folder.Subfolders | Foreach { Pull $_ $directory }
}

Write "Copying files recursively"
Pull $folder $directory

$web.Dispose()

I have tested this script in SharePoint 2007 and 2010. It works. Let me know if you find this useful or have some suggestions.

Delete all list items with jsom

Today I needed to “clean” a list, meaning to remove all list items. For some time ago I wrote a post about different ways of removing list items in bulk: Server Object Model, SPLinq and RPC.  This time I had only the web browser. So I tried the jsom way. By the way, the javascript documentation for jsom on msdn is getting really good. Don’t miss that: How to: Complete basic operations using JavaScript library code in SharePoint 2013.

Now here comes theworking code I used to remove all items in my list:

var ctx = SP.ClientContext.get_current(),
   list = ctx.get_web().get_lists().getByTitle('MyList'),
   query = new SP.CamlQuery(),
   items = list.getItems(query);
ctx.load(items, "Include(Id)");
ctx.executeQueryAsync(function () {
    var enumerator = items.getEnumerator(),
        simpleArray = [];
    while (enumerator.moveNext()) {
        simpleArray.push(enumerator.get_current());
    }
    for (var s in simpleArray) {
        simpleArray[s].deleteObject();
    }
    ctx.executeQueryAsync();
});

Enjoy

Paging with JSOM

If there are many list items you try retrieve with javascript object model,paging could be very useful. Today I came across a wonderful blog post series about javascript object model in SharePoint: The SharePoint javascript object model – Resources and Real World Examples posted by David Mann and published on Aptilon Blog.

There is an example how to achieve paging with JSOM. The key is items.get_listItemCollectionPosition() and query.set_listItemCollectionPosition()

I have refactored David’s example to avoid global variables and to put into a module. Here is it. If you have a Tasks list in your site with many items, just hit F12 to open the console and paste this and see the result:

(function(SP) {
   var 
      ctx = SP.ClientContext.get_current(),
      list = ctx.get_web().get_lists().getByTitle('Tasks'),
      position,
      enumerator,
      view = '<View><ViewFields><FieldRef Name="Title"/></ViewFields><RowLimit>10</RowLimit></View>',
      query = new SP.CamlQuery(),
      items,
      init = function() {
         query.set_viewXml(view);			
      },
      loadChunks = function() {
         query.set_listItemCollectionPosition(position);
         items = list.getItems(query);
         ctx.load(items);
         ctx.executeQueryAsync(success, error);
      },
      success = function() {
         console.log("\nFound Matching Items! ");
         enumerator = items.getEnumerator();
         while(enumerator.moveNext()) {
            console.log("Title: " + enumerator.get_current().get_item("Title") );
         }
         position = items.get_listItemCollectionPosition();
         //when there are no items position is null
         position && loadChunks();
      },
      error = function(sender, args) {
         console.log('Request failed. Error: ' + args.get_message() + '. StackTrace: ' + args.get_stackTrace());
      };

   init();
   loadChunks();
})(SP);

Add Comments column to your sharepoint list

If you have used Issue tracking list template in SharePoint you must have marked that the comments are added and marked with author name and datetime. It is handy to have these micro “discussion boards” on items. The comment-formed communication can help to fine-tune task definitions. By the way, have you seen Trello?

So the question is how we can create this column in other lists? Here is a little tutorial how to create “append-only comments”, as they are called:

Go to your list and enable versioning

Add “Append-only comments” column from existing site columns

(In Swedish this Site Column is called: “Lägg endast till kommentarer”)

Go ahead and write comments

 

Add append-only comments in XML

 

ListUrl on EventReceiver

When you create an eventreceiver, you get the ListTemplateId attribute.

It works fine. But if you want the eventreceiver to trigger on one particular list, just replace ListTemplateId attribute with ListUrl. For Pages you can use:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Receivers ListUrl="$Resources:cmscore,List_Pages_UrlName;">
    <Receiver>
      <Name>NewsPageEventReceiverItemUpdated</Name>
      <Type>ItemUpdated</Type>
      <Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
      <Class>Contoso.EventReceivers.NewsPageEventReceiver.NewsPageEventReceiver</Class>
      <SequenceNumber>1000</SequenceNumber>
    </Receiver>
  </Receivers>
</Elements>

TryGetList

How do we get a list? Perhaps like that:

var list = web.Lists[listname"];

But we must be aware of exceptions that can appear and we must handle them. A better way to get a list is to use TryGetList:

var list = web.Lists.TryGetList(listname);

WithWeb-pattern of Jonas Nilsson

Jonas Nilsson shows an interesting approach for working with SPSite and SPWeb which must be disposed. Create a helper method WithWeb and send an Action parameter:

public void WithWeb(string uri, Action<SPWeb> action)
{
    using (SPSite site = new SPSite(uri))
    {
        using (SPWeb web = site.OpenWeb())
        {
            action(web);
        }
    }
}

Here is my implementation of this pattern:

public static class DisposalService
{
    public static void WithWeb(string uri, Action<SPWeb> action)
    {
        using (var site = new SPSite(uri))
        {
            using (var web = site.OpenWeb())
            {
                web.UnsafeUpdate(action);
            }
        }
    }
    public  static void WithElevatedWeb(string uri, Action<SPWeb> action)
    {
        SPSecurity.RunWithElevatedPrivileges(() => WithWeb(uri, action));
    }
}

Here I use another fancy way to consolidate the unsafe updates. I added an additional method: WithElevatedWeb, it creates the web as system account.

And here comes an example of using this static method:

Action<SPWeb> update = (SPWeb w) =>
                        {
                            UserService.UpdateOwners(owners, w);
                            UserService.UpdateMembers(members, w);
                        };
DisposalService.WithWeb(SPContext.Current.Web.Url, update);

Se om det är en tråd eller inlägg i diskussionsforum

Diskussioner sparas i en vanlig lista. En ny tråd sparas som en folder. Alla svar sparas som SPListItem i den foldern. En folder är så klart också en SPListItem, fast har en annan typ.

För att se om det är en tråd, kan man jämföra ett fält som har ett guid som man kommer åt via SPBuiltInFieldId.FSObjType.

 

var item = properties.ListItem;
var type = Convert.ToInt32(item[SPBuiltInFieldId.FSObjType]);
var foldertype = (int) SPFileSystemObjectType.Folder;
if (type == foldertype)
{
	//Yes, this is the thread head
	var body = item["Body"].ToString();
}

 

 

AfterProperties kräver InternalName

AfterProperties kommer inte leda till Exception, men de kommer returnera bara null, om du använder DisplayName. Man måste ha InternalName. Här är ett litet exempel på hur man kan få ut värden före och efter uppdateringen. Exemplet har testats i ItemUpdated-eventreceiver.

var before = properties.BeforeProperties;
var after = properties.AfterProperties;
var contentDisplayName = "News Content";
var list = properties.List;
var contentInternalName = list.Fields[contentDisplayName].InternalName;
var contentBefore = before[contentInternalName];
var contentAfter = after[contentInternalName];

ID på befintliga kolumner

Det finns en fin lista över id på kolumner som man kan lägga till. Här är ett exempel hur man kan använda det.

Här är ett exempel på två extra kolumner i contenttype:

    
<FieldRefs>
      <FieldRef ID="{23f27201-bee3-471e-b2e7-b64fd8b7ca38}"/>
      <!-- Enterprise Keywords-->
      <FieldRef ID="{3de94b06-4120-41a5-b907-88773e493458}"/>
      <!-- PublishingImage-->
 </FieldRefs>
Вула Чăвашла

VulaCV - Чăвашла вулаттаракан сайт

Discovering SharePoint

And going crazy doing it

Bram de Jager talking Office 365, SharePoint and Azure

My view and thoughts on Productivity and more.

My programming life

and everything in between

SharePoint Development Lab by @avishnyakov

It is a good place to share some SharePoint stories and development practices.

SharePoint Dragons

Nikander & Margriet on SharePoint

RealActivity - Real-time and trustworthy

Blog site of founder, RealActivty - Paul J. Swider

Mai Omar Desouki - Avid SharePointer

Egyptian & Vodafoner - Senior SharePoint Consultant

Cameron Dwyer | Office 365, SharePoint, Outlook, OnePlace Solutions

Office 365, SharePoint, OnePlace Solutions & Life's Other Little Wonders

paul.tavares

Me and My doings!

Share SharePoint Points!!

By Mohit Vashishtha

Jimmy Janlén "Den Scrummande Konsulten"

Erfarenheter, synpunkter och raljerande om Scrum från Jimmy Janlén

SPJoel

SharePoint for everyone

SharePointRyan

Ryan Dennis is a SharePoint Solution Architect with a passion for SharePoint and PowerShell

SharePoint 2020

The Vision for a Future of Clarity

Aharoni in Unicode, ya mama

Treacle tarts for great justice

... And All That JS

JavaScript, Web Apps and SharePoint