Monday, June 29, 2009

Restricting access to sharepoint views

Scenario:
Some one asked me how to restricting access to sharepoint views because he has a view with one column with information only meant for Contributors and not for Visitors.

Solution:
There is no out of box solution and they are many work-around for the problem.
Here's one which is simple and can be handled from sharepoint UI itself.

Steps ( Set 1 ) :

01. Go to the list you want to restrict the access.
02. Make sure AllItems.aspx , or your default view doesn't have the column you want to hide.
03. Now create a new View based on previous view, you can name it Contributors view.
04. Add the columns you want to show to Contributors View and save it.

Steps ( Set 2 ) :

05. Now go Site Actions > Create
06. Create a new Web Part Page with name "Contributors View", select the library you want to save it to.
07. Click on Add webpart option and Insert list view webpart by selecting the list name
08. Edit this webpart to point it to new View ( Contributors View )
09. Exit the editor mode

Steps ( Set 3 ) :

10. Go to library you have used in the above step.
11. Look for the page you have created.
12. From the context menu of the page > Manage Permission , Allow only Contributors

Steps ( Set 4 ) :

13. Now visit the list again ( trust me this is the last time )
14. List Settings > delete the view ( Contributors View ).

That's it , you are almost done 95%.
Missing 5% work goes to the solution you need to find out to provide some way for your Contributors to navigate to this newly created page. :-)

Saturday, June 27, 2009

Choosing a right port number for WCF Endpoint

Scenario:
While installing an endpoint for WCF Host service, we wanted to look for a port which is not being used by any other program for communication, to avoid any issues. Also we had many other WCF Host services in pipeline, so we wanted a standard way of choosing an unused port on our servers.

Solution:
PortQry was the solution. There is a command line version and a UI based version available for download from Microsoft.

PortQryV2.exe is a command-line utility that you can use to help troubleshoot TCP/IP connectivity issues. Portqry.exe runs on Windows 2000-based computers.The utility reports the port status of TCP and UDP ports on a computer you choose.

Download Command Line Utility

Download UI Based Utility

Examples :

The following command tries to resolve "reskit.com" to an IP address and then queries TCP port 25 on the corresponding host:

portqry -n reskit.com -p tcp -e 25
The following command tries to resolve "169.254.0.11" to a host name and then queries TCP ports 143,110, and 25 (in that order) on the host that you selected. This command also creates a log file (Portqry.log) that contains a log of the command that you ran and its output.
portqry -n 169.254.0.11 -p tcp -o 143,110,25 -l portqry.log
The following command tries to resolve my_server to an IP address and then queries the specified range of UDP ports (135-139) in sequential order on the corresponding host. This command also creates a log file (my_server.txt) that contains a log of the command that you ran and its output.
portqry -n my_server -p udp -r 135:139 -l my_server.txt
Article:
KB Article

Monday, June 22, 2009

Feature testing in different sharepoint environment

Scenario:
Often in sharepoint we need to test a particular functionality with respect to different permission groups ( visitors / members / owners ) for testing.

Solution:
One common approach is to create multiple accounts for each type of access, but that eats up good amount of time switching back and forth.

So I wrote a quick script , which create 1 top level site and 3 more site collections for each type of access group.

Checkout the Urls in the script , you will understand what I meant by that. Idea is that same account will be in different permission group in different site collections. So you can switch permissions based on different urls.

Code:

@SET AdminAccount="training\administrator"
@SET AdminEmail="admin@training.com"
@SET SiteTemplate="STS#0"


Echo ==================================
@SET MAINSITEURL="http://localhost"

Echo Dropping the site collection
stsadm -o deletesite -url %MAINSITEURL%

Echo Recreating fresh site collection
stsadm -o createsite -url %MAINSITEURL% -sitetemplate %SiteTemplate% -ownerlogin %AdminAccount% -owneremail %AdminEmail% -title "Home"

Echo ==================================
@SET MAINSITEURL="http://localhost/sites/visitor"

Echo Dropping the site collection
stsadm -o deletesite -url %MAINSITEURL%

Echo Recreating fresh site collection
stsadm -o createsite -url %MAINSITEURL% -sitetemplate %SiteTemplate% -ownerlogin %AdminAccount% -owneremail %AdminEmail% -title "Visitors"

Echo ==================================
@SET MAINSITEURL="http://localhost/sites/members"

Echo Dropping the site collection
stsadm -o deletesite -url %MAINSITEURL%

Echo Recreating fresh site collection
stsadm -o createsite -url %MAINSITEURL% -sitetemplate %SiteTemplate% -ownerlogin %AdminAccount% -owneremail %AdminEmail% -title "Members"

Echo ==================================
@SET MAINSITEURL="http://localhost/sites/owners"

Echo Dropping the site collection
stsadm -o deletesite -url %MAINSITEURL%

Echo Recreating fresh site collection
stsadm -o createsite -url %MAINSITEURL% -sitetemplate %SiteTemplate% -ownerlogin %AdminAccount% -owneremail %AdminEmail% -title "Owners"

Echo ==================================
pause

Sunday, June 21, 2009

Avoiding memory leaks from beggining

Scenario:
Sharepoint is relatively new for lot of developers and its hard to just guide them on something by saying its best practice. I believe in finding ways to provide tools to easily discover common issues so that automatically code can comply with best practice.

You can bing about Best Practice for Disposing objects and there is a utility which can help you to find some of the common problems. You can download it from here -- > Download MSI

Solution:
As I said tools should be easy to use , so here a small tip to make SPDisposeCheck part of you every project.



Steps:
1. Install SPDisposeCheck locally.
2. Open Visual Studio
3. Go to Tools > External Tools > Add a new tool with below settings

Setting:

Title : SPDisposeCheck

Command : C:\Program Files\Microsoft\SharePoint Dispose Check\SPDisposeCheck.exe <-- ( This is default location where SPDisposeCheck is installed )

Arguments : "$(BinDir)$(TargetName)$(TargetExt)" -debug

Use Output Window : Checked
Now just go to Tools > SPDisposeCheck and you can see the report in your output window.

Article:
Best practices

STSADM solution sync issue

