Showing posts with label SharePoint. Show all posts
Showing posts with label SharePoint. Show all posts

Tuesday, June 25, 2013

SharePoint 2010 Content Type Issues

I've been working on a project where I had two document libraries (one upload library and one archive library) and a number custom Site Content Types. We have an event receiver on the upload library that will sometimes move the document (using the C# API SPListItem.File.MoveTo method) to the archive library.

Here's what we saw happening:

We had content type Child which was based on content type Parent. When in the upload library, the document was categorized as Child. When the event receiver moved it to the archive library it would arrive as content type Parent. This made no sense!

We stepped through the code and there was nothing at all that indicated a problem with our event receiver. It was truly happening inside the MoveTo function, so it seems to be a SharePoint bug.

Our only solution was to change recreate the Child content type (as Child2)so that it didn't inherit from Parent and instead inherited from something else (like document). Once we created Child2, we then changed all the documents of Child to Child2. Then we removed the Child content type from both libraries, and deleted the site content type. Finally we renamed the Child2 site content type, and then the instances of that content type in each libary.

Problem solved!

Tuesday, June 4, 2013

Deploy Globally vs Deploy by Web Application SharePoint 2010

I've been working on a SharePoint 2010 solution for a client that is currently in UAT and we've been deploying bug fixes every other night. The solution includes some site scoped features and was always deployed Globally in our development environment. This was never a problem.

Once we moved to UAT and deployed the solution to our Stage environment we found deploying the solution took almost 40 minutes. This stage environment contains two Web Front Ends as well as a few web applications.

So I began looking into how I could get this solution which contained only site scoped features, to deploy to only our web application.

Everything I read said that a modification to the manifest.xml was the answer, specifically adding in SafeControl entries that will be added to the web.config file of that web application.

We're using Visual Studio 2012, so I figured there had to be some way that I could get VS to create these entries automatically.

The solution was to open up the Package folder of my project and then double click the Package.package and go to the Advanced tab. I then went through each assembly I had and Clicked the Edit button. Low and behold, there was the Safe Controls section. I clicked the button that let me add a new one, typed in the Namespace and Assembly Name for each assembly and then pressed OK (VS had OK greyed out until I moved my cursor focus outside of the Safe Controls section for some reason).

Now, even when I deploy from Visual Studio in my development environment, my solution gets deployed to only my web application! I was then able to use a PowerShell script with the -WebApplication flag to deploy it much faster in our Stage environment.

Friday, April 12, 2013

SharePoint ECMAScript to Create List of all Files & Folders in library

