Monday, July 9, 2012

Sharepoint Online (o365 ) - Architectural Considerations


Sharepoint Online :   It is available at all times, It is constantly upgraded and it saves you loads on hardware and operational costs - isn't this what the cloud is all about? Then why is there a hesitation when we talk about the cloud to the clients and our trusted partners? Security of data, limited feature set, cost of migration of the existing environment, adoption, governance..?

Perhaps the greatest threat to adoption of Office 365 is not competitors, but rather reluctance of organizations to move data and applications from on-premise servers and clients to Microsoft’s cloud. Well... with all the tension free life of no-maintenance of the hardware and software comes a tradeoff of the rich feature set which is slightly compromised in the online version and the security of your hosted data concerns.

I spend a lot of time over the net searching for a central location to get the feature set comparison and was lost in the results. Finally, I decided to have all the information collated at one glance to help me in my understanding. The primary ones which shape the architecture of your o365 SharePoint online portal are mentioned here with details on the others tabled below. Hope this helps you as well.

Microsoft decided to exclude certain functionality from SharePoint Online at the time of launch in June 2011. In short, the most important capabilities missing in SharePoint Online are listed below which is very imnportant for architects like me to design a new portal or migrate the existing :


S. No. Features Not Supported
1 FAST Search 
2 Advanced Search Configuration 
3 Record Center - Last I heard this was being offered in the Dedicated online version.
4 Word Automation Services 
5 Business Intelligence Center 
6 Secure Store Service 
7 Web Analytics 
8 Mail Enabled Document Libraries are not supported, impacting Scanners & Scanner software
9 Locally installing custom software - Any third party agent cannot be run on the server.
10 Advanced public facing web sites 
11 Vanity URLs are not supported for intranet zone sites. They can only be applied to your SharePoint Online Public Website.
12 BCS within SharePoint Online does not support a direct connection to SQL Azure. A WCF endpoint is required.
13 Access reporting is not supported by SharePoint Online Access Services because SQL Server Reporting Services (SSRS) is not currently enabled for SharePoint Online. Access reporting requires SSRS.
14 SharePoint Online does not support auditing capabilities to log the opening and downloading of files in a document library.
15 SharePoint Online does not support PerformancePoint Services.
16 Editing the Public Website with SharePoint Designer is not supported.

Feature SharePoint 2010 Server SharePoint Online 
Developer Dashboard Yes No
SharePoint Timer Jobs Yes No
Business Data Integration with the Office Client Yes No
Business Connectivity Services Profile Page Yes No
Audit Opening and Downloading Yes No
Records Center Yes No
Word Automation Services Yes No
Business Intelligence Center Yes No
Chart Web Parts Yes No
Data Connection Library Yes No
PerformancePoint™ Services Yes No
Dashboards Yes No
Decomposition Tree Yes No
Excel Services and PowerPivot for SharePoint Yes No
Advanced Content Processing Yes No
Tunable Relevance with Multiple Rank Profiles Yes No
Business Intelligence Indexing Connector Yes No
SharePoint 2010 Search Connector Framework Yes No
Contextual Search Yes No
Deep Refinement Yes No
Federated Search Yes No
Query Suggestions, "Did You Mean?” and Related Queries Yes No
Relevancy Tuning Yes No
Rich Web Indexing Yes No
Enterprise Scale Search Yes No
Windows 7 Search Yes No
Similar Results Yes No
Thumbnails and Previews Yes No
Visual Best Bets Yes No
Advanced Sorting Yes No
Web Analytics Yes No


Feature SharePoint 2010 Server SharePoint Online 
Office Web Apps integration Yes – separate installation Yes – comes preconfigured


Feature SharePoint 2010 Server SharePoint Online 
Secure Store Service Yes Yes – Tenant Level
Public Website No Yes (one per tenant)

