Opzioni di spostamento per SharePoint

Questo articolo descrive i siti delle opzioni di spostamento con pubblicazione sharepoint abilitata in SharePoint. La scelta e la configurazione dello spostamento influiscono in modo significativo sulle prestazioni e sulla scalabilità dei siti in SharePoint. Il modello di sito di pubblicazione di SharePoint deve essere usato solo se necessario per un portale centralizzato e la funzionalità di pubblicazione deve essere abilitata solo in siti specifici e solo quando necessario in quanto può influire sulle prestazioni se usata in modo errato.

Nota

Se si usano opzioni di spostamento di SharePoint moderne, ad esempio mega menu, spostamento a cascata o spostamento nell'hub, questo articolo non si applica al sito. Le architetture dei siti di SharePoint moderne sfruttano una gerarchia del sito più appiattita e un modello hub-and-spoke. In questo modo è possibile ottenere molti scenari che NON richiedono l'uso della funzionalità di pubblicazione di SharePoint.

Panoramica delle opzioni di spostamento

La configurazione del provider di navigazione può influire in modo significativo sulle prestazioni per l'intero sito e deve essere presa in considerazione la scelta di un provider di navigazione e di una configurazione che venga ridimensionata in modo efficace per i requisiti di un sito di SharePoint. Sono disponibili due provider di spostamento predefiniti, nonché implementazioni di spostamento personalizzate.

La prima opzione, struttura di spostamento, è l'opzione di spostamento consigliata in SharePoint per i siti di SharePoint classici, se si attiva la memorizzazione nella cache di spostamento strutturale per il sito. Questo provider di spostamento visualizza gli elementi di spostamento sotto il sito corrente e facoltativamente il sito corrente e i relativi elementi di pari livello. Fornisce funzionalità aggiuntive, ad esempio il taglio della sicurezza e l'enumerazione della struttura del sito. Se la memorizzazione nella cache è disabilitata, ciò avrà un impatto negativo sulle prestazioni e sulla scalabilità e potrebbe essere soggetto a limitazione.

La seconda opzione, Managed (Metadata), rappresenta gli elementi di spostamento usando un set di termini metadati gestiti. È consigliabile disabilitare la limitazione della sicurezza, a meno che non sia necessario. La limitazione della sicurezza è abilitata come impostazione sicura per impostazione predefinita per questo provider di navigazione; Tuttavia, molti siti non richiedono il sovraccarico della limitazione della sicurezza perché gli elementi di spostamento sono spesso coerenti per tutti gli utenti del sito. Con la configurazione consigliata per disabilitare la limitazione della sicurezza, questo provider di navigazione non richiede l'enumerazione della struttura del sito ed è altamente scalabile con un impatto accettabile sulle prestazioni.

Oltre ai provider di navigazione predefiniti, molti clienti hanno implementato con successo implementazioni di spostamento personalizzate alternative. Vedere Search scripting sul lato client in questo articolo.

Vantaggi e svantaggi delle opzioni di spostamento di SharePoint

La tabella seguente riepiloga i vantaggi e i svantaggi di ogni opzione.

Esplorazione strutturale Esplorazione gestita Esplorazione basata sulla ricerca Provider di spostamento personalizzato
Pro:

Facile da gestire
Sicurezza troncata
Viene aggiornato automaticamente entro 24 ore dalla modifica del contenuto
Pro:

Facile da gestire
Pro:

Sicurezza troncata
Si aggiorna automaticamente man mano che i siti vengono aggiunti
Tempo di caricamento rapido e struttura dell'esplorazione memorizzata nella cache
Pro:

Più ampia scelta di opzioni disponibili
Caricamento rapido quando la memorizzazione nella cache viene usata correttamente
Molte opzioni funzionano bene con la progettazione di pagine reattive
Contro:

Influisce sulle prestazioni se la memorizzazione nella cache è disabilitata
Soggetto a limitazione
Contro:

Non si aggiorna automaticamente per riflettere la struttura del sito
Influisce sulle prestazioni se la limitazione della sicurezza è abilitata o quando la struttura di navigazione è complessa
Contro:

Non è possibile ordinare facilmente i siti
Richiede la personalizzazione della pagina master (necessarie competenze tecniche)
Contro:

È necessario lo sviluppo personalizzato
È necessaria l'origine dati esterna/la cache archiviata, ad esempio Azure

L'opzione più appropriata per il sito dipende dai requisiti del sito e dalle funzionalità tecniche. Se si vuole un provider di navigazione facile da configurare che si aggiorna automaticamente quando il contenuto viene modificato, la navigazione strutturale con memorizzazione nella cache abilitata è una buona opzione.

Nota

L'applicazione dello stesso principio dei siti di SharePoint moderni semplificando la struttura complessiva del sito in una struttura flat e non gerarchica migliora le prestazioni e semplifica il passaggio ai siti di SharePoint moderni. Ciò significa che invece di avere una singola raccolta siti con centinaia di siti (web secondari), un approccio migliore consiste nell'avere molte raccolte siti con pochissimi siti secondari (web secondari).

Analisi delle prestazioni di navigazione in SharePoint

Lo strumento Diagnostica pagine per SharePoint è un'estensione del browser per i browser Microsoft Edge e Chrome che analizza sia il portale moderno di SharePoint che le pagine classiche del sito di pubblicazione. Questo strumento funziona solo per SharePoint e non può essere usato in una pagina di sistema di SharePoint.

Lo strumento genera un report per ogni pagina analizzata che mostra le prestazioni della pagina rispetto a un set predefinito di regole e visualizza informazioni dettagliate quando i risultati di un test non rientrano nel valore di base. Gli amministratori e i progettisti di SharePoint possono usare lo strumento per risolvere i problemi di prestazioni per garantire che le nuove pagine siano ottimizzate prima della pubblicazione.

SPRequestDuration in particolare è il tempo necessario per l'elaborazione della pagina da parte di SharePoint. La navigazione pesante(ad esempio, le pagine nello spostamento), le gerarchie di siti complesse e altre opzioni di configurazione e topologia possono contribuire notevolmente a durare più a lungo.

Uso della struttura di spostamento in SharePoint

Questa è la struttura di spostamento predefinita usata per impostazione predefinita ed è la soluzione più semplice. Non richiede alcuna personalizzazione e un utente nontecnico può anche aggiungere facilmente elementi, nascondere elementi e gestire la navigazione dalla pagina delle impostazioni. È consigliabile abilitare la memorizzazione nella cache, in caso contrario si verifica un compromesso costoso sulle prestazioni.

Come implementare la memorizzazione nella cache di spostamento strutturale

In Site SettingsLook and Feel>Navigation (Esplorazione > aspetto impostazioni sito) è possibile verificare se è selezionata la struttura di spostamento strutturale per lo spostamento globale o corrente. La selezione di Mostra pagine avrà un impatto negativo sulle prestazioni.

Spostamento strutturale con l'opzione Mostra siti secondari selezionata.

La memorizzazione nella cache può essere abilitata o disabilitata a livello di raccolta siti e a livello di sito ed è abilitata per entrambi per impostazione predefinita. Per abilitare a livello di raccolta siti, in Impostazioni> sito SiteCollection Administration>Site Collection Navigation (Spostamento raccolta siti amministrazione raccolta siti) selezionare la casella Abilita memorizzazione nella cache.

Abilitare la memorizzazione nella cache a livello di raccolta siti.

Per abilitare a livello di sito, in Spostamento impostazioni> sito selezionare la casella Abilita memorizzazione nella cache.

Abilitare la memorizzazione nella cache a livello di sito.

Uso di spostamento gestito e metadati in SharePoint

Lo spostamento gestito è un'altra opzione predefinita che è possibile usare per ricreare la maggior parte delle stesse funzionalità dello spostamento strutturale. I metadati gestiti possono essere configurati in modo che la limitazione della sicurezza sia abilitata o disabilitata. Se configurato con la limitazione della sicurezza disabilitata, lo spostamento gestito è abbastanza efficiente perché carica tutti i collegamenti di spostamento con un numero costante di chiamate al server. L'abilitazione della limitazione della sicurezza, tuttavia, nega alcuni dei vantaggi in fatto di prestazioni dello spostamento gestito.

