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!

Friday, June 7, 2013

User Profile Synchronization Service Hangs on Starting (SharePoint 2013)

I was recently setting up User Profile Sync Service in SharePoint 2013 and of course, I was faced with the hung on starting issue.

In the past I've used Bill Daugherty's excellent post on resolving the hanging issue for SharePoint 2010, so I figured I'd give it a try for 2013, and it worked!

Thanks Bill!

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.

Wednesday, May 22, 2013

Setting Up A Provider Hosted SharePoint 2013 App

Last night I had to work through getting a Provider Hosted App deployed to a SharePoint 2013 farm. We ran into a few issues that I thought would be good to note:

Deploying an App from the App Catalog:
The process for this seemed a little unintuitive to me so I figured I'd document it here:
  • First you go to your AppCatalog site, click on manage SharePoint Apps and add your .app file into the library.
  • The next step is you actually have to add that app to your AppCatalog site (strange). So, go to the settings link, Add an App. Find your app and add it.
  • Once it's been added you can go to Site Contents, locate your app and click the elipses, then select Deployment.
  • Now you can add in the site or template that you want to deploy your app for.
Host Header Web Applications:
Due to the client wanting Forms Based Authentication on this site, but not another we went with setting up a separate web application that used FBA with a host header. In all my other environments I had always used Host Named Site Collections (HNSC), so this was the first time in SP2013 trying to deploy apps with Host Headers.
 
First thing we realized was we had to create a new AppCatalog for this Web Application (that's right, the AppCatalog is for the Web Application). Then when we tried to deploy it to our site we received an error. Thanks to this blog by Mirjam I knew where to look for the solution.
 
When using Host Headers you need to make sure you have a web application that doesn't have a host header at all AND there needs to be a root site collection created. The later is what we were missing. Creating a Team Site as the root site collection resolved the issue and we were able to deploy our app to our site.
 
Setting up Forms Based Authentication against Active Directory
We used this great walk through of setting up FBA from Nishant Shah, and instead of extending the SharePoint web application, we just modified our application to be fully FBA. Completing this got the form up and running, however we were unable to login.
 
After some troubleshooting we found that we needed to add the membership and roleManager entries into the web.config of the root folder of the SharePoint Webservice sites as well. In our case we had to reconfigure the web service to get things working.

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 28, 2013

Fixed Position Div Vertically Only

Recently a client asked for a footer on a portal, that had a fixed width, was fixed on the screen (so it would stay on the screen when scrolling up and down), but wanted to make sure if the browser window was small enough where horizontal scrolling was needed, that the footer would scroll.

CSS position:fixed just wasn't going to cut it alone, as when you set that fixed position, it basically ignores all scrolling, both vertical and horizontal.

I ended combining CSS with some jQuery. Assuming our footer is in a div:

<div id="footer">Something in my footer</div>

I applied this CSS

.footer {
    position:fixed;
    margin:auto;
    width:900px;
    bottom:0px;
    height:30px;
}

And then I put this in my javascript file (you could put it in script tags too)


//Keeps the header and footer on the screen and scrolling horizontally
$(window).resize(positionFooter);
$(window).scroll(positionFooter);

function positionFooter() {
    if ($(window).width() < 900) {
        $('#footer').css('left', -$(window).scrollLeft());
    }
    else {
        $('#footer').removeAttr('style');
    }
}

This attaches an the function positionFooter() to the window scroll event as well as the resize event. The function itself checks to see if the width of the window is large enough to fit my footer (900 in this case) and if it isn't, it sets the left attribute as an inline style on the footer div making it move to the left simulating scrolling. If the window is large enough, it ensures the inline style tag is removed so the margin:auto will work again.

Worked like a charm.

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