How To: Wie behebt man das Problem mit der Add-On Meldung bzgl. der NAME.DLL?

August 8th, 2008 by oliver wirkus

Vielleicht hat der eine oder andere Leser meines Blogs dieses Problem selbst schon einmal gehabt: man surft im Internet und m”chte sich eine neue auf SharePoint-basierende Site ansehen und bekommt von seinem Browser folgende Meldung angezeigt:

   

Gerade bei Internet-Auftritten von Unternehmen ist diese Meldung nicht nur l„stig, sondern wirkt auf Kunden oft auch befremdlich. In Zeiten eines zum Glck deutlich gestiegen Sicherheitsbewuátseins beim Surfen im Internet hinterl„át eine solche Meldung bei vielen Surfern ein ungutes Gefhl.

Bei Intranet-Auftritten ist die NAME.DLL, die sich letztendlich hinter dieser Meldung verbirgt, ntzlich und hilfreich. Dahinter verbirgt sich u.a. die Smart-Tag bzw. Presence-Funktionalit„t. Bei Internet-Auftritten hat diese NAME.DLL meist aber keine sinvolle Funktion, sondern st”rt eher dadurch, dass es Besucher einer Site verwirrt oder sogar abschreckt.

Microsoft beschreibt in einem Artikel zu KB931509, wie man dieses Problem l”sen kann. Hier werden mehrere L”sungsm”glichkeiten beschrieben, denn man kann dieses Problem auch einfach dadurch l”sen, dass man die betreffende Site zur Liste der vertrauenswrdigen Sites im IE7 hinzufgt. Fr Intranet-Sites mag das eine L”sung sein, bei Internet-Auftritten wird man seinen Besuchern kaum zumuten wollen, den neuen Firmenauftritt als vertrauenswrdig einzustufen! Im KB-Artikel von Microsoft wird aber auch eine serverseitige L”sungsm”glichkeit beschrieben, die ein paar Žnderungen an der Masterpage voraussetzt.

šbrigens: im Zusammenhang mit einem SharePoint Add-On gibt es noch ein weiteres bekanntes Problem, welches sogar den IE7 zum Abstrzen bringen kann! Darber und ber eine L”sungsm”glichkeit habe ich hier bereits vor einiger Zeit berichtet.

 

Add to Technorati Favorites

How to: Mails mit Anhang aus Workflows heraus verschicken

July 17th, 2008 by oliver wirkus

Das neue VisualStudio 2008 untersttzt einen Entwickler sehr gut bei der Erstellung von Workflows fr SharePoint. Besonders der grafische Editor erleichtert das Erstellen der Workflow-Grundstruktur enorm. Momentan arbeite ich an einem benutzerdefinierten Workflow fr einen inhouse Kunden. Dieser Workflow wird von einer SharePoint-Liste beim Einfgen eines neuen Elements getriggert. Eine der Aufgaben dieses Workflows soll das Verschicken einer E-Mail sein. Keine groáe Sache – schlieálich bietet der grafische Workflow-Editor in VisualStudio 2008 das SendMail-Objekt genau fr solche Zwecke an. Aber sobald man versucht, mit diesem SendMail-Objekt eine E-Mail mit einem Anhang (Attachment) zu verschicken, st”át man auf Schwierigkeiten. Das SendMail-Objekt untersttzt leider keine Attachments!

Es blieb mir also nichts anderes brig, als statt des SendMail-Objekts eine normale Code-Activity zu verwenden. Erschwerend kommt noch hinzu, dass die Datei, die als E-Mail Attachment verschickt werden soll, nicht im Dateisystem abgelegt wurde, sondern als Anhang an einem Listen-Eintrag einer SharePoint-Liste vorlag. Es ergaben sich also 2 Probleme, fr die ich eine L”sung brauchte:

  1. wie verschickt man E-Mails mit Anhang?
  2. wie bekommt man den Dateianhang aus der Liste heraus und in die E-Mail herein?

