Globale Navigation – Schnell und praktisch
avatar

Als ich heute nach einiger Zeit mal wieder eines meiner alten SharePoint 2013 Entwicklungssysteme hochgefahren hatte, wollte ich mir erst einmal einen Überblick darüber verschaffen, was auf der virtuellen Maschine überhaupt noch für Websitesammlungen und Unterwebseiten existieren. Dabei musste ich leider mal wieder feststellen, dass das gar nicht mal so komfortabel ist, wie es eigentlich sein könnte. 🙁

Über die zentrale Administration bekommt man zwar alle Websitesammlungen angezeigt. Es ist aber leider erforderlich die jeweilige Webapplikation umzuschalten; und dann hat man noch lange keinen Einblick in die tiefere Webseitenstruktur innerhalb der Sammlungen.  Ja, und wenn man auf die vierte Webanwendung umgeschaltet hat, dann sind die Webseitensammlungen von der ersten im Zweifel schon wieder vergessen.

Das muss auch einfacher gehen!!!

 

Die Lösung: Selbst ist der Entwickler!

Über die integrierte SharePoint-Suche ist es wunderbar einfach nach Websitesammlungen und Webseiten zu suchen. Die folgenden Suchkriterien sind dazu der Schlüssel zum Erfolg:

  • contentclass:“STS_Web“ findet alle Webseiten; aber nicht das “RootWeb” und somit nicht die “Site Collection” an sich
  • contentclass:“STS_Site“ findet alle Websitesammlungen

Über die out-of-the-box Suche in der SharePoint-Benutzeroberfläche kann man die Suchabfrage direkt absetzen und bekommt auch die richtigen Ergebnisse zurück. Allerdings ist die Darstellung der Ergebnisse als flache Liste für meine Zwecke nicht ganz zielführend. Mir schwebte eher eine strukturierte Sitemap vor!

 

Meine Lösung musste schnell und unkompliziert bereitzustellen sein. Und viel Zeit hatte ich auch nicht.  Daher habe ich mich für eine reine JavaScript-basierte Lösung entschieden, die direkt in einem Content Editor Webpart verwendet werden kann.

UI.

Das besagte Content Editor Webpart beinhaltet das folgende HTML mit der Ladelogik für die erforderlichen JavaScript-Bibliotheken und dem HTML-Container (<div id=“search-nav“></div>), der die generierte Sitemap aufnehmen soll. Das eigentliche Erstellen der Navigation(Sitemap) habe ich in eine separate JavaScript-Datei ausgelagert, um die Funktionalität bei Bedarf einfacher wiederverwenden zu können. Zudem ist es deutlich besser für die Übersichtlichkeit. (Es funktioniert aber auch komplett nur im Webpart!)

 

<script language="javascript">
    function Load() {     
        loadScript(_spPageContextInfo.siteAbsoluteUrl + "/Style%20Library/jquery-1.10.2.min.js", getSearchResultsUsingREST);
    }   loadScript(_spPageContextInfo.siteAbsoluteUrl + "/Documents/myscript.js", undefined);
    //start after all SP Stuff ist loaded
    _spBodyOnLoadFunctionNames.push("Load");   function loadScript(url, callback)
    {
        // Adding the script tag to the head as suggested before
        var head = document.getElementsByTagName('head')[0];
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = url;   script.onload = callback;   // Fire the loading
        head.appendChild(script);
    }   </script>   <div id="search-nav"></div>

 

Für das Generieren der Sitemap ist der nachfolgende JavaScript-Code zuständig. Er holt die Suchergebnisse über die REST-Schnittstelle vom SharePoint ab, strukturiert diese anhand der URL und baut das entsprechende HTML für die Darstellung zusammen. (Ich gebe zu, dass Zusammenbauen vom HTML geht auch eleganter.)

  //Gets the search result 
function getSearchResultsUsingREST() {
    var queryUrl = _spPageContextInfo.siteAbsoluteUrl + "/_api/search/query?"
                        + "QueryText="
                        + "'"
                            + 'contentclass:"STS_Web" contentclass:"STS_Site"+ '
                            //+ path + ' '
                            //+ keywords + ' 
                        + "'"
                        + "&SelectProperties="
                        + "'"
                            + "Path,Title"
                        + "'"
                        + "&trimduplicates=false"
                        + "&rowlimit=" + "50"; //500 is the standart maximum   $.ajax({
        url: queryUrl, method: "GET", headers: { "Accept": "application/json; odata=verbose" },
        success: function (data) {
            var results = data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results;
            $("#search-nav").html(buildNavigation(results));  
        },
        error: function (error) {
            console.log("Error while receiving search items!");
            console.log(error);
            throw error;
        },
        complete: function () {
            console.log("COMPLETE!");
        }
    });   //Creates the navigation HTML
    function buildNavigation(results) {
        var resultFieldIndexPath = 2;
        var resultFieldIndexTitle = 3;
        var navArray = [];   $.each(results, function (index) {
            navArray.push({
                url: this.Cells.results[resultFieldIndexPath].Value,
                title: this.Cells.results[resultFieldIndexTitle].Value
            })
        });   navArray.sort(function (a, b) {
            return a.url.localeCompare(b.url);
        });   var html = "<ul>";
        $.each(navArray, function (index) {
            var count = String(this.url).split("/").length-1;   if (count <= 4) {
                html = html + "<li>";
                html = html + "  <div style='margin-top: 10px;'>";
                html = html + "    <a href='" + this.url + "' style='font-weight:bold; margin-top:10px;'> " + this.title + "</a>";
                html = html + "    <div> Having URL: " + this.url + "</div>";
                html = html + "  </div>";
                html = html + "</li>";
            }
            else {
                html = html + "<li style='list-style-type: none;'><ul style='margin-left:" + String((count - 4) * 20) + "px; margin-top:10px;'><li>";
                html = html + "<a href='" + this.url + "' >" + this.title + "</a>";
                html = html + "<div> Having URL: " + this.url + "</div>";
                html = html + "</li></ul></li>";
            }     
        });
        html = html + "</ul>";
        return(html);
    }
}  

Auf diesem Wege bekommt man alle Websitesammlungen inkl. Unterwebsites auf einen Blick angezeigt, auf die man Zugriffsrechte besitzt. Die Suche stellt das Security Trimming sicher. Daher ist dieser Ansatz nicht nur für die administrative Verwaltung interessant, sondern auch für jeden anderen Anwender bei der täglichen Arbeit.

Ich wollte mit diesem Blogbeitrag auch noch mal signalisieren, dass es nicht immer eine App sein muss. Ein Content Editor Webpart (inklusive Inhalt und Code) kann man prima exportieren und verteilen! 🙂

Schreibe einen Kommentar