Friday 28 September 2012

101 Code Samples SharePoint 2010

Data Querying Options (SharePoint 2010) – Server OM vs Client OM vs REST vs LINQ vs Search API


SharePoint 2010 allows various options to query SharePoint data within SharePoint and from outside of SharePoint.It allows both server side and client side data access technologies. This article will provide high level features, pros, and cons of each server side and client side data access technologies. Additionally, SharePoint allows developers to write custom WCF Services and write custom queries based on Enterprise Search API. Hopefully at the end of this article, it would allow you to make decision which option would work best for appropriate use cases.

Server Side Object Model -  SPSite, SPWeb, SPList, SPDocumentLibrary, SPListItem, SPView, SPField, SPContentType etc.
  • Major features
    • Implemented as Microsoft.SharePoint Namespace
    • Code must run on the SharePoint Servers
    • Programmatic approach to interact with SharePoint objects
    • APIs to provision and maintain building blocks like lists, site columns, content types, or pages
    • APIs to iterate through farm, web application, site, web, list, list item, fields, content types collections
  • Pros
    • Virtually any APIs or objects available to access SharePoint data
    • Easy to learn API, familiar concepts such as foreach() to loop through collections
    • Supported in Sandbox solutions. Site collection level APIs works great in Sandbox Solutions
  • Cons
    • Not available on the remote, Non-SharePoint servers
    • foreach(), GetItemById, GetListItems increase resource utilization and may affect performance
    • Requires IISREST while deploying farm level code on the server, Sandbox solutions doesn’t require IISReset
Server Side Object Model – SPQuery and SPSiteDataQuery
  • Major features
    • SPQuery available since SharePoint 2003 days and SPSiteDataQuery available since MOSS 2007 days
    • Implemented as Microsoft.SharePoint Namespace
    • Code must run on the SharePoint Servers
    • Programmatic approach to interact with SharePoint objects
    • SPQuery executes query against single list or performs join with CAML Predicate on multiple lists on lookup columns with in site collection
    • SPDataQuery executes query  against multiple lists to  aggregate results, scoped at site collection level.
    • SPDataQuery returns data as ADO.NET data table. It has potential performance issues for rollup views because it first loops through all the lists and then process the query – multiple round trips
    • Fastest query option but good only for small scopes
    • Developer must parse CAML fragments and access to columns is not strongly typed
    • SPQuery can query external list data within a site
  • Pros
    • CAML is very fast – compared to foreach, fastest way to data access
    • Supports complex queries, such as sorting, filtering, and joining on multiple sources, much better options compared to SPList.GetListItems
    • Supported in Sandbox solutions. Site collection level APIs works great in Sandbox Solutions
    • Query Throttling override is supported
      • You must use this option to execute queries that return large sets, by default more than 5000 records are blocked, these settings are web application level, defined in central admin, must adhere in Office 365
      • QueryThrottleMode = SPQueryThrottleOption.Override, Object Model Override = Yes, and Query Runs under Super Users (User with Full Read or Full Control Permission)
      • Use List Level Indexing in conjunction with SPQuery to avoid List Throttling limit
  • Cons
    • Not available on the remote, Non-SharePoint servers
    • Small scope, e.g. targeted to a particular list or library
    • Affected by List Throttling Limit (default List View Threshold is >5000)
    • CAML means low developer productivity – Hard coded column names, not strongly typed, quirky syntax, lack of tooling support, and difficult debugging
    • Requires IISREST while deploying farm level code on the server, Sandbox solutions doesn’t require IISReset
Server Side Object Model – LINQ to SharePoint
  • Major features
    • New in SharePoint 2010
    • Implemented as Microsoft.SharePoint.Linq Namespace
    • Executes query against single list or performs join on multiple lists on lookup columns with in site collection, You can’t aggregate multiple lists data without lookup column
    • Microsoft supplies spmetal.exe to generate LINQ to SharePoint entities
    • Strongly typed entities to perform CRUD operations against SharePoint data
    • Translates into CAML query during runtime
  • Pros
    • Not fastest as CAML but still very fast compare to foreach, Approx. 10% slower than SPQuery
    • Performs compile time errors against strongly typed objects from spmetal.exe
    • High productivity, IntelliSense, and less error-prone programming
    • Supported in Sandbox solutions.
  • Cons
    • Needs to regenerate the entities if List Schema changes
    • Query Throttling override is not supported
    • Small scope, e.g. targeted to a particular list or library
    • SPMetal can’t generate entity classes for more than one web
    • Although its minor, additional overhead occurs while converting query to CAML during runtime
    • Can’t query external list data within a site
    • Requires IISREST while deploying farm level code on the server, Sandbox solutions doesn’t require IISReset
