3

I have had this issue for a while, and been asking piece-meal questions for a few weeks. I finally created a full solution that worked. And wanted to provide the full solution, that was both EFFICIENT on the server and thorough. Many solutions on the internet did not work.

So, the question. How can I get All sites a User has access to across a web application, without bogging down the server?

This solution requires a mix of client and server side code, in order to provide that efficiency.

2

3 Answers 3

3
<button id='btnGetSites'>Get Sites</button>

<div id='list'>

</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" type="text/javascript"></script>
<script type='text/javascript'>


(function(){
    var siteUrl = _spPageContextInfo.siteAbsoluteUrl;
    var url = siteUrl + "/_api/search/query?querytext='(contentclass:STS_Site OR contentclass:STS_Web)'";
    $('#btnGetSites').click(function(){
        console.log('start..');

        $.ajax({
            method: 'GET',
            url: url,
            dataType: 'json',
            success: success,
            error: error
        });

        function success(data){
            var html = '';

            $.each(data.PrimaryQueryResult.RelevantResults.Table.Rows, function(index, row){                
                var title = '';
                var url = '';
                $.each(row.Cells, function(i, obj){             
                    if(obj.Key === 'Title'){
                        html += obj.Value; 
                    }
                    else if(obj.Key === 'Path'){
                        html += ": " + obj.Value + "<br/>";
                    }               
                });         
            });

            $('#list').append(html);
        }
        function error(err){
            console.log(err);
        }       
        return false;
    });

}());

</script>

You can build the html as needed...

2
  • So now you are loading every single subsite on page load at the same time. You also have no way of registering a hierarchy... this is not what we were looking for. Commented Jan 7, 2016 at 14:36
  • 1
    What do you mean by loading every single subsite? I am using the search REST API to return all the search results(at once) and then looping through the results to build the html. Each search result has a 'ParentLink' property(just like 'Title' and 'Path') which can be used to register the hierarchy. Commented Jan 7, 2016 at 14:55
2

Here is the final product. Subsites are only loaded when a user clicks on the '+'

Image of final product

ENJOY!

In Visual Studio create a Farm Solution. Make it a Visual Web Part.

Create a Class titled. SitesList. add these getters and setters.

class SitesList
{
    public int SiteCollid { get; set; } /// a counter to give the css class for uniqueness
    public string SiteUrl { get; set; }
    public bool isRoot { get; set; }
    public string Title { get; set; }

}

Add this block of code to override the CreateClientControls, within the VisualWebPart.ascx.cs. I had many issues with calling Methods I created within the CreateChildControls. So, this code is a bit of a large block. The purpose of this code is to get all Top level sites a user has access to, it will build the controls. Then we will have sp.js get each subsite when a user wants to expand the SiteCollection.

    protected override void CreateChildControls()
    {
        base.CreateChildControls();
        List<SitesList> list = new List<SitesList>();
        Guid app1Id = new Guid();

        int counter = 0;
        string userName = Microsoft.SharePoint.SPContext.Current.Web.CurrentUser.LoginName;

        using (SPSite newsite = new SPSite("https://DomainWebApp.XX.com")) /// Feel free to use current context we know that the webapp will never change
        {  app1Id = newsite.WebApplication.Id;  }

        SPSecurity.RunWithElevatedPrivileges(delegate()
        {
            SPServiceCollection services = SPFarm.Local.Services;

            foreach (SPService eachService in services)
            {
                if (eachService is SPWebService)
                {
                    SPWebService webService = (SPWebService)eachService;

                    foreach (SPWebApplication webApp in webService.WebApplications)
                    {
                        if (webApp.Id == app1Id)
                        {
                            SPSiteCollection siteColl = webApp.Sites;

                            foreach (SPSite site in siteColl)
                            {
                                SPBasePermissions permissionToCheck = SPBasePermissions.ViewPages;
                                using (SPSite spSite = new SPSite(site.RootWeb.Url))
                                {
                                    try
                                    {
                                        using (SPWeb indiWeb = spSite.OpenWeb(spSite.RootWeb.ServerRelativeUrl))
                                        {


                                            if (indiWeb.DoesUserHavePermissions(userName, permissionToCheck))
                                            {
                                                list.Add(new SitesList()
                                                {
                                                    SiteCollid = counter,
                                                    SiteUrl = indiWeb.Url,
                                                    Title = indiWeb.Title,
                                                    isRoot = true
                                                });

                                                }

                                            }
                                        }

                                    catch (Exception ex)
                                    {
                                        ///Feel free to add your own exception throws.
                                        throw;
                                    }
                                    finally
                                    {

                                        counter++;

                                    }
                                }
                            }
                        }
                    }
                }
            }
        });

        list.Sort((x, y) => (x.Title).CompareTo(y.Title)); //inplace sort

            foreach (SitesList s in list)
            {
                HyperLink hyp1 = new HyperLink();
                hyp1.CssClass = "AccessSitesLinks " + s.isRoot.ToString() + " " + s.SiteCollid.ToString();
                hyp1.Text = s.Title;
                hyp1.NavigateUrl = s.SiteUrl;
                this.Controls.Add(hyp1);
            }

        }