Scenario:
We have a big farm and commonly found a problem of solutions not getting sync on all servers, what i meant was we can feature features and other files not getting copied on all the servers and so causing issues.

Solution:
Few things to note before I talk about solution

-- Dont rush while installing solution on multi-server farm because timer jobs runs at different timings and can take some time before you see all the deployed files.So make sure to give your solution atleast 5 minutes before you declare its an issue.

-- If your deployment is stuck then you can run following command to force stuck solution deployment.

STSADM -o execadmsvcjobs
-- Still you see issues with solution files, then you can run
STSADM -o syncSolution -name mysolution.wsp
-- Also I recommend using Solution Installer as this has robust installation mechanism and can rollback in case of some issues.

Article:
STSADM

Follow me on Twitter

Exception Handling in WCF

Scenario:
Robust exception handling in key to any good system , specially with a new technology when you have lot of unknowns. WCF is easy to adopt but without a robust error handling its hard to debug in an environment where you have limited control.

Solution:
Here's a sample from MSDN , but important thing to note is the sequence in which exceptions are handled. Most specific once first and then the generic once.

Code:

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using Microsoft.WCF.Documentation;

public class Client
{
public static void Main()
{
// Picks up configuration from the configuration file.
SampleServiceClient wcfClient = new SampleServiceClient();
try
{
// Making calls.
Console.WriteLine("Enter the greeting to send: ");
string greeting = Console.ReadLine();
Console.WriteLine("The service responded: " + wcfClient.SampleMethod(greeting));
Console.WriteLine("Press ENTER to exit:");
Console.ReadLine();
}
catch (TimeoutException timeProblem)
{
Console.WriteLine("The service operation timed out. " + timeProblem.Message);
wcfClient.Abort();
Console.ReadLine();
}
// Catch the contractually specified SOAP fault raised here as an exception.
catch (FaultException<GreetingFault> greetingFault)
{
Console.WriteLine(greetingFault.Detail.Message);
Console.Read();
wcfClient.Abort();
}
// Catch unrecognized faults. This handler receives exceptions thrown by WCF
// services when ServiceDebugBehavior.IncludeExceptionDetailInFaults
// is set to true.
catch (FaultException faultEx)
{
Console.WriteLine("An unknown exception was received. "
+ faultEx.Message
+ faultEx.StackTrace
);
Console.Read();
wcfClient.Abort();
}
// Standard communication fault handler.
catch (CommunicationException commProblem)
{
Console.WriteLine("There was a communication problem. " + commProblem.Message + commProblem.StackTrace);
Console.Read();
wcfClient.Abort();
}
}
}
Article:
MSDN

Follow me on Twitter

Saturday, June 20, 2009

Referencing SPWeb / SPSite throws error on layout page

Scenario:
On a custom application page, when you are trying to get to SPWeb or SPSite object from SPContext, you got following error

Error : Object Reference not sent to an instance of an object.

Solution:
This can be because of :

-- First reason can be Web / Site Collection itself is missing. Make sure you can access the web's default page before calling the custom application page in its context.
If you are trying to get to ( http://localhost/sites/test/_layouts/client/customPage.aspx ) and this getting error, make sure you can get to ( http://localhost/sites/test/default.aspx )

-- If you using Form Authentication and trying to get the SPWeb / SPSite object from a page which is not authenticated ( usually these are the pages inheriting from UnSecuredLayoutPage class ), then you can get this error.

Solution:
ASP.Net Zindabad . you need to use the SMTPClient to send email for all such pages which can be accessed without Authentication ..like Login.aspx

Follow me on Twitter

Line graph for Tasks list

Scenario:
Showing graphical representation of Tasks list.

Solution:


Whats different about this one from so many you already saw on web , is that you dont need to change anything, just import to your webpart gallery and it will connect to your Task list :-)

Tasks list Line-graph webpart <-- Import it and use it , no change required.

Article:
Yaroslav Pentsarskyy’s SharePoint and .NET adventures

Follow me on Twitter

Friday, June 19, 2009

Sending email with details exception handling

Scenario:
Sending email is one of the most common requirement and it usually works great in DEV environment because Mail Server is usually not locked down. But you never know the rules set up in Production.

Solution:
Robust Error handling is the answer to most production issues.Here's a modified version of the robust email solution in ASP.net

Code:

private void SendMail(string from, string to , string subject, string body)
{
//Create message object and populate w/ data from form
System.Net.Mail.MailMessage message = new System.Net.Mail.MailMessage();
message.From = new System.Net.Mail.MailAddress(from);
message.To.Add(to);
message.Subject = subject;
message.Body = body;

//Setup SmtpClient to send email. Uses web.config settings.
System.Net.Mail.SmtpClient smtpClient = new System.Net.Mail.SmtpClient();

//Error handling for sending message
try
{
smtpClient.Send(message);
}
catch (System.Net.Mail.SmtpFailedRecipientsException recExc)
{
for (int recipient = 0; recipient < recExc.InnerExceptions.Length - 1; recipient++)
{
System.Net.Mail.SmtpStatusCode statusCode;
//Each InnerException is an System.Net.Mail.SmtpFailed RecipientException
statusCode = recExc.InnerExceptions[recipient].StatusCode;
//Log error to event log.
}
}
//General SMTP execptions
catch (System.Net.Mail.SmtpException smtpExc)
{
//Log error to event log using StatusCode information in
//smtpExc.StatusCode
}
catch (Exception ex)
{
//Log error to event log.
}
}
Article:
SmtpFailedRecipientsException , SmtpException
Follow me on Twitter

Kerberose Test Utility

Scenario:
We wanted to test access to our service for kerberose.

Solution:
Small console application to test if i can reach the resource. Make sure you are testing a secured resource , and the username used to make the call has SPN created.

Code:

using System;
using System.Collections;
using System.IO;
using System.Net;
using System.Text;