Client Side Object Model
  • Major features
    • New in SharePoint 2010,  Subset of server OM functionality but similar APIs
    • Implemented as WCF Service – Client.svc, Adding Client.svc reference directly in Visual Studio is prohibited
    • Three different Models - .NET Managed, Silverlight, ECMAScript/JavaScript
      • For Javascript CSOM, reference SP.js is located under : %ProgramFiles%/Common Files/Microsoft Shared/Web Server Extensions/14/TEMPLATE/LAYOUTS
      • For Silverlight CSOM, Add the reference to Microsoft.SharePoint.Client.Silverlight.dll  and Microsoft.SharePoint.Client.Silverlight.Runtime.dll located under %ProgramFiles%/Common Files/Microsoft Shared/Web Server Extensions/14/TEMPLATE/LAYOUTS/ClientBin
      • For .NET Manage Client CSOM, Add reference to Microsoft.SharePoint.Client.dll and        Microsoft.SharePoint.Client.Runtime.dll located under %ProgramFiles%/Common Files/Microsoft Shared/web server extensions/14/ISAPI
    • CSOM executes query against single list or performs join with CAML Predicate on multiple lists on lookup columns with in site collection
    • Typically .NET Managed CSOM code will run outside of SharePoint in remote .NET or CRM applications, Silverlight CSOM is used only in Silverlight applications, and JavaScript CSOM  is must be used only in SharePoint Site, JCSOM can’t be used in a classic ASP.NET Site due to cross-scripting issue.
  • Pros
    • Ideal for accessing cross farm data when Search API is not an option or real-time remote data access is required
    • Allows submitting CAML queries directly from client side logic, making it fastest client side data access option
    • Supports Async processing, Request Throttling, and Batch object handling
    • CSOM can query external list data within a site
    • Supports Site Collection level SharePoint Object Model. It doesn’t provide administrative objects or objects that are scoped higher than site collection.
    • No IISREST is required while deploying a Silverlight or JavaScript CSOM based implementation. e.g. JavaScript CSOM can be implemented with just a Content Editor Web part.
  • Cons
    • Can’t access data outside of current site collection
    • Query Throttling override is not supported
    • Since these are web services interface, can’t accessed from Sandbox Solutions
    • Requires learning of CAML to query small result set – Hard coded, Un complied, Not strongly typed CAML query
    • Can’t elevate the user privilege using RunWithElevatedPrivilege or Impersonate using CSOM. It means, results retrieved using CSOM are always security trimmed by default.
    • Requires IISREST while deploying farm level, .NET managed CSOM code on the server
REST Based API – WCF Data Services
  • Major Features
    • New in SharePoint 2010
    • Implemented as REST based WCF Data Services – ListData.svc, REST is HTTP-based XML data transfer design pattern based on OData protocol – Stateless, cacheable, uniform, Able to maintain & browse lists using HTTP verbs – GET, POST, PUT, DELETE, and MERGE
    • Returns results in ATOM Feed or JSON format (For JavaScript clients)
    • Available for Lists (ListData.svc) and Excel Services REST API (ExcelRest.aspx)
    • Add Service Reference to ListData.svc, Proxy class provides LINQ-based strongly-typed access
  • Pros
    • Confronts Open Data (OData) protocol to support batching, concurrency, and partial updates
    • Client-side technique with strongly typed access, Intelligence Support, LINQ style productivity, and compile time errors checking against from proxy class
    • Can be used in Silverlight as a replacement for Client OM for strongly typed access
    • Can be query from any kind of client : Silverlight, Javascript, and even from PHP and JAVA
  • Cons
    • Supported only for Lists and Excel Services
    • Needs to refresh the proxy class if List Schema changes
    • Since these are web services interface, can’t accessed from Sandbox Solutions
    • Overhead of three Query conversations from URL based REST Queries to LINQ Queries to CAML Queries
    • Can be used against only SPList, can’t used against SPWeb, SPSite, or SPUser, doesn’t support SharePoint Object Model
    • Can’t query external list data within a site
    • Can’t query more than 1000 items at a time
SOAP Based API – ASMX Web Services
  • Major Features
    • Uses Legacy ASMX  ASP.NET Web Services technology
    • Deprecated, other options should be preferred over this
    • Scoped at the List or Library level data access, requires custom web services for multiple list joins
    • Small subset of APIs compared to SSOM
    • Provides legacy approach for remote data access
    • Developer must parse CAML fragments and access to columns is not strongly typed
    • Used in conjunction with SPServices for jQuery clients, SPServices supports Anonymous access and cross- site collection and cross-domain access
  • Pros
    • Ideal for accessing cross farm data when Enterprise Search API is not an option but CSOM should given preference here for remote cross farm data access in farm level solutions
    • Ideal for Office 365 Cross Site Collection data access in Sandbox Solutions in conjunction with SPServices, none of the other options like CSOM or REST APIs would work in sandbox
    • Supports advanced SharePoint Server capabilities like search, social, or user profile services, which are unavailable through the CSOM or REST APIs
    • Supports access from JavaScript and non-windows clients
  • Cons
    • Requires learning of CAML to query small result set – Hard coded, not strongly typed CAML query, no intelisense
    • Difficult to use in current format, often custom wrapper is required
    • Can be used against only SPList, Doesn’t support SharePoint Foundation Object Model