Das erste Problem l„át sich mit der Klasse System.Net.Mail.MailMessage l”sen. Dieses Objekt l„át sich „hnlich verwenden, wie das SendMail-Objekt, untersttzt aber Anh„nge. Im Gegensatz zum SendMail-Objekt muss man aber hier den zu verwendenden SMTP-Server und die Absenderadresse angeben. Beides ist normalerweise in SharePoint (ber die Zentraladministration) bereits definiert und man kann sich diese Einstellungen ber das Objektmodell besorgen.

Das zweite Problem ist etwas kniffliger. šber den Dateinamen und die URL des SharePoint-Listen-Anhangs kann man sich ein SPFile-Objekt erzeugen. Dieses SPFile-Objekt kann man dann durch ein Attachment-Objekt auslesen lassen. Danach kann man dieses Attachment-Objekt der Attachments-Collection von MailMessage hinzufgen.

Auch wenn es ursprnglich nach einer einfachen Sache aussah, stellte sich das Problem "wie versende ich eine E-Mail mit Dateianhang aus einem Workflow" als interessante Herausforderung dar. Fr interessierte Leser fge ich diesem Posting ein kleines Code-Snippet bei. Es zeigt eine M”glichkeit, wie man einen (bzw. den ersten) Dateianhang eines Eintrags in einer SharePoint-Liste als Anhang einer E-Mail verschicken kann.

private void SendCustomerMail_ExecuteCode(object sender, EventArgs e)
{
   string strPathAttachment = string.Empty;
   SPListItem oItem = workflowProperties.Item;
   SPWebApplication oWebApp = workflowProperties.Site.WebApplication;

   MailMessage oMail = new MailMessage();

   oMail.From = new MailAddress(oWebApp.OutboundMailSenderAddress);
   oMail.To.Add(m_strMailAddressCustomer);
   oMail.Subject = m_strMailSubjectCustomer;
   oMail.Body = m_strMailTextCustomer;
   oMail.IsBodyHtml = false;

   SPAttachmentCollection oAttachments = oItem.Attachments;

   if (0 < oAttachments.Count)
   {
     strPathAttachment = oAttachments.UrlPrefix + oAttachments[0];

     SPFile oFile = oItem.ParentList.ParentWeb.GetFile(strPathAttachment);
     Attachment oAttachment = new Attachment(oFile.OpenBinaryStream(), oFile.Name);

     oMail.Attachments.Add(oAttachment);
   }

   string strSmtpServer = oWebApp.OutboundMailServiceInstance.Server.Address;
   SmtpClient oSmtpClient = new SmtpClient(strSmtpServer);
   oSmtpClient.UseDefaultCredentials = true;

   oSmtpClient.SendAsync(oMail, "UserState");
}

An dieser Stelle m”chte ich noch auf ein „lteres Posting von mir hinweisen: dem MailMessage-Objekt kann man per Eigenschaft (IsBodyHTML) mitteilen, dass es die E-Mail als 'Plain-Text' verschicken soll. Beim SendMail-Objekt geht das leider nicht so einfach. Es ist aber dennoch m”glich – hier der Link zu meinem Beitrag.

 

Add to Technorati Favorites

Programmierung: Potentielle Memory-Leaks bei SPSite und SPWeb

July 10th, 2008 by oliver wirkus

Beim Programmieren im SharePoint-Umfeld sollte man groáen Wert auf saubere und sichere Programmierung legen. Kaum etwas ist „rgerlicher, als wenn das selbstentwickelte WebPart im Portal des Kunden bei jedem Aufruf Speicher anfordert und diesen nicht wieder zurck gibt.

Gerade beim SharePoint Objektmodell sollte man sehr genau wissen, was man macht bzw. wie man es macht – dies gilt besonders bei der Verwendung von SPSite und SPWeb.

Eine gute Zusammenfassung ber das Thema habe ich im Blog von Roger Lamb gefunden. Sein Artikel zum Thema SharePoint 2007 and WSS 3.0 Dispose Patterns by Example ist fr alle SharePoint-Programmierer sehr interessant.

 