namespace Training {
class Program {
public static void Main(string[] args) {
Console.WriteLine("Kerberose Test Application");

string sUrl = @"http://localhost/getDoc.aspx?id=66";

try
{
//Display what Authentication modules are registered
DisplayRegisteredModules();

// Unregister the standard Basic, NTLM and Negotiate and Digest modules, leaving only Kerberos
// AuthenticationManager.Unregister("Basic");
// AuthenticationManager.Unregister("NTLM");
// AuthenticationManager.Unregister("Negotiate");
// AuthenticationManager.Unregister("Digest");
AuthenticationManager.Unregister("Kerberos");

//Display what Authentication modules are left registered
DisplayRegisteredModules();

// Prepare the web page we will be asking for
var request = (HttpWebRequest)WebRequest.Create(sUrl);
request.UserAgent = "MOSSPH";
request.Proxy = null;

Console.WriteLine();
Console.WriteLine(string.Format("Trying to access :{0}",sUrl));

// TODO. Establish your own security context.
//request.Credentials = CredentialCache.DefaultCredentials;
request.Credentials = new NetworkCredential("username", "password", "domain");
//request.Credentials = new NetworkCredential(args[0], args[1], args[2]);

//CredentialCache wrCache = new CredentialCache();
//wrCache.Add(new Uri(sUrl), "Kerberos", new NetworkCredential("username", "password", "domain"));
//request.Credentials = wrCache;

//CredentialCache wrCache = new CredentialCache();
//wrCache.Add(new Uri(sUrl), "Negotiate", new NetworkCredential("username", "password", "domain"));
//request.Credentials = wrCache;

var response = (HttpWebResponse)request.GetResponse();

// we will read data via the response stream
Console.WriteLine(response.ContentType.ToString());
Console.WriteLine((ulong)response.ContentLength);
Console.WriteLine(response.StatusDescription.ToString());

Stream stream = response.GetResponseStream();
DisplayPageContent(stream);

// Displays all the headers present in the response received from the URI.
Console.WriteLine("\r\nThe following headers were received in the response:");
// Displays each header and it's key associated with the response.
for (int i = 0; i < response.Headers.Count; ++i)
Console.WriteLine("\nHeader Name:{0}, Value :{1}", response.Headers.Keys[i], response.Headers[i]);

Console.WriteLine("\nHeader Name:{0}, Value :{1}", response.ContentType.ToUpper(), response.ContentLength.ToString());

// Releases the resources of the response.
response.Close();
}
catch (Exception ex) {
Console.Write("Error occured:" + ex);
}
finally {
Console.Read();
}
}

private static void DisplayRegisteredModules() {
IEnumerator registeredModules = AuthenticationManager.RegisteredModules;

Console.WriteLine("\r\nThe following authentication modules are now registered with the system:");

while (registeredModules.MoveNext())
{
Console.WriteLine("\r \n Module : {0}", registeredModules.Current);
var currentAuthenticationModule = (IAuthenticationModule)registeredModules.Current;
Console.WriteLine("\t CanPreAuthenticate : {0}", currentAuthenticationModule.CanPreAuthenticate);
}
}

// The DisplayPageContent method display the content of the selected page.
private static void DisplayPageContent(Stream receiveStream) {
// Define the byte array to temporarily hold the current read bytes.
var read = new Byte[512];

// Read the first 512 bytes.
int bytes = receiveStream.Read(read, 0, 512);
Console.WriteLine("\r\nPage Content...\r\n" + Encoding.ASCII.GetString(read, 0, bytes));
}
}
}
Follow me on Twitter

Getting a download count for the documents

Scenario:
Getting a download count for the documents.

Solution:
I personally feel this may not be the best way to get an accurate counter. But if this is just to get an idea of document popularity , then this will work.

Code below shows my test console application , this need need to run in timer job to able to update the counter regularly.

Code:

using Microsoft.SharePoint;

namespace Training {

class Program
{
static void Main(string[] args)
{
var lastVersion = "N/A";

SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (var elevatedSiteCollection = new SPSite("http://localhost"))
{
using (var elevatedSite = elevatedSiteCollection.OpenWeb())
{
var list = elevatedSite.Lists["Shared Documents"];
var item = list.Items.GetItemById(6);

var wssQuery = new SPAuditQuery(elevatedSiteCollection);
wssQuery.RestrictToListItem(item);
SPAuditEntryCollection auditCol = elevatedSite.Audit.GetEntries(wssQuery);

item["Counter"] = 0;
foreach (SPAuditEntry entry in auditCol)
{
if ((entry.Event == SPAuditEventType.View) ||
(entry.Event == SPAuditEventType.View))
{
if ((lastVersion == "N/A") ||
(ParseVersionNumber(entry.EventData) == lastVersion)) {
item["Counter"] = int.Parse(item["Counter"].ToString()) + 1;
}
else {
lastVersion = ParseVersionNumber(entry.EventData);
}

}
}
item.SystemUpdate(false);
}
}
});
}

static string ParseVersionNumber(string versionString)
{
try
{
int startMajor = versionString.IndexOf("<Major>") + 7;
int endMajor = versionString.IndexOf("</Major>");
int lengthMajor = endMajor - startMajor;
int startMinor = versionString.IndexOf("<Minor>") + 7;
int endMinor = versionString.IndexOf("</Minor>");
int lengthMinor = endMinor - startMinor;

string majorNumber = versionString.Substring(startMajor, lengthMajor);
string minorNumber = versionString.Substring(startMinor, lengthMinor);

if (majorNumber == "0" && minorNumber == "-1")
return "N/A";

return majorNumber + "." + minorNumber;
}
catch{
return "N/A";
}
}
}
}
Follow me on Twitter

Thursday, June 18, 2009

Programatically reading the My Links into datatable

Scenario:
Webpart to display all the my links

Solution:
Create a simple webpart


Code:

using System.Data;
using Microsoft.Office.Server;
using Microsoft.Office.Server.UserProfiles;
using Microsoft.SharePoint;