Custom WCF Services
  • Major features
    • Create your own web service interface for SharePoint
    • Extend out of box SharePoint functionality, requires to expose SharePoint functionality that is unavailable through the existing APIs
    • Can be hosted in SharePoint, IIS, or any other .NET client
  • Pros
    • Ideal for third party vendors and ISVs
    • Ideal for organizations to integrate enterprise systems with SharePoint as service oriented architecture
  • Cons
    • Requires knowledge of complex WCF Services development
Enterprise Search API
  • Major Features
    • Query against Search Index – KeywordQuery and FullTextQuery API
    • This is Enterprise Search capability, not FAST Search
    • Perfect for Cross-farm, Cross-Web Application, or Cross-Site Collection Data Access
    • Use Search API to query large amount of data
  • Pros
    • Cross boundaries, including: farm, web application, site collections, sub sites, libraries, etc.
    • Ideal for Cross-Site Collection Navigation Menu
    • Can query across large data corpuses
    • Queries are fast even with huge data corpuses
    • Greater scalability, All other techniques are mostly SQL-based
  • Cons
    • Requires advanced search service administration and configuration.
    • Content needs to be in the indexed (dependency)
    • Content must be tagged with metadata (to be effective)
    • Crawl properties must be in index and Managed properties must be mapped to crawled properties.
Comparing SharePoint 2010 Server Side Data Technologies

Wiki Pages vs Web Part Pages vs Publishing Pages for SharePoint 2010 Intranet Content Pages


SharePoint 2010 has three major options to create content pages – publishing pages, web part pages, and wiki pages.

As usual, to make proper architectural decision, I ended up documenting characteristics of each type of pages to allow me make final decision. Here are the high level characteristics of web part pages, wiki pages, and publishing pages.

Publishing Pages
  • Introduced in MOSS 2007 as web content management & publishing infrastructure
  • Publishing pages are designed for creating content pages in more controlled manner with consistent look & feel. These pages are based on page layouts and content types. Typically page layouts are designed by IT & page designers and publishing content pages can be created based on pre-defined page layouts by authors. This process is common for web content management systems.
  • Publishing pages has pre-defined content areas like web part zones & publishing field controls. This allows page designers to define page layouts and full control over how these page content can be rendered or different controls can be branded.
  • Publishing pages content can be versioned and and display history of changes. Publishing pages optionally allows passing articles through proper page approval and page scheduling life cycle.
  • Publishing pages are stored in Pages library. This library is available when publishing infrastructure is enabled on the site. There is only one pages library per site. You can enable multiple page layouts by enabling multiple content types to the pages library.
  • Publishing pages are created by site members or above security groups (contribute permission)
  • Publishing pages are edited by site members or above security groups (contribute permission)
  • You can access publishing infrastructure and create page layouts using Microsoft.SharePoint.Publishing APIs. Please note that you can’t use this namespace in Office 365 sandboxed solution. Alternatively in Office 365 and even on-premises, you can use SharePoint Designer to create page layouts.
Web Part Pages
  • Heavily used in MOSS 2007 and SharePoint 2003 days
  • All content on a web part page is displayed using web parts. Web Part pages are structured web part content including lists, libraries, and other collaborative content including rich media, other web pages, search results, and an aggregation of information. You can’t add text or image easily on web part pages – it requires web parts like content editor or image web parts. This is one of the major disadvantages of web part pages over wiki pages because adding simple graphics, images would require web parts or knowledge of content editor web part.
  • Web part pages provides ideal middle landscape where you still have semi-strict formatting control like publishing pages and partially-controlled collaborative editing capabilities like wiki pages.
  • Web part page has pre-defined content areas like web part zones where web parts can be added and moved as needed
  • Web part page content can be versioned with limited functionality. Please see Maurice Prather’s article – http://www.bluedoglimited.com/SharePointThoughts/Lists/Posts/Post.aspx?ID=305
  • Web part page layout and content configured for all users and optionally personalized for individual users
  • Web Part pages can be stored any document library. Mainly it stored in site assets, site pages, or any other document library you would like to use.
  • Web Part pages can be created by designers or above security groups (design permission)
  • Web Part Page can be edited by members or above security groups (contribute permission)
  • It’s easy to maintain web parts (add, update, remove) in web part pages programmatically using server side object model.
    • Programmatically add web parts to the web part pages by programmatically dropping web parts on web part zones using web part manager