Add to Technorati Favorites

Desktop-Icons / Layout speichern und Wiederherstellen

July 4th, 2008 by oliver wirkus

Wenn ich fr einen Vortrag oder eine Pr„sentation zu einem Kunden fahre, nehme ich gern mein Pr„sentations-Notebook mit und schlieáe es vor Ort an einen Beamer an. Mein Notebook paát dann aber die Aufl”sung an die maximale Aufl”sung des angeschlossenen Beamers an und zerwrfelt mir dabei jedesmal die Sortierung der Icons auf meinem Desktop. Da ich meist einige Icons auf meinem Desktop habe, ist ein Punkt der blichen Nachbereitung das manuelle (und l„stige) Wiederherstellen meines gewohnten Desktop-Icon Layouts.

Ich denke, jeder der schon einmal eine Pr„sentation mit seinem Notebook und einem Beamer gehalten hat, kennt dieses Problem.

Bei meiner letzten Kundenpr„sentation hatte ein Mitarbeiter im Anschluá an meinen Vortrag einen guten Tipp fr mich: es gibt ein kleines Tool, mit dem man das aktuelle Icon-Layout auf dem Desktop speichern und sp„ter bei Bedarf wieder herstellen kann. Dieses praktische Tool kann man sich hier herunterladen.

Leider klappt die Installation nur fr Systeme, auf denen Windows XP l„uft. Wer Vista oder -wie ich- Vista 64-Bit einsetzt, findet eine erweiterte Version hier.

So eine Funktion sollte es direkt in Windows Vista geben …

Nachtrag: von Midi-Ox gibt es nun auch eine L”sung, um die Icon-Position auf dem Desktop zu speichern und wiederherzustellen.

 

Add to Technorati Favorites

How to: Sharepoint-Liste als eigenen Suchbereich einrichten

June 20th, 2008 by oliver wirkus

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

June 6th, 2008 by oliver wirkus

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

June 4th, 2008 by oliver wirkus

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

Design-Tipp: Formatierung des Web-Title durch Änderungen an der Masterpage

May 29th, 2008 by oliver wirkus

Eine der Arbeiten der letzten Wochen war die Design-Žnderung unseres eigenen Intranets – natrlich auf SharePoint-Basis! Ziel war, das Design moderner zu gestalten und besser an das CI unserer Firma anzupassen. Eine kleine Herausforderung dabei war die Startseite, denn der Titel sollte farblich hervorgehoben werden. Bisher lautete der Titel der Startseite "MITARBEITER-PORTAL" – dieser sollte in "MITARBEITER-PORTAL" ge„ndert werden.

Der Titel der Startseite entspricht normalerweise dem Titel des Webs zu dem die Startseite geh”rt. Dieser Titel wird als Text eingegeben und ber die Masterpage auf jeder Seite des Webs eingeblendet. Die Frage, die sich fr uns nun stellte war: wie kann man diese Texteinblendung manipulieren? Da dieser Mechanismus ber die Masterpage gesteuert wird, ergab sich eine weitere Frage: wenn Žnderungen an der Masterpage n”tig sind, wie kann man diese Žnderungen auf die Startseite bzw. das Root-Web begrenzen?

Wie immer gibt es mehrere L”sungsm”glichkeiten. Wir haben uns fr die folgende L”sung entschieden:

Wir haben auf der Masterpage ein kleines Script hinterlegt:

 public void CheckForRoot()
 {
     SPWeb oWeb = SPControl.GetContextWeb(Context);
     if(false == String.IsNullOrEmpty(oWeb.Name))
     {
         Response.Write("<div id="OWNTITLE" class="style2"><strong>" + oWeb.Title.ToString() + "</strong></div>");
     }
     else
     {
      Response.Write("<div id=OWNTITLE class=style3><strong><span class=ms-menuitemdescription>M</span><span class=style4>IT</span><span class=ms-menuitemdescription>ARBEITER-PORTAL</span></strong></div>");
     }
 }

