Troubleshooting SharePoint 2010 : Tools & Techniques
Posted by Amit Kumawat in SharePoint 2010 on May 25, 2012
Troubleshooting is needed to develop and maintain complex systems where the symptoms of a problem can have many possible causes. One such complex system is SharePoint 2010.
Troubleshooting SharePoint requires logical, systematic search for the source of a problem so that it can be solved, and so the SharePoint Farm or Solution can be made operational again. If you are experiencing problems with a SharePoint farm or solution, you can use below tools and techniques to find the root cause and fix it.
1. Enable Debugging in Web.config.
You can make below changes in web.config of your web application to see the actual exceptions\errors on the page itself . If you want to debug application pages you may need to the changes in web.config at 14\TEMPLATE\LAYOUTS also.
- Turn on the call stack (CallStack=”true”)
- Disable custom errors in Visual Studio (<customErrors mode=”Off” />)
- Enable compilation debugging (<compilation debug=”true”>)
The resulting web.config looks like :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <configuration> ... <SharePoint> <SafeMode MaxControls="200" CallStack="true" DirectFileDependencies="10" TotalFileDependencies="50" AllowPageLevelTrace="false"> ... </SafeMode> ... </SharePoint> <system.web> ... <customErrors mode="Off" /> ... <compilation debug="true"> ... </compilation> ... </system.web> ... </configuration>
2. Visual studio F5 Debugging
It is the most common for of debugging used by developers. Just set a break point in the code and attach to the worker process(w3wp) for your web application.If you have too many web application you can run below command (in command prompt) to find the w3wp Process ID.
%windir%\system32\inetsrv\appcmd.exe list wp
If you want to see the exact set of steps to do Visual Studio debugging, check here
Please note that to debug Timer jobs and waiting Workflows you need to attach to OWSTIMER process instead on w3wp.
3. Trace(ULS) Logs
Trace Logs are text files and can be opened with any tool that works with text files. The default location of trace logs is in the LOGS directory of the SharePoint root (also called the 14 Hive) at C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14 . The naming format for the trace log files is machinename-YYYYMMDD-HHMM.log, in 24-hour time.
[ Note : By default, a new log file is created every 30 minutes. You can change it using PowerShell with the Set-SPDiagosticConfig command. For example, to set for every two hours use : Set-SPDiagnosticConfig Set-SPDiagnosticConfig -LogCutInterval 180]
Trace logs can give you important information to debug a particular issue. The trace logs have the following columns:
- Timestamp — Date and time of the event
- Process — Process that generated the log entry
- TID — Thread ID
- Area — Area of the event
- Category — The category of the Area of the event
- Message — Actual error message
- Correlation — The correlation ID
Correlation ID
The correlation ID is a globally unique identifier GUID that is assigned to each conversation a user or process has with SharePoint. When an error occurs, an administrator can use the correlation ID to track down all of the events in the trace logs that pertain to the conversation where the problem occurred.

