Monday, October 17, 2011

Send a SharePoint 2010 document to an external web service using Records Center (I)

One of the main demands that a SharePoint customer -that has just jumped into the platform- usually makes is “how can I connect SharePoint to send/receive documents from SAP/Documentum/MyOwnRepository/whatever?”.

To receive documents or just any kind of data with associated metadata and, i.e., upload these items to a List, I would say that one of the best options is to use standard Web Services. If the standard services are not enough, then we can create our own service and then use the Object Model to build that extra-functionality.

To send documents (again, with or without associated metadata), the chosen solution in out last customer was use some Records Management functionality as an intermediate step to send the document to the customer’s system. The Records Center is an Enterprise site template available since SharePoint 2007 which functionality has been slightly modified for 2010. The cool point of this solution is that the document can be sent to the Records Center either using the contextual menu (Send To –> “Name of the external connection”), or with a custom Activity in a workflow (to send batches of documents)

These are the steps to implement this solution:

  • Add a a new connection in the configuration in Central Admin –> General Application Settings –> Configure send to connections.
  • Create a new webservice in a different Web Application (normal ASP.NET service). This will hold the bridge connection between SharePoint and the external service.

So far, it seems a simple and fast to implement solution, but there are a couple of “special issues features” that may give you headaches for days.

In this first post I am going to cover the connection configuration step and just take a look around to the Records Center, to understand how it works, and what are we going to do.

Let’s begin going to Central Admin –> General Application Settings –> Configure send to connections and add a new connection. You will need to use the service OfficialFile.asmx, as this service implements the methods to copy files into the Records Center. You can find thousands of resources about this service, but the official specification is here: http://download.microsoft.com/download/8/5/8/858F2155-D48D-4C68-9205-29460FD7698F/[MS-OFFICIALFILE].pdf

To set up a new connection using this service, the URL should have the format http://<server>/<recordsCenterUrl>/_vti_bin/officialfile.asmx. Notice that you should have created a Records Center site in order to use this functionality.

image

Now you should be able to see a new link in the contextual menu of the documents libraries. This link wil execute the action specified in the connection settings (copy to Records Center, i.e).

image

Click the “Official File” link in the contextual menu will copy the document to the Records Center using the service OfficialFile.asmx, and that is exactly what we want to do with our own service. Now that we –barely- know how it is going to work, we can create our own service based in the OfficialFile.asmx, and override it to provide a connection to the external service. I personally found this solution great because it is going to use a connection to an external system inside SharePoint context. My point: we are gonna have everything under control.

In the next post I will implement the second part of the solution. That is, develope a “Hello World” web service and give a couple of useful tips to make it work.

Thursday, September 29, 2011

SharePoint Timer Job stops at “Initialized” and clear SharePoint Cache

There are a lot of configuration items that SharePoint cache by default: features, solutions and timer jobs among many other stuff. According to Joe Rodgers in this entry, “The config cache is where we cache configuration information (stored in the config database) on each server in the farm. Caching the data on each server prevents us from having to make SQL calls to pull this information from the configuration database. Sometime this data can become corrupted and needs to be cleared out and rebuilt.”. Yeah.

So just imagine, what could happen if you deploy a solution with a custom timer job, and you forgot in the first line of code something like

System.Diagnostics.Debugger.Launch();


Boahhh: no trace, no error, no action, nothing.


Then you will probably need to clear the SharePoint configuration cache (like Joe Rodgers explain) or at least, if you know what is causing your problem, re-install the feature of your TimerJob. That will clear the cache for that feature/timerjob and when you upgrade your solution and install the feature again, SharePoint will cache the right one.

Tuesday, September 13, 2011

Check if a SharePoint user is member of an AD group

There are several ways to get this information:

But the easiest way I found was using the System.DirectoryServices.AccountManagement namespace. Incredible short implementation, best results… so something like this will solve the problem.


using System.DirectoryServices.AccountManagement;

protected bool CurrentUserIsMemberOfGroup(string groupName)
{
string userLogin = SPContext.Current.Web.CurrentUser.LoginName;
// To get the right context, run with elevated privileges
SPSecurity.RunWithElevatedPrivileges(delegate()
{
var principalContext = new PrincipalContext(ContextType.Domain);
var userPrincipal = UserPrincipal.FindByIdentity(principalContext, System.DirectoryServices.AccountManagement.IdentityType.SamAccountName, userLogin);
var group = GroupPrincipal.FindByIdentity(principalContext , groupName);
return userPrincipal.IsMemberOf(group);
});
}

Notice the SPSecurity.RunWithElevatedPrivileges, as it is necessary to get the info from our AD (in case it is not located in the same machine as our beloved SharePoint). Otherwise, you won’t get access to the “ContextType.Domain”.


Hope this helps somebody.


Cheers!

Sunday, September 4, 2011

Custom MySites TopLinkBar placed in a not-MySites site

It is hard to describe in only one line what I have been doing last week. Sorry about that, but I guess the problem is quite common between customers that want to integrate several different kind of SharePoint 2010 templates with the nice look & feel that MySites offers.

In short, I was asked to keep the same TopLinkBar from MySites in an intranet Team site and in a Basic Search Center site. They 3 were running obviously in 3 different web applications, and they should use the same links.

image

My first idea: add the control

<SharePoint:DelegateControl runat="server" ControlId="GlobalNavigation"/>

to the v4.master (for the TeamSite) and the minimal.master (for the SearchCenter) pages. Then just modify through the SiteSettings the Top Link Bar links in each of the Site Collections. The problem is that this kind of configuration is specifically for MySite based templates, and this will not work in any other type of templates. So although you can see now the TopLinkBar in your site, it is in fact a useless dummy bar that contains links to nowhere…