Dieses Script macht sich eine Eigenschaft des SharePoint-Objektmodels zu Nutze: Zitat aus dem MSDN Developer Network:"The Name property returns an empty string if the site is a top-level site.". Man kann also ein Root-Web u.a. daran erkennen, dass die Name-Eigenschaft leer ist. Erkennt das Script, dass es gerade im Context des Root-Webs l„uft, gibt es einen HTML-String zurck, der die Ausgabe "MITARBEITER-PORTAL" erzeugt. Ansonsten wird einfach die Title-Eigenschaft des aktuellen Webs zurckgegeben.

Damit dieses Script aber auch wirklich das gewnschte Resultat bringt, mssen noch weitere Žnderungen vorgenommen werden. In der Masterpage muss der Code, der normalerweise fr die Ausgabe der Title-Eigenschaft zust„ndig ist, durch einen Aufruf des obigen Scripts ersetzt werden.

Das sieht dann z.B. folgendermaáen aus:

 <asp:ContentPlaceHolder id="PlaceHolderSiteName" runat="server">
    <h1 class="ms-sitetitle">
    <br><% CheckForRoot(); %></h1>
 </asp:ContentPlaceHolder>

Die interessante Zeile ist in roter Fettschrift dargestellt.

Weiterhin muss in der web.config noch eingestellt werden, dass Scripte in der Masterpage erlaubt sind. Das sieht dann aus wie folgt:

<configuration>

  <SharePoint>

    <SafeMode MaxControls="200" CallStack="false" DirectFileDependencies="10" TotalFileDependencies="50" AllowPageLevelTrace="false">

      <PageParserPaths>

         <PageParserPath VirtualPath="/_catalogs/masterpage/*" CompilationMode="Always" AllowServerSideScript="true" IncludeSubFolders="true" />

      </PageParserPaths>

    </SafeMode>

  </SharePoint>

</configuration>

Die in roter Fettschrift dargestellte Zeile muss einfach eingefgt werden. Damit werden Scripte in der Masterpage erlaubt.

Auf diese Weise l„át sich durch ein wenig Scripting in der Masterpage eine einfache Dynamik in der Darstellung von Seiten bzw. Webs erzeugen.

 

Add to Technorati Favorites

Bug in der SharePoint Benutzerverwaltung

April 23rd, 2008 by oliver wirkus

In unserem Kundenportal ist vor einigen Tagen ein bemerkenswerter Fehler aufgetreten. Nachdem wir Probleme mit einem Account bzw. dessen Profildaten hatten, haben wir diesen Account aus dem ActiveDirectory und aus der SharePoint-internen Benutzerverwaltung gel”scht und wieder unter dem gleichen Namen neu angelegt. Um zu probieren, ob dieser neu-angelegte Account wieder einwandfrei funktioniert, habe ich mich mit diesem Account an unserem Kundenportal angemeldet. Ich war verwundert, dass ich eine Fehlermeldung von unserem SharePoint Server bekam, die sinngem„á besagte, dieser Account h„tte keine Rechte fr die aktuelle Seite. Ich habe mich dann als Administrator angemeldet um nachzusehen, was mit dem Account nicht stimmt. In der SharePoint-Benutzerverwaltung wurde dieser Account aus allen Gruppen entfernt, in denen er zuvor Mitglied war.