Wiki Pages
  • Introduced in SharePoint 2010
  • Wiki pages consists of rich text editor providing WYSIWYG in-browser editing experience using Web Edit technology. It is designed to add free-form text & rich content including text, tables, links, images, as well as SharePoint lists and web parts anywhere on the page without any needs for web part zones like in web part pages or field controls like in publishing pages. e.g. it doesn’t require web parts like content editor or image web parts to add texts or images.
    • While you can easily add web parts on web part pages (requires advanced skills to use content editor web part for advanced customization), it’s much easier to add text, images, and links including web parts on a on a Wiki page. This would make wiki pages far more flexible to add contents compare to web part pages.
    • Wiki pages are easier to change than Web Part pages by end users with minimal IT dependency. This is major advantage of wiki pages over web part pages. On the flip note, because it’s easy to apply different fonts and styles on wiki pages content, web part pages would allow you better governance and control over standardization of presentation, formatting, and look and feel. It is important to note that advanced power users with SharePoint designer and HTML skills can easily apply different styles and look & feel in web part pages as well. Only way to fully control layout and style of the content in content pages are publishing pages without web part zones. Please note that even publishing pages with web part zones, advanced power users can add different web parts and apply different styles and look & feel.
    • One of the major issues of wiki pages is hidden HTML tags while editing the page in WYSIWYG editor. This might frustate end users who doesn’t have knowledge of HTML and how to remove those hidden tags and spaces. In most cases, you need advanced power user tools like SharePoint Designer to delete those troubled HTML tags like hidden paragraphs and DIV tags. Web part pages with fixed layout of web part zones & web parts would provide little bit better experience than wiki pages while adding content in more strict manner.
    • Another major issue of wiki pages are targeting specific section to brand wiki pages. Because wiki pages uses MS-RTE CSS classes to wrap most of the content tags like DIV, it’s hard to target specific section of wiki pages to brand. Web part pages can easily allow wrapping web part zones by DIV tags to target specific area using CSS.
  • Wiki pages have HTML zones where content can be added directly on the page.
  • Wiki pages can’t be personalized. It is designed to share information in collaborative way where multiple people can add content.
  • Wiki pages can be versioned and display history of changes.
  • Wiki pages are stored in site pages library
  • Wiki pages are created by site members or above security groups (contribute permission)
  • Wiki pages are edited by site members or above security groups (contribute permission)
  • All the content added to wiki pages are added as HTML markup. There is no direct API available to programmatically maintain (add or remove) web parts on the wiki pages.
Overall, if properly architected, in the bigger picture, I still see wiki pages are better option for intranet content pages because of end-user empowerment for content creation & IT non-dependency. In most cases, because of versatility of wiki pages, I personally prefer wiki pages over web part pages for intranet content pages. Some of the greatest benefits of the wiki pages are not only it’s flexibility to add all kind of contents easily but allows more intuitive experience for non-IT business users to create these pages easily without IT’s assistance to build their sites which may contribute into intranet adoption.
Some may argue that ease of use may be one of the biggest concerns of wiki pages over web part pages, which further cause content governance or uneven content look & feel issues especially on major department home pages. More formal editing experience makes web part pages ideal candidate for home pages for department or major business areas where IT would like to balance standardization of page layout and agility to allow business owners to manage content on the home pages.

In our client intranet environment, we ended up using all three types of pages for different scenarios.
  • Publishing Pages – Main Intranet Home Page – Since this page is main landing page for all employees, it required most structured content with fixed page layout making publishing pages ideal choice
  • Web Part Pages – Department or Major business area Home Pages – Since most of department home pages were maintained by department content owners, IT wanted to standardize the look & feel and layout of home pages across intranet. Web part pages were ideal choice for us to allow department content owners to add content in more controlled manner.
  • Wiki Pages – Intranet Content Pages and Team Site Home Pages – Because of it’s WYSIWYG in-page editing capabilities, this is ideal candidate for end-users to create and manage contents on their team sites in more flexible manner where content presentation and standardization of layouts is less important.
Additional Resources

Easy way to deploy .resx (resource) files under App_GlobalResources folder in SharePoint 2010

Resource files in SharePoint 2010

Resource files are mainly used for localization of the custom solution.
While working with SharePoint 2010, many times the customizations need to be localized and hence, it means that developers have to play around the language specific resx files. Deployment of resx files through the SharePoint solution can be achieved using SharePoint mapped folder named "Resources". However the files those goes under this path can be accessed using SPUtility.GetLocalizedString function. Majority of the times, you may have customizations related to the user interface which are declarative. SharePoint 2010 is based on ASP.NET technology so for, any tags related to resources the resx files will be searched in App_GlobalResources folder located under intepub\wwroot\wss\<<application>> folder hierarchy.
This puts additional overhead of deployment to the developer who want to use the traditional ASP.NET way of localizing UI resources. As, we need to ensure that the resx files should be deployed under both 14 hive and App_GlobalResources folders.
There are multiple ways to deploy the resource files. Following are few of them
  • Write an event handler to copy resx files
  • Custom timer job
  • PowerShell Script as a part of deployment
  • Manual copy and so on....