An error with Correlation ID displayed to user
Every error\event\information recorded in the trace logs has a correlation ID. You can open trace log in Notepad or ULS Viewer and search for the Correlation ID. Then you can find the actual problem related to the error.
The correlation IDs can be used to find error details in the trace logs, Windows Event Logs and even the SQL trace. The correlation ID is considered by many administrators to be one of the best new features in SharePoint 2010.
You can also View and filter log events by using Windows PowerShell using Get-SPLogEvent
4. Event Throttling
In SharePoint 2010, You can choose to monitor specifically events which you think are the problem areas. Go to Central Administration>Monitoring>Reporting>Configure diagnostic logging to view\change the settings.
Using Event Throttling settings you can also control the severity of events captured in the Windows event log and the trace logs. As the severity decreases, the number of events logged will increase.
Also, you have option to ” Restore to default” settings once you are done with troubleshoot. Any event category which is selected appears bold. For example, If BCS is selected to diagnose, below is how it appears when you view the screen second time.
[Note : Use the Verbose setting only when needed. For Verbose-level events, the system will log every action that SharePoint takes. Verbose-level logging can quickly use drive space and affect drive and server performance. You can use verbose-level logging to record a greater level of detail when you are making critical changes and then re-configure logging to record only higher-level events after you make the change]
Options for Event log
| Level | Definition |
|---|---|
| None | No logging occurs. |
| Critical | This message type indicates a serious error that has caused a major failure in the solution. |
| Error | This message type indicates an urgent condition. All error events should be investigated. |
| Warning | This message type indicates a potential problem or issue that might require attention. Warning messages should be reviewed and tracked for patterns over time. |
| Information | Information messages do not require any action, but they can provide valuable data for monitoring the state of your solution. |
| Verbose | This event log level corresponds to lengthy events or messages. |
Options for Trace log
| Level | Definition |
|---|---|
| None | No trace logs are written. |
| Unexpected | This level is used to log messages about events that cause solutions to stop processing. When set to log at this level, the log will only include events at this level. |
| Monitorable | This level is used to log messages about any unrecoverable events that limit the solution’s functionality but do not stop the application. When set to log at this level, the log will also include critical errors (Unexpected level). |
| High | This level is used to log any events that are unexpected but which do not stall the processing of a solution. When set to log at this level, the log will include warnings, errors (Monitorable level) and critical errors (Unexpected level). |
| Medium | When set to this level, the trace log includes everything except Verbose messages. This level is used to log all high-level information about operations that were performed. At this level, there is enough detail logged to construct the data flow and sequence of operations. This level of logging could be used by administrators or support professionals to troubleshoot issues. |
| Verbose | When set to log at this level, the log includes messages at all other levels. Almost all actions that are performed are logged when you use this level. Verbose tracing produces many log messages. This level is typically used only for debugging in a development environment. |
5. Developer Dashboard
SharePoint 2010 introduces a new developer dashboard that allows you to see all the calls made on a page on the page itself. Those calls can be ones that SharePoint is making or they can be your custom code. By looking at the call stack, response times, and utilization, you can quickly uncover where your code is performing poorly and try to fix it.
Using Developer Dashboard you can track down whether the problem is bad code, bad database calls the code makes, or coding errors that cause excessive CPU, disk, or memory utilization. It gives you information for below counters:
- Thread execution time
- Number, duration, call stack information and query text of each SQL Server query generated by the page
- Number, duration, and call stack information of each WCF call
- URL or timer job name
- Current user
- Execution start time
- Any of the preceding statistics for code enclosed by SPMonitoredScope
You can turn on the Developer Dashboard on Demand using any one of below ways. After getting enabled, the Developer Dashboard appears on any page that use the default master page or any page using a custom master page with the Dashboard control is included.
PowerShell:
$contentService =[Microsoft.SharePoint.Administration.SPWebService]::ContentService $dashboardSetting = $contentService.DeveloperDashboardSettings $dashboardSetting.DisplayLevel = [Microsoft.SharePoint.Administration.SPDeveloperDashboardLevel]::OnDemand $dashboardSetting.TraceEnabled = $true; $dashboardSetting.Update()
Stsadm
stsadm -o setproperty -pn developer-dashboard -pv ondemand
Object Model
SPWebService cs = SPWebService.ContentService; cs.DeveloperDashboardSettings.DisplayLevel = SPDeveloperDashboardLevel.OnDemand; cs.DeveloperDashboardSettings.Update();
Monitoring Your Own Code with the Dashboard using SPMoniteredScope()
By default, the developer dashboard display some information about custom code, but the information is about the calls the code is making, not the source code itself. For example, if you ’ ve added web part to the page, and you accidentally put in some poor code that takes a long time to render. If you looked at the developer dashboard, you would see the time it took to add your web part to the page and how long the pre – rendering or rendering for your web part took. However, you wouldn ’ t see how long your code took to run, unless you calculated this off the total rendering time of the page and how long it took SharePoint to do its operations.
So, to add your own monitored code sections to the developer dashboard, you need to implement the SPMonitoredScope class in your custom code. You can wrap all your custom code that runs in SharePoint using the SPMonitoredScope class.
using (new SPMonitoredScope("My Code"))
{
Thread.Sleep(5000);
}
6. Other Useful Tools for Troubleshooting
SPDisposeCheck
We have to call the Dispose() method on SPSite and SPWeb objects otherwise we get memory leaks in your application. SPDisposeCheck can scan your code and identify where objects that are not disposed.
Internet Explorer\Chorme Developer Tools
The latest browsers Internet Explorer 8 and Chrome includes tools to debug the HTML, script, and Cascading Style Sheets (CSS) . IE 8 also includes a JavaScript profiler that shows you the performance of your script code. With these tools, you can track down any issues in your client – side code.
Visual Round Trip Analyzer
The Visual Round Trip Analyzer (VRTA) visualizes how long it takes a client to talk to a server — information that you can then use to understand if you are making excessive round trips, if your code is slowing down the pages (for example, because of loading many small JavaScript or CSS files), or if there are network issues causing any problems between your application and the server.
Fiddler\HTTPWatch
Fiddler and HTTPWatch are a web debugging proxy that logs all HTTP traffic between your computer and the Internet. They allows you to inspect all HTTP traffic and view your incoming and outgoing data. It is an essential tool to help you understand what server variables are coming back from your server, what the payload is, how many calls your client – side code took, and other factors that provide insight into
your applications.
7. Troubleshooting few common problems in SharePoint Solutions : http://msdn.microsoft.com/en-us/library/ee231594.aspx
8. Troubleshooting SharePoint Packaging and Deployment : http://msdn.microsoft.com/en-us/library/ee330922.aspx
SharePoint Tag Cloud using Deep refiners in Fast Search
Posted by Amit Kumawat in SharePoint 2010 on April 10, 2012
SharePoint 2010 offers good enhancements and capabilities over the previous versions. If you exploit them wisely you can produce great solutions.
Recently, I wanted to have a Tag cloud to show the most heavily used tags\keywords in the pages. The important part of the requirement is that the Tag Cloud should provide aggregate view of the tags applied to pages at different scopes like:
- All sites in a Site Collection
- All site collections in a Web Application
- All Web applications in a Farm
Also, the tags should be hyperlinks which would take user to page with list of all pages with similar tags.
I thought to use Managed Metadata feature of SharePoint 2010 that provides efficient way to manage Tags. However, later I discovered that the Tag cloud web part provided by SharePoint 2010 can only be used for Social Tags and not for Managed Metadata columns.
I also found a Tag cloud at Codeplex that uses Managed Metadata: http://metadatawebpart.codeplex.com/ . However this web part is not able to meet all of my requirements.
After exploring all the available options, I decided to design my own solution to meet all of the above requirements.
The solution is based on Deep Refiners in Fast Search.
FAST Search provides the capability of result refining on metadata associated with all results.
Moreover, FAST Search shows counts for each of the refiners.
The Refinement is also available in SharePoint 2010 Search with the capability of refining results by the most important metadata. However, only FAST Search provides the capability of result refining on metadata associated with all results called Deep Refinement.
Below is a screenshot showcasing deep refiners showing facets for all items found in search:
So, the Refinement panel on a Fast Search results page, more or less, acts like a Tag cloud.
However, you cannot use the panel as standalone web part since it works in collaboration with other Search web parts. So there is need to programmatically access the get the deep refiners and display them as Tags where the font size or weight of a tag in is determined by its refinement count.
Before we access the deep refiners in code, There are some prerequisite steps to follow :
Note : You should have Fast Search installed and configured with Search Service applications running.
1 . Create Managed Property in Fast Search Administration that maps the Managed Metadata column (It can be any other type of column as well if you want. I used Managed Metadata as it provides auto fill and more control on tags)
2. Set a managed property as deep refiner: You can do it via PowerShell or Fast Search Administration UI.
For PowerShell, here are the details.
For UI, You can do it via ‘Fast Query Service Application’ management interface as below:
3. Do a Full Crawl.
Now we are ready to get refiners in code. We can use Keyword query class like I have demonstrated in another post with some changes as below :
1. Set the result provider to Fast Search
keywordQuery.ResultsProvider = SearchProvider.FASTSearch;
2. Specify the refiners using KeyworQuery.Refiners property
//Tags is the name of the managed property mapped to managed metadata column keywordQuery.Refiners = "Tags";
This property is used to set the set of refiners to be returned in a search result. The value for each refiner is a format string that specifies the name of a refiner and optionally a set of refinement options for this refiner. No refiners will be returned with the query result if the Refiners property is empty.
3. Specify that the results set should contain refined results matching the search query.
keywordQuery.ResultTypes = ResultType.RefinementResults;
4. Specify a query to search all items. The below query look for all list items. You can provide custom query to restrict the scope of search.
keywordQuery.QueryText = "contentclass:STS_ListItem";
Below is the complete code that you can use in a console application to test the result :
using (SPSite site = new SPSite("http://sp2010site"))
{
SearchServiceApplicationProxy SSAProxy = (SearchServiceApplicationProxy)SearchServiceApplicationProxy. GetProxy(SPServiceContext.GetContext(site));
KeywordQuery keywordQuery = new KeywordQuery(SSAProxy);
keywordQuery.ResultsProvider = SearchProvider.FASTSearch;
keywordQuery.QueryText = "contentclass:STS_ListItem";
keywordQuery.ResultTypes = ResultType.RefinementResults;
keywordQuery.EnableFQL = false;
keywordQuery.StartRow = 0;
keywordQuery.RowLimit = 100000;
keywordQuery.KeywordInclusion = KeywordInclusion.AllKeywords;
keywordQuery.Refiners = "Tags";
ResultTableCollection searchResults = keywordQuery.Execute();
if (searchResults.Exists(ResultType.RefinementResults))
{
ResultTable searchResult = searchResults[ResultType.RefinementResults];
Console.WriteLine(searchResult.TotalRows);
DataTable result = new DataTable();
result.TableName = "SearchResults";
result.Load(searchResult, LoadOption.OverwriteChanges);
DataSet ds = new DataSet();
ds.Tables.Add(result);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(ds.GetXml());
XmlNodeList nodeList = xmlDoc.DocumentElement.SelectNodes("/NewDataSet/SearchResults[RefinerName='tags']");
foreach (XmlNode myNode in nodeList)
{
if (myNode != null)
Console.WriteLine(myNode.SelectSingleNode("RefinementName").InnerText + "(" + myNode.SelectSingleNode ("RefinementCount").InnerText+")");
}
Console.Read();
In above code, The RefinementName gives you the value of tag and RefinementCount gives you the count i.e. how many times the tag is used
Below is the output in console application :
Now, you can transform the above code to a webpart.
You can decide on font size of a tag based on the RefinementCount : Greater RefinementCount , higher the font size. You can also use below formula to calculate font size on fly :
for
; else 
: display fontsize
: max. fontsize
: count
: min. count
: max. count
Now, You need to link the Tag to the page which has a list of pages with similar tag. You can use out of box search page for this or again build your own with a webpart using Keyword query.
In a tag cloud, you can dynamically create link for Tags like below :
<a href="/search.aspx?k=Tags:Microsoft"><font size=15>Microsoft<font></a>,<a href="/search.aspx?k=Tags:Yahoo"><font size=12>Yahoo<font></a>
When a user click the Tag named “Microsoft” for example , the search page will present results with Keyword Query “Tags:Microsoft” i.e. All pages or list items tagged with keyword “Microsoft”.
So, you have a Tag cloud which you can fine tune according to your need. The use of Search to find Tags provides good performance even if you aggregate huge data .
Enjoy!
Visual Studio 2010 : Error occurred in deployment step ‘Activate Features’
Posted by Amit Kumawat in SharePoint 2010 on February 21, 2012
Problem : You suddenly see the below error in Visual studio 2010 while deploying SharePoint 2010 solution:
Error occurred in deployment step ‘Activate Features’: Feature with Id ‘#featureid#’ is not installed in this farm, and cannot be added to this scope
Cause : This error generally comes when a new WFE server is added to the SharePoint farm.
Resolution : Remove WFE or manually install the solution or features.
Event Receiver enhancements in SharePoint 2010
Posted by Amit Kumawat in SharePoint 2010 on December 27, 2011
With SharePoint 2010, there are some good enhancements in Event Receivers.
- New Events
- New Registration technique
- Synchronous After-Events
- Custom error pages and redirection
- Impersonation enhancements (SPEventPropertiesBase.OriginatingUserToken)
Lets discuss one by one.
1. New Events
| Event Name | Event Description | Event Receiver Class |
WebAdding |
A synchronous event that happens before the web is added. Some URL properties may not exist yet for the new site, since the new site does not exist yet. |
SPWebEventReceiver |
WebProvisioned |
A synchronous or asynchronous after-event that occurs after the web is created. You make the event synchronous or asynchronous by using the Synchronization property and setting it to synchronous or Synchronous. This is located under the Receiver node in the elements.xml file for your feature. |
SPWebEventReceiver |
ListAdding |
A synchronous event that happens before a list is created. |
SPListEventReceiver |
ListAdded |
A synchronous or asynchronous after-event that happens after a list is created but before being it is presented to the user. |
SPListEventReceiver |
ListDeleting |
A synchronous event that happens before a list is deleted. |
SPListEventReceiver |
ListDeleted |
A synchronous or asynchronous after-event that happens after a list is deleted. |
SPListEventReceiver |
2. New Event Registration technique using the <Receivers/> tag
SharePoint 2010 has added a new registration mechanism for registering your event receivers, using the <Receivers> XML block.
<Receivers ListTemplateId = "Text" ListTemplateOwner = "Text" ListUrl = string RootWebOnly = TRUE | FALSE Scope = Site | Web> </Receivers>
With this new capability, you can register your event receiver at the site collection level by using the new Scope attribute and setting it either to Site or Web, depending on the scope that you want for your event receiver.
- SPSite-Level Binding
In SharePoint 2010, you have the ability to deploy an event receiver not only at the web level, but also at the Site Collection level. In fact, all of the event receivers except the SPEmailEventReceiver can be deployed at the Site Collection level, so you can share their behavior across all the websites within that Site Collection. For instance, you can share at the Site Collection level, a list-level event receiver that checks list provisioning and/or custom fields using a unique deployment step.
- Event Binding by List Template
Also, In SharePoint 2010 we can bind an event receiver to a list template, using <ListTemplateId> and then apply that event receiver to every list instance based on that ListTemplateId.
Below example displays a feature element that installs an event receiver for a list.
<?xml version=”1.0” encoding=”utf-8”?> <Elements xmlns=”http://schemas.microsoft.com/sharepoint/”> <Receivers ListTemplateId=”104”> <Receiver> <Name>News Update Event Receiver</Name> <Type>ItemUpdating</Type> <Assembly>$SharePoint.Project.AssemblyFullName$</Assembly> <Class>AmitKumawat.SP2010.EventReceivers.NewsItemUpdateReceiver</Class> <Data>Contains a string that is used to pass parameters to the receiver via the ReceiverData property</Data> <SequenceNumber>1002</SequenceNumber> </Receiver> </Receivers> </Elements>
The <Data> tag of the Receiver element allows you to define a custom configuration text that will be provided to the event receiver via the ReceiverData property of the SPEventPropertiesBase base class, whenever the receiver is invoked. Note that the length of the content inside Data element is limited to 255 chars.
Another important one is the ListUrl attribute, which allows you to scope your receiver to a particular list by passing in the relative URL.
Note: ListUrl works only if feature has Scope=”Web”. In case the feature has Scope=”Site”, event receiver is fired for every list, ListUrl is ignored.
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Receivers ListUrl="Lists/Tasks">
<Receiver>
<Assembly>MyReceiverAssembly, Version=1.0.0.0, Culture=Neutral,
PublicKeyToken=12e5e5525fb3d28a</Assembly>
<Class>MyReceiverAssembly.MyReceiverClass</Class>
<Type>ItemAdded</Type>
<Name>My ItemAdded Event Receiver</Name>
<Synchronization>Synchronous</Synchronization>
<SequenceNumber>1000</SequenceNumber>
</Receiver>
</Receivers>
</Elements>
You can tell SharePoint to just have the receiver work on the root site by using the RootWebOnly attribute on the <Receivers> node.
3. Synchronous After-Events
We now have support for synchronous after – events, such as listadded , itemadded , or webprovisioned .
In SharePoint, all the Before events are executed synchronously within the same process and thread of the current user request, while all After events are executed asynchronously, on a background thread and potentially in a process different from the current user request.
Important Bullet Points:
- Synchronous event receivers are called in sequential order based on the sequence number specified during event binding. This applies to both Before and After synchronous events.
- Asynchronous After event receiver threads are initiated in sequential order based on the sequence number. However, there is no guarantee that they will finish in that same order.
- An asynchronous After event can start at any time after its associated user action is performed. It may start before, at the same time as, or after the Web request is completed.
To make the After events, such as ItemUpdated, synchronous, you need to set the Synchronization property either through the SPEventReceiverDefinition object model if you are registering your events programmatically or by creating a node in your <Receiver> XML that sets the value to Synchronous or Asynchronous.
The property can take one of the following values:
- Default : Before events are synchronous; After events are asynchronous.
- Synchronous : The current event is executed synchronously.
- Asynchronous The current event is executed asynchronously.
To configure this property, you can define it in the feature element XML file or in Code
<?xml version=”1.0” encoding=”utf-8”?> <Elements xmlns=”http://schemas.microsoft.com/sharepoint/”> <Receivers ListTemplateId=”105”> <Receiver> <Name>News Update Event Receiver</Name> <Type>ItemUpdating</Type> <Assembly>$SharePoint.Project.AssemblyFullName$</Assembly> <Class>AmitKumawat.SP2010.EventReceivers.NewsItemUpdateReceiver</Class> <Data>Contains a string that is used to pass parameters to the receiver via the ReceiverData property</Data> <SequenceNumber>1002</SequenceNumber> <Synchronization>Synchronous</Synchronization> </Receiver> </Receivers> </Elements>
Another option is to configure the Synchronization property in code, as shown below:
using (SPSite site = new SPSite(“http://sp2010site/”))
{
using (SPWeb web = site.OpenWeb())
{
NewsItemEventReceiverConfiguration config = new NewsItemEventReceiverConfiguration();
SPList list = web.Lists[“Contacts”];
var newReceiver = list.EventReceivers.Add();
Assembly asm = Assembly.LoadFrom(
@”..\..\AmitKumawat.SP2010.EventReceivers.dll”);
newReceiver.Assembly = asm.FullName;
newReceiver.Class = asm.GetType(
“AmitKumawat.SP2010.EventReceivers.NewsItemEventReceiver”).FullName;
newReceiver.Name = “News Receiver”;
newReceiver.Type = SPEventReceiverType.ItemUpdated;
newReceiver.SequenceNumber = 110;
newReceiver.Data = XmlSerializationUtility.Serialize<NewsItemEventReceiverConfiguration>(config);
newReceiver.Synchronization =SPEventReceiverSynchronization.Synchronous;
newReceiver.Update();
}
}
4. Custom Error Pages
In Sharepoint 2007, you can cancel events and return an error message to the user,which provides limited interactivity and is not much helpful to the user beyond what the error message says.
In Sharepoint 2010 , you can cancel the event and redirect the user to a custom error page that you create. This allows you to have more control of what the users see, and you can try to help them figure out why their action is failing.
However, Please note that the custom error pages and redirection will only work for synchronous Before-Events(ending with ..ing), so you cannot achieve this for synchronous After-Events such as ListAdded . Also, this will only work when the client is a Browser.
The way to implement custom error pages is to set the Status property on your property bag for your event receiver to SPEventReceiverStatus.CancelWithRedirectUrl , set the RedirectUrl property to a relative URL for your error page, and set the Cancel property to true .
properties.Cancel = true; properties.Status = SPEventReceiverStatus.CancelWithRedirectUrl; properties.RedirectUrl = "/_layouts/mycustomerror.aspx";
5. Impersonation enhancements.
Generally, this is okay, but there may be certain times when you want to let a user perform actions on lists or libraries that the current user does not have permissions to do. In most cases, you would use SPSecurity.RunwithElevatedPrivileges method. However, you may want to revert to the originating user on some operations.
With 2010, the event property bag contains the OriginatingUserToken ,UserDisplayName , and UserLoginName, which you can use to revert to the original user.
Below is an example that perform some tasks by using the OriginatingUserToken to impersonate the originating user.
public override void ItemUpdated(SPItemEventProperties properties) {
using (SPSite site = new SPSite(properties.SiteId,
properties.OriginatingUserToken)) {
using (SPWeb web = site.OpenWeb(properties.RelativeWebUrl)) {
// Some tasks here impersonating the originating user token
}
}
base.ItemUpdated(properties);
}
Technorati Tags : SharePoint
Develop in Asp.net or SharePoint ?
Posted by Amit Kumawat in SharePoint 2010 on November 24, 2011
I encountered an interesting question on Sharepoint.StackExchange : SharePoint 2010 custom application or ASP.Net web application ?
I feel many developers, when they move to SharePoint from ASP.NET, think over the same question many times. Below are my thoughts ( I answered the same at SSE as well). What do you think ? :)
SharePoint is an ASP.NET application on steroids. Almost everything that works in ASP.NET(3.5) will work in SharePoint as well.
Having said that, the decision to choose between ASP.NET and SharePoint depends on N no of factors …. Requirements, Budget, Skills, Infrastructure, Corporate politics
to name a few.
At the developer level, I think decision can be made on the basis of requirements only ( after they come to you finally). Also, If you are new to SharePoint chances are high that you would like to develop in ASP.NET
Some points worth to consider:
1) See how complex your database model can be.
It is easy to define and manage data structures in lists as they don’t require intervention of a database admin. However, If your application has complex DB design, It may not be feasible using SharePoint. Check out this as well Using SharePoint Lists vs. Database Tables
2) Identify the Features your application will support
SharePoint provide many features out-of-box for which you may have to code in ASP.NET. If your application need revolve around below, SharePoint is worth to consider:
- Search
- Information Portals
- Business Intelligence and Dashboards
- Simple Workflows
- Content Management
- Social networking ( Blogs, Wikis, Communities, People Search)
- MS Office Integration
- Surfacing External Data Systems
- Future Integration with other Systems
3) Cross Cutting Concerns
The majority of web applications contain common functionality that spans layers and tiers. This functionality typically supports operations such authentication, authorization, caching, communication, exception management, logging and instrumentation, and validation.
SharePoint provides effective way for managing common crosscutting concerns, such as authentication and authorization, storage, scale, provisioning of services, and governance controls.The value in managing crosscutting concerns in SharePoint is that you don’t have to continually reinvent the wheel.
It may be boring to write crosscutting concerns in ASP.NET
SharePoint 2010 and the associated tools promote the increased productivity and accelerated development developers are looking for.
Hiding Menu Items in the ECB from SharePoint List Items
Posted by Amit Kumawat in Extreme, Moss (SharePoint 2007), SharePoint 2010 on October 29, 2011
Edit Control Block (ECB) is the context menu that is displayed for all items in Lists and Document Libraries. This post focus on editing or hiding existing ECB menu items.
If you want to add new custom actions or menu items to the ECB menu, You can do it by:
1) Using the element <CustomAction Location=”EditControlBlock” > in a Feature [recommended way].
2) Create a javascript function named Custom_AddListMenuItems and add it to a page or master page. For e.g.
//This function is called automatically from core.js in-built functions like AddListMenuItems(), AddDocLibMenuItems() etc... function Custom_AddListMenuItems(m, ctx) { //This function creates a new ECB item. CAMOpt(m, "Test ECB item", "alert(I am created from script!!');", "/_layouts/images/testECB.GIF"); //This function creates a seperator line. CAMSep(m); }
If you want to hide or delete an existing menu item which is provided out-of-box by SharePoint, we cannot do it by using a feature. The out-of-box actions or menu items in ECB are rendered by using a JavaScript file, which is core.js [Note : Modifying the core.js file is not supported or recommended]. So there are two ways we can hide existing menu items :
- Create a copy of core.js and edit it to remove the ECB menu items we don’t want.
- Use JQuery to hide the ECB menu items without editing core.js
Important:Please do not edit existing core.js
Following below steps to create a customcore.js file and refer it in the master page.
- Copy the core.js file from its default location ( For SP2010 : %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\TEMPLATE\LAYOUTS\1033; For SP2007:%ProgramFiles%\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS\1033) place it in the same folder, and rename as customcore.js file.
- Remove unwanted ECB menu items by editing the appropriate functions like CreateMenuEx()(for SP2007 core.js), BuildMenu()(for SP2010 core.js), AddVersionMenuItems(), AddDocLibMenuItems() etc. For more details on the functions please see the section ”ECB menu items in core.js” below.
- In the master page, add the following line to render the customcore.js file [Note: core.jsis also required]:
<!--It is important to refer core.js even if you create and refer customcore.js--> <SharePoint:ScriptLink language="javascript" name="core.js" Defer="true" runat="server"/> <SharePoint:ScriptLink language="javascript" name="customcore.js" Defer="true" runat="server"/> - Save and publish the master page.
The JavaScript function CreateMenu() (for SP2007) or BuildMenu() (for SP2010) in core.js creates the menu items when you hover or click to edit a list item.
//This function is used to create ECB menu in SP2010 function BuildMenu(a) { ULSrLq: ; var b = CMenu(currentItemID + "_menu"); if (!b) return; else if (a.isVersions) //Add ECB menu item if versioning is enabled in a list or document library(For eg. "Check Out","Version History") AddVersionMenuItems(b, a); else if (a.listTemplate == 121) //Add ECB menu item for solution catalog AddSolutionsCatalogMenuItems(b, a); else if (a.listBaseType == 1) //Add menu item for items in document library (For eg. "Edit Document") AddDocLibMenuItems(b, a); else if (a.listTemplate == 200) //Add menu item for items in meetings AddMeetingMenuItems(b, a); else //Add menu item for items in standard List (For eg. "View Item","Edit Item") AddListMenuItems(b, a); InsertFeatureMenuItems(b, a); return b } //This function is used to create ECB menu in SP2007 function CreateMenu(e) { if (!IsContextSet()) return; var ctx=currentCtx; if (e==null) e=window.event; var srcElement=e.srcElement ? e.srcElement : e.target; if (itemTable==null || imageCell==null || (onKeyPress==false && (srcElement.tagName=="A" || srcElement.parentNode.tagName=="A"))) return; return CreateMenuEx(ctx, itemTable, e); }
The CreateMenu() function in SP2007 core .js calls CreateMenuEx() which calls respective functions to create ECB menu items for list items in a library, meeting or custom list.
function CreateMenuEx(ctx, container, e)
{
if (container==null)
return;
IsMenuShown=true;
document.body.onclick="";
var m;
m=CMenu(currentItemID+"_menu");
if (!m)
return;
else if (ctx.isVersions)
//Add menu item if versioning is enabled in a list or document library(For eg. "Check Out","Version History")
AddVersionMenuItems(m, ctx);
else if (ctx.listBaseType==1)
//Add menu item for items in document library (For eg. "Edit Document")
AddDocLibMenuItems(m, ctx);
else if (ctx.listTemplate==200)
//Add menu item for items in meetings
AddMeetingMenuItems(m, ctx);
else
//Add menu item for items in standard List (For eg. "View Item","Edit Item")
AddListMenuItems(m, ctx);
InsertFeatureMenuItems(m, ctx);
currentEditMenu=m;
container.onmouseout=null;
OMenu(m, container, null, null, -1);
itemTable=GetSelectedElement(container, "TABLE");
m._onDestroy=OutItem;
e.cancelBubble=true;
return false;
}
We can edit the JS functions(and inner functions) called above to remove the ECB menu items.We can comment the lines of code which create menu items. These lines are :
menuOption=CAMOpt(m, strDisplayText, strAction, strImagePath, null, 700);
menuOption.id="ID_[**action**]";
The CAMOpt() function creates a new ECB menu item.We can comment the lines which call it to create a specific ECB menu item. Below are few examples. To remove “Check Out” ECB menu item, we can comment the below lines in AddCheckinCheckoutMenuItem() function:
// menuOption=CAMOpt(m, strDisplayText, strAction, strImagePath, null, 700); // menuOption.id="ID_Checkout";
To remove “Delete” ECB menu item from a list item, we can comment the below lines in AddListMenuItems() function:
//menuOption=CAMOpt(m, strDisplayText, strAction, strImagePath, null, 300); //menuOption.id="ID_DeleteItem";
Use JQuery to hide the ECB menu items
The ECB menu html which is created on fly by CreateMenu() looks like below. This can be manipulated by JQuery as well.
<!--the menu id is formed by Item Id + "_menu" -->
<menu id="268_menu" class="ms-SrvMenuUI">
<span type="option" text="View Properties" onmenuclick="STSNavigate('/Documents/Forms/DispForm.aspx?ID=268')" sequence="200" id="ID_ViewProperties"></span>
<span type="option" text="Edit Properties" onmenuclick="STSNavigateWithCheckoutAlert('/Documents/Forms/EditForm.aspx?ID=268',1,'0','\u002fDocuments\test.pdf','http://sp2007site')" iconsrc="/_layouts/images/edititem.gif" iconalttext="" sequence="220" id="ID_EditProperties"></span>
<span type="option" text="Manage Permissions" onmenuclick="NavigateToManagePermsPage('http://sp2007site', '{A8073644-42BB-46C2-9CEC-33371585C51C}','268')" iconsrc="/_layouts/images/manageperm.gif" iconalttext="" sequence="250" id="ID_MngPerms"></span>
<span type="option" text="Delete" onmenuclick="DeleteDocLibItem('/_vti_bin/owssvr.dll?CS=65001&Cmd=Deletesequence="310" id="ID_DeleteDocItem"></span>
<span type="submenu" text="Send To" iconalttext="" sequence="400" id="ID_Send"><span type="option" text="Other Location" onmenuclick="STSNavigate('http://sp2007site/_layouts/copy.aspx?SourceUrl=%2FDocuments%2F090505%2Dtest%2Epdf&')" iconsrc="/_layouts/images/sendOtherLoc.gif" iconalttext="" id="ID_OtherLocation"></span>
<span type="separator"></span>
<span type="option" text="E-mail a Link" onmenuclick="javascript:navigateMailToLinkNew('http://sp2007site/Documents/090505-test.pdf')" iconsrc="/_layouts/images/gmailnew.gif" iconalttext="" id="ID_SendToEmail"></span>
<span type="option" text="Create Document Workspace" onmenuclick="STSNavigate('http://sp2007site/_layouts/createws.aspx?list={A8073644-42BB-46C2-9CEC-33371585C51C}&item=268&RootFolder=%2FDocuments')" iconalttext="" sequence="1140" id="ID_CreateDWS"></span>
<span type="option" text="Download a Copy" onmenuclick="STSNavigate('http://sp2007site/_layouts/download.aspx?SourceUrl=%2FDocuments%2F090505%2Dtest%2Epdf&') iconalttext="" id="ID_DownloadACopy"></span>
</span>
<span type="separator"></span>
<span type="option" text="Check Out" onmenuclick="CheckoutDocument('http://sp2007site', '%2FDocuments%2F090505%2Dtest%2Epdf', '.3')" iconsrc="/_layouts/images/checkout.gif" iconalttext="" sequence="700" id="ID_Checkout"></span>
<span type="option" text="Version History" onmenuclick="NavigateToVersionsAspx('http://sp2007site', 'list={A8073644-42BB-46C2-9CEC-33371585C51C}&ID=268&FileName=%2FDocuments%2F090505%2Dtest%2Epdf')" iconsrc="/_layouts/images/versions.gif" iconalttext="" sequence="800" id="ID_Versions"></span>
<span type="option" text="Workflows" onmenuclick="STSNavigate('http://sp2007site/_layouts/Workflow.aspx?ID=268&List={A8073644-42BB-46C2-9CEC-33371585C51C}&Source=http%3A%2F%2Fsp2007site%2FDocuments%2FForms%2FAllItems%2Easpx')" iconsrc="/_layouts/images/workflows.gif" iconalttext="" sequence="900" id="ID_Workflows"></span>
<span type="separator"></span>
<span type="option" text="Alert Me" onmenuclick="NavigateToSubNewAspx('http://sp2007site', 'List={A8073644-42BB-46C2-9CEC-33371585C51C}&ID=268')" iconalttext="" sequence="1100" id="ID_Subscribe"></span>
<span type="separator"></span>
</menu>
The below jquery snippet hides the “Check Out” ECB menu item at mouseover on the div(class=ms-MenuUIPopupBody) that is created on fly everytime a ECB menu is constructed.
$(document).ready(function(){
$('.ms-MenuUIPopupBody').live('mouseover', function() {
$('#ID_Checkout').parent().hide();
$('#ID_Checkout').remove();
});
});
Using KeywordQuery Class in SharePoint Query Object Model
Posted by Amit Kumawat in SharePoint 2010 on October 28, 2011
In order to develop custom search web parts or applications that support ‘search-by-keyword’ scenario, SharePoint Query object model exposes KeywordQuery Class.
Using KeywordQuery Class, We can construct keyword search queries for :
- SharePoint Foundation Search
- SharePoint Server Enterprise Search
- FAST Search Server 2010
This class exposes QueryText property which accepts query(keywords) in Keyword query syntax or KQL (Keyword Query Language). Using KQL, search terms are passed directly to the Search Service.
KQL allows keywords, phrases, and Managed Property names to create sophisticated search queries.In addition, KQL supports Boolean operators and wild cards.
Some Keyword Query Examples
- Moss AND Sharepoint: Returns items containing both “Moss” and “Sharepoint”
- Lastname:K* : Returns people whose last name starts with K
- AverageRating>1 : Returns items whose average rating is greater than one
- Finance +isDocument:1 : Returns documents that are related to Finance
- Finance +Author:”Amit Kumawat” : Returns items authored by Amit Kumawat
- Finance Scope:”Local SharePoint Sites” Scope:”Internet Sites” NOT Scope:”Shared Folder”
In SharePoint Server 2010, KQL also supports :
- Prefix matching for free-text and property restriction queries
- Property operators with non-text fields
For more details on forming queries based on KQL, Please refer Keyword Query Syntax Reference
In below example, We create a custom web part that uses KeywordQuery Class to perform search. Create a new SharePoint project in Visual Studio 2010 and add reference to below dlls (from Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI):
- Microsoft.Office.Server.dll
- Microsoft.Office.Server.Search.dll.
[Note: If you are working with SharePoint Foundation, you need to use another namespace, Microsoft.SharePoint.Search.Query, that contains a subset of the SharePoint Server functionality. It comprises the query object model with reduced functionality and is found in Microsoft.SharePoint.Search.dll.]
using System;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Data;
using Microsoft.Office.Server.Search.Query;
using Microsoft.Office.Server.Search.Administration;
namespace AmitKumawat.SP2010.Samples.CustomSearchWebPart
{
[ToolboxItemAttribute(false)]
public class CustomSearchWebPart : WebPart
{
protected Button btnSearch;
protected TextBox txtSearchBox;
protected Label lblResults;
protected GridView gridSearchResults;
protected override void CreateChildControls()
{
txtSearchBox = new TextBox();
this.Controls.Add(txtSearchBox);
lblResults = new Label();
this.Controls.Add(lblResults);
btnSearch = new Button();
btnSearch.Text = "Find";
btnSearch.Click += new EventHandler(btnSearch_Click);
this.Controls.Add(btnSearch);
}
void btnSearch_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(txtSearchBox.Text))
{
lblResults.Text = "Please enter words to search for";
return;
}
ExecuteKeywordQuery(txtSearchBox.Text);
}
void ExecuteKeywordQuery(string keywordQueryText)
{
//Get a proxy for Search Service Application(SSA). The SSA serves search queries.
SearchServiceApplicationProxy SSAProxy = (SearchServiceApplicationProxy)SearchServiceApplicationProxy.
GetProxy(SPServiceContext.GetContext(SPContext.Current.Site));
KeywordQuery keywordQuery = new KeywordQuery(SSAProxy);
//keyword search using default provider. We can use "FAST" or "SharepointSearch" to be specific.
keywordQuery.ResultsProvider = SearchProvider.Default;
keywordQuery.QueryText = keywordQueryText;
keywordQuery.ResultTypes |= ResultType.RelevantResults;
ResultTableCollection searchResults = keywordQuery.Execute();
if (searchResults.Exists(ResultType.RelevantResults))
{
ResultTable searchResult = searchResults[ResultType.RelevantResults];
DataTable result = new DataTable();
result.TableName = "SearchResults";
result.Load(searchResult, LoadOption.OverwriteChanges);
PopulateResultsGrid(result);
}
}
private void PopulateResultsGrid(DataTable resultsTable)
{
gridSearchResults = new GridView();
gridSearchResults.DataSource = resultsTable;
gridSearchResults.DataBind();
Controls.Add(gridSearchResults);
}
protected override void Render(HtmlTextWriter writer)
{
writer.Write("<table width='100%'><tr><td>");
txtSearchBox.RenderControl(writer);
writer.Write("</td><td>");
btnSearch.RenderControl(writer);
writer.Write("</td></tr><tr><td colspan='2'>");
lblResults.RenderControl(writer);
writer.Write("</td></tr><tr><td colspan='2'>");
if(gridSearchResults!=null)
gridSearchResults.RenderControl(writer);
writer.Write("</td></tr></table>");
}
}
}
If you want to use a specific SSA , you can get reference of it by using its name as:
SearchQueryAndSiteSettingsServiceProxy settingsProxy = SPFarm.Local.ServiceProxies.GetValue<SearchQueryAndSiteSettingsServiceProxy>();
// Get the SSA proxy by name
SearchServiceApplicationProxy SSAProxy = settingsProxy.ApplicationProxies.GetValue<SearchServiceApplicationProxy>("Search Service Application 1");
In your SharePoint farm, You must create and configure a Search service application(SSA) before using Search. You can follow steps here to create SSA.
Custom Security Trimming in Sharepoint 2010 Search Results : ISecurityTrimmer2 Interface
Posted by Amit Kumawat in SharePoint 2010 on October 12, 2011
In my previous post I demonstrated custom security trimming in Moss (SharePoint 2007). This post focuses on implementing custom security trimming in SharePoint 2010.
In general, SharePoint 2010 provides security trimming out-of-box for the content which is indexed from WSS 3.0, SharePoint 2010, windows file system or any location where objects or files have ACL associated with them. An ACL (access control list) is a list of permissions attached to a file or folder and it determines which users are granted access to files and what operations are allowed on given files.
SharePoint stores the ACL information while crawling the content. When a user performs search, this information is used to filter search results at run time. However, this is not the case for every type of content …like non-SharePoint websites. For some cases, like one here, It does not happen even if the content is in SharePoint.
For such extreme ( sorry for overloading the term many times
) cases where specific business rules need to be applied before search results are displayed to user, SharePoint 2010 provides ISecurityTrimmer2 Interface (I am not sure why it is 2 and not 1) defined in Microsoft.Office.Server.Search.Query namespace.
Just like ISecurityTrimmer in Moss, following three steps required to use a custom security trimmer for SharePoint 2010 Enterprise Search:
- Implement the ISecurityTrimmer2 interface
- Deploy the custom security trimmer
- Register the custom security trimmer
- Debug the custom security trimmer(optional)
Implementing the ISecurityTrimmer2 interface
In VS2010, Create an empty SharePoint project as a farm solution and add reference to
- Microsoft.Office.Server.Search.dll (14\ISAPI\Microsoft.Office.Server.Search.dll)
- Microsoft.IdentityModel.dll (C:\Program Files\Reference Assemblies\Microsoft\Windows Identity Foundation\v3.5\Microsoft.IdentityModel.dll) [Note: This reference is only required in case of Claims Mode Authentication. By default, SharePoint 2010 site have Classic Mode Authentication (Integrated Windows Authentication) and so you will not need it (most probably).Also, You should have Windows Identity Foundation SDK installed. Download and install WindowsIdentityFoundation-SDK-3.5.msi .Take care to download V3.5 and not V4]
Create a custom class and implement ISecurityTrimmer2. There are two methods in the interface we need to implement:
1. ISecurityTrimmer2.Initialize () : In this method, we can provide or retrieve values for configuration properties for the trimmer.
Two parameters which are passed into the method are:
- NameValueCollection Object: It contains the properties configured for the security trimmer when it is registered (using stsadm) with the Search service application.
- SearchServiceApplication Object: It is the Search service application with which security trimmer is registered.
In ISecurityTrimmer2.Initialize () method, we can retrieve values for some properties which were configured at the time of registering the security trimmer using stsadm. For example, to optimize performance, you may want to limit the amount of crawled urls to be processed by your custom security trimmer. For that we need to retrieve CheckLimitProperty. Below is the code you write to do that.
//to provide default configuration limit for search results on which security trimming is applied private int intResultsCheckLimit = 150; public void Initialize(NameValueCollection trimmerProperties, SearchServiceApplication searchApplication) { //retrieve the value of CheckLimitProperty if it is configured while registering the trimmer using stsadm and use it if found. if (trimmerProperties["CheckLimitProperty"] != null) { intResultsCheckLimit = Convert.ToInt32(trimmerProperties["CheckLimitProperty"]); } }
2. ISecurityTrimmer2.CheckAccess() : This is the method where security trimming is applied to the search results. The CheckAccess() can execute multiple times for one search query, depending on the number of results that are passed to the security trimmer.
This method has some differences between Moss and SharePoint 2010. In SharePoint 2010 we get an IIdentity and in MOSS we use WindowsIdentity.GetCurrent()(For Windows Authentication) or HttpContext.Current.User (For Forms Authentication).
Three parameters which are passed into the method are:
- IList<String> documentCrawlUrls : It is collection of URLs for each content item from the search results that match the crawl rule.
- IDictionary<String, Object> sessionProperties : This object is the collection of configuration properties and it is used (by developers like us) to track or preserve custom information across multiple CheckAccess() method calls for the same search query
- IIdentity passedUserIdentity : The identity of the user who issued the query. We can also use IIdentity as mentioned instead of passedUserIdentity parameter : IIdentity userIdentity = System.Threading.Thread.CurrentPrincipal.Identity;
This ISecurityTrimmer2.CheckAccess() method returns a BitArray which is a collection of bit values, represented as boolean, where true indicates that the bit is on (1) and false indicates the bit is off (0).
If the bit has a value of 1, the corresponding crawl URL is displayed in the Search results; otherwise the crawl URL is not be displayed in the Search results.
For a content source that is configured to crawl external websites using http protocol, the documentCrawlUrl is the exact url of the a page crawled.
For a content source that is configured to crawl SharePoint sites, the documentCrawlUrl may take below forms depending on the protocol:
- sps://wssv2site/site$$$category/bucketid=6/itemid={7c436fba-8618-4558-a3d0-709f29fb3d8e} [ This is generally when a WSS 2.0 site is crawled]
- sts3://spsite/siteurl=/siteid={f208bff2-a0dd-4f8d-9dac-9bb3dceb4114}/weburl=/webid={567c4e3d-0c7b-450b-bfc9-f8a4ee60bc94}/listid={bf8a9b92-3bdb-483b-91a5-76b63de5a575}/folderurl=/itemid=182 [ This is when a SP2010 site is crawled]
It is important that we know the format of documentCrawlUrls because we will use that information in our security trimming logic, like I did here for draft items
public BitArray CheckAccess(IList<String> documentCrawlUrls, IDictionary<String, Object> sessionProperties, IIdentity passedUserIdentity)
{
BitArray retArray = new BitArray(documentCrawlUrls.Count);
IIdentity userIdentity = System.Threading.Thread.CurrentPrincipal.Identity;
//For claims, use IClaimsIdentity. It is defined in Microsoft.IdentityModel.Claims;
//IClaimsIdentity claimsIdentity = (IClaimsIdentity)passedUserIdentity
for (int x = 0; x < documentCrawlUrls.Count; x++)
{
string currentDocUrl = documentCrawlUrls[x];
/* Add code here to check if user associated with userIdentity can access documentCrawlUrls[x]. If so: retArray[x] = true; If not: retArray[x] = false; */
}
return retArray;
}
Deploy and Register the Custom Security Trimmer
1) Deploy the assembly to GAC. [Note: If you have multiple Web front-end (WFE) servers, you must deploy to GAC on all WFEs].
2) Creating a Crawl Rule for the Custom Security Trimmer.
Before you register the custom security trimmer, we need to create the crawl rule for the content that the custom security trimmer applies to.To create the crawl rule Open SharePoint 2010 Central Administration, and then navigate to the Application Management page.
3) Register the Custom Security Trimmer
Using PowerShell :In SharePoint 2010 Management Shell, run below command (Replace class , assembly,rulepath and Search Service Application details with yours)
New-SPEnterpriseSearchSecurityTrimmer -SearchApplication "Search Service Application 1" -typeName "SP2010.Samples.CustomSharepoint2010SecurityTrimmer, SP2010.Samples, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f4496ec7e0f80a75" -RulePath http://wssv2site/* -id 1 //Code
Using PluggableSecurityTrimmerManager : Using PluggableSecurityTrimmerManager Class, we can programmatically access already registered security trimmers and also we can register new trimmers.
4) Do a full crawl the Content Source. The content source in this example is configured to crawl external web sites as below.
The complete code will look like :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Office.Server.Search.Query;
using Microsoft.Office.Server.Search.Administration;
using System.Collections.Specialized;
using System.Collections;
using System.Security.Principal;
using Microsoft.IdentityModel.Claims;
namespace SP2010.Samples.CustomSharepoint2010SecurityTrimmer
{
public class CustomSharepoint2010SecurityTrimmer : ISecurityTrimmer2
{
//to provide default configuration limit for search results on which security trimming is applied
private int intResultsCheckLimit = 150;
public void Initialize(NameValueCollection trimmerProperties, SearchServiceApplication searchApplication)
{
//retrieve the value of CheckLimitProperty if it is configured while registering the trimmer using stsadm and use it if found.
if (trimmerProperties["CheckLimitProperty"] != null)
{
intResultsCheckLimit = Convert.ToInt32(trimmerProperties["CheckLimitProperty"]);
}
}
public BitArray CheckAccess(IList<String> documentCrawlUrls, IDictionary<String, Object> sessionProperties, IIdentity passedUserIdentity)
{
BitArray retArray = new BitArray(documentCrawlUrls.Count);
//Check if the search results obtained are under limit (150 or that from trimmerProperties["CheckLimitProperty"]) when the current search query is executed
if (!this.CheckLimit(sessionProperties, documentCrawlUrls.Count))
{
throw (new PluggableAccessCheckException("Reached Limit!This Security trimmer will not be applied"));
}
IIdentity userIdentity = System.Threading.Thread.CurrentPrincipal.Identity;
//For claims, use IClaimsIdentity. It is defined in Microsoft.IdentityModel.Claims;
//IClaimsIdentity claimsIdentity = (IClaimsIdentity)passedUserIdentity
for (int x = 0; x < documentCrawlUrls.Count; x++)
{
string currentDocUrl = documentCrawlUrls[x];
/* Add code here to check if user associated with userIdentity can access document
or page with url documentCrawlUrls[x]. If so: retArray[x] = true; If not: retArray[x] = false; */
}
return retArray;
}
private bool CheckLimit(IDictionary<String, Object> sessionProperties, int documentCrawlUrlsCount)
{
Object currentCount;
//Use of sessionproperties to store and preserve checkcount across multiple CheckAcess() calls
sessionProperties.TryGetValue("currentCheckCount", out currentCount);
if (currentCount == null)
{
sessionProperties["currentCheckCount"] = documentCrawlUrlsCount;
return (true);
}
int currentCountInt = Convert.ToInt32(currentCount);
currentCountInt += documentCrawlUrlsCount;
sessionProperties["currentCheckCount"] = currentCountInt;
if (currentCountInt <= intResultsCheckLimit)
{
return true;
}
else
{
return false;
}
}
}
}
Security trimmer or ISecurityTrimmer2 account :
Got to Central Administration >Configure service accounts.
Check the account for “Service Application Pool – SharePoint Web Services Default”. This pool hosts the Search Service Application. It is not recommended not to manage this app pool account yourself because it hosts other services as well which may get affected.
Important to read : Performance Implications of implementing custom security trimmer
A custom security trimmer has affect on performance of the Search, so we should go for it only if the out-of-box security trimming functionality does not meet our requirements.
Also, It is recommend to limit the number of access checks the trimmer performs for a single query. Without a limit, the CheckAccess method is called many times until the document that the user has access to is returned. In this scenario, performance would be much better if the number of access checks are limited(to 150 for example). The access checks are stopped after the limit is exceeded and a message is presented to the user to refine his query for better results.
We can track the number of items checked and throw a PluggableAccessCheckException exception if limit exceeds.We can pass a string message for the user to the constructor of the PluggableAccessCheckException class. The Search then returns this text instead of any results in response.
Technorati Tags : SharePoint 2010 Search
Sharepoint 2010 Sandbox Solutions : You see it a new feature ?
Posted by Amit Kumawat in .NET, Extreme, SharePoint 2010 on October 7, 2011
“Amit! SharePoint 2010 has many new features. I found Sandbox Solutions…a cool new feature !!” –exclaimed my friend… who started his career as a SharePoint developer. If you are a pure SharePoint developer, chances are high that you may feel the same
.
The concept of ‘Sandbox’ existed in .NET right from the beginning. SharePoint in nothing but a .net application on steriods. With Sharepoint 2010, it is good that we can exploit the concept of “Sandbox” like we were(and we are) able to do in .NET
A sandbox is an environment where every aspect of security is controlled by the host.The host is a separate process which can be a windows service, console application or IIS website or SharePoint 2010 Sandbox Worker Process (which runs sandbox solutions in SharePoint).
Before going into a sandbox, let me talk a bit about of Permissions in .NET. Permissions are the different powers or abilities which CLR can grant or deny to .NET code. The .NET Framework has a set of permissions to control access to system which you’ll find in the System.Security.Permissions namespace.
In .NET, Permissions provide a layer of security (apart from operating system security) in two ways:
- Sandboxing: Limiting the type of operations that partially trusted .NET assemblies can perform. Sandboxing uses code access permissions.
- Authorization: Limiting who can do what. Authorization uses identity and role permissions.
To understand how a Sandbox work, we should also know about code access security (CAS) in order to :
- Write assemblies that will run in a limited permissions environment (a sandbox).
- Create your own hosting environment, like Sandbox Worker Process (SPUCWorkerProcess.exe) we have in SharePoint 2010, those sandboxes other assemblies.
How Code Access Security Is Applied to Sandbox
When you run a .NET exe from the Windows shell or command prompt, it runs with unrestricted permissions. This is called full trust.
If you execute an assembly via another hosting environment – such as a SQL Server CLR integration host or Sandbox Worker Process (SPUCWorkerProcess.exe), the host decides what permissions to give your assembly ( a sandbox web part for example).
To be technically correct, a host does not apply restricted permissions to your assembly. Rather, it creates an application domain with restricted permissions, and then loads your assembly into that sandboxed domain. This means that any assemblies that load into that domain run in that same sandbox with the same permissions.
There are two exceptions, however:
• Assemblies registered in the GAC like Farm level solutions in SharePoint
• Assemblies that a host has nominated to fully trust. We know that Sandbox solutions can use Proxy, SPProxyOpertion, which is fully trusted and allows us to extend sandbox solutions capability in SharePoint 2010.
Code Access Security Restrictions for Sandbox Solutions in SharePoint
The sandboxed solutions are enforced by a restrictive code access security policy. This restricts sandboxed solutions to use a specific subset of the Microsoft.SharePoint namespace and access to external resources or databases.
The Web.config file in the 14\Usercode folder contains the CAS policies that apply to sandboxed solutions in SharePoint.
Permission Restrictions
Apart from code access security policy , there are restrictions on the actions that you can perform from sandbox solution as the sandbox worker process uses an account with a limited permission set.
Because sandbox solutions run in a partial-trust environment, any assembly that contains method to be called from the sandbox must include the attribute AllowPartiallyTrustedCallersAttribute.
Creating a sandbox solution in .NET
Imagine you want write an application that allows your friends to install third-party plugins. One example is Visual studio- It allows you to install plugins like SPDisposeCheck. Right?
Most likely you’d want to prevent plug-ins from exploiting your privileges as a trusted application, so as not to destabilize your application or the user’s machine. The purpose of Sandbox solutions in SharePoint 2010 is also the same – to prevent code from destabilizing the SharePoint farm. And the best way to achieve this is to run each plug-in in its own sandboxed Application domain.
This is what the Sandbox Worker Process (SPUCWorkerProcess.exe) does. For every sandbox solution, it creates a separate application domain which is, of course, subject to execution and access constraints.
For this example, we’ll assume a plug-in is a .NET exe called sandboxplugin.exe. Here’s the complete code, for the host program:
using System;
using System.IO;
using System.Net;
using System.Reflection;
using System.Security;
using System.Security.Policy;
using System.Security.Permissions;
class Program
{
static void Main()
{
string sandboxPluginPath = @"c:\sandboxfolder\sandboxplugin.exe";
//Creating restricted permissions for plugin to execute
PermissionSet ps = new PermissionSet (PermissionState.None);
ps.AddPermission (new SecurityPermission (SecurityPermissionFlag.Execution));
ps.AddPermission (new FileIOPermission (FileIOPermissionAccess.PathDiscovery |
FileIOPermissionAccess.Read, plugInPath));
ps.AddPermission (new UIPermission (PermissionState.Unrestricted));
AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
//Creating a new application domain for plugin
AppDomain sandbox = AppDomain.CreateDomain ("sbox", null, setup, ps);
//Any problem in running the plugin will not destablize your host application.
sandbox.ExecuteAssembly(sandboxPluginPath);
AppDomain.Unload (sandbox);
}
}
Technorati Tags : SharePoint Sandbox Solutions, Application Domains











Recent Comments