static DataTable GetQuickLinks(SPSite siteCollectionUrl, string loginName){
// string siteCollectionUrl = @"http://localhost";
// string loginName = @"domain\username";
var quickLinksTable ;

using (SPSite siteCollection = new SPSite(siteCollectionUrl))
{
using (SPWeb webSite = siteCollection.OpenWeb())
{
ServerContext serverContext = ServerContext.GetContext(siteCollection);
UserProfileManager userProfileManager = new UserProfileManager(serverContext);

if (userProfileManager.UserExists(loginName))
{
// Retreive the user profiles.
UserProfile userProfile = userProfileManager.GetUserProfile(loginName);

// Retreveing Quick Links Items from User Profile.
QuickLinkManager quickLinkManager = userProfile.QuickLinks;
QuickLink[] links = quickLinkManager.GetItems();

// Moving Items from Array of links to Data Table to bind with the Grid
quickLinksTable = new DataTable();

quickLinksTable.Columns.Add(new DataColumn("Title", typeof(System.String)));
quickLinksTable.Columns.Add(new DataColumn("Url", typeof(System.String)));

foreach (QuickLink oUserQuickLinks in links)
{
var rowLinks = quickLinksTable.NewRow();
rowLinks["Title"] = oUserQuickLinks.Title;
rowLinks["URL"] = oUserQuickLinks.Url;
quickLinksTable.Rows.Add(rowLinks);
}
}
}
}
return quickLinksTable;
}
Note:
Make sure you reference Microsoft.Office.Server.UserProfiles namespace and not Microsoft.SharePoint.Portal, as Microsoft.SharePoint.Portal is obsolete.
Article:
User Profile and Social Networking , User profile and web services
microsoft.office.server.userprofiles.quicklinkmanager
Follow me on Twitter

Wednesday, June 17, 2009

Calling WCF service from different domain

Scenario:
We had a requirement to call a WCF endpoint which was hosted in all together different domain. We had minimal trust between the domains. Creating direct proxy gave us SOAP Security Negotiation error.

This was Net TCP binding and we have Security mode Windows

Solution:
1. Create a local account on the server hosting the WCF Endpoint
2. Before opening the channel , set the client credentials as shown below.

Code:

//Connecting to WCF
var proxyClient = new MyWCFServiceClient();
proxyClient.ClientCredentials.Windows.ClientCredential.UserName = @"domain\username";
proxyClient.ClientCredentials.Windows.ClientCredential.Password = "sharepoint";
proxyClient.Open();
Follow me on Twitter

WCF Error : The requested upgrade is not supported

Scenario:
While trying to connect to WCF endpoint you got following error :

Error : The requested upgrade is not supported by 'net.tcp://servername:8080/ServiceName/mex'. This could be due to mismatched bindings (for example security enabled on the client and not on the server).


Solution:
-- Try to connect to WCF endpoint by adding a service reference using Visual Studio
-- Make sure service host is running.
-- Make sure the Configuration is same on both side ( server and client )
-- Try turning off security mode = None (case sensitive) as shown below (This may not be the best solution)

Code:

<bindings>
<netTcpBinding>
<binding name="MyService.Binding" closeTimeout="00:10:00" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" transactionFlow="false" transferMode="Buffered" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" listenBacklog="10" maxConnections="10">
<readerQuotas maxDepth="65536" maxStringContentLength="65536" maxArrayLength="65536" maxBytesPerRead="65536" maxNameTableCharCount="65536" />
<security mode ="None" />
</binding>
</netTcpBinding>
</bindings>

Follow me on Twitter

WCF Error : TCP Error Code 10061

Scenario:
While trying to connect to WCF endpoint you got following error :
Error : Could not connect to net.tcp://servername:8080/ServiceName. The connection attempt lasted for a time span of 00:00:01.*. TCP error code 10061: No connection could be made because the target machine actively refused it 198.0.0.1:8080.

Solution:
-- Try to connect to WCF endpoint by adding a service reference using Visual Studio
-- Make sure service host is running.
-- Make sure the Configuration is same on both side ( server and client )
-- Try turning off security mode = None (case sensitive) as shown below
-- In case there is no trust between domain , you might need to set firewall exception to open the port for communication

Code:

<bindings>
<netTcpBinding>
<binding name="MyService.Binding" closeTimeout="00:10:00" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" transactionFlow="false" transferMode="Buffered" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" listenBacklog="10" maxConnections="10">
<readerQuotas maxDepth="65536" maxStringContentLength="65536" maxArrayLength="65536" maxBytesPerRead="65536" maxNameTableCharCount="65536" />
<security mode ="None" />
</binding>
</netTcpBinding>
</bindings>

Follow me on Twitter

Sunday, June 14, 2009

XmlUrlDataSource sharepoint answer to XML based data

Scenario:
I was trying to bind one RSS feed to my grid and so started investigating and found another great control

Solution:
You can create a XMLUrlDataSource instance and bind it to grid.

Example:

 var source = new XmlUrlDataSource()
{
HttpMethod = "GET",
EnableCaching = true,
ErrorCacheDuration = 300,
CacheDuration = 0x1c20,
AuthType = "None",
SelectCommand = "http://feeds.feedburner.com/sandeepnahta",
XPath = ""
};



Follow me on Twitter

Web Application scoped Feature get auto activated

Scenario:
I had a timer job running with scope of Web Application and I faced an issue where I wanted to use it for only one Web Application , but when ever some one create a new Web Application , this feature was getting activated automatically.


I suspected of some auto activate property because I used AutoActivateInCentralAdmin before, and that's what it was. Feature definition support a property ActivateOnDefault , unfortunately its default value is TRUE. I will say big mistake from Microsoft.

As per my fellow friend Sahil this property has no effect on feature scoped at Web and Site level , it only works for Farm and Web Application level.

Solution:
Setting the ActivateOnDefault to FALSE solved the issue.

Code:

<?xml version="1.0" encoding="utf-8"?>  
<Feature Id="GUID" Title="Web App Timer" Scope="WebApplication" Version="1.0.0.0" Hidden="FALSE" DefaultResourceFile="core" ActivateOnDefault="False"> xmlns="http://schemas.microsoft.com/sharepoint/"
<ElementManifests>
<ElementManifest Location="elements.xml" />
</ElementManifests>
</Feature>
Follow me on Twitter

SPDataSource another powerful sharepoint control

Scenario:
I think I mentioned about this one in my previous blogs but I binged and couldn't find anything. So thought of writing about it.

We have seen many powerful controls of sharepoint and one of them is SPDataSource

I am sure you heard about SqlDataSource data source control which represents data in an SQL relational database to data-bound controls. You can use the SqlDataSource control in conjunction with a data-bound control to retrieve data from a relational database and to display, edit, and sort data on a Web page with little or no code.