Ensure these using statements.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web.UI.WebControls.WebParts;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using System.Web.UI.WebControls;

using System.Linq;

Create These folders CSS JS in the Style Library of the sites that will contain the WebPart.

Create this CSS File and place it in the CSS Folder

    .ContainingBox2{
      padding: 2px 15%;
    }

    .AccessSitesLinks.True {
      padding: 2px 2%;
      font-size: 1.2em;
      width: 40%;
    }


    .AccessSitesLinks {
      text-decoration: none !Important;
      font-family: "Segoe UI","Segoe",Tahoma,Helvetica,Arial,sans-serif;
      color: #444;
      font-size: 1em;
      width: 30%;
      margin: 2px;
    }

    .SubSitesAccess {
      padding: 2px 2%;
      font-size: 1.2em;
      width: 40%;
      text-decoration: none !Important;
      font-family: "Segoe UI","Segoe",Tahoma,Helvetica,Arial,sans-serif;
      color: #444;
      font-size: 1.2em;
      width: 40%;
      margin: 2px;
    }

Create this Javascript file. and place it in the JS folder.

    $(document).ready(function(){ 
    var webCollection = null;
            SP.SOD.executeFunc('sp.js', 'SP.ClientContext', buildRootTree);
    });

    function buildRootTree(){
        var urlString = "";

        var siteRoot = $('.AccessSitesLinks.True');
        var arr = siteRoot.map(function(_, o) {
            return {
                t: $(o).text(),
                h: $(o).attr('href'),
                c: $(o).attr('class')
            };
          }).get();

        siteRoot.each(function(i, o) {
            console.log(arr[i].c);

        $(o).text('+');
        $(o).wrap("<div class='ContainingBox "+ arr[i].c + "'></div>");
        $(o).parent().append("<a href='" + arr[i].h + "' class='" + arr[i].c + "'>" + arr[i].t + "</a>")
        $(o).parent().data('urlString', $(this).attr('href'));
        $(o).removeAttr('href');
        });

        $('.ContainingBox').click(function() {
            if(!$(this).attr('boolcheck')) {


                $(this).attr('boolcheck', 'Exists');
                buildNode($(this).data('urlString'), $(this));
            }
            else{

            $('.ContainingBox2', this).toggle();
           }
        });
    }

    function buildNode(urlString, domElement){
    console.log(urlString);
        var clientContext = new SP.ClientContext(urlString);
        console.log("clientContext");
        var web = clientContext.get_web();
        console.log("get_web");
        this.webCollection = web.getSubwebsForCurrentUser(null);
        console.log("bout to load");
        clientContext.load(this.webCollection);
        clientContext.executeQueryAsync(Function.createDelegate(this, function(){ onQuerySucceeded(domElement)}), Function.createDelegate(this, this.onQueryFailed));
        }

    function onQuerySucceeded(domElement) {

        var webEnumerator = this.webCollection.getEnumerator();
        while (webEnumerator.moveNext()){
        var web = webEnumerator.get_current();
        console.log(web.get_title());
        console.log(domElement);
        domElement.append("<a href='" + web.get_url() + "' class='SubSitesAccess'" + ">" +  web.get_title() + "</a>");
        }
        domElement.children('.SubSitesAccess').wrap("<div class='ContainingBox2'></div>");
    }

    function onQueryFailed(sender, args) {

    }

Create a Script editor on the page where you have put your webparts and edit the snipet to include. Make sure the file you name matches the one in the code below. We used "MyFavorites".

    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script>
    SP.SOD.executeOrDelayUntilScriptLoaded(LoadMyScript, 'sp.js');

    function LoadMyScript() {
       var currWebUrl = _spPageContextInfo.webServerRelativeUrl; // use siteServerRelativeUrl for site-collection 
       //Dynamic loading of scripts
       var jq = document.createElement('script');
       jq.src = currWebUrl + "/Style%20Library/JS/MyFavorites.js"; ///Be sure to use the name of your JS file you created
       var css = document.createElement('link');
       css.href = currWebUrl + "/Style%20Library/CSS/MyFavorites.css"; ///Be sure to use the name of your CSS file you created
       css.type = "text/css";
       css.rel = "stylesheet";
       document.getElementsByTagName('head')[0].appendChild(jq);
       document.getElementsByTagName('head')[0].appendChild(css);
    }
    </script>
1
  • 3
    Seems way too much effort for a simple requirement...
    – Akhoy
    Commented Jan 6, 2016 at 20:04
1

For those who just want a full list of sites the user has access to, find the below rest code using

/_api/search/query?querytext='(contentclass:STS_Web)'&rowlimit=500&selectproperties=%27Url,Title%27

Read more here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.