Dieses Problem muáten wir genauer untersuchen. Ich habe deswegen in einer virtuellen Umgebung mit lokalen Benutzerkonten versucht, dieses Problem nachzuvollziehen. Folgendermaáen bin ich dabei vorgegangen:

  • in der lokalen Benutzerverwaltung des Betriebssystems habe ich einen neuen Account angelegt.
  • in der SharePoint-Benutzerverwaltung habe ich diesen Account der SharePoint-Gruppe 'Mitglieder' hinzugefgt.
  • ich habe nun versucht, mich mit diesem neuen Account bei SharePoint anzumelden. Wie erwartet klappte dies problemlos!
  • zur Sicherheit habe ich mich wieder abgemeldet und das Browserfenster geschlossen.
  • in der lokalen Benutzerverwaltung habe ich nun den eben angelegten Account gel”scht
  • in der SharePoint-Benutzerverwaltung habe ich diesen Account auch gel”scht.
  • ein Anmelden mit diesem gel”schten Account schl„gt nun -wie erwartet- fehl.
  • nun habe ich in der lokalen Benutzerverwaltung des Betriebssystems den zuvor gel”schten Account mit exakt den gleichen Daten wieder angelegt.
  • in SharePoint habe ich diesen neu angelegten Account wieder der SharePoint-Gruppe 'Mitglieder' hinzugefgt.
  • beim Anmelden an SharePoint mit diesem neu-angelegten Account kommt es nun wieder zu der genannten Fehlermeldung "Sie haben keine Berechtigung …."
  • als ich mich wieder als Administrator angemeldet habe, stellt ich fest, dass der neu-angelegte Account aus der SharePoint-Gruppe 'Mitglieder' verschwunden war.

Dies erkl„rt zwar die Fehlermeldung, aber warum verschwindet ein gel”schter Account, der mit den gleichen Daten wieder neu angelegt wurde, nach dem ersten Anmelden an SharePoint aus allen SharePoint-Gruppen?

SharePoint speichert alle Accounts in einer Tabelle in der Content Database und weist diesen Accounts eine eigene ID zu. Diese ID wird sichtbar, wenn man mit der Maus auf einen Benutzernamen in der SharePoint-internen Benutzerverwaltung zeigt (also Mauszeigen nur auf den Benutzernamen schieben, nicht klicken!).

Nachdem ein Account in SharePoint gel”scht wird, wird dieser nicht aus der User-Tabelle entfernt, sondern dort nur als gel”scht markiert. Erzeugt man nun einen neuen Account, der einem gel”schten Account exakt entspricht und fgt diesen Account einer SharePoint-Gruppe hinzu, tauchen in der User-Tabelle zwei Accounts mit dem gleichen Namen, aber unterschiedlichen IDs auf. Einer (der mit der kleineren ID) ist als gel”scht markiert, der andere mit der gr”áeren ID ist der neu-angelegte Account. Loggt man sich nun mit einem solchen gel”scht-und-neu-angelegt Account bei SharePoint ein, kommt SharePoint offensichtlich durcheinander und verwendet die ID des gel”schten Accounts. Dies kann man sehen, wenn man sich die IDs wie oben beschrieben ansieht.

Bevor ich mich mit diesem Fehler an den Microsoft-Support wenden wollte, wollte ich erst sichergehen, dass es sich wirklich um einen Fehler handelte. Fabian Moritz war so freundlich, mich ein wenig zu untersttzen. Er konnte mir das geschilderte Verhalten best„tigen, denn in seinem Blog-Artikel hatte er bereits Untersuchungen zur SharePoint-internen Benutzerverwaltung angestellt.

Grunds„tzlich gibt zwei L”sungsm”glichkeiten fr dieses Problem – diese beiden M”glichkeiten konnte mir der Microsoft-Support im Nachhinein auch best„tigen:

  • der Person Synchronization Timer Service startet normalerweise ungef„hr 1 Mal pro Stunde und sollte die Unstimmigkeiten in der Benutzerverwaltung wieder korrigieren. Bedeutet: man muá mindestens einen Timerzyklus abwarten. Der Microsoft-Support erw„hnte aber auch noch, dass manchmal auch ein manueller vollst„ndiger Profilimport n”tig sei. In Ausnahmef„lle msse der vollst„ndige Profilimport auch mehrmals durchgefhrt werden. Diese Aussagen des Microsoft-Supports habe ich nicht weiter berprft.
  • es gibt ein STSADM-Kommando, mit welchem man die Unstimmigkeiten in der SharePoint-internen Benutzerverwaltung manuell korrigieren kann: STSADM -o migrateuser <oldlogin> <newlogin> -ignoresidhistory