On the same lines , SPDataSource data source control represents Windows SharePoint Services data to data-bound controls. You can use the control in conjunction with a data-bound control to retrieve data from lists, list items, and Web sites and to display, edit, and modify data with little or no code.

-- Like other data source controls,SPDataSource can also be used in UI and Code behind.
-- Unlike other data source controls, the SPDataSource control is associated with only one view. The GetViewNames method always returns a collection that contains the name of just one data view.

Example:
Here's a quick view to get datasource instance from Announcement List

Code:

  var datasource = new SPDataSource
{
DataSourceMode = SPDataSourceMode.List,
List = SPContext.Current.Web.Lists["Announcements"]
};
Article:
MSDN SPDataSource , SPDataSource every sharepoint developer friend 1 , SPDataSource every sharepoint developer friend 2 , SPdatasource-every-developer-friend , spdatasource-mystery-modes-webs-listoflists , Another very good example


Follow me on Twitter

My Sites webpart to display what all site collection you have acces to

Scenario:
Situation is very simple, Assuming there are 100s of site collections in the organization and logged in user should be able to be able to see what all site he have access to.

Solution:
Simple webpart


Code:

using System.Data;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.WebControls;

namespace SKN
{
public class MySites : System.Web.UI.WebControls.WebParts.WebPart
{
protected SPGridView _griDView;

protected override void CreateChildControls()
{
_griDView = new SPGridView {AutoGenerateColumns = false};

var hlf = new HyperLinkField
{
HeaderText = "Title",
DataNavigateUrlFields = new[] {"Url"},
DataNavigateUrlFormatString = "{0}",
DataTextField = "Title"
};

_griDView.Columns.Add(hlf);

DataTable table = CreateTable();

base.CreateChildControls();

SPSecurity.RunWithElevatedPrivileges(delegate
{

SPSite site = SPContext.Current.Site;
SPWebApplication webApplication = site.WebApplication;

foreach (SPSite siteCollection in webApplication.Sites)
{
using (SPWeb website = siteCollection.RootWeb)
{
if (website.DoesUserHavePermissions(SPBasePermissions.ViewListItems))
{
var row = table.NewRow();
row["Title"] = website.Title;
row["Url"] = siteCollection.Url;

table.Rows.Add(row);
}
}
siteCollection.Dispose();
}
}
);

Controls.Add(_griDView);

_griDView.DataSource = table.DefaultView;
_griDView.DataBind();
}

private static DataTable CreateTable()
{
var table = new DataTable("MySites");
// Declare variables for DataColumn and DataRow objects.

// Create second column.
var column1 = new DataColumn
{
DataType = System.Type.GetType("System.String"),
ColumnName = "Title",
AutoIncrement = false,
Caption = "Title",
ReadOnly = false,
Unique = false
};
// Add the column to the table.
table.Columns.Add(column1);

// Create new DataColumn, set DataType,
// ColumnName and add to DataTable.
var column2 = new DataColumn
{
DataType = System.Type.GetType("System.String"),
ColumnName = "Url",
AutoIncrement = false,
Caption = "Url",
ReadOnly = false,
Unique = false
};
// Add the column to the table.
table.Columns.Add(column2);

return table;
}
}
}
Possible improvements:
-- Please not in case you have multiple web applications then this solution will not work. You need to iterate through other Web Applications also and make sure you have proper account which can iterate all web applications.

-- If you have really hundreds of site collections. Then consider caching, that was one reason I used DataTable. ( Srini you can do it ;-) )

-- Also Url is not clickable , Pass me the code when you finish doing it for your client because I am tired :-) <-- Implemented

Follow me on Twitter

Saturday, June 13, 2009

Sharepoint search case-sensitive url issue

Scenario:
I faced this problem long back but forget to blog about it. Sharepoint search has a known issue : case-sensitive url issue.

What that mean is , both the Url listed below will be treated as one. So if Sharepoint search hits the first Url , it will crawl the Url content but will ignore the other one.

http://localhost/_layouts/custompage.aspx?Code=axVP <--
http://localhost/_layouts/custompage.aspx?Code=AXvp <--

Situations :
Reading files from other OS based system which supports case sensitive filenames

Solution:
Microsoft hot fix to enable the case sensitive file names search.
Also I will recommend to avoid designing any custom solution with case sensitive parameters.

Note: Hotfix will not solve the issue of Url case sensitivity.It will solve the issue of document name being case sensitive.

Article:
Hotfix Details

Follow on Twitter

Friday, June 12, 2009

Creating a nice view for the list storing process steps

Scenario :
I saw a nice list view showing steps to execute something and my sharepoint mind said I need to have something similar in sharepoint. So here it is.

Solution :
I used Sharepoint Designer but for you I will make it little simple.


Steps :
1. Create a Custom List ( because we need ID and Title column for this view )
2. Create some items in the list
3. Download the files from the link below and extract it. Upload the CSS file and circles.gif file somewhere in your sharepoint site.
4. Open the CSS file and update the location of Circles.gif file
5. Open the AllItems.aspx view in SPD
6. Right click on the view and Convert the view to XSLT
7. Look for ( xsl:template name="dvt_1.body" ) and ( xsl:template name="dvt_1.rowview" )
8. From the StepsXSLTChange.txt file you downloaded , replace the XSLT Templates.
9. Save the view and thats it, you have a nice view as shown above.

---> Download <----

Follow on Twitter

Thursday, June 11, 2009

Redirecting to a page after site creation

Scenario:
Sometime you want to redirect the user to some page after the website is created.

Solution:
You can use ExecuteUrl node of Configuration to specify the redirection.

Code:

<ExecuteUrl Url="_layouts/clientname/additionalsettings.aspx" />
Note:
1. This redirection will not work if you creating RootWeb ( from CA ).
2. If you are trying update any list / webpart , this might throw Exception if they are still getting created.
3. Only works for Team Site Templates.
4. It only works for Meeting Workspace and Document Workspace templates when the sites are created from the Create function in Sharepoint. The ExecuteUrl element is also ignored if a Meeting Workspace is created from Microsoft Outlook.

