SharePoint Developer Support Team Blog

The Official blog of the SharePoint Developer Support Team

May, 2012

  • A case of custom email templates not used when alerts are created from code in SharePoint

    Recently, I had a case where customer was creating SPAlert object using SharePoint object model code and things were not working out well.

    What seem to happen was that whenever the condition that warrants alert trigger is met (e.g., an item is changed in a list), the alert email that was sent out was not the custom alert email template customer had setup but it was the out of the box email template.

    The customer had created a custom alert template by following How to create a custom e-mail alert handler in Microsoft Office SharePoint Server article.

    After investigation we found that the code they were originally using did not have SPAlert.AlertTemplate property assigned with the correct value.  Once we provided the correct alert template to this property, the alert that was created through SharePoint object model code recognized the custom email template and used that template in the emails that were sent when the alert trigger condition is met.

    Below is the sample code.

       1: void bElevated_Click(object Sender, EventArgs e)
       2: {
       3:      String ListName = "TestList";
       4:  
       5:      SPUser oCurrUser = SPContext.Current.Web.CurrentUser;
       6:      String CurrUserEmail = oCurrUser.Email.ToString();
       7:  
       8:      SPUserToken oSysToken = SPContext.Current.Web.Site.SystemAccount.UserToken;
       9:      using (SPSite oSite = new SPSite("http://team.contoso.com", oSysToken))
      10:      {
      11:           using (SPWeb oWeb = oSite.AllWebs[0])
      12:           {
      13:                oWeb.AllowUnsafeUpdates = true;
      14:                SPList oList = oWeb.Lists[ListName];
      15:  
      16:                try
      17:                {
      18:                     SPUser ospuser = oWeb.SiteUsers.GetByEmail(CurrUserEmail);
      19:                     SPAlert oalert = ospuser.Alerts.Add();
      20:                     oalert.AlertType = SPAlertType.Item;
      21:                     oalert.AlertTemplate = oList.AlertTemplate;
      22:                     oalert.Item = oList.Items[0];
      23:                     oalert.EventType = SPEventType.All;
      24:                     oalert.AlertFrequency = SPAlertFrequency.Immediate;
      25:                     oalert.Update();
      26:                }
      27:                catch (Exception ex)
      28:                {
      29:                     // exception handling logic
      30:                }
      31:                oWeb.AllowUnsafeUpdates = false;
      32:           }
      33:      }
      34: }

    Line number 21 above was the statement missing in the original code.  We need to specify the SPAlert.AlertTemplate property in order to get alert emails sent out using the custom email template.

    Hope this helps!

    Cross post from http://blogs.msdn.com/sridhara
  • How to subscribe for an alert for a “Post” in discussion board programmatically

    This post is a contribution from Jaishree Thiyagarajan, an engineer with the SharePoint Developer Support team.

    Let’s first talk about some basic facts about discussion board and alert terminologies.

    Discussion Board

    We all know about discussion board. Discussion board is where we can add “posts” and for every single post we can add “replies (comments)”. The discussion board is designed in such a way that all posts are stored as Folders and replies to the posts are stored as items within the folder.

    In SharePoint, we can subscribe for alerts at the list/library levels and also at individual item levels.

    Now that we know that “posts” are added as folder and “replies” are added as items to the folder in the discussion board , the first question that pops in our mind is “Which alert-type (SPAlertType) should I use?”

    If I use SPAlertType.Item for a post then I would not get alert for newly added replies. If I use SPAlertType.List then I may end up receiving alerts for items that I didn’t subscribe to.

    If you are looking for a solution to receive the alerts for a “post you are subscribed” and “any replies added to the subscribed post”, then you are at the right blog post.

    Now, let’s check out the alert terminology for better understanding.

    Alerts

    In SharePoint, there are two types of alerts:

    1. Subscription alert: Whenever a user subscribes for an item/document/list, the user will get an email stating “you have subscribed for an alert…”.
    2. Changed alert (commonly referred to as alert): Based on the subscription option chosen (added, modified etc.,) alert email will be triggered.

    As we can see, any site administrator can subscribe any user for an alert.  These subscription alerts are NOT security trimmed.  But the changed alerts are security trimmed.  The alert emails will only be triggered if the subscribed user has sufficient permissions for the item (for item level alerts) or lists (for list level alerts).

    Now, if your users complain that they are not receiving the changed alerts, your first action should be to check whether they have sufficient permissions for subscribed item or not.

    Ok – we’ve learnt some details on discussion board and alerts.  Now, let’s check out how to subscribe for a Post in discussion board programmatically.

    Subscribe for a  “Post” in discussion board programmatically

    Posts are added as items (i.e., in UI when you open a discussion board, you can see all the posts listed as items; the folder structure mentioned above is the internal design) to a discussion board and so whenever we subscribe for a post, item level alerts would be added via UI.  Assuming this, if we specify SPAlertType.Item when programmatically subscribing to an item in discussion board, alerts would not be sent out for replies to the post.

    To circumvent this, if we set SPAlertType.List, alerts would be sent out for any new post to the discussion board, but the requirement is to receive alert emails for just a particular post and replies to that post.

    The trick to achieve this requirement programmatically is to specify alert filter in the code.

    SPList list = web.Lists["Team Discussion"];
    SPListItem item = list.GetItemById(8);
    SPUser user = web.AllUsers[@"mydomain\username"];
    SPAlert alert = user.Alerts.Add();
     
    alert.AlertType = SPAlertType.List;
    alert.AlertTemplate = list.AlertTemplate;
    alert.AlertFrequency = SPAlertFrequency.Immediate;
    alert.EventType = SPEventType.All;
     
    // Here, we filter the alert for the specific discussion, so only responses to that discussion get an alert
    alert.Filter = string.Format("<Query><BeginsWith><FieldRef Name=\"ItemFullUrl\"/><Value type=\"string\">{0}</Value></BeginsWith></Query>", item.Url);
    alert.List = list;
    alert.Title = "Team Discussion: Sports";

    The above code will work for discussion board present in the root site; but for sub-sites it won’t work.

    After breaking my head for a long time and comparing programmatically added subscription with the out of the box subscription (in the ImmediateSubscription table in SharePoint content database, thanks to my colleague Selvakumar Ganapathi, escalation resource from SharePoint team), I finally figured out the value passed to “ItemFullUrl” should begin with a “/” (forward slash).

    Here’s the complete and working code for subscribing an alert for a post in a discussion board
    string url = "";
    if (item.ParentList.ParentWebUrl == "/")
    {
         url = item.Url;
    }
    else
    {
         url = item.ParentList.ParentWebUrl + "/" + item.Url;
    }
     
    string itemFullUrl = url.StartsWith("/") ? url.Substring(1) : url;
    string itemBeginswithFilter = itemFullUrl + "/";
     
    string filter = string.Format("<Query><Or><Eq><FieldRef Name=\"ItemFullUrl\"/><Value type=\"string\">{0}</Value></Eq><BeginsWith><FieldRef Name=\"ItemFullUrl\"/><Value type=\"string\">{1}</Value></BeginsWith></Or></Query>", itemFullUrl, itemBeginswithFilter);
    SPList list = web.Lists["Sports"];//list name
    SPListItem item = list.GetItemById(1);//post ID
    SPUser user = web.AllUsers["mydomain\\username"];//user
    SPAlert alert = user.Alerts.Add();
     
    alert.AlertType = SPAlertType.List;
    alert.AlertTemplate = list.AlertTemplate;
    alert.AlertFrequency = SPAlertFrequency.Immediate;
    alert.EventType = SPEventType.Add;
     
    alert.Filter = filter;
    alert.Title = "Sports: Cricket"; //title
    alert.List = list;
     
    alert.Update();

    The above code works for alerts subscribed to a discussion board post at the root as well as sub-sites.

    Happy Coding! Smile

  • CMIS consumer web part unable to perform search?

    This post is a contribution from Chanchal Jain, an engineer with the SharePoint Developer Support team.

    If you have a SharePoint Server installation and you have the SharePoint Search Application configured, you’ll be able to perform search in the SharePoint Search box. However when you perform a Search in the CMIS Consumer web part’s Search box, it’ll say no results returned unless your SharePoint site is already crawled by the Foundation Search. Yes that’s the default behavior.

    The reason behind is CMIS Connector uses the SharePoint Foundation Search assembly to do search on SharePoint. (You read it correct!!). CMIS Connectors are not supported for SharePoint Foundation 2010. Refer SharePoint 2010 Administration Toolkit (SharePoint Server 2010).

    So, assuming you haven’t already provisioned SharePoint Foundation search in your SharePoint farm, how do you make the CMIS Consumer web part search results working?

    For CMIS Consumer Search to work, your site should be crawled by the SharePoint Foundation Search service. SharePoint does not provide any GUI to configure SharePoint Foundation Search which becomes “SharePoint Helper Search” in a Server Installation. Here’s are the steps to get your site crawled by SharePoint Foundation search:

    1. Open the Central Administration site of your farm.
    2. Navigate to Application Management –>Service Applications –> Manage Service Applications –>Search Service Application.
    3. Open the Content Sources configured for the Search Service Application.
    4. Remove the Site\ Web Application URL (the Producer site for your CMIS Consumer) which you want to be crawled by SharePoint Foundation Search Service from the Content Source.
    5. Start the Foundation Search Service from the System Settings –>Manage services on server –> SharePoint Foundation Search.
    6. Configure the Search and set the values as desired.
    7. Navigate to Application Management –>Databases –> Manage Content Databases.
    8. Select the Content Database for the CMIS Producer Web Application.
    9. Select the server where your Foundation Search Service is running for the “Search Server” property.
    10. Select the preferred Timer Jobs Server for “Preferred Server for Timer Jobs” properties
    11. Click on OK.  Refer Image1 and Image2 for illustration on steps 7 – 10 below.
    12. Go to Monitoring –>Timer Jobs –>Review job definitions.
    13. Find the “SharePoint Foundation Search Refresh” and click on Run Now.
    14. After the job has run successfully, you will start seeing the results in the CMIS Consumer Search.  But wait, your Enterprise Search would have stopped working.
    15. Now you can add your site or Web Application URL to the Content Source for your Search Service Application.
    16. Do a full crawl of the new added Content Source.
    17. So, now we have made SharePoint crawl my Content Source both in the Foundation Search and Enterprise Search and yes it’s a challenge!

    imageimage

    If you have followed the above steps religiously, both your CMIS Consumer Search will start giving results as well as your code with contains query will start giving results.

    NOTE: In case you are using search query to retrieve data from SharePoint 2010 using CMIS CONTAINS predicate and not getting search results back, the above steps could help too.

    Even after performing the above steps if your code is unable to query SharePoint repository using the CMIS APIs the last thing you need to check is that in the imported project the URL of the web service is not that of FQDN name of server or Alternate Access mapping URL of the site.

    e.g. your Site is hosted as http://Server:Port and the FQDN URL for the site is http://Server.Domain.com:Port and the Alternate Access Mapping URL is http://alternateurl.domain.com.

    Your Service reference and the related configuration files in the code project should have the URL that you had added in the Content Source. i.e. http://Server:Port.

    Hope this help the CMIS enthusiasts to connect to SharePoint!!!

  • How to use CMIS APIs to extract information from SharePoint repositories?

    What’s CMIS Connector for SharePoint 2010?

    This post is a contribution from Chanchal Jain, an engineer with the SharePoint Developer Support team.

    Content Management Interoperability Services (CMIS) connector for Microsoft SharePoint Server 2010 enables SharePoint users to interact with content that is stored in any repository that has implemented the CMIS standard. The connector also makes SharePoint Server 2010 content available to any application that has implemented the CMIS standard. The CMIS connector is available as part of the SharePoint 2010 Administration Toolkit.

    For more details, refer:

    Content Management Interoperability Services (CMIS) connector overview (SharePoint Server 2010)

    Microsoft SharePoint 2010 Administration Toolkit v2.0

    How to use CMIS APIs to extract information from SharePoint repositories?

    Once the CMIS connector is installed on the SharePoint Server, it exposes a set of WCF Services to perform read/write and various operations on the repositories (maps to SharePoint list and libraries).

    The set of mapping is available at Mapping the CMIS data model to SharePoint concepts.

    I used the Discovery Service <http://<Site url>/_vti_bin/CMIS/soap/discoveryService.svc > exposed by the CMIS Connector to read information from the SharePoint list.

    The sample code after adding a Service reference in my project to get information from the Repository (translated to SharePoint List & Document Library) is as:

       1: string RepositoryID = "c4e0c77c-16ad-48d2-8a1e-0747af0d3b08"; //The List ID
       2: string keyword = "Microsoft"; //Keyword to be searched
       3: string filePath = "C:\TempFolder\QueryResponse.xml"; 
       4: DiscoveryServicePortClient DiscoveryClient = null;
       5:  
       6: DiscoveryClient = new 
       7: DiscoveryServicePortClient("BasicHttpBinding_IDiscoveryServicePort");                    
       8: DiscoveryClient.ClientCredentials.Windows.AllowedImpersonationLevel = 
       9: System.Security.Principal.TokenImpersonationLevel.Impersonation;
      10: DiscoveryClient.ClientCredentials.Windows.ClientCredential = 
      11: CredentialCache.DefaultNetworkCredentials;
      12: DiscoveryClient.ClientCredentials.Windows.AllowNtlm = true;
      13: DiscoveryClient.Open();
      14: cmisExtensionType DisCMISExtType = new cmisExtensionType();
      15: string query = "SELECT cmis:objectId, cmis:name FROM  cmis:document where CONTAINS('" + keyword+ "') ";//CMIS Query
      16: cmisObjectListType resultQuery = DiscoveryClient.query(RepositoryID, query,
      17: true, true, enumIncludeRelationships.none, "", "255", "0", DisCMISExtType, null);
      18: Stream streamWrite = File.Create(filePath);
      19: XmlSerializer soapWrite = new XmlSerializer(typeof(cmisObjectListType));
      20: soapWrite.Serialize(streamWrite, resultQuery);
      21: streamWrite.Close();