Feature SharePoint 2010 Server SharePoint Online 
Ask Me About Yes Yes
Blogs Yes Yes
Colleague Suggestions Yes Yes
Colleagues Network Yes Yes
Discussions Yes Yes
Enterprise Wikis Yes Yes
Keyword Suggestions Yes Yes
Memberships Yes Yes
My Site: My Content Yes Yes
My Site: My Newsfeed Yes Yes
My Site: My Profile Yes Yes
Note Board Yes Yes
Organization Browser Yes Yes
Photos and Presence Yes Yes
Ratings Yes Yes
Recent Activities Yes Yes
Status Updates Yes Yes
Tag Clouds Yes Yes
Tag Profiles Yes Yes
Tags Yes Yes
Tags and Notes Tool Yes Yes
Wikis Yes Yes
Client Object Model (OM) Yes Yes
Event Receivers Yes Yes
Language Integrated Query (LINQ) for SharePoint Yes Yes
Solution Packages Yes Yes
REST and ATOM Data Feeds Yes Yes
Ribbon and Dialog Framework Yes Yes
Silverlight Web Part Yes Yes
Workflow Models Yes Yes
Access Services Yes Yes
Browser-Based Customizations Yes Yes
Business Data Connectivity Service Yes Yes
External Data Column Yes Yes
Business Data Web Parts Yes Yes
External Lists Yes Yes
SharePoint Designer 2010 Yes Yes
Forms: Out-of-Box workflows and customization through SharePoint Designer 2010 Yes Yes
InfoPath Forms Services Yes Yes
Sandboxed Solutions Yes Yes
Workflow Yes Yes
Microsoft Visual Studio® 2010 SharePoint Developer Tools Yes Yes
Windows 7 Support Yes Yes
Workflow Templates Yes Yes
SharePoint Service Architecture Yes Yes
In-Place Legal Holds Yes Yes
Document Sets Yes Yes
Metadata-driven Navigation Yes Yes
Multi-Stage Disposition Yes Yes
Rich Media Management Yes Yes
Shared Content Types Yes Yes
Support for Accessibility Standards Yes Yes
Content Organizer Yes Yes
Unique Document IDs Yes Yes
Managed Metadata Service Yes Yes
Excel Services Yes Yes
Visio Services Yes Yes
Calculated KPIs Yes Yes
Best Bets Yes Yes
Extensible Search Scale Yes Yes
Duplicate Detection Yes Yes
Metadata-Driven Refinement Yes Yes
Mobile Search Experience Yes Yes
People and Expertise Search Yes Yes
Phonetics and Nickname Search Yes Yes
Recently Authored Content Yes Yes
Search Scopes Yes Yes
Single Site Collection Search No Yes
Site Search Yes Yes
Click-Through Relevancy Yes Yes
View in Browser Yes Yes
Basic Sorting Yes Yes
SharePoint Lists Yes Yes
Web Parts Yes Yes
Improved Governance Yes Yes
Large List Scalability and Management Yes Yes
Multi-Lingual User Interface (MUI) Yes Yes
Permissions Management Yes Yes
Quota Templates Yes Yes
Connections to Microsoft Office Clients Yes Yes
Audience Targeting Yes Yes
Cross-Browser Support Yes Yes
External sharing No Yes
SharePoint Ribbon Yes Yes
Mobile Connectivity Yes Yes
SharePoint Workspace 2010 Yes Yes
Out-of-the-Box Web Parts Yes Yes
Scalability Yes Yes
Templates Yes Yes
Accessibility Yes Yes
Configuration Wizards Yes Yes


An exact list of all capabilities missing from the SharePoint online offering is listed in the current Service Description. Over the upcoming service releases, scheduled every 90 days, Microsoft will begin introduce some of the aforementioned features and I am hopeful that the rich feature set is completely available in the online version as well.



Security of Data : Some countries and organizations have legal limitations on where their data can be hosted, this primarily is applicable for some sensitive data which is of a specific category. Microsoft’s Data Centers are located throughout the world and this might be a legal issue if you chose to go this route.  If your organization’s data cannot leave your region or continent, then Office 365 is not the solution for you currently.

Reference :
MSFT site
SharePoint Online Architectural Considerations

Thursday, June 28, 2012

Sharepoint Reports : Group By View


Creating a group by view is perhaps one of the most powerful tools in Sharepoint. Out of the Box, Sharepoint provides you with a great reporting feature which can help you get data presented in a very logical and "report like" fashion.

The steps below will describe the process :

Create a column in the document library which maps to your folder name (e.g. Category)


Upload document(s) to this library, add category info   




On the ribbon > Go to Library Tab and select Create View




Choose a base Existing view: All items



Choose public  view and Give your view a name: say “Group By” and choose the fields to be displayed on your view



Go to the Group by section to select the Category as your Group By parameter




You can access this view from the breadcrumbs as shown below



To add this view on any page > go to Edit page




In the web part settings:




Friday, June 8, 2012

Data Migration to the cloud!

Data Migration from a third party application to Sharepoint is often a requirement and I had this challenge on the Sharepoint online environment, where we do not have access to the Central Admin or the server itself.