Article:
http://www.pointsharepoint.com/2009/01/using-executeurl-element-in-onetxml.html
Follow on Twitter

Wednesday, June 10, 2009

Post build script for sharepoint based projects

Scenario:
Visual Studio projects doesn't provide any support for the WSP Deployment. And usually a developer need to perform lot of addition steps to make sure deployment is smooth. Here's a script i commonly used

Steps:
Assuming you have a single server farm
1. Make sure you have the correct WSP Builder location
2. Make sure to have a Solution folder created where this script will drop the WSP

Code:


@SET TEMPLATEDIR="c:\program files\common files\microsoft shared\web server extensions\12\Template"
@SET STSADM="c:\program files\common files\microsoft shared\web server extensions\12\bin\stsadm"
@SET WSPPBUILDER="C:\Code\WSPBuilder\Buildx86\WSPBuilder.exe"
@SET WSPNAME="MySolution.wsp"
@SET MAINSITEURL="http://localhost"
@SET CENTRALADMINURL="http://localhost:80808"

Echo Creating Solution Package
del Solution\*.* /Q

@echo off
Echo Creating Solution Package
%WSPPBUILDER% -outputpath Solution -wspname %WSPNAME%

Echo Recycling AppPool ( To avoid any file locking issues )
IISRESET

Echo ==== Starting Clean up ===

Echo Deactivating Features
%STSADM% -o deactivatefeature -name MyFeature -url % MAINSITEURL%

Echo Uninstalling Features
%STSADM% -o uninstallfeature -name MyFeature -force

Echo Retracting Solution
%STSADM% -o retractsolution -name %WSPNAME% -immediate
%STSADM% -o execadmsvcjobs

Echo Deleting Solution
%STSADM% -o deletesolution -name %WSPNAME%

Echo Deleting all the deployed files including GAC-ed Assemblied
rmdir %TEMPLATEDIR%\FEATURES\MyFeature /S /Q

Echo ==== Done Clean up ===
Echo
Echo ==== Starting Fresh ===
Echo Adding Solution
%STSADM% -o addsolution -filename Solution\%WSPNAME%

Echo Deploying solution
%STSADM% -o deploysolution -name %WSPNAME% -immediate -allowGacDeployment
%STSADM% -o execadmsvcjobs

Echo Recycling IIS
IISRESET

Echo Activating Feature
%STSADM% -o activatefeature -name MyFeature -url % MAINSITEURL%

Echo ==== Done fresh deployment ===
Note:
This script hold good for sandbox and need changes to script for an other type deployment like Development , Staging and Production.

Tuesday, June 9, 2009

Database checklist

Naming Guidline
-- Setup a standard for table/view/column/stored procedure names
-- Use full name , avoid abbrevations ( yes /no )
-- No underscores
-- Use pascal casing
-- Not more than three characters to name objects
-- If using any prefixes , standardize it
-- DataTypes standardization , GUID vs ID , Varchar minimum 100 length etc
-- Always use primary key
-- Use unique key constraint for other unique columns
-- Specify not null fields
-- Decide on maximum number of columns per table ( i.e. 20 )
-- Cascase delete ( yes / no )
-- No inline t-sql command, use only stored procedures
-- Do not use SELECT * in queries
-- For large table index the columns most commonly used
-- Store sensitive data ( passwords, credit card , ssn etc ) in encrypted format
-- Use Triggers wisely
-- T-SQL command should order the results in case its master data used in Selection Controls like drop down.

Security
-- Use Database roles / Application roles ( read/read-write )
-- Permission should be granted on tables / view , only for stored procedures

Quality
-- Header comment to discribe the purpose of stored procedure and application details
-- Inline comment with in stored procedures

Clean-up
-- Plan for clean-up scripts to move/ delete old data
-- Back up schedules

Articles:
Guidelines

Follow me on Twitter

Custom Site Defintion and Feature stapling issue

Scenario:
We had 2 requirements
1. Develop a custom site definition
2. Develop a feature which is applied to only Out of Box defintions and not to the Custom Definition developed in step 1.

Here's how we had our feature stapling elements
Stapling:

<FeatureSiteTemplateAssociation Id="FEATURE_GUID" TemplateName="GLOBAL" />
<FeatureSiteTemplateAssociation Id="FEATURE_GUID" TemplateName="STS#1" />
Now the issue was it was applying the feature ( FEATURE_GUID ) to Custom Site Definition also.

Solution:
Site Definition declaration support an attribute AllowGlobalFeatureAssociations which can be set to FALSE to avoid binding with GLOBAL. STS site definition also uses the same technique.

Code:
<?xml version="1.0" encoding="utf-8"?>
<Templates>
<Template Name="CustomSiteDefinition" ID="10099">
<Configuration ID="1" Title="Custom Site Definition" Description="A site for teams to quickly organize, author, and share information. It provides a document library, and lists for managing announcements, calendar items, tasks, and discussions." Hidden="FALSE" ImageUrl="/_layouts/images/Client/Custom Site DefinitionLogo.jpg" DisplayCategory="Client" AllowGlobalFeatureAssociations="FALSE" />
</Template>
</Templates>
Article:
http://msdn.microsoft.com/en-us/library/ms476942.aspx
Follow on Twitter

Quickly getting new GUID in text editor

Scenario:
During my sharepoint development I need to generate new Guid's all the time

Solution:
Visual Studio does provide a GuidGen.exe tool, but its kinda multi-step process.
After some research I got the solution - MACRO , my old pal :-)

Step:
1. VS > Macros > Macro Explorer

2. Create a new Module and paste the below Macro code into it


3. Go to VS > Tools > Options

4. Create a key board short cut of your choice , I used Ctrl+Alt+G as shown below


Macro Code:

Sub Create_GUID
Dim sel As TextSelection
sel = DTE.ActiveDocument.Selection
sel.Text = System.Guid.NewGuid.ToString("D").ToLower()
End Sub
Article:
Generating-guids-in-the-visual-studio-ide , Create online guid

Follow on Twitter

Monday, June 8, 2009

Sharepoint Administrator's Checklist