My second idea: create an own TopLinkBar, using a copy of the User Control TopNavBar.ascx located in CONTROLTEMPLATES which is the control rendered when you place the


<SharePoint:DelegateControl runat="server" ControlId="GlobalNavigation"/>

in the master pages. We can set this new control to our solution in a Module, adding this to the elements.xml


<Control Id="GlobalNavigation" Sequence="10" ControlSrc="~/_CONTROLTEMPLATES/CustomTemplates/CustomTopNavigation.ascx" />

Then, inherit from the class MySiteDataSource, which is being used to set the links in the navigation bar, and modify programmatically the navigation to set my own links. Here again there is a problem, which is the sentence in the MSDN article: This class and its members are reserved for internal use and are not intended to be used in your code. So no, I could not access these methods…


My third and final idea: it is pretty much the second one (create an own CustomTopNavigation.ascx control, add it to the master pages, blablabla) BUT, create my own SiteMapDataSource in my CustomTopNavigation control that I will set to the TopNavigationMenu itself in the DataSourceID parameter.


<SharePoint:AspMenu
     ID="MySiteTopNavigationMenu"
     Runat="server"
     EnableViewState="false"
     DataSourceID="MySiteTopNavDS"
     AccessKey="<%$Resources:wss,navigation_accesskey%>"
     UseSimpleRendering="true"
     UseSeparateCss="false"
     Orientation="Horizontal"
     StaticDisplayLevels="1"
     MaximumDynamicDisplayLevels="1"
     PopOutImageUrl=""
     SkipLinkText=""
     CssClass="s4-mysitetn">
  </SharePoint:AspMenu>
  <asp:SiteMapDataSource runat="server" id="MySiteTopNavDS"  SiteMapProvider="MySiteMapProvider" ShowStartingNode="false" />

Then, create somewhere in the code a custom SiteMap provider and set my links.


public class CustomNavigation : PortalSiteMapProvider
    {
        public override SiteMapNodeCollection GetChildNodes(System.Web.SiteMapNode node)
        {
            PortalSiteMapNode pNode = node as PortalSiteMapNode;
            if (pNode != null)
            {
                if (pNode.Type == NodeTypes.Area)
                {
                    SiteMapNodeCollection nodeColl = base.GetChildNodes(pNode);
                            SiteMapNode childNode = new SiteMapNode(…, "My Newsfeed");
                            SiteMapNode childNode1 = new SiteMapNode(..., "My Content");
                            SiteMapNode childNode2 = new SiteMapNode(…, "My Profile");
                            SiteMapNode childNode3 = new SiteMapNode(…, "New Link");
                            nodeColl.Add(childNode);
                            nodeColl.Add(childNode1);
                            nodeColl.Add(childNode2);
                            nodeColl.Add(childNode3);

                    return nodeColl;
                }
                else
                    return base.GetChildNodes(pNode);
            }
            else
                return new SiteMapNodeCollection();
        }
    }


And then, finally, modify in the web.config files of the web applications we want to change (not for MySites web application, obviously) the entry for the SiteMap provider. That means, replace


<add name="MySiteMapProvider" description="MySite provider that returns areas and based on the current user context" type="Microsoft.SharePoint.Portal.MySiteMapProvider, Microsoft.SharePoint.Portal, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

with


<add name="MySiteMapProvider" type="[namespace].CustomNavigation, […]" NavigationType="Global" /> 

I know it is not the cleanest way to modify this TopLinkBar, so if anyone has made it with another (and easier) method, I would really apprecciate this info Smiley.


Cheers!

Sunday, June 19, 2011

Show webpart custom property (PersonalizationScope.User) for Contribute users

Weeks ago in a project I added several custom webparts with one or more of the following custom properties:

[Category("Guisu properties")]
[Personalizable(PersonalizationScope.User)]
[WebDisplayName("Show Company")]
[WebBrowsable(true)]
public bool ShowCompany
{
get
{
return showCompany;
}
set
{
showCompany = value;
}
}


My idea was to have different levels of accessing these properties, so the administrator could see ALL the properties, including those with PersonalizationScope.Shared, and the users with Contribute permissions could only see the properties with PersonalizationScope.User. Moreover, according to MSDN, these properties should be user-specific.

Cool, huh? Well, it is not so easy. Thanks to this entry in http://akifkamalsyed.wordpress.com/ blog, I realized it is necessary to change several SafeControl entries.

This could be a problem for deployments... if we did not have the safe control entries configuration in VS2010 :) So the normal SafeControl properties for a default webpart are shown like this:




Just change them to the following configuration, redeploy, and it should work:

Thursday, June 9, 2011

New city, new country, new job...

Hi all,

So yes, it is true. I just left my job and my friends in BCN to move to Germany. That's why I have not been able to update the blog so often as I wanted (sorry to the 2 guys who have asked things about the Facebook API ;)).

Anyway, in the following days I will start to post again about SharePoint and (I hope) interesting technology stuff in my pseudo-English. Maybe I will use the blog to post funny things about Germany as well.

To sum up: I work right now for a small (but full of SharePoint experts) company called PlanB. There are a lot of people here that are very VERY good, so take a look to their blogs (some of them are in English and not in German :P).

Of course I would like to thank my previous workmates of Spenta for all the fun we had working together. It was a great time guys. I know the office will not be same after me, but you know, you will have to keep on living :D

That was enough for now... I have already a couple of posts in mind, so stay tuned.

Auf die Plätze, fertig, los!