Recently I was asked by a client to provide a list of all files and folders (including name and ID) in a library. This library had over 15,000 files and folders and I was not allowed access to the SharePoint servers (so PowerShell & C# API were not possible). The farm's authentication method used Ping, and the way it was configured made it impossible to connect using the client side C# API.

Since I needed a flat list of all folders (not just files) I couldn't just create a list that ignored folder.

So, I resorted to some ECMAScript.

I first created a new view in the library, then used SharePoint Designer to edit the page. I removed the DataView webpart and replaced it with an HTML Form webpart. I uploaded the jquery-1.9.1.min.js file to the Forms folder (where the new view lived) and then I wrote the following JavaScript:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<script src="./jquery-1.9.1.min.js" type="text/javascript"></script>
<script type="text/ecmascript">

var context;
var web;
var list;
var query;
var allItems;
var contentTypeCollection;

function ViewAllFiles()
{
	context = new SP.ClientContext.get_current();
	web = context.get_web();
	this.contentTypeCollection = web.get_contentTypes();
	context.load(this.contentTypeCollection);
	list = web.get_lists().getByTitle("Documents");
    //First 50
    query = new SP.CamlQuery();
    query.set_viewXml("<view Scope='RecursiveAll'><viewfields><fieldref Name='Title'/><fieldref Name='FileLeafRef'/>"
    	+ "<fieldref Name='ContentTypeId'/><fieldref Name='ID'/></ViewFields><rowlimit>50</RowLimit><query>"
    	+ "<orderby Override='TRUE'><fieldref Name='ID' /></OrderBy></Query></View>");
    allItems = list.getItems(query);
    context.load(allItems);
	context.executeQueryAsync(Function.createDelegate(this, this.success), Function.createDelegate(this, this.failed));
}
function success()
{
	var ListEnumerator = this.allItems.getEnumerator();
	while(ListEnumerator.moveNext())
	{
		var currentItem = ListEnumerator.get_current();
		var _contentTypeId = currentItem.get_item('ContentTypeId');
		if(_contentTypeId == "0x010100A7875921E4CCDA46ACBCEA4C01F659F2")
			$("#documentReport").append("
<tr><td>File:" + currentItem.get_item('ID') + ":" + currentItem.get_item('FileLeafRef') + "</td></tr>
");
		else
			$("#documentReport").append("
<tr><td>Folder:" + currentItem.get_item('ID') + ":" + currentItem.get_item('Title') + "</td></tr>
");
	}
 	var position = allItems.get_listItemCollectionPosition();
	if (position != null) {
        query.set_listItemCollectionPosition(position);
        allItems = list.getItems(query);
        context.load(allItems);
        context.executeQueryAsync(Function.createDelegate(this, this.success), Function.createDelegate(this, this.failed));
    } 
}

function failed(sender, args) {
	alert("failed. Message:" + args.get_message());
}
</script>
<a href="#" onclick="javascript:ViewAllFiles();">View All Files</a>​
<div>
<table id="documentReport"></table>
</div>

To use this you just need to change out the library name ("Documents" in this case) with your own library, and then change out the content type Id that is hard coded ("0x010100A7875921E4CCDA46ACBCEA4C01F659F2" in this case) with your own Folder's content type Id. For this client the folders were a custom content type.

What this will do is essentially page through all the items (50 at a time) and dynamically add a row to the documentReport table for each one of them. It will continue to run until it "prints" all items on the screen. You can actually watch it happen.

Once it has completed you can then highlight all the rows and past it into Excel (using a Split Text with a : delimiter) if you wanted to then compare the list to something else.

This immensely helped my client when trying to verify that a data migration into SharePoint had been completed properly, and they actually identified files that had been missed in the migration!

Thursday, March 14, 2013

HTML5 using JavaScript and CSS3 Microsoft Cert

I passed the HTML5 Microsoft Certification exam this morning. I'm really impressed with Microsoft and how they've really embraced jQuery and the open source community with their latest exams. I'm excited to continue on the MCSD track for SharePoint 2013. From what I hear there will be one more exam that I can take to upgrade my MCPD but I think I'll probably take the other two exams that may be coming down the line. I've heard that they should be released in beta around the first week in April.

- Owen Runnals
SharePoint Practice Manager @ General Networks Corp

Thursday, March 7, 2013

Visual Studio ignoring Step In and Referencing GAC DLL instead of local copy

I've been working on a large project for SharePoint 2010 and we just started using Visual Studio 2012. I needed to reference and use a SharePoint helper project that I wrote in the past so I added it to my solution (as I knew I'd need to update some of the functions) and then referenced it in my new class.

When building and trying to step into methods in my SharePoint helper project VS2012 was ignoring my Step In and was just stepping over.

I finally found out that VS was ignoring my direction to copy the referenced dll locally and was instead loading the dll from the GAC. I hadn't incremented the assembly version but had changed the code of the SharePoint helper so I couldn't even manually load the symbol file.

So all I did was increment the assembly version on the SharePoint helper project and immediately it copied the dll locally and stepped in properly during debugging.

Lesson learned!

Thursday, February 28, 2013

SharePoint ModalDialog and Refreshing the page upon close

Recently I've been working on a project where we're creating a number of custom pages in a SharePoint 2010 solution, some of which need to be opened in a SharePoint dialog box. I've written a couple of openDialog functions before and this time was focused on the different ways to refresh the parent page after the dialog is closed. Here's the base function I was using:

function showDialog(title, url) {
 function showDialogCallback(dialogResult, returnValue) {
  if (dialogResult == SP.UI.DialogResult.OK) {
   SP.UI.Notify.addNotification(returnValue, false);
  }
  else{
   SP.UI.Notify.addNotification(title + " Cancelled", false);
  }
  setTimeout("SP.UI.ModalDialog.RefreshPage(dialogResult);", 1000);
 } 
 var dialogOptions = {
  url: url,
  title: title,
  dialogReturnValueCallback: showDialogCallback
 };
 SP.UI.ModalDialog.showModalDialog(dialogOptions); 
}

This simply allows me to pass in my title and url and the dialog opens with the dynamic size I need.

In my showDialogCallback function I like to use the SP.UI.Notify functionality (causes the yellow box to animate in on the top right with some information) to let the user know what happened.

The last line in this function has a setTimeout calling the ModalDialog.RefreshPage function, as when this function is called it refreshes the page and clears my Notify from the screen. Setting a timeout allows my notification to show for one second before the page refreshes.

There are a few different refresh calls that I've used:

SP.UI.ModalDialog.RefreshPage(dialogResult); 

This will only refresh if the dialogResult == SP.UI.DialogResult.OK. It has the same effect as putting it in the if statement directly above. I read some where that this kind of refresh isn't a complete reload of the page, instead it does some ajax calls to reload the content of the page you're on, though I haven't confirmed that to be true, and can't say from a user prospective that I see any difference with the next method.
location.reload(true);

This is pretty straight forward. It's just going to relaod the entire page grabbing all information from the server again.

Depending on what I'm trying to accomplish I find I use a combination of the setTimeout with either of these reloads.

A few helpful sites I found in my research:
Collabware SharePoint 2010 ModalDialog Tips & Tricks
Antonio Lanaro Blog Post

Monday, January 14, 2013

Document Sets in a SharePoint 2010 Library Template

Recently I needed to create a SharePoint Library template w/ content for a client. They had over 300 document sets with 3 metadata fields that they wanted inside the template. Each year they needed to create a new library with this exact same layout, so saving a document library as a template seemed like the perfect fit.

I created the library, created all the document sets and setup all the metadata for each one and then saved that library as a template.

Unfortunatey when I created a new library based on my shiny new template, all the document sets were generated as Folders and errored out when I tried to access them. Once I manually changed the content type from Folder to my Document Set content type, all worked fine and all metadata fields had the proper values.

So this lead me to research how I could quickly modify over 300 folders content types to the Document Set type.

I then stumbled on this great article by Phil Childs on Changing the Content Type Set on Files in SharePoint.

This PowerShell didn't quite do what I needed it to do however, as it was just looking at the Items and not the Folders, so I modified it slightly to produce what I wanted. My change required creating an array of the Folders IDs as updating a folder changes the collection and would error out of a ForEachObject loop.

Here's what I came up with:
function Reset-SPFolderContentType ($WebUrl, $ListName, $OldCTName, $NewCTName)
{
    #Get web, list and content type objects
    $web = Get-SPWeb $WebUrl
    $list = $web.Lists[$ListName]
    $oldCT = $list.ContentTypes[$OldCTName]
    $newCT = $list.ContentTypes[$NewCTName]
    $newCTID = $newCT.ID
    
    #Check if the values specified for the content types actually exist on the list
    if (($oldCT -ne $null) -and ($newCT -ne $null))
    {
        #Go through each item in the list
	$folderIds = @()
	$list.Folders | ForEach-Object {
		$folderIds = $folderIds + $_.ID
	}
	ForEach ($folderId in $folderIds) {
            #Check if the item content type currently equals the old content type specified
	    $item = $list.GetItemById($folderId)
            if ($item.ContentType.Name -eq $oldCT.Name)
            {
                    #Change the content type association for the item
                    write-host "Resetting content type for file" $item.Name "from" $oldCT.Name "to" $newCT.Name
                    $item["ContentTypeId"] = $newCTID
                    $item.Update()
            }
            else
            {
                write-host "File" $item.Name "is associated with the content type" $item.ContentType.Name "and shall not be modified"
            }
        }
    }
    else
    {
        write-host "One of the content types specified has not been attached to the list"$list.Title
    }
    $web.Dispose()
}

To use it, you copy the entire function, open a SharePoint PowerShell command prompt and paste it in. Hit enter until the >> is no longer you prompt. Then you can use the function like so:

Reset-SPFolderContentType –WebUrl “http://sharepoint/sites/audit” –ListName “2002” –OldCTName “Folder” –NewCTName “Audit Document Set”

Thanks Phil!
- Owen Runnals
SharePoint Practice Manager @ General Networks Corp

Thursday, December 20, 2012

PowerShell Script to Create Site Collections for SharePoint 2013

After setting up my SharePoint 2013 farm with Host Name Site Collections I wanted a quick easy way to create a new Site Collection, since you can't do it properly through Central Admin.

So I sat down and wrote this PowerShell script that will prompt the user for all necessary information to create a new SC with an option of creating a new Database.

To run you can copy the below script and paste it into a ps1 file.


Add-PSSnapin Microsoft.SharePoint.Powershell
Write-Host "This script can be used to create Site Collections for SharePoint 2013"
$webApp = Get-SPWebApplication | Where-Object {$_.DisplayName -eq "SharePoint HNSC Host - 80"}
IF ($webApp){
 $ans = Read-Host "I found a HNSC Host Web App, would you like me to use it?. Y or N"
 
}
ELSE {
 $ans = 'N'
}
IF ($ans -eq 'N' -or $ans -eq 'n') {
 $webApp = $null
    Write-Host "Here are the web applications I see on your farm"
    Get-SPWebApplication
 while (!$webApp) {
  $webAppName = Read-Host "What is the DisplayName of the Web Application you'd like to use? "
  $webApp = Get-SPWebApplication | Where-Object {$_.DisplayName -eq $webAppName}
  IF (!$webApp) {Write-Host "I didn't find a Web Application with that name. Be sure to enter the full name exactly as it's listed above."}
 }
}
$siteName = Read-Host "Site Name"
$siteDescription = Read-Host "Site Description"
$siteUrl = Read-Host "Site Url (ie http://site.sp2013.com)"
$seeTemplates = Read-Host "Would you like to see a list of templates codes? Y or N"
IF ($seeTemplates -eq 'y' -or $seeTemplates -eq 'Y') {Get-SPWebTemplate}
$siteTemplate = Read-Host "Site Template (ie Blank - STS#0, Team STS#1, etc)"
$siteAdmin = Read-Host "Site Admin (ie ms\devspadmin)"
$createDB = Read-Host "Finally would you like to create a new Content Database for this Site Collection?"
$db = $null
IF($createDB -eq 'Y' -or $createDB -eq 'y') {
    $dbName = Read-Host "Database Name"
    Write-Host "Creating Database..."
    $db = New-SPContentDatabase -name $dbName -webApplication $webApp
}
ELSE {
    Get-SPContentDatabase -webapplication $webApp
    while(!$db) {
        $dbName = Read-Host "What Content Database would you like to use?"
        $db = Get-SPContentDatabase -Identity $dbName
        IF (!$db) {Write-Host "I didn't find a Content Database with that name. Be sure to enter the full name exactly as it's listed above." }
    }
}
Write-Host "Creating Site Collection..."
New-SPSite -Name $siteName -Url $siteUrl –HostHeaderWebApplication $webApp -Template $siteTemplate -OwnerAlias $siteAdmin -ContentDatabase $db -Description $siteDescription
Write-Host "Script Complete"

 
- Owen Runnals
SharePoint Practice Manager @ General Networks Corp