-- Mail Server is configured
-- Validate SSL is configured
-- Validate if Office Web Services are accessible from IIS
-- Logging has been configured
-- Change the Log location,make sure support team have read access to this location
-- Review the Blocked file types
-- Backup all the web.configs locally
-- Maintain a folder for scripts, specially solution clean up / retracting scripts
-- Alias for SQL Server should be set
-- Host headers are configued
-- Fore-front security is installed and configured
-- PDF IFilters are installed and PDF icons are configured

Farm Governance
-- Track the various custom site definition IDs to avoid clash
-- List of all the system files being updated by scripts/solution and way to revert them back
-- If any new Window Service is installed to host WCF or Web Service , make sure to assign new port numbers to avoid port clash

Follow on Twitter

Sunday, June 7, 2009

Line graph using XML and XLST Transform

Scenario:
I love XSLT for it flexibility and keep looking for ways to use it. To explain one of my colleague , I used XML Web part which ships with WSS.

Solution:

This example has one Sample XML Data file , you can download it from
XSLT file is given below. After transformation this webpart can show you books cheaper than 5.95 and costlier than this in graphical manner.

Download Files XSLT:
XML
XSLT

Article
XSLT Reference

Follow me on Twitter

Saturday, June 6, 2009

Programatically adding text to clipboard

Scenario:
For one of my tiny console application , I wanted to make it easier for my end users . This console application generates some text and users need to copy it to clipboard and then paste it on to another application. Thought of making it simpler.

Solution:
I discovered its preety easy with clipboard class

Code:

using System.Window; // For this example, the data to be placed on the clipboard is a simple
// string.
string textData = "I want to put this string on the clipboard.";

// After this call, the data (string) is placed on the clipboard and tagged
// with a data format of "Text".
Clipboard.SetData(DataFormats.Text, (Object)textData);
Article:
Clipboard class
Follow on Twitter

HTTPModule to monitor File downloaded

Scenario:
Easy way to see who downloaded what.

Solution:
HTTPModule can help.

Code:


using System;
using System.Web;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;

namespace SPCustomHTTPModule
{
public class CustomHttpModule :IHttpModule
{
public void Init(HttpApplication context)
{
context.PostAuthenticateRequest += ContextPostAuthenticateRequest;
}

static void ContextPostAuthenticateRequest(object sender , EventArgs e)
{
var app = sender as HttpApplication;
if (app != null)
{
string requesturl = app.Request.Url.ToString();
if (requesturl.EndsWith(".docx") || requesturl.EndsWith(".pdf"))
{
CreateEntry(requesturl, app.Request.LogonUserIdentity.Name);
}
}
}
public static void CreateEntry(string fileUrl,string userName)
{
const string rootSite = "http://localhost";
const string listName = "Links";


using (var site = new SPSite(rootSite))
{
using (SPWeb web = site.RootWeb)
{
web.AllowUnsafeUpdates = true;
SPList list = web.Lists[listName];

SPListItem listItem = list.Items.Add();

listItem["Title"] = "File downloaded ";
listItem["Comments"] = userName + " downloaded " + SPEncode.UrlEncodeAsUrl(fileUrl);
listItem.Update();
web.AllowUnsafeUpdates = false;
}
}
}

public void Dispose() { }
}
}
Web.Config( Add to httpModules section):
<add name="SPCustomHTTPModule" type="SPCustomHTTPModule.CustomHttpModule, SPCustomHTTPModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a56fc096c595e650" />
Articles:
HTTP Handlers and HTTP Modules Overview

Follow on Twitter

Friday, June 5, 2009

SPCore on Codeplex

Project Description
This project is a utility project and will help *Developers* and *Administrators* to code faster some of the most common tasks in sharepoint.

Download

Utility includes methods for :
Alerts , Web.Config , WebParts , Audit ,OOTBFile ,Timer , Navigation , Migration , Features and many more...

-- Add a webpart to a page
-- Add a custom security group
-- Add a custom permission level
-- Web Config modification for Net 3.5
-- many more...

How to use
download the binary and reference it in your project. Also you need to use the namespace in your classes.

_using SKN;_

Want to contribute
- code
- documentation
- usage samples
- input validation & robust error handling

please leave me a comment here : subject SPCore contribution

Follow on Twitter

Sharepoint Merge Web Config on Codeplex

Project Description:
This utility is great for merging the web.config files for sharepoint based web applications.

Limitations:
-- This will only work for SharePoint web applications ( WSS and MOSS )
-- This need to be executed on all the Web Front ends ( I know painful , but SPWebConfigModification class is buggy and unreliable ). I have tested it on Single Server Farm so far
-- This utility can be run only one time per web application
-- To run it again on same web application [ no recommended for Production ] , all the GUIDs for the merge file need to be renewed. Check the sample merge xml file

Advantage:
-- This can merge many files from the mergefile location

Download

Usage
1. Download the sample merge file and change it according to requirement

2. Download SPMergeWebConfig.exe and run the following command with proper parameters

SPMergeWebConfig.exe -file 'C:\Inetpub\wwwroot\wss\VirtualDirectories\80\' -mergefile 'C:\Inetpub\wwwroot\wss\VirtualDirectories\80\'

Note : -file parameter takes a location value where you web.config file reside
-mergefile paramter takes a location value for the merge file you downloaded , utility can accept more than one files with the file name pattern as +webconfig.*.xml+

Follow on Twitter

Site Definition with subwebs

Scenario:
My good friend Shishir asked me a way to create sub-webs from the site definition.He knows it that I say its not possible in very rare situations :-)

parentChild.xml( 12/TEMPLATE/XML):