But what about the solution uninstallations, upgrades and moreover if you have multi server environments, manual deplooyment of resx files is more tedious and erroneous.
SharePoint 2010 Projects supports feature called as "Elements". Using Elements we can deploy number of files at specific locations. Following solution makes use of Elements to deploy resx files to App_GlobalResources folder. Few advantages of this are as below
  • No need to write custom code for deployment
  • Resx files are packaged as a part of WSP
  • No need to worry about upgrades and uninistallations its done automatically in SharePoint way
  • Works perfectly on multi server enviornment
  • No manual steps

 How to do it?

  • Create new Empty SharePoint 2010 Project name it as ResxDeployment
  • Choose a local site for debugging and, choose deployment type Deploy as farm solution
 
  • In Solution Explorer, right click on ResxDeployment project and Add SharePoint Mapped Folder
  • In Add SharePoint Mapped Folder dialog box, choose Resources and click OK
     
  • In Solution Explorer, right click on ResxDeployment project and Add New Item
  • Add few sample resx files to the Resources folder
 
  • In Solution Explorer, right click on ResxDeployment project and Add New Item. Add Empty Element and name it as required for e.g. App_GlobalResources
 
  • In Solution Explorer, click on Show All Files icon to view all files. Open SharePointProjectItem.spdata files located under App_GlobalResources node
 
  • Add the relavent information to the xml file which includes Source, and Type of the file. The type should be set to AppGlobalResource and source should be a relative path of resx file. Following is the example
<?xml version="1.0" encoding="utf-8"?> 
<ProjectItem Type="Microsoft.VisualStudio.SharePoint.GenericElement" DefaultFile="Elements.xml" SupportedTrustLevels="All" SupportedDeploymentScopes="Web, Site, WebApplication, Farm, Package" xmlns="http://schemas.microsoft.com/VisualStudio/2010/SharePointTools/SharePointProjectItemModel"> 
 <Files> 
 <ProjectItemFile Source="Elements.xml" Target="App_GlobalResources\" Type="ElementManifest" /> 
 <ProjectItemFile Source="..\Resources\Contoso.resx" Type="AppGlobalResource"/> 
 <ProjectItemFile Source="..\Resources\Contoso.de-DE.resx" Type="AppGlobalResource"/> 
 <ProjectItemFile Source="..\Resources\Contoso.en-GB.resx" Type="AppGlobalResource"/> 
 <ProjectItemFile Source="..\Resources\Contoso.en-US.resx" Type="AppGlobalResource"/> 
 <ProjectItemFile Source="..\Resources\Contoso.es-ES.resx" Type="AppGlobalResource"/> 
 <ProjectItemFile Source="..\Resources\Contoso.it-IT.resx" Type="AppGlobalResource"/> 
 </Files> 
</ProjectItem> 
  • In Solution Explorer, right click on ResxDeployment project and choose Deploy
  • If everything goes well the solution will be deployed on given web application. Also, you can verify that the resx files are deployed under App_GlobalResources and 14\Resources folders

Points to be noted :

  1. Resource files are deployed at two diffrent locations. You can directly add resx files to only elemets folder if needed.
  2. There is no need to copy the files from Resources mapped folder to App_GlobalResources element folder in Visual studio. There is only one copy of resx file hence, no need to worry about multiple location changes
  3. For every additional resx file, SharePointProjectItem.spdata file should be updated otherwise the file will not be copied to App_GlobalResources folder of web application
  4. Please set the Scope of the Feature that includes this module as relavent. It is preferrable to set, Application Level Scoped
In this way, by using OOTB SharePoint features we can deploy resx files to App_GlobalResources folder.

SharePoint 2010 Solution Deployment/Retraction and Feature Activation/Deactivation Process


SharePoint 2010 Solution Deployment/Retraction and Feature Activation/Deactivation Process:

   
Have you ever wondered what really happens when you deploy/retract SharePoint WSPs and Activate/Deactivate site collection or sub site level features at the farm level? As you execute different PowerShell commands or activate/deactivate features from browser interface, SharePoint 2010 makes several changes at the different places on the servers and databases.
This article provides high level overview of what really happens when you execute different farm level PowerShell commands. Hopefully it will be helpful. 


Central Admin
Site/Web Level Features
GAC
Web.Config
14-Root Features
SharePoint Config DB, Objects Table – Solutions
SharePoint Config DB, Objects Table – Features
Web App Content DB, Features Table
Add-SPSolution
Solution Added but Not Deployed
N/A
N/A
N/A
N/A
Yes – Solution Definition Added
N/A
N/A
Install-SPSolution
Deployed to Web App
Installed Feature
Yes – DLLs Added
Yes – Safe Control Entries Added
Yes – Features Added
Yes – Solution Definition Still Exists
Yes – Feature Definition Added
N/A
Enable-SPFeature
Solution Still Deployed
Activated feature to Site/Web
Yes – Still Exists
Yes – Still Exists
Yes – Still Exists
Yes – Solution Definition Still Exists
Yes – Feature Definition Still Exists
YES – Feature Activation Link Added
Disable-SPFeature
Solution Still Deployed
Deactivated feature from Site/Web
Yes – Still Exists
Yes – Still Exists
Yes – Still Exists
Yes – Solution Definition Still Exists
Yes – Feature Definition Still Exists
NO – Feature Activation Link Deleted
UnInstall-SPFeature
Solution Still Deployed
Uninstalled feature
Yes – Still Exists
Yes – Still Exists
Yes – Still Exists
Yes – Solution Definition Still Exists
No – Feature Definition Deleted
N/A
UnInstall-SPSolution
Retracted from Web App
Uninstalled feature
No – DLLs Deleted
No – Safe Control Entries Deleted
No – Features Deleted
Yes – Solution Definition Still Exists
No – Feature Definition Deleted
N/A
Remove-SPSolution
Solution Deleted
N/A
N/A
N/A
N/A
Yes – Solution Definition Deleted
N/A
N/A

 