Se è necessario abilitare il taglio della sicurezza, è consigliabile:

  • Aggiornare tutti i collegamenti URL descrittivi a collegamenti semplici
  • Aggiungere i nodi di taglio di sicurezza necessari come URL descrittivi
  • Limitare il numero di elementi di spostamento a non più di 100 e non più di tre livelli di profondità

Molti siti non richiedono un taglio di sicurezza, poiché la struttura di spostamento è spesso coerente per tutti gli utenti del sito. Se la limitazione della sicurezza è disabilitata e viene aggiunto un collegamento alla struttura di spostamento a cui non tutti gli utenti hanno accesso, il collegamento verrà comunque visualizzato, ma genererà un messaggio di accesso negato. Non c'è alcun rischio di accesso involontaria al contenuto.

Come implementare l'esplorazione gestita e i risultati

Sono disponibili diversi articoli su Microsoft Learn sui dettagli dello spostamento gestito. Ad esempio, vedere Panoramica dello spostamento gestito in SharePoint Server.

Per implementare lo spostamento gestito, è necessario configurare i termini con URL corrispondenti alla struttura di spostamento del sito. La navigazione gestita può anche essere curata manualmente per sostituire la navigazione strutturale in molti casi. Ad esempio:

Struttura del sito di SharePoint.

Utilizzo di script sul lato client basato sulla ricerca

Una classe comune di implementazioni di spostamento personalizzate include modelli di progettazione con rendering client che archivia una cache locale di nodi di spostamento.

Questi provider di navigazione presentano un paio di vantaggi principali:

  • In genere funzionano bene con le progettazioni di pagine reattive.
  • Sono estremamente scalabili e performanti perché possono eseguire il rendering senza costi di risorse (e aggiornarsi in background dopo un timeout).
  • Questi provider di navigazione possono recuperare i dati di spostamento usando diverse strategie, che vanno da semplici configurazioni statiche a vari provider di dati dinamici.

Un esempio di provider di dati è l'uso di una struttura di spostamento basata su Search, che consente flessibilità per enumerare i nodi di spostamento e gestire in modo efficiente il taglio della sicurezza.

Esistono altre opzioni comuni per creare provider di spostamento personalizzati. Per altre indicazioni sulla creazione di un provider di spostamento personalizzato, vedere Soluzioni di spostamento per i portali di SharePoint .

Utilizzando la ricerca è possibile utilizzare gli indici sviluppati in background tramite la ricerca per indicizzazione continua. I risultati della ricerca vengono recuperati dall'indice di ricerca e i risultati sono limitati per motivi di sicurezza. Questa operazione è in genere più veloce rispetto ai provider di navigazione predefiniti quando è necessario limitare la sicurezza. Utilizzando la ricerca per l'esplorazione strutturale, soprattutto se si dispone di una struttura di siti complessa, è possibile velocizzare notevolmente tempi di caricamento delle pagine. Il principale vantaggio di questa esplorazione gestita è che è possibile beneficiare della limitazione per motivi di sicurezza.

Questo approccio implica la creazione di una pagina master personalizzata e la sostituzione del codice di spostamento predefinito con codice HTML personalizzato. Seguire questa procedura descritta nell'esempio seguente per sostituire il codice di navigazione nel file seattle.html. In questo esempio si aprirà il seattle.html file e si sostituirà l'intero elemento id="DeltaTopNavigation" con codice HTML personalizzato.

Esempio: sostituire il codice di spostamento predefinito in una pagina master

  1. Andare alla pagina Impostazioni sito.
  2. Aprire la raccolta di pagine master facendo clic su Pagine master.
  3. Da qui è possibile spostarsi nella libreria e scaricare il file seattle.master.
  4. Modificare il codice usando un editor di testo ed eliminare il blocco di codice nello screenshot seguente.
    Eliminare il blocco di codice visualizzato.
  5. Rimuovere il codice tra i <SharePoint:AjaxDelta id="DeltaTopNavigation"> tag e <\SharePoint:AjaxDelta> e sostituirlo con il frammento di codice seguente:
<div id="loading">
  <!--Replace with path to loading image.-->
  <div style="background-image: url(''); height: 22px; width: 22px; ">
  </div>
</div>
<!-- Main Content-->
<div id="navContainer" style="display:none">
    <div data-bind="foreach: hierarchy" class="noindex ms-core-listMenu-horizontalBox">
        <a class="dynamic menu-item ms-core-listMenu-item ms-displayInline ms-navedit-linkNode" data-bind="attr: { href: item.Url, title: item.Title }">
            <span class="menu-item-text" data-bind="text: item.Title">
            </span>
        </a>
        <ul id="menu" data-bind="foreach: $data.children" style="padding-left:20px">
            <li class="static dynamic-children level1">
                <a class="static dynamic-children menu-item ms-core-listMenu-item ms-displayInline ms-navedit-linkNode" data-bind="attr: { href: item.Url, title: item.Title }">

                 <!-- ko if: children.length > 0-->
                    <span aria-haspopup="true" class="additional-background ms-navedit-flyoutArrow dynamic-children">
                        <span class="menu-item-text" data-bind="text: item.Title">
                        </span>
                    </span>
                <!-- /ko -->
                <!-- ko if: children.length == 0-->
                    <span aria-haspopup="true" class="ms-navedit-flyoutArrow dynamic-children">
                        <span class="menu-item-text" data-bind="text: item.Title">
                        </span>
                    </span>
                <!-- /ko -->
                </a>

                <!-- ko if: children.length > 0-->
                <ul id="menu"  data-bind="foreach: children;" class="dynamic  level2" >
                    <li class="dynamic level2">
                        <a class="dynamic menu-item ms-core-listMenu-item ms-displayInline  ms-navedit-linkNode" data-bind="attr: { href: item.Url, title: item.Title }">

          <!-- ko if: children.length > 0-->
          <span aria-haspopup="true" class="additional-background ms-navedit-flyoutArrow dynamic-children">
           <span class="menu-item-text" data-bind="text: item.Title">
           </span>
          </span>
           <!-- /ko -->
          <!-- ko if: children.length == 0-->
          <span aria-haspopup="true" class="ms-navedit-flyoutArrow dynamic-children">
           <span class="menu-item-text" data-bind="text: item.Title">
           </span>
          </span>
          <!-- /ko -->
                        </a>
          <!-- ko if: children.length > 0-->
         <ul id="menu" data-bind="foreach: children;" class="dynamic level3" >
          <li class="dynamic level3">
           <a class="dynamic menu-item ms-core-listMenu-item ms-displayInline ms-navedit-linkNode" data-bind="attr: { href: item.Url, title: item.Title }">
            <span class="menu-item-text" data-bind="text: item.Title">
            </span>
           </a>
          </li>
         </ul>
           <!-- /ko -->
                    </li>
                </ul>
                <!-- /ko -->
            </li>
        </ul>
    </div>
</div>

6. Sostituire l'URL nel tag di ancoraggio dell'immagine di caricamento all'inizio, con un collegamento a un'immagine di caricamento nella raccolta siti. Dopo aver apportato le modifiche, rinominare il file, quindi caricarlo nella raccolta di pagine master. Ciò genera un nuovo file con estensione master.
7. Questo CODICE HTML è il markup di base che verrà popolato dai risultati della ricerca restituiti dal codice JavaScript. È necessario modificare il codice per modificare il valore per var root = "URL raccolta siti" come illustrato nel frammento di codice seguente:
var root = "https://spperformance.sharepoint.com/sites/NavigationBySearch";