<?xml version="1.0" encoding="utf-8" ?>
<portal xmlns="PortalTemplate.xsd">
<web name="Parent"
siteDefinition="STS#0" displayName="Parent" description="This is parent web" >
<webs>
<web name="Child"
siteDefinition="sts#1"
displayName="Child"
description="I am kid" />
</webs>
</web>
</portal>
webtemp.parentchild.xml(12/TEMPLATE/1033/XML):
<?xml version="1.0" encoding="utf-8"?>
<!-- _lcid="1033" _version="12.0.4518" _dal="1" -->
<!-- _LocalBinding -->
<Templates xmlns:ows="Microsoft SharePoint">
<Template Name="ParentChildSTS" ID="5200">
<Configuration ID="0" Title="ParentChild" Hidden="FALSE" ImageUrl="/_layouts/1033/images/sts.gif" Description="This is parent child configuration"
ProvisionAssembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" ProvisionClass="Microsoft.SharePoint.Publishing.PortalProvisioningProvider"
ProvisionData="xml\\parentchild.xml" DisplayCategory="Custom" >
</Configuration>
</Template>
</Templates>
Note:
-- You are using it in MOSS environment because the binary referenced below is part of MOSS.
-- Make a copy of STS folder in the same location and rename it to ParentChildSTS. This will act as you new site. If you have own site definition please make necessary changes.
-- IISReset is required after the configuration
-- This is tested method :-)

Follow on Twitter

'System.Collections.IEnumerator': type used in a using statement must be implicitly convertible to 'System.IDisposable'

Scenario:
While executing code below I got the following error

Error :'System.Collections.IEnumerator': type used in a using statement must be implicitly convertible to 'System.IDisposable'

Code with Error :

using(IEnumerator enumerator = base.internalDataSource.GetEnumerator())
{

// Execute the code here

}
Solution:
The 'using' statement is, as the message states, closely coupled with IDisposable. For this to work, 'MyType' must implement IDisposable, which is an interface wich defines the member 'void Dispose()'; If MyType does not implement IDisposable, t.Dispose() can't be called. That's why you're getting the message.

1. Implement the interface
2. Correct the code as shown below with no disposing

Correct Code:
{
IEnumerator enumerator = base.internalDataSource.GetEnumerator();

// Execute the code here

}


Article:
Follow on Twitter

Thursday, June 4, 2009

Retract/Deloy solution or Upgrade solution ?

Scenario:
I got this question all the time about best way to upgrade the solution already deployed to the farm.

Solution:
There is no simple answer to the question.As it totally depends upon artifacts and also STSADM commands have limitations.

STSADM UpgradeSolution:
Upgrading a solution involves replacing a previous version of a solution with a current version of the solution. Specifically, an upgrade occurs when a solution is deployed that shares a solution ID with another solution.

During upgrade, files that are associated with the previous version of a solution are removed, and files contained in the current version are added.You can add new files in a solution upgrade and remove old versions of the files, but you cannot install Features or use Feature event handlers to run code for Feature installation and activation.

STSADM Retract Deploy Solution:
Retracting a SharePoint solution does not remove any information from the content database. Therefore, sites are intact after redeployment.

Similarly, you must include SharePoint feature deactivation in the installation script if you want deactivation logic to execute before retracting the original SharePoint solution. You should use caution with this approach if you do not have a time when the sites that use the solution can be taken offline. If a site is based on a site definition that is being removed and reinstalled, users will experience unpredictable behavior during the upgrade process.

Common Behaviour:
You must reactivate any installed features on any sites that use the SharePoint features

Article:
MSDN , MSDN , P&P Good Example

Follow on Twitter

Wednesday, June 3, 2009

Cleaning up the file names for uploading to sharepoint

Scenario:
Sharepoint imposes some restriction on the file and folder names . Also it doesn't allow some special characters in the file / folder name.

Solution:
Here's a quick fix to the file / folder name change using Regular expression.

Note : I am replacing the special characters with underscore and not with space because one space translate to %20 and occupies three character space in Url.

Code:

 using System.Text.RegularExpressions;

private string CleanUpSpecialCharacters(string fileName)
{
string fileTitle = fileName.Substring(0, fileName.LastIndexOf("."));
string fileExtenstion = fileName.Substring(fileName.LastIndexOf("."));

string pattern = @"[#%&*:<>?/{|}/\\.]";
Regex rgx = new Regex(pattern);

// Replace runs of white space in the input string with a
// underscore.
string newfileTitle = rgx.Replace(fileTitle, "_");

return string.Concat(newfileTitle, fileExtenstion);

}

private string Truncate(string fileName)
{
if (fileName.Length > 127)

return fileName.Substring(0, 127);
else
return fileName;
}

Tuesday, June 2, 2009

Impersonating User Identity

Scenario:
You might want to impersonate a specific WSS user identity before creating a new object so that WSS user is recognized as the owner of the new object.
In order to impersonate a WSS user identity, you must first create an SPUserToken object. You can do this by accessing the UserToken property of an SPUser object. Once you have the SPUserToken object, you can use it to create a new SPSite object using an overloaded version of the SPSite class constructor.

Solution:
This is a very good alternate to RunWithElevatedPrivileges.

Code:

SPSite siteCollection = SPContext.Current.Site;
SPWeb site = SPContext.Current.Web;

// get SPUser object and acquire token
SPUser targetUser = site.SiteUsers[@"LITWAREINC\BrianC"];
SPUserToken token = targetUser.UserToken;

// create new SPSite and SPWeb object to impersonate user
using (SPSite impersonatedSiteCollection =
new SPSite(siteCollection.ID, token)) {
using (SPWeb impersonatedSite =
impersonatedSiteCollection.OpenWeb(site.ID)) {
// WSS identity switched to impersonate BrianC
// Windows identity does not change
}
}
Article:http://msdn.microsoft.com/en-us/magazine/cc163287.aspx

Adding webpart to a page using webpart file name

Scenario:
Adding webpart to a page using webpart file name

Code:

 public static void AddWebPartUsingFileName(SPWeb web, string pageName, string dwpFileName, string zoneID)
{
string errorMessage;

using (SPSite site = web.Site)
{
using (SPWeb rootWeb = site.RootWeb)
{
string url = rootWeb.Url + "/_catalogs/wp/" + dwpFileName;
XmlTextReader reader = new XmlTextReader(new StringReader(rootWeb.GetFileAsString(url)));
SPLimitedWebPartManager wpMgr = web.GetLimitedWebPartManager(pageName, PersonalizationScope.Shared);
WebPart webPart = (WebPart)wpMgr.ImportWebPart(reader, out errorMessage);
wpMgr.AddWebPart(webPart, zoneID, 1);
wpMgr.Web.Dispose();
}
}
}
Download :
This method is also a part of SPCore now ( http://spcore.codeplex.com )