SharePoint 2010 - Access denied for users that have full control on the site

SharePoint 2010 - Access denied for users that have full control on the site

Scenario:
  • Users get "Access Denied" over the whole site, despite having Full Control permission
  • Site Collection Administrators have no problem logging in
Explanation:

A SharePoint 2010 site that uses claims-based authentication has been extended to Intranet zone that uses AD as well as FBA. The site has number of users in the default owners and members groups.
All the site users always get access denied over the whole site even though they clearly have access to the site through the site groups.
Site Collection Administrators are allowed to access the site and have no problems logging in.
Resolution:
Make sure that all the Master page, CSS files, any other files that are required are published.
If there are files that are required in the master pages and are not published, users will get access denied even if they have full control on the site.
If you are ok with giving all authenticated users atleast road-only access to all files inorder to prevent the access denied problem, then you can try the below.

Add a new "User Policy" for the web application that allows "All Authenticated Users" the permissions "Full Read" on the desired zone.
UserPolicy
 I have updated the post with some screenshots that might be helpful.
Go to Central Administration
Click on Application Management
Click on Manage Web Applications

Choose the desired web application:

Web Apps

Click on "User Policy"

Web Apps User Policies

Click on "Add Users" to add a new User Policy:

Choose Zone

Select the zone that you want to apply the new policy to. If you are not sure what to choose, leave the defaut value selected (All Zones) and cick next

Add User

Choose the permissions that you would like to give to the user(s) in this new user policy. If you do not want to give the user(s) full permission, choose "Full Read". This permission ensures that all the users in this policy can alteast access the site. Then click on "Browse" icon in the "Choose Users" area.

Add Authenticated Users

If you would ike to give All Authenticated Users / AD users / FBA users "Full Read" access, then choose the appropriate group(s).
In the next screens, click OK to get out of the wizard.
Your new user policy should be ready now.

Thursday 27 September 2012

Cross web application navigation SharePoint 2010-Part II (publishing sites)

Cross web application navigation SharePoint 2010-Part II (publishing sites)

 
In my previous post I described how you can implement consistent cross-site navigation for non-publishing sites (i.e. those sites which are created using WSS site templates). As statistics shows mentioned post is very popular. So I decided to postpone other themes and finish this series as they are so interesting for people.

I will repeat the task here for convenience: suppose that we have site collection (SPSite) with several sub sites (SPWeb). We want to keep the same global navigation (top navigation) for all sites (probably for all site collections within our web application – as you will see below it is possible as well. More over with technique I’m going to describe here it is also possible to use navigation from completely separate Sharepoint site which can be located on another web server). E.g. we have one SPWeb web site with configured navigation items and we want to use navigation from this particular web sites on all another sites.

As I wrote in previous part there is a problem with implementing cross site navigation for publishing sites. If you remember from previous part, SPNavigationProvider has public virtual property Web:

    public class SPNavigationProvider : SiteMapProvider
     {
        // ...
        protected virtual SPWeb Web { get; }
     }
 
Also SPNavigationProvider is not sealed so we can inherit it and override its Web property (always return our navigation source web site) – see part1. But with publishing sites this approach can not be used because PortalSiteMapProvider, which is used in publishing sites, doesn’t have any virtual property which returns SPWeb web site which should be a source for navigation data. If we will investigate its code in Reflector we will see that actually it has CurrentSite and CurrentWeb properties which are very similar to those we are looking for:

    public class PortalSiteMapProvider : SiteMapProvider
    {
         // ...
        public SPSite CurrentSite { get; set; }
        public SPWeb CurrentWeb { get; set; }
        // ...
    }
 