Where there is a will there is a way! We had a challenging situation and we figured out a workaround to do the needful...

Since it was Lotus application data- we needed a means to get the data in a Microsoft format so that we could read it ( any third party connections are not supported in the cloud yet), Lotus connectors are not supported.

Solution : get all the data ported to an excel format which can be read from Sharepoint environment!
The data includes attachments as well - so design your excel in such a manner that the last 2 columns have the below information:


  1. Number of attachments
  2. Path and Name of attachments in the format ( c://attachments/recordId/test1.docx)

This uses the sharepoint client object model to upload the data and since the attachments behaves a little sneaky I used the web services to upload the attachments once the record was created!
There are enough comments in the code to explain the logic but in case you have any questions please let me know!

This console application needs to run in the same domain for picking up Admin credentials of the logged ion user for the upload process and takes a config file which details the parameters like, site, list name etc. Add a web Reference to the
http://localhost/_vti_bin/Lists.asmx and call it "SharepointListWS"

Here is the sample excel format : The second hidden row of the excel is the sharepoint column names from the List where the data is uploaded to.

Here is the code snippet that I created to import data from Lotus apps ( in a Excel format) to SPO world.

   
  
  
  private static void importFromExcel(StreamWriter streamwriterW)
  {
  
  try
  
 {
  
 Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.ApplicationClass();
  
 string workbookPath = _ExcelPath;
  
 Microsoft.Office.Interop.Excel.Workbook excelWorkbook = excelApp.Workbooks.Open(workbookPath, 0, false, 5,
  
 "", "", false, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "", true, false, 0, true);
  
 Microsoft.Office.Interop.Excel.Sheets excelSheets = excelWorkbook.Worksheets;
  
 Microsoft.Office.Interop.Excel.Worksheet excelSheet = (Microsoft.Office.Interop.Excel.Worksheet)excelSheets.get_Item("Sheet1");
  
 object[,] values = excelSheet.get_Range(_ExcelRange).Value as object[,];
  
 ClientContext context = new ClientContext(_SharePointSiteUrl);
  
 context.AuthenticationMode = ClientAuthenticationMode.Default;
  
 Web web = context.Web;
  
 List list = web.Lists.GetByTitle(_ListName);
  
 for (int row = 3; row <= Convert.ToInt32(_ExcelRowNo); row++) // no. of rows of data in excel
  
 {
  
  ListItemCreationInformation listItemCreation = new ListItemCreationInformation();
  
 ListItem listItem = list.AddItem(listItemCreation);
  
 iRecord++;
  
 for (int column = 1; column <= Convert.ToInt32(_iColumnsToImport) + 1; column++) // no of fields/columns in excel to be imported
  
 {
  
 string columnName = values[2, column].ToString();
  
 strcolumnName = columnName;
  
 if (values[row, column] != null)
  
 {
  
  if (columnName == "AttachmentNo")
  
 {
  
 _strNoOfAttachments = values[row, column].ToString();
  
 strcolumnValue = _strNoOfAttachments;
  
 }
  
  else
  
 {
  
 listItem[columnName] = values[row, column];
  
 strcolumnValue = listItem[columnName].ToString(); 
  
 }
  
 }
  
 }
  
 listItem.Update();
  
 context.ExecuteQuery();
  
 string strListItemID = listItem.Id.ToString();
  
 _NoOfRowsImported++;
  
 
 logToFile("Record imported from Excel Row No :" + (iRecord + 2) + " to SP Record Id : " + strListItemID, streamwriterW);
  
 logToFile("No. of Attachments : " + _strNoOfAttachments, streamwriterW);
  
   if (_strNoOfAttachments != "0" && _strNoOfAttachments != null)
  
 {
  
 addAttachment(strListItemID, values[row, Convert.ToInt32(_iColumnsToImport) + 2].ToString(), streamwriterW);// if no. of attachment >0 
  
 }
  
 }
  
 excelWorkbook.Close();
  
 }
  
  catch (Exception e)
  
 {
  
 // log error here 
  
 }
  
 }
  
  private static void addAttachment(string strNewListItemID, string strAttachmentPathUnparsed, StreamWriter streamwriterW)
  
 {
  
  string attachmentUrl = string.Empty;
  
 try
  
 {
  
 //Parse path if delimiter exists 
  
 string[] splitAttachmentPath = strAttachmentPathUnparsed.Split(new Char[] { '|' });
  
 foreach (string strAttachmentPathParsed in splitAttachmentPath)
  
 {
  
  if (strAttachmentPathParsed.Trim() != "")
  
 {
  
 strAttachmentPath = strAttachmentPathParsed;
  
  if (!System.IO.File.Exists(strAttachmentPathParsed))
  
 {
  
 // log error

   return;
  
 }
  
  string fileName = Path.GetFileName(strAttachmentPathParsed);
  
 // Get file contents
  
 Byte[] contents = getFileStream(strAttachmentPathParsed);
  
 // Add the attachment to the new item in the SharePoint list & Instantiate a SharePoint
  
 // List Web Service object
  
 listWs = new SharePointListWS.Lists();
  
 // set the Web Service Credentials
  
 listWs.Credentials = System.Net.CredentialCache.DefaultCredentials;
  
 listWs.Url = _SharePointSiteUrl + "/_vti_bin/lists.asmx";
  
 attachmentUrl = listWs.AddAttachment(_ListName, strNewListItemID, fileName, contents);
  
 }
  
 }
  
    
 }
  
  catch (Exception ex3)
  
 {
  
 //log error  
 }
  
  if (!String.IsNullOrEmpty(attachmentUrl))
  
 {
  
     // log error

 }
  
 }
  
 }
  
 }
  

