SharePoint security trimmed site list

Okay, so it has been a bit since I have posted anything, mainly due to that I have not anything notable to post.  This is not due to me not doing anything notable but that the things that I have done that were notable were proprietary and I did not feel comfortable disclosing in a public forum. 

So the problem that I was given was that when people landed on the root site within the root site collection of our managed path / application the user hit a page that was essentially blank.  From there they did not know where they should go and did not really know what they would be able to access.  So here comes a webpart that can help solve the problem.  What the goal of this webpart was is to create a security trimmed list of sites the user would have access at the root level.  I am not going to go through each and every step for creating a webpart but I will give some high level info since these are steps that I struggled with when creating the webpart.

 

  1. So first off, when you are creating this webpart select the "Visual Web Part option" in the project type.
  2. You MUST select DEPLOY AS FARM SOLUTION.  The sandboxed solution will not have access to the necessary objects needed for this webpart.  Specifically getting all site collections in a web application (managed path).
  3. To start we will create two classes.  SiteLookup and SPSiteInfo. 

    SiteLookup

    1. using Microsoft.SharePoint;
    2. using System;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5. using System.Text;
    6. using System.Threading.Tasks;
    7.  
    8. namespace SPSiteListing.ListSPSites
    9. {
    10. class SiteLookup
    11. {
    12. private Boolean _EnableTrimming;
    13. private SPContext _Context;
    14. private string _CurrentUserName;
    15. public SiteLookup(SPContext context, Boolean enablePermissionTrimming)
    16. {
    17. _EnableTrimming = enablePermissionTrimming;
    18. _Context = context;
    19. _CurrentUserName = context.Web.CurrentUser.LoginName;
    20. }
    21. public List<SPSiteInfo> GetSites()
    22. {
    23. if (IsRootInApplication())
    24. {
    25. var list = new List<SPSiteInfo>();
    26. list.AddRange(GetSitesUnderCurrentWeb());
    27. list.AddRange(GetSitesUnderManagedPath());
    28. return list;
    29. }
    30. else
    31. return GetSitesUnderCurrentWeb();
    32. }
    33. public Boolean IsRootInApplication()
    34. {
    35. if (_Context.Site.RootWeb.Url != _Context.Site.Url)
    36. return false;
    37. if (_Context.Site.WebApplication.Sites[0].Url != _Context.Site.Url)
    38. return false;
    39. return true;
    40. }
    41. public List<SPSiteInfo> GetSitesUnderManagedPath()
    42. {
    43. var sites = new List<SPSiteInfo>();
    44. var applicationSites = _Context.Site.WebApplication.Sites;
    45. foreach (SPSite item in applicationSites)
    46. {
    47. //you must set disable catching access exceptions to prevent sharepoint from catching it
    48. item.CatchAccessDeniedException = false;
    49. try
    50. {
    51. if (item.RootWeb.DoesUserHavePermissions(_CurrentUserName, SPBasePermissions.Open) || !_EnableTrimming)
    52. sites.Add(new SPSiteInfo(item));
    53. }
    54. catch (UnauthorizedAccessException)
    55. {
    56. //The user does not have access to check their access. So an exception will be thrown.
    57. //This will not cause a problem to not do anything with it, since we are security trimming
    58. //we do not want this one listed anyway.
    59. }
    60. }
    61. return sites;
    62. }
    63. public List<SPSiteInfo> GetSitesUnderCurrentWeb()
    64. {
    65. var sites = new List<SPSiteInfo>();
    66. if (_EnableTrimming)
    67. {
    68. foreach (SPWeb item in _Context.Web.GetSubwebsForCurrentUser())
    69. {
    70. sites.Add(new SPSiteInfo(item));
    71. }
    72. }
    73. else
    74. {
    75. foreach (SPWeb item in _Context.Site.AllWebs)
    76. {
    77. sites.Add(new SPSiteInfo(item));
    78. }
    79. }
    80. return sites;
    81. }
    82. }
    83. }

    SPSiteInfo

    1. using Microsoft.SharePoint;
    2. using System;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5. using System.Text;
    6. using System.Threading.Tasks;
    7.  
    8. namespace SPSiteListing.ListSPSites
    9. {
    10. class SPSiteInfo
    11. {
    12. public string SiteName { get; private set; }
    13. public string SiteUrl { get; private set; }
    14. public string HTMLLink
    15. {
    16. get
    17. {
    18. return String.Format(@"<a href=""{0}"">{1}</a>",
    19. SiteUrl,
    20. String.IsNullOrEmpty(SiteName) ? SiteUrl : SiteName);
    21. }
    22. }
    23. public SPSiteInfo(SPSite site)
    24. {
    25. try
    26. {
    27. SiteName = site.RootWeb.Title;
    28. }
    29. catch (UnauthorizedAccessException)
    30. {
    31. //since the user does not have access to get the title, we can do something
    32. //here if we want with demonstrating that. but it isn't necessary
    33. }
    34. SiteUrl = site.Url;
    35. }
    36. public SPSiteInfo(SPWeb site)
    37. {
    38. SiteName = site.Name;
    39. SiteUrl = site.Url;
    40. }
    41. }
    42. }
  4. So now that we have the classes written to get the data, we need a something to display the data in the webpart. To do that we will use the ascx file created already (just adding a ul with an id and a runat) and add some code in the page_load method.

    ASCX file

    1. <ul id="siteList" runat="server">
    2.  
    3. </ul>

    ASCX code behind

    1. protected void Page_Load(object sender, EventArgs e)
    2. {
    3. SiteLookup query = new SiteLookup(SPContext.Current, true);
    4. foreach (var item in query.GetSites())
    5. {
    6. var li = new HtmlGenericControl("li");
    7. li.InnerHtml = item.HTMLLink;
    8. siteList.Controls.Add(li);
    9. }
    10. }


So that is basically it. There are some details that are not covered in this post, but this should get you past the things that I struggled with when I created my webpart.


Good Luck !!