Although PortalSiteMapProvider is also not sealed (i.e. we can inherit from it), mentioned properties are not virtual. So unfortunately we can’t just override them in the custom inheritor of PortalSiteMapProvider class because OTB Sharepoint functionality which has only reference on PortalSiteMapProvider will use its implementation instead of ours. Is there a way to solve this problem without inheriting? Yes – solution exists, but it is not easy.
We will need to implement our own custom navigation provider by ourselves. As a base class we can use standard ASP.Net SiteMapProvider. But in our case better option will be StaticSiteMapProvider class, which has already implementation of several methods so less work will be required with it (e.g. StaticSiteMapProvider is used as a base class for standard XmlSiteMapProvider). Documentation of this class says:
The StaticSiteMapProvider class is a partial implementation of the abstract SiteMapProvider class and supplies two additional methods: AddNode and RemoveNode, as well as the abstract BuildSiteMap and protected Clear methods.
The StaticSiteMapProvider class supports writing a site map provider (for example, an XmlSiteMapProvider) that translates a site map that is stored in persistent storage to one that is stored in memory. The StaticSiteMapProvider class provides basic implementations for storing and retrieving SiteMapNode objects.
If you are extending the StaticSiteMapProvider class, the three most important methods are the GetRootNodeCore, Initialize, and BuildSiteMap methods. The Clear and FindSiteMapNode methods have default implementations that are sufficient for most custom site map provider implementations.
It is very similar to our case because we can treat SPWeb site as a “persistent storage” for our site map. The remaining question is how to retrieve navigation data (i.e. collection of PortalSiteMapNode) from existing publishing site? Obvious answer is to use PortalSiteMapProvider – but we need to call methods of this provider in context of navigation source site. We can make it using web services, i.e. we will use the following schema:

image

We need to register and use our custom site map provider on all sites where we want to show navigation from navigation source site. Our custom provider then will call custom web service Navigation.asmx (which is located in 12/Templates/Layouts/Custom folder on the file system) in context of navigation source site. E.g. if we have 2 sites http://example.com/site1 and http://example.com/site2 where site1 is navigation source, we need to call Navigation.asmx web service from site2 using the following URL: http://example.com/site1/_layout/Custom/Navigation.asmx. As result codebehind of Navigation.asmx will be executed in context of site1, so we will be able to use OTB PortalSiteMapProvider in order to retrieve site map nodes from site1. Simple, isn’t it?

Now when I’ve described the basic idea lets looks a bit on actual implementation. First of all we need to implement custom site map provider – inheritor of StaticSiteMapProvider, which will call external web service Navigation.asmx. The basic implementation is shown below:

    public class CustomNavigationProvider : StaticSiteMapProvider
    {
        private const string SITE_MAP_SESSION_KEY = "CustomNavigationMap";
     
        private SPWeb getNavigationContextWebUrl()
       {
           // instead of hardcoding site url you can use your own logic here
            using (var web = SPContext.Current.Site.OpenWeb("/site1"))
            {
              return web.Url;
           }
       }
    
       private NavigationService initWebService(string contextWebUrl)
       {
           var proxy = new NavigationService();
           proxy.Url = SPUrlUtility.CombineUrl(contextWebUrl, "/_layouts/Custom/Navigation.asmx");
           // use another credentials if required instead of DefaultCredentials
           proxy.Credentials = CredentialCache.DefaultCredentials;
           return proxy;
       }
    
     public override void Initialize(string name, NameValueCollection attributes)
       {
           base.Initialize(name, attributes);
           // here you can add your initialization logic, e.g. initialize web service URL
       }
       
       public override SiteMapNode BuildSiteMap()
       {
           SiteMapNode node;
           if (HttpContext.Current.Session[SITE_MAP_SESSION_KEY] == null)
           {
               node = tryGetNavigationNodesFromContextWeb();
               HttpContext.Current.Session[SITE_MAP_SESSION_KEY] = node;
           }
           node = HttpContext.Current.Session[SITE_MAP_SESSION_KEY] as SiteMapNode;
           return node;
       }
    
       private SiteMapNode tryGetNavigationNodesFromContextWeb()
       {
           try
           {
               string webUrl = this.getNavigationContextWebUrl();
               var proxy = this.initWebService(webUrl);
               var doc = proxy.GetMenuItems();
               var collection = ConvertHelper.BuildNodesFromXml(this, doc);
               if (collection == null)
                   return null;
               if (collection.Count == 0)
                  return null;
               return collection[0];
           }
           catch(Exception x)
           {
               return new SiteMapNode(this, "/", "/", "");
           }
       }
    
       protected override SiteMapNode GetRootNodeCore()
       {
           SiteMapNode node = null;
           node = BuildSiteMap();
           return node;
       }
   }
 