8. I risultati vengono assegnati alla matrice self.nodes e una gerarchia viene creata dagli oggetti usando linq.js l'assegnazione dell'output a una matrice self.hierarchy. Questa matrice è l'oggetto in cui è associato il codice HTML. Questa operazione viene eseguita nella funzione toggleView() passando l'oggetto utente alla funzione ko.applyBinding().
Quindi, in questo modo la matrice di gerarchia va associata al codice HTML seguente:
<div data-bind="foreach: hierarchy" class="noindex ms-core-listMenu-horizontalBox">

I gestori eventi per mouseenter e mouseexit vengono aggiunti alla struttura di spostamento di primo livello per gestire i menu a discesa del sito secondario eseguiti nella addEventsToElements() funzione .

Nell'esempio di spostamento complesso, un nuovo caricamento di pagine senza la memorizzazione nella cache locale mostra che il tempo dedicato al server è stato ridotto rispetto alla struttura di navigazione di benchmark per ottenere un risultato simile all'approccio di spostamento gestito.

Informazioni sul file JavaScript...

Nota

Se si usa JavaScript personalizzato, assicurarsi che la rete CDN pubblica sia abilitata e che il file si trova in un percorso della rete CDN.

Di seguito è riportato l'intero file JavaScript:

//Models and Namespaces
var SPOCustom = SPOCustom || {};
SPOCustom.Models = SPOCustom.Models || {}
SPOCustom.Models.NavigationNode = function () {

    this.Url = ko.observable("");
    this.Title = ko.observable("");
    this.Parent = ko.observable("");

};

var root = "https://spperformance.sharepoint.com/sites/NavigationBySearch";
var baseUrl = root + "/_api/search/query?querytext=";
var query = baseUrl + "'contentClass=\"STS_Web\"+path:" + root + "'&trimduplicates=false&rowlimit=300";

var baseRequest = {
    url: "",
    type: ""
};


//Parses a local object from JSON search result.
function getNavigationFromDto(dto) {
    var item = new SPOCustom.Models.NavigationNode();
    if (dto != undefined) {

        var webTemplate = getSearchResultsValue(dto.Cells.results, 'WebTemplate');

        if (webTemplate != "APP") {
            item.Title(getSearchResultsValue(dto.Cells.results, 'Title')); //Key = Title
            item.Url(getSearchResultsValue(dto.Cells.results, 'Path')); //Key = Path
            item.Parent(getSearchResultsValue(dto.Cells.results, 'ParentLink')); //Key = ParentLink
        }

    }
    return item;
}

function getSearchResultsValue(results, key) {

    for (i = 0; i < results.length; i++) {
        if (results[i].Key == key) {
            return results[i].Value;
        }
    }
    return null;
}

//Parse a local object from the serialized cache.
function getNavigationFromCache(dto) {
    var item = new SPOCustom.Models.NavigationNode();

    if (dto != undefined) {

        item.Title(dto.Title);
        item.Url(dto.Url);
        item.Parent(dto.Parent);
    }

    return item;
}

/* create a new OData request for JSON response */
function getRequest(endpoint) {
    var request = baseRequest;
    request.type = "GET";
    request.url = endpoint;
    request.headers = { ACCEPT: "application/json;odata=verbose" };
    return request;
};