Mit diesem STSADM-Kommando konnten wir unser Problem l”sen. Wichtig ist aber, dass man hier den Parameter -ignoresidhistory nicht vergisst! šbrigens tritt das geschilderte Problem mit lokalen Benutzerkonten und mit Benutzerkonten in einem ActiveDirectory auf (Stichwort: Windows-Authentifizierung). Auf einer Testinstallation, die mit Forms Authication arbeitet, trat das Problem nicht auf.

Wer sich noch ein wenig ber die interne SharePoint-Benutzerverwaltung informieren m”chte, findet entsprechende Informationen im Blog von Fabian Moritz in seinem Artikel ber die UserInfo-Tabelle.

 

Add to Technorati Favorites

Kleiner 'Bug' im Data View Webpart bei der Formatierung von Währungsangaben

April 7th, 2008 by oliver wirkus

Vor ein paar Tagen sind wir auf einen Bug im Data View Webpart (bzw. auf deutsch: Datenansichtswebpart) gestossen, als wir den Inhalt einer SharePoint-Liste in einem anderen Web mithilfe des SharePoint-Designers anzeigen wollten. Ausgangsbasis war eine deutschsprachige SharePoint-Installation.

In der ursprnglichen Liste gab es u.a. eine Spalte mit dem Namen 'Preis', die als W„hrung in ? definiert war. Der Inhalt dieser Liste sollte mit dem Data View-Webpart auf einer Seite in einem anderen Web angezeigt werden. Dies klappte auch anfangs problemlos, nur als in der ursprnglichen Liste Preise mit Nachkommastellen (also z.B. 49,95?) auftauchten, wurde dieser Preis im Data View-Webpart nicht mehr angezeigt. An der Stelle, an der wir eigentlich 49,95? erwartet haben, wurde nichts mehr angezeigt. Bei Preisen ohne Nachkommastellen klappt die Anzeige hingegen wie erwartet.

Nach einigem Recherchieren und Ausprobieren sind wir zu der Erkenntnis gekommen, dass das Data View-Webpart offensichtlich ein Problem mit Punkt und Komma und l„nderspezifischen Einstellungen hat. Zwar wird im entsprechenden XSLT-String die deutsche LCID mit angegeben, aber trotzdem klappt die korrekte Formatierung von W„hrungsspalten nicht.

Im Original sieht dieser XSLT-String folgendermaáen aus:

<xsl:value-of select="format-number(@Preis, '?#.##0,00;-?#.##0,00', 'lcid1031')" />

Eigentlich ist diese Formatierung richtig, denn in Deutschland werden die Nachkommastellen mit einem Komma abgetrennt. Das Problem kommt daher, dass wir auch einen deutschen SQLServer installiert haben – SharePoint aber wohl insgeheim von einem englischen SQLServer und damit von einem englischen Zahlenformat ausgeht.

Zum Glck kann man ber den XSLT-Editor des Data View-Webparts diesen String ein wenig ver„ndern – und ber diesen Weg l„át sich der beschriebene Fehler im Data View-Webpart dann auch korrigieren.

Wir haben den XSLT-String ein wenig modifiziert:

<xsl:value-of select="format-number(translate(@Preis,',','.'), '#.##0,00 ?;-#.##0,00 ?', 'lcid1031')" />

Mit diesem String ersetzen wir vor der eigentlichen Formatierung ein Komma durch einen Punkt – und haben damit eine englischsprachige Zahlenformatierung erreicht. Dieses englischsprachige Zahlenformat kann das Data View-Webpart jetzt problemlos als deutsche W„hrung formatieren und ausgeben.

Weitere Informationen zu diesem Thema finden sich in diesem MSDN-Artikel zu KB293469.

 

Add to Technorati Favorites