I removed many real-life stuff from this code in order to keep only valuable places. So we override 3 methods of StaticSiteMapProvider as was said in documentation: GetRootNodeCore(), Initialize() and BuildSiteMap(). Also you will need to add a web reference to your web service in order to be able to use proxy in Visual Studio. As we don’t want to perform web service call on each request to site2 (it will be very slow) I added simple caching logic using Session (as you know session state in Sharepoint is stored in SQL Server so described approach will work on multiple WFE environments).
Now lets see implementation details of the Navigation.asmx web service (its codebehind to be more clear):

    [WebService(Namespace = "http://example.com")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [ToolboxItem(false)]
    public class NavigationService : WebService
    {
       [WebMethod]
        public XmlDocument GetMenuItems()
        {
           PortalSiteMapDataSource ds = new PortalSiteMapDataSource();
           ds.SiteMapProvider = "CombinedNavSiteMapProvider";
           ds.EnableViewState = false;
           ds.StartFromCurrentNode = true;
           ds.StartingNodeOffset = 0;
           ds.ShowStartingNode = true;
           ds.TreatStartingNodeAsCurrent = true;
           ds.TrimNonCurrentTypes = NodeTypes.Heading;
           
           AspMenu m = new AspMenu();
           m.DataSource = ds;
           m.EnableViewState = false;
           m.Orientation = Orientation.Horizontal;
           m.StaticDisplayLevels = 2;
           m.MaximumDynamicDisplayLevels = 1;
           m.DynamicHorizontalOffset = 0;
           m.StaticPopOutImageTextFormatString = "";
           m.StaticSubMenuIndent = 0;
           m.DataBind();
           
           var doc = ConvertHelper.BuildXmlFromMenuItem(m.Items);
           return doc;
       }
   }
 
There is one web method GetMenuItems() which is called from custom site map provider via proxy (see above). I used a little trick here: instead of using PortalSiteMapProvider I used PortalSiteMapDataSource and AspMenu classes in order to return exactly the same navigation items which are shown on the navigation source site (site1). There is a difference between site map nodes which exist for particular site and site map nodes which are actually displayed here. Of course you can use PortalSiteMapProvider  and return all navigation items from it as well.
The remaining thing which should be described is helper class ConverterHelper which is used for the 2 following purposes:
  1. in order to convert in-memory representation of navigation items into xml in order to send it via web service;
  2. and opposite: build in-memory collection of navigation items from xml in custom site map provider.
Here is implementation of ConverterHelper class:

    public static class ConvertHelper
    {
        public const string TAG_ROOT = "root";
        public const string TAG_NODE = "node";
        public const string ATTR_PATH = "path";
        public const string ATTR_URL = "url";
        public const string ATTR_TITLE = "title";
     
      public static SiteMapNodeCollection BuildNodesFromXml(SiteMapProvider provider, XmlNode doc)
      {
           try
           {
               var collection = new SiteMapNodeCollection();
               if (doc.ChildNodes.Count == 1 && doc.ChildNodes[0].Name == TAG_ROOT)
               {
                   doc = doc.ChildNodes[0];
              }
    
               buildNodesFromXml(provider, doc, collection);
               return collection;
           }
           catch (Exception x)
           {
               return null;
           }
       }
    
      private static void buildNodesFromXml(SiteMapProvider provider, XmlNode parentNode, SiteMapNodeCollection collection)
       {
          foreach (XmlNode xmlNode in parentNode.ChildNodes)
          {
               if (xmlNode.Name == TAG_NODE)
               {
                   var node = new SiteMapNode(provider, xmlNode.Attributes[ATTR_PATH].Value,
                                              xmlNode.Attributes[ATTR_URL].Value,
                                              xmlNode.Attributes[ATTR_TITLE].Value);
   
                   if (xmlNode.HasChildNodes)
                   {
                       var childNodes = new SiteMapNodeCollection();
                       buildNodesFromXml(provider, xmlNode, childNodes);
                       node.ChildNodes = childNodes;
                   }
    
                   collection.Add(node);
               }
           }
       }
    
       public static XmlDocument BuildXmlFromMenuItem(MenuItemCollection collection)
      {
           if (collection == null || collection.Count == 0)
           {
               return null;
           }
    
           var doc = new XmlDocument();
    
           var element = doc.CreateElement(TAG_ROOT);
           doc.AppendChild(element);
    
           foreach (MenuItem item in collection)
           {
               buildXmlFromMenuItem(item, doc, element);
           }
    
           return doc;
       }
    
       private static void buildXmlFromMenuItem(MenuItem item, XmlDocument doc, XmlNode xml)
       {
           if (item == null)
               return;
  
            XmlElement element = doc.CreateElement(TAG_NODE);
            element.SetAttribute(ATTR_PATH, item.DataPath);
            element.SetAttribute(ATTR_TITLE, item.Text);
            element.SetAttribute(ATTR_URL, item.NavigateUrl);
    
            xml.AppendChild(element);
    
          foreach (MenuItem childItem in item.ChildItems)
          {
               buildXmlFromMenuItem(childItem, doc, element);
          }
       }
   }
 
The last action you need to perform is to configure your masterpage to use your custom site map provider. I will not repeat it here – you can see it in the part1.

These are the components which you need to implement in order to use web service based approach. One of advantages of the this approach – is that you are not limited by single site collection or web application. Actually you are not limited even by single web server. You can call external web server from separate web server and use navigation data from it (although currently I hardly can imagine the useful application of this ability :) ). But from other side you should be very careful with performance: you should check that you don’t perform web service call each time when your site is requested, because site map provider is called very frequently during requests to the masterpage for example (i.e. to the pages which use masterpage where you use your custom site map provider).
In examples above I removed many non important parts and tried to make it as much self descriptive as possible. So you can use it as direction for your work (instead of treating it as final solution).