/* Navigation Module*/
function NavigationViewModel() {
    "use strict";
    var self = this;
    self.nodes = ko.observableArray([]);
    self.hierarchy = ko.observableArray([]);;
    self.loadNavigatioNodes = function () {
        //Check local storage for cached navigation datasource.
        var fromStorage = localStorage["nodesCache"];
        if (false) {
            var cachedNodes = JSON.parse(localStorage["nodesCache"]);

            if (cachedNodes && timeStamp) {
                //Check for cache expiration. Currently set to 3 hrs.
                var now = new Date();
                var diff = now.getTime() - timeStamp;
                if (Math.round(diff / (1000 * 60 * 60)) < 3) {

                    //return from cache.
                    var cacheResults = [];
                    $.each(cachedNodes, function (i, item) {
                        var nodeitem = getNavigationFromCache(item, true);
                        cacheResults.push(nodeitem);
                    });

                    self.buildHierarchy(cacheResults);
                    self.toggleView();
                    addEventsToElements();
                    return;
                }
            }
        }
        //No cache hit, REST call required.
        self.queryRemoteInterface();
    };

    //Executes a REST call and builds the navigation hierarchy.
    self.queryRemoteInterface = function () {
        var oDataRequest = getRequest(query);
        $.ajax(oDataRequest).done(function (data) {
            var results = [];
            $.each(data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results, function (i, item) {

                if (i == 0) {
                    //Add root element.
                    var rootItem = new SPOCustom.Models.NavigationNode();
                    rootItem.Title("Root");
                    rootItem.Url(root);
                    rootItem.Parent(null);
                    results.push(rootItem);
                }
                var navItem = getNavigationFromDto(item);
                results.push(navItem);
            });
            //Add to local cache
            localStorage["nodesCache"] = ko.toJSON(results);

            localStorage["nodesCachedAt"] = new Date().getTime();
            self.nodes(results);
            if (self.nodes().length > 0) {
                var unsortedArray = self.nodes();
                var sortedArray = unsortedArray.sort(self.sortObjectsInArray);

                self.buildHierarchy(sortedArray);
                self.toggleView();
                addEventsToElements();
            }
        }).fail(function () {
            //Handle error here!!
            $("#loading").hide();
            $("#error").show();
        });
    };
    self.toggleView = function () {
        var navContainer = document.getElementById("navContainer");
        ko.applyBindings(self, navContainer);
        $("#loading").hide();
        $("#navContainer").show();

    };
    //Uses linq.js to build the navigation tree.
    self.buildHierarchy = function (enumerable) {
        self.hierarchy(Enumerable.From(enumerable).ByHierarchy(function (d) {
            return d.Parent() == null;
        }, function (parent, child) {
            if (parent.Url() == null || child.Parent() == null)
                return false;
            return parent.Url().toUpperCase() == child.Parent().toUpperCase();
        }).ToArray());

        self.sortChildren(self.hierarchy()[0]);
    };


    self.sortChildren = function (parent) {

        // sjip processing if no children
        if (!parent || !parent.children || parent.children.length === 0) {
            return;
        }

        parent.children = parent.children.sort(self.sortObjectsInArray2);

        for (var i = 0; i < parent.children.length; i++) {
            var elem = parent.children[i];

            if (elem.children && elem.children.length > 0) {
                self.sortChildren(elem);
            }
        }
    };

    // ByHierarchy method breaks the sorting in chrome and firefox
    // we need to resort  as ascending
    self.sortObjectsInArray2 = function (a, b) {
        if (a.item.Title() > b.item.Title())
            return 1;
        if (a.item.Title() < b.item.Title())
            return -1;
        return 0;
    };


    self.sortObjectsInArray = function (a, b) {
        if (a.Title() > b.Title())
            return -1;
        if (a.Title() < b.Title())
            return 1;
        return 0;
    }
}

//Loads the navigation on load and binds the event handlers for mouse interaction.
function InitCustomNav() {
    var viewModel = new NavigationViewModel();
    viewModel.loadNavigatioNodes();
}

function addEventsToElements() {
    //events.
      $("li.level1").mouseover(function () {
          var position = $(this).position();
          $(this).find("ul.level2").css({ width: 100, left: position.left + 10, top: 50 });
      })
   .mouseout(function () {
     $(this).find("ul.level2").css({  left: -99999, top: 0 });
   
    });
   
     $("li.level2").mouseover(function () {
          var position = $(this).position();
          console.log(JSON.stringify(position));
          $(this).find("ul.level3").css({ width: 100, left: position.left + 95, top:  position.top});
      })
   .mouseout(function () {
     $(this).find("ul.level3").css({  left: -99999, top: 0 });
    });
} _spBodyOnLoadFunctionNames.push("InitCustomNav");

Per riepilogare il codice illustrato in precedenza nella jQuery $(document).ready funzione è presente un viewModel object oggetto creato e quindi viene chiamata la loadNavigationNodes() funzione su tale oggetto. Questa funzione carica la gerarchia di navigazione compilata in precedenza archiviata nell'archiviazione locale HTML5 del browser client oppure chiama la funzione queryRemoteInterface().