Thursday, May 31, 2012

Custom RSS feed Display


Really Simple Syndication (RSS) is a widely used technology for transmitting information across the Internet and intranets. Many Web sites offer RSS Feeds you can subscribe to so you can get the latest information from the source automatically. You can also subscribe to RSS Feeds from SharePoint libraries, lists, and other elements, so you can see when someone adds a document or changes a list item.

You can use a RSS Viewer Web Part to display an RSS Feed on a SharePoint site. One can also keep track of changes in libraries, calendars, and lists on other SharePoint sites. Sharepoint 2010 has provided with a great OOB web part to display the RSS feed and one can modify it for stying and customize it per requirements. more information on how to set this up can be found at  here

I had a requirement to put the RSS feed as an output in a webpart and associate it to the master page.

To get around the credentials I had to use the class XmlUrlResolver

Here is a sample of a function which uses the class SyndicationFeed   and reads the RSS via XML reader, to get the active items only.
   protected void ReadRSS(string ListRssUrl)
        {
            try
            {
                SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    XmlReaderSettings settings = new XmlReaderSettings();

                    // Create a resolver with default credentials.
                    XmlUrlResolver resolver = new XmlUrlResolver();
                    resolver.Credentials = 

                System.Net.CredentialCache.DefaultCredentials;

                    // Set the reader settings object to use the resolver.
                    settings.XmlResolver = resolver;


                    // Create the XmlReader object.
                    XmlReader reader = XmlReader.Create(ListRssUrl, settings);

                    var feed = SyndicationFeed.Load(reader);

                    string strCategory = string.Empty;
                    StringBuilder sb = new StringBuilder();
                    if (feed == null)
                    {
                        sb.Append("No Feeds in RSS");
                        Div1.InnerHtml = sb.ToString();

                    }
                    else
                    {

                       
                        foreach (SyndicationItem item in feed.Items)
                        {
                            var category = item.Categories;
                            foreach (var cat in category)
                            {
                                // get the active category items only...
                                if (cat.Name == "Active")
                                {

                                    foreach (var link in item.Links)
                                    {
                                      RenderRSS(item.Title.Text,link.Uri.ToString(),sb);

                                    }
                                }
                            }

                        }
                        Div1.InnerHtml = sb.ToString();
                    }


                });
            }
            catch (Exception e)
            {
                //exception handling
            }
        }
  private void RenderRSS(string strRSSTitle, string strRSSLink, StringBuilder sb)
        {
            sb.Append("<li>"); // Begin  Li for each RSSFeedURL feed
            sb.Append("<a href="+"'"+strRSSLink+ "'" + ">"+strRSSTitle+"</a>"); 
            sb.Append("</li>"); //End Li for each RSSFeedURL feed
        }

Monday, April 30, 2012

Helpers in Sharepoint Code :

Last week I managed to secure decent score in getting my MCTS Sharepoint configuration exam.
Being a hectic week ( I had a >100% utilization as well at work), I wanted to share some good code snippets with everyone which helps us get things done faster in Sharepoint ...I am panning to share with everyone my preperation guide and my strategy to get going in a week's timeframe if someone's interested in getting certified!

Now..coming back to the blog...
Helper functions to the rescue: Often as developers we use helper functions, I have planned to use this space to collate them here to get all of them at one Central location!

  • Get Custom User profile properties
Often we create custom properties in Active directory which need to be accessible via code. In my most recent project we had such a scenario and a generic helper function below helped us in code reuse

accountName can be the account of the current logged in user  or any user and property is the string which we need to find.
   // this function fetches a property of a user
        public static string GetUserProperty(SPSite site, string accountName, string property)
        {
            try
            {
                string userProperty = string.Empty;
                SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    SPServiceContext context = SPServiceContext.GetContext(site);
                    var UserProfMgr = new UserProfileManager(context);
                    UserProfile UserProf = UserProfMgr.GetUserProfile(accountName);

                    if (UserProf[property].Value != null)
                    {
                        userProperty = UserProf[property].Value.ToString();
                    }
                });
                return userProperty;
            }

            catch (Exception ex)
            {
                throw new Exception("Could not get the property:" + property + "of the User:" + accountName);
            }

        }
    }
  • Trace Log an Exception
Often for the development team it is a challenge to debug issues in share point. Catching and Throwing proper exceptions is the Best way to troubleshoot issues. the one class i have started using recently is below :

    Define an enum for the severity :

   public enum LogSeverity
    {
        Unexpected,
        High,
        Medium,
        Info,
        Monitorable,
        Verbose,
        VerboseEx,
        None
    }


Class file :
 public static class TraceLog
    {
        private static DiagnosticsService myULS = null;

        public static void Log(string msg, LogSeverity severity)
        {
            if (myULS == null)
            {
                myULS = DiagnosticsService.Local;
            }

            if (myULS != null)
            {
                TraceSeverity tSeverity = mapTraceSeverity(severity);
                SPDiagnosticsCategory cat = myULS[CategoryId.General];

                myULS.WriteTrace(5555, cat, tSeverity, msg, myULS.TypeName);
            }
        }

        public enum CategoryId
        {
            None = 0,
            Deployment = 100,
            Provisioning = 200,
            CustomAction = 300,
            Rendering = 400,
            WebPart = 500,
            General = 600
        }

        [System.Runtime.InteropServices.GuidAttribute("DBEWW5AB-C5A7-46B5-A2BB-5581F960C333")]
        class DiagnosticsService : SPDiagnosticsServiceBase
        {
            private static string DiagnosticsAreaName = "Project Name";

            public DiagnosticsService()
            {
            }

            public DiagnosticsService(string name, SPFarm farm)
                : base(name, farm)
            {
            }

            protected override IEnumerable<SPDiagnosticsArea> ProvideAreas()
            {
                List<SPDiagnosticsCategory> categories = new List<SPDiagnosticsCategory>();
                foreach (string catName in Enum.GetNames(typeof(CategoryId)))
                {
                    uint catId = (uint)(int)Enum.Parse(typeof(CategoryId), catName);
                    categories.Add(new SPDiagnosticsCategory(catName, TraceSeverity.Verbose, EventSeverity.Error, 0, catId));
                }

                yield return new SPDiagnosticsArea(DiagnosticsAreaName, categories);
            }

            public static DiagnosticsService Local
            {
                get
                {
                    return SPDiagnosticsServiceBase.GetLocal<DiagnosticsService>();
                }
            }

            public SPDiagnosticsCategory this[CategoryId id]
            {
                get
                {
                    return Areas[DiagnosticsAreaName].Categories[id.ToString()];
                }
            }
        }

        private static TraceSeverity mapTraceSeverity(LogSeverity severity)
        {
            TraceSeverity tr = TraceSeverity.None;
            switch (severity)
            {
                case LogSeverity.Unexpected:
                    tr = TraceSeverity.Unexpected;
                    break;
                case LogSeverity.High:
                    tr = TraceSeverity.High;
                    break;
                case LogSeverity.Monitorable:
                    tr = TraceSeverity.Monitorable;
                    break;
                case LogSeverity.Medium:
                    tr = TraceSeverity.Medium;
                    break;
                case LogSeverity.Info:
                case LogSeverity.Verbose:
                    tr = TraceSeverity.Verbose;
                    break;
                case LogSeverity.VerboseEx:
                    tr = TraceSeverity.VerboseEx;
                    break;
                case LogSeverity.None:
                    tr = TraceSeverity.None;
                    break;
            }
            return tr;
        }
    }

A simple call in catch will, serve the purpose

catch (Exception ex)
            {
                throw new Exception("Exception Occurred, Details :" + " + ex.message.tostring());
            }