in

SharePoint Blogs

The Best Place for SharePoint-related Blogs

This Blog

Syndication

News

Ein guter Blog lebt auch vom Feedback der Leser. Aus diesem Grund möchte ich alle Leser bitten und auffordern, Feedback und Bewertung für einzelne Posts abzugeben. Ich freue mich natürlich auch, wenn mein Blog oder auch einzelne Posts verlinkt werden. Dies hilft anderen Leser und ist zugleich auch Ansporn für mich!

Meine SharePoint-Notizen

SharePoint-Notizen aus meiner täglichen Projektarbeit mit dem Microsoft Office SharePoint Server 2007

June 2008 - Posts

  • How to: Sharepoint-Liste als eigenen Suchbereich einrichten

    Die Suche von SharePoint ist ein sehr mächtiges Instrument und mit ein wenig Konfigurationsarbeit kann man sehr mächtige Suchcenter einrichten. Allerdings gibt es ein Manko: man kann keine einzelne SharePoint-Liste direkt als eigenen Suchbereich definieren. Als Basis für einen Suchbereich läßt SharePoint nur die folgende Inhaltsquellen zu: SharePoint-Websites, allg. Websites, Dateifreigaben, öffentliche Exchange-Ordner und Geschäftsdaten zu (siehe folgendes Bild).

     

    In unserem eigenen Intranet (natürlich basiered auf einem SharePoint Server 2007) existiert eine Bücherliste, die mittlerweile beachtliche Ausmaße angenommen hat. Bisher wurde diese Bücherliste in den allgemeinen Suchindex aufgenommen. Leider bedeutet dies aber auch, dass Mitarbeiter u.U. eine größere Suchergebnisliste angezeigt bekommen, selbst wenn sie nur nach einem Buch gesucht haben. Aus diesem Grund habe ich mir Gedanken darüber gemacht, wie man unsere Bücherliste als eigenständigen Suchbereich einrichten kann. Mein Ziel ist: ich möchte in unserem Intranet-Suchcenter einen eigenen Suchbereich Bücherliste einrichten und die Ergebnisliste soll nur aus Einträgen aus dieser Bücherliste bestehen.

    In diesem Post möchte ich eine auf Metadaten basierte Lösung vorstellen.

    Zuerst habe ich in unserer Bücherliste eine neue Spalte eingefügt. Diese Spalte bekommt den Namen MetaTagBookList und ist vom Typ Text. Da hier niemand Daten eingeben soll (bzw. darf), wird diese Spalte fest auf den Defaultwert Bücherliste gesetzt. Diese Spalte soll nur dazu dienen, jeden Eintrag in der Bücherliste mit einem festen MetaTag zu kennzeichnen. Um sicherzustellen, dass hier niemand einen anderen Wert eingibt oder verändert, empfiehlt es sich, diese Spalte aus den Ansichten auszublenden. Wer dies gründlich machen möchte, kann dazu mein SharePoint-Tool "List Field Form Properties" aus diesem Post benutzen. Etwas unschön ist nur die Tatsache, dass man nun alle Einträge in dieser Liste nochmals öffnen und speichern muss, damit jeder Eintrag in der Bücherliste einen gesetzten MetaTag Bücherliste hat. (Dieses Problem läßt sich aber zum Glück mit ein paar Zeilen Code und einem kleinen Entwicklertool lösen.)

    So - nun ist die Bücherliste vorbereitet. Als nächstes muss unsere neu-hinzugefügte Spalte MetaTagBookList in die Liste der SharePoint-Metadaten aufgenommen werden. Hierzu startet man einen Crawl der betreffenden Inhaltsquelle. Eigentlich sollte ein inkrementelles Crawling ausreichend sein. Dies hat bei mir aber nicht immer geklappt, weswegen ich dazu übergegangen bin, einen vollständigen Crawl zu starten.

    Nachdem das Crawling erfolgreich abgeschlossen wurde, sollten wir unseren neuen Metadaten-Tag MetaTagBookList in der Liste der Metadaten wiederfinden. Dazu öffnen wir die Zentraladministration, klicken auf den SharedService und danach unter der Rubrik Suchen auf den Link Sucheinstellungen. Es öffnet sich eine neue Seite mit den Crawleinstellungen. Ein paar Zeilen tiefer findet sich der Link Eigenschaftenzuordnung für Metadaten, auf den wir jetzt klicken. Es öffnet sich nun eine Liste mit den Eigenschaftenzuordnungen für Metadaten. Hier klicken wir auf Neue verwaltete Eigenschaft und fügen im folgenden Dialog eine neue verwaltete Eigenschaft mit dem Namen Bücherliste hinzu und wählen aus, dass wir diese Eigenschaft in Suchbereichen verwenden möchten! (siehe folgendes Bild).

    Nun haben wir eine neue verwaltete Eigenschaft mit dem Namen Bücherliste erzeugt - und diese können wir benutzen, um einen entsprechenden Suchbereich zu definieren. Hierzu gehen wir zurück zu den SharedService-Einstellungen in der Zentraladministration und klicken wieder auf den Link Sucheinstellungen im Bereich Suchen. Wir lassen uns alle Bereiche anzeigen und klicken oben auf Neuer Bereich. Es öffnet sich ein Dialog und wir geben dort unserem neuen Bereich den Namen Bücherliste und wählen aus, dass wir die Standardseite für Suchergebnisse verwenden wollen (siehe folgendes Bild).

    Als nächstes müssen wir noch eine Regel für diesen neuen Bereich hinzufügen - schließlich wollen wir nur Ergebnisse aus der Bücherliste als Ergebnis einer Suche in diesem neuen Bereich sehen. Wir klicken also auf Regel hinzufügen und wählen Eigenschaftsabfrage aus. Darunter wählen wir in der Dropdownbox Suchkriterien unsere Bücherliste aus und im darunterliegenden Textfeld geben wir "Bücherliste" ein. Bedeutet: wir wollen nur Ergebnisse, bei denen die Eigenschaft Bücherliste den Wert "Bücherliste" hat. Damit sichergestellt ist, nur Ergebnisse aus der Bücherliste zu bekommen, müssen wir unter Verhalten noch "Erforderlich - Jedes Element muss mit dieser Regel übereinstimmen"  auswählen. (siehe folgendes Bild).

    Nach einer Aktualisierung der Bereiche (kann automatisch erfolgen oder manuell angestoßen werden) sollte unser neuer Bereich existieren. Nun können wir diesen Bereich als weiteren Bereich für die Suche definieren. Dazu öffnen wir die Websiteeinstellungen der obersten Ebene und klicken auf Suchbereich unter der Rubrik Websitesammlungsverwaltung. In der Anzeigegruppe Suchdropdown fügen wir unseren neuen Bereich Bücherliste hinzu (siehe Bild).

    Sollte in der Spalte Einträge 0 angezeigt werden, ist u.U. ein weiterer Crawl-Durchlauf erforderlich. Nun können wir unser Suchcenter aufrufen und sollten im Bereichsdropdown unseren neuen Bereich Bücherliste vorfinden. Zeit für einen ersten Probelauf: Bereich Bücherliste auswählen und einen Suchbegriff eingeben, der in der Bücherliste vorkommt. Im Suchergebnis sollten jetzt nur Ergbnisse aus unserer Bücherliste auftauchen. Für diesen Post habe ich mir eine kleine Demo erstellt (aus der auch die Screenshots stammen) und da sieht das Ergebnis nun so aus, wie im folgenden Bild dargestellt.

    Diese Metadaten kann man auch verwenden, um ein Suchcenter zu erstellen, welches z.B. nur und ausschließlich in unserer Bücherliste sucht. Dies ist dann interessant, wenn man ein Suchcenter mit mehreren spezialisierten Suchfeldern auf unterschiedlichen Seiten haben möchte. Eine kleine Modifikation am Suchfeld-Webpart macht dies möglich. Dazu öffnet man die Webpart-Eigenschaften des Suchfeld-Webparts und trägt in der Rubrik Abfragetextfeld in das Textfeld Zusätzliche Abfrageausdrücke "Bücherliste:Bücherliste" ein - bedeutet auch hier wieder: Bücherliste muss den Wert "Bücherliste" haben. Nun kann man das Bereichsdropdownfeld noch ausblenden und fertig ist ein Suchfeld, dass ausschließlich in unserer Bücherliste sucht (siehe folgendes Bild).


     

    Zusammenfassung: Auf diese Weise kann man Benutzern eines Suchcenters sehr anwenderfreundliche Suchbereiche zur Verfügung stellen und es ermöglichen, Suchergebnisse auf eine SharePoint-Liste zu beschränken. Wer die Ausgabe des Suchergebnisses noch anpassen möchte (z.B. das Buchcover einblenden), der findet bei Tobias Zimmergren einen guten Artikel über dieses Thema.

     

    Add to Technorati Favorites
  • Interessantes über das "! Neu" Tag in SharePoint-Listen

    Ehrlich gesagt - meine Kollegen haben mich das schon einige Male gefragt, aber ich konnte Ihnen keine kompetente Antwort auf die Frage "Wie lange wird eigentlich dieser grüne ! Neu-Schriftzug angezeigt?" geben. Nun habe ich die Antwort darauf in einem Blogpost von Asif Rehmani gefunden. Hier der Link zum Post.

    Zusammengefasst: man kann mit STSADM steuern, ob und wie lange dieses Tag angezeigt wird. Dies geht mit diesem Aufruf:

    stsadm.exe –o setproperty –pn days-to-show-new-icon –pv (number of days) –url (Virtual server address) 

    Um das Tag ganz abzuschalten, kann man diesen Aufruf verwenden:

    stsadm.exe –o setproperty –pn days-to-show-new-icon –pv 0 –url http://(your server name)

    Wenn ich in Zukunft wieder nach diesem grünen ! Neu-Schriftzug gefragt werde, kann ich jetzt endlich auch darauf eine Antwort geben. Wink

     

    Add to Technorati Favorites
  • How to: Benutzerdefinierte TimerJobs - die Basics

    Vor kurzem wurde ich mit einem Sharepoint-Problem eines Kunden konfrontiert, für das ich nur einen selbstdefinierten TimerJob als sinnvolle Lösungsmöglichkeit sah. Da ich mich aber bisher noch nicht mit SharePoint TimerJobs auseinandergesetzt habe, war dies ein willkommene Gelegenheit, dies nachzuholen. Zwar findet man im Internet einige interessante Posts über SharePoint TimerJobs, aber ich wollte mir zunächst selbst die Basics erarbeiten.

    Und genau über diese Basics möchte ich hier berichten:

    Ein SharePoint TimerJob besteht aus einer Klasse, die von der Basisklasse SPJobDefinition abgeleitet ist. In der Ableitung wird im Wesentlichen die Methode Execute() mit der gewünschten Funktionalität überschrieben und der eigentliche TimerJob ist bereits fertig. Zusätzlich kann man auch noch die Konstruktoren überschreiben, um z.B. dem eigenen TimerJob einen aussagekräftigen Titel zu geben.

    Ein TimerJob kann dann z.B. folgendermaßen aussehen:

    using System;
    using Microsoft.SharePoint.Administration;

    namespace TimerJobNotifyExpiredPassword
    {
        public class NotifyExpiredPasswordTimerJob : SPJobDefinition
        {
            public NotifyExpiredPasswordTimerJob()
                : base()
            {
            }

            public NotifyExpiredPasswordTimerJob(string strJobName, SPService oService, SPServer oServer, SPJobLockType oTargetType)
                : base(strJobName, oService, oServer, oTargetType)
            {
            }

            public NotifyExpiredPasswordTimerJob(string strJobName, SPWebApplication oWebApp)
                : base(strJobName, oWebApp, null, SPJobLockType.ContentDatabase)
            {
                this.Title = "NotifyExpiredPasswordTimerJob";
            }

            public override void Execute(Guid oContentDBId)
            {
                // Hier den auszuführenden Timer-Code einfügen
            }
        }
    }
     

    An der grün markierten Stelle würde man den eigenen Code einfügen, der vom SharePoint TimerDienst zyklisch aufgerufen werden soll. OK, nun haben wir bereits die Klasse, die vom SharePoint TimerDienst aufgerufen werden könnte - wenn es uns nun noch gelingen würde, die obige Klasse als SharePoint TimerJob auf einem SharePoint-Server zu installieren. Dies ist leider nur mit ein wenig zusätzlicher Programmierarbeit möglich. Das Verfahren ist ähnlich, wie es z.B. auch bei einem benutzerdefiniertem EventHandler angewendet werden kann. Wir werden unseren TimerJob über ein SharePoint Feature installieren und uns an den Event hängen, der beim Installieren bzw. beim Deinstallieren eines Features ausgelöst wird. Beim Installieren fügen wir unsere neue TimerJob-Klasse als neuen TimerJob der entsprechenden SharePoint-Liste hinzu, beim Deinstallieren entfernen wir unsere Klasse aus der Liste der SharePoint-TimerJobs. Im Folgenden möchte ich zeigen, wie man das genau macht:

    Zuerst benötigen wir eine zweite Klasse, die aber diesmal von der Basisklasse SPFeatureReceiver abgeleitet wird. Wir erstellen uns also einen eigenen EventReceiver und überschreiben hier z.B. die Methoden FeatureActivated() und FeatureDeactivating(). Beim Aktivieren unseres Features wird unsere Methode FeatureActivated() aufgerufen und hier fügen wir nun den Code ein, um unseren TimerJob zu installieren. Beim Deaktivieren unseres Features wird dann die Methode FeatureDeactivating() aufgerufen und wir fügen hier den Code ein, um unseren TimerJob wieder zu deinstallieren.

    Dies kann dann z.B. folgendermaßen aussehen:

    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Administration;

    namespace TimerJobNotifyExpiredPassword
    {
        class FeatureReceiver : SPFeatureReceiver
        {
            public override void FeatureActivated(SPFeatureReceiverProperties oProperties)
            {
                SPSite oSite = oProperties.Feature.Parent as SPSite;

                // Falls ältere Instanz vorhanden, diese zuvor löschen
                foreach (SPJobDefinition oJob in oSite.WebApplication.JobDefinitions)
                {
                    if (oJob.Name == "TimerJobNotifyExpiredPassword")
                    {
                        oJob.Delete();
                    }
                }

                // Timer-Job installieren
                TimerJobNotifyExpiredPassword.NotifyExpiredPasswordTimerJob oTimerJob = new TimerJobNotifyExpiredPassword.NotifyExpiredPasswordTimerJob("TimerJobNotifyExpiredPassword", oSite.WebApplication);

                SPMinuteSchedule oSchedule = new SPMinuteSchedule();
                oSchedule.BeginSecond = 0;
                oSchedule.EndSecond = 59;
                oSchedule.Interval = 1;  // <- diesen Job jede Minute starten

                oTimerJob.Schedule = oSchedule;

                oTimerJob.Update();
            }
           
            public override void FeatureDeactivating(SPFeatureReceiverProperties oProperties)
            {
                SPSite oSite = oProperties.Feature.Parent as SPSite;
               
                // Job suchen und löschen
                foreach (SPJobDefinition oJob in oSite.WebApplication.JobDefinitions)
                {
                    if (oJob.Name == "TimerJobNotifyExpiredPassword")
                    {
                        oJob.Delete();
                    }
                }
            }

            public override void FeatureInstalled(SPFeatureReceiverProperties oProperties)
            {
            }

            public override void FeatureUninstalling(SPFeatureReceiverProperties oProperties)
            {
            }
        }
    }

     

    Das Ganze - also unsere beiden Klassen habe ich in einem einfachen VisualStudio 2008 Projekt für eine Klassenbibliothek verpackt und übersetzt. Um dies jetzt als SharePoint Feature installieren zu können, benötigen wir nur noch eine einzige Datei. Diese Datei trägt den Namen feature.xml und enthält eine XML-Beschreibung unsers Features. Für mein einfaches Beispiel reicht diese feature.xml völlig aus:

    <?xml version="1.0" encoding="utf-8" ?>
    <Feature xmlns="
    http://schemas.microsoft.com/sharepoint/
        Id="04496CA0-316D-11DD-BD11-0800200C9A66" 
        Title="TimerJobNotifyExpiredPassword" 
        Description="TimerJobNotifyExpiredPassword" 
        Version="1.0.0.0" 
        Scope="Site" 
        ReceiverAssembly="TimerJobNotifyExpiredPassword, Version=1.0.0.0, Culture=neutral, PublicKeyToken=639ff2c7cd9696b7" 
        ReceiverClass="TimerJobNotifyExpiredPassword.FeatureReceiver">
    </Feature>

    So einfach diese Datei auch aussieht, es gibt hier einiges, was man beachten muss:

    • unser Feature muss über eine eindeutige GUID identifizierbar sein. Die GUID in der Zeile "Id=" muss deswegen ausgetauscht werden
    • gleiches gilt für die Zeilen Title und Description. Hier gibt man dem Feature einen Namen und eine Beschreibung
    • ausgetauscht bzw. angepasst werden müssen auch die letzten beiden Zeilen, die die Assembly und die Klasse angeben, die als Receiver für die oben beschreibenen Feature-Events verwendet werden soll. Übrigens: den PublicKeyToken bekommt man recht einfach mit dem .NET Reflector von Lutz Roeder heraus.
    • wichtig ist auch die Zeile "Scope=". Hier gibt man den Gültigkeitsbereich des Features an. Wichtig zu beachten: der hier eingetragene Scope muss mit dem Scope übereinstimmen, den wir beim Installieren bzw. Deinstallieren unseres TimerJobs verwendet haben!

    So - das waren alle Dateien, die man unbedingt benötigt, um einen eigenen TimerJob auf einem SharePoint-Server zu installieren. Stellt sich die Frage: wie geht es nun weiter?

    Zuerst übersetzten wir unsere beiden Klassen. Sofern beide in einem Projekt zusammengefasst wurden, sollte dabei eine DLL erstellt werden. Diese DLL kopieren wird in den Global Assembly Cache. Als nächstes erzeugen wird im sogenannten 12-Hive unter ../TEMPLATE/FEATUES ein neues Verzeichnis mit dem Namen, dem wir dem Feature in der Datei feature.xml gegeben haben und kopieren die Datei feature.xml dort hin. Jetzt sind wir bereit, unser Feature zu installieren.

    Dies geschieht mit folgendem Auruf:

    stsadm -o installfeature -name TimerJobNotifyExpiredPassword 

    Nun müssen wir das Feature nur noch Aktivieren, damit unser EventReceiver aufgerufen wird und unseren TimerJob installieren kann:

    stsadm -o activatefeature -name TimerJobNotifyExpiredPassword -url http://mossbasis

    Die URL muss natürlich durch die URL des jeweiligen Ziels ersetzt werden.

    Beide Aufrufe kann man auch in einer kleinen Batch-Datei zusammenfassen. Für meine Testinstallationen verwende ich gern diese Batch-Datei:

    REM TO BE SAFE FIRST DEACTIVATE and UNINSTALL FEATURE
    stsadm -o deactivatefeature -name TimerJobNotifyExpiredPassword -url
    http://mossbasis
    stsadm -o uninstallfeature -name TimerJobNotifyExpiredPassword -force
    PAUSE

    REM NOW INSTALL FEATURE
    stsadm -o installfeature -name TimerJobNotifyExpiredPassword
    PAUSE

    REM FINALLY ACTIVATE FEATURE
    stsadm -o activatefeature -name TimerJobNotifyExpiredPassword -url
    http://mossbasis 

    Der beispielhafte TimerJob führt zwar keine Aktion aus, dennoch können wir mit dem Debugger schnell überprüfen, ob alles wie gewünscht funktioniert. Dazu setzten wir einen Breakpoint auf die Methode Execute() und verbinden uns mit dem Prozess OWSTIMER.EXE. Es kann notwendig sein, einen Haken neben "Prozesse aller Benutzer anzeigen" im Fenster "An den Prozess anhängen" des Debuggers zu setzen. Nach maximal einer Minute sollte der Debugger beim Aufruf der Methode Execute() anhalten - sofern am obigen Aktivierungsintervall (SPMinuteSchedule) nichts geändert wurde!

    Auf ähnliche Weise kann man übrigens auch Testen, ob unser Feature-EventReceiver funktioniert. Einfach je einen Breakpoint auf die überschriebenen Methoden setzen und diesmal an den Prozess W3WP.EXE anhängen. Möglicherweise gibt es den Prozess in mehreren Instanzen. Sofern man die richtige Instanz nicht kennt, kann man sich auch einfach -zu Testzwecken- an alle Instanzen hängen.

    Unser Feature läßt sich natürlich auch über die Zentraladministration aktivieren bzw. deaktiviren - in der Websitesammlungsverwaltung klickt man auf Websiteauflistungsfeatures. In der Liste Websitesammlungs-Features sollte sich unser TimerJob-Feature finden.

    Ich habe hier das grundsätzliche Vorgehen beim Erstellen und Installieren eines benutzerdefinierten SharePoint TimerJobs beschrieben. Auf diese Weise funktioniert es zwar, aber für das Deployment auf ein produktives System ist diese Vorgehensweise natürlich nicht geeignet. Hierfür bietet es sich an, aus unserem SharePoint-Feature eine SharePoint-Solution zu machen

     

    Add to Technorati Favorites

Need SharePoint Training? Attend a SharePoint Bootcamp!

Posts (c) their respective authors. Everything else (c) 2007 SharePoint Experts