QueryRemoteInterface() compila una richiesta usando la getRequest() funzione con il parametro di query definito in precedenza nello script e quindi restituisce i dati dal server. Questi dati sono sostanzialmente una matrice di tutti i siti nella raccolta di siti rappresentati come oggetti di trasferimento dei dati con alcune proprietà.

Questi dati vengono quindi analizzati negli oggetti definiti SPO.Models.NavigationNode in precedenza che usano Knockout.js per creare proprietà osservabili da usare associando i valori nel codice HTML definito in precedenza.

Gli oggetti vengono quindi inseriti in una matrice di risultati. Questa matrice viene analizzata in JSON utilizzando Knockout e memorizzata nell'archivio locale del browser per migliorare le prestazioni su caricamenti di pagina futuri.

Vantaggi di questo approccio

Uno dei principali vantaggi di questo approccio è che usando l'archiviazione locale HTML5, la navigazione viene archiviata in locale per l'utente al successivo caricamento della pagina. Si ottengono miglioramenti delle prestazioni principali utilizzando l'API di ricerca per l'esplorazione strutturale; tuttavia, sono necessarie alcune capacità tecniche per eseguire e personalizzare questa funzionalità.

Nell'implementazione di esempio, i siti vengono ordinati allo stesso modo dello spostamento strutturale predefinito; ordine alfabetico. Se si desidera deviare da questo ordine, diventa più complesso lo sviluppo e la manutenzione. Inoltre, questo approccio richiede di deviare dalle pagine master supportate. Se la pagina master personalizzata non viene mantenuta, il sito perderà gli aggiornamenti e i miglioramenti apportati da Microsoft alle pagine master.

Il codice precedente presenta le dipendenze seguenti:

La versione corrente di LinqJS non contiene il metodo ByHierarchy usato nel codice precedente e interromperà il codice di navigazione. Per risolvere il problema, aggiungere il metodo seguente al file Linq.js prima della riga Flatten: function ().

ByHierarchy: function(firstLevel, connectBy, orderBy, ascending, parent) {
     ascending = ascending == undefined ? true : ascending;
     var orderMethod = ascending == true ? 'OrderBy' : 'OrderByDescending';
     var source = this;
     firstLevel = Utils.CreateLambda(firstLevel);
     connectBy = Utils.CreateLambda(connectBy);
     orderBy = Utils.CreateLambda(orderBy);

     //Initiate or increase level
     var level = parent === undefined ? 1 : parent.level + 1;

    return new Enumerable(function() {
         var enumerator;
         var index = 0;

        var createLevel = function() {
                 var obj = {
                     item: enumerator.Current(),
                     level : level
                 };
                 obj.children = Enumerable.From(source).ByHierarchy(firstLevel, connectBy, orderBy, ascending, obj);
                 if (orderBy !== undefined) {
                     obj.children = obj.children[orderMethod](function(d) {
                         return orderBy(d.item); //unwrap the actual item for sort to work
                     });
                 }
                 obj.children = obj.children.ToArray();
                 Enumerable.From(obj.children).ForEach(function(child) {
                     child.getParent = function() {
                         return obj;
                     };
                 });
                 return obj;
             };

        return new IEnumerator(

        function() {
             enumerator = source.GetEnumerator();
         }, function() {
             while (enumerator.MoveNext()) {
                 var returnArr;
                 if (!parent) {
                     if (firstLevel(enumerator.Current(), index++)) {
                         return this.Yield(createLevel());
                     }

                } else {
                     if (connectBy(parent.item, enumerator.Current(), index++)) {
                         return this.Yield(createLevel());
                     }
                 }
             }
             return false;
         }, function() {
             Utils.Dispose(enumerator);
         })
     });
 },

Panoramica dell'esplorazione gestita in SharePoint Server

Memorizzazione nella cache e prestazioni dello spostamento strutturale