Ganz still und heimlich hat IBM im Fix Pack 3 zum IBM Domino-Server in
der Version 9.0.1 ein kleines Juwel versteckt.
Okay, das ist jetzt nichts für jeden,
aber wer einige riesige Datenbanken auf seinem Server hat, kann seinen
Benutzern jetzt vielleicht etwas weniger Wartezeiten bescheren.
Worum geht es? Wenn in einer Datenbank
neue Dokumente erstellt oder vorhandene geändert werden, müssen die Ansichten-Indexe
aktualisiert werden - das sind interne Datenstrukturen zu den Ansichten,
die im Wesentlichen alle Informationen enthalten, die der Notes-Client
dem Benutzer anzeigt.
Diese Aufgabe übernimmt der View Indexer
ziemlich verlässlich und normalerweise im Hintergrund. Nur wenn er nicht
"rechtzeitig" fertig wird, bekommt der Benutzer, der eine noch-nicht-aktuelle
Ansicht in der Datenbank öffnet, die Mitteilung, dass die Ansicht gerade
noch aktualisiert werden muss und er derweil ja woanders weiter arbeiten
könne (sic!).
Ein Randproblem dabei ist, dass nur
ein View Indexer gleichzeitig auf einer Datenbank arbeiten darf und er
diese blockiert. Also auch wenn der Benutzer eine kleine Ansicht öffnet,
während der View Indexer noch für einen anderen Benutzer eine große Ansicht
aktualisiert, muss der Benutzer ziemlich lange warten.
Die Neuerung ist der "Dedicated
View Thread".
Damit kann man besonders wichtige und
große Ansichten in einer Datenbank markieren, die dann von einem eigenen
Thread des View Indexers aktualisiert werden. Damit blockiert nicht mehr
die ganze Datenbank, sondern Benutzer der "kleinen" Ansicht können
schnell wieder weiter arbeiten.
Noch wichtiger aber ist, dass Benutzer
auch die große Ansicht schnell öffnen können - und zwar im Zustand vor
den letzten Änderungen, also dem Zustand, als der Ansichten-Index das letzte
Mal aktualisiert wurde. Sie sehen dann zwar nicht die allerneuesten Informationen
(aus den letzen x Sekunden), können dafür aber sofort und ohne zu warten
weiter arbeiten.
Jeder Administrator bzw. Datenbankverantwortlich
muss natürlich betrachten, ob das für die jeweilige Datenbank möglich ist
oder immer und auf jeden Fall die aktuellsten Informationen dargestellt
werden müssen.
Das wichtigste "Bekannte Problem"
ist, dass bis zur Installation des Fix Pack 4 sich die Funktion nicht mehr
dauerhaft deaktivieren lässt.
Nach meiner Beobachtung wird das übrigens
im "$Index"-Item der View gespeichert.
Quellen:
What
is the "Dedicated View Thread" feature in IBM Domino?
Known
issues with the "Dedicated View Thread" feature in IBM Domino
Posts Tagged: ‘Quick-Tipp’
Quick Tipp: Schnelladressierung zuerst auf dem Server suchen lassen


Das liegt natürlich zunächst einmal mit daran, dass Notes natürlich nicht von sich aus zwischen beruflich und privat unterscheiden könnte - wie sollte auch das gehen? Aber hängt auch damit zusammen, dass Vorschläge nach Häufigkeit der Korrespondenz gewichtet angezeigt werden.
In diesem Fall war es jedoch so, dass das Workspace-Verzeichnis, in dem die Datenbank für die Gewichtung (DIP) abgelegt ist, zurückgesetzt worden war. Somit waren private und die berufliche Adressen erst einmal theoretisch gleichwertig. Allerdings fließt hier außerdem die Standardsuchreihenfolge ein, die bei der Schnelladressierung immer erst lokale Adressbücher und dann erst Serveradressbücher berücksichtigt.
Aber genau das ist einer der Punkte, an denen man einhaken kann, indem man die Schnelladressierung die Adressen vom Server stärker gewichten lässt als die lokalen. Zu diesem Zweck muss, beginnend mit Notes 8.5.3, die Variable:
TypeAheadShowServerFirstDefault=1
in die notes.ini eingetragen werden.
Details dazu können auch hier nachgelesen werden: "New Type-ahead Feature in Notes v9"
Das Ergebnis sieht aus, wie in folgendem Screenshot:

Die Treffer von Server-Adressbüchern inklusive Verzeichnisunterstützung werden oben gelistet und erst dann unter "Local Directory" die Treffer aus allen lokalen Adressbüchern.
Dieses Ergebnis mag auch nicht für alle Nutzer und alle Situationen nützlich sein. Das ist sehr stark abhängig von der Gesamtkonfiguration der Adressbücher und der persönlichen Nutzung. Daher gibt es hier keinen goldenen Weg.
Die Schnelladressierung in Notes ist ein sehr hilfreiches und wertvolles Feature. Aber auch das Feature das vermutlich alleine mit am meisten Supportfälle verursacht. Nicht zuletzt, weil es nahezu untrennbar mit dem nicht minder wertvollen Feature "Letzte Kontakte", das seinerseits für einige Fragen gut ist, verheiratet ist. An dieser Stelle sind Administratoren herausgefordert, ihre Nutzer zu kennen und sinnvolle Vorgaben zu machen und gleichzeitig den First-Level-Support zu schulen, bestimmte Fragen diesbezüglich beantworten bzw. die Konfiguration wie oben kurzfristig ändern zu können.
Quick-Tipp: E-Mails mit POP3 abholen – E-Mail-Server mag nur TLS
Seit früher in diesem Jahr erlauben viele E-Mail-Provider nur noch das
verschlüsselte Abholen und Senden von E-Mails über POP3, IMAP und SMTP.
Wegen eines Sicherheitsproblems mit SSL v3 (POODLE) schalten einige Server-Betreiber
dieses Protokoll jetzt ab und erlauben nur noch TLS. Dazu gehören auch
die E-Mail-Provider GMX und web.de.
Wenn man mit dem Notes-Client E-Mails per POP3 von E-Mail-Servern abholen will, die nur TLS erlauben, muss man den Notes-Client aktualisieren - und zwar (mindestens) auf die Version 9.0.1 Fix Pack 2 Interims Fix 2, den es im Moment nur relativ versteckt und nur für Windows gibt. Danke, Daniel, für den Tipp: Some Additonal TLS 1.0 Information).
Danach funktionierte es bei mir leider immer noch nicht, aber das lag einfach an einer falschen Einstellung.
Im Benutzerkonto (Account)-Dokument gibt es unter Erweitert die Einstellung "SSL-Sitezertifikate annehmen", die bei mir auf "Nein" stand - auf "Ja" korrigiert, läuft die E-Mail-Abfrage sofort.
Danke an Wollgraeser für den Tipp in die richtige Richtung: Re: Domino Client mit GMX Mail und SSL - TLS
Wenn man mit dem Notes-Client E-Mails per POP3 von E-Mail-Servern abholen will, die nur TLS erlauben, muss man den Notes-Client aktualisieren - und zwar (mindestens) auf die Version 9.0.1 Fix Pack 2 Interims Fix 2, den es im Moment nur relativ versteckt und nur für Windows gibt. Danke, Daniel, für den Tipp: Some Additonal TLS 1.0 Information).
Danach funktionierte es bei mir leider immer noch nicht, aber das lag einfach an einer falschen Einstellung.
Im Benutzerkonto (Account)-Dokument gibt es unter Erweitert die Einstellung "SSL-Sitezertifikate annehmen", die bei mir auf "Nein" stand - auf "Ja" korrigiert, läuft die E-Mail-Abfrage sofort.
Danke an Wollgraeser für den Tipp in die richtige Richtung: Re: Domino Client mit GMX Mail und SSL - TLS
Quick-Tipp: Nach Umbenennung eines Sametime-Servers startet DB2 nicht mehr
Kürzlich habe ich den Namen eines Sametime-Servers geändert, genauer den
Hostnamen der (virtuellen) Maschine. Auf der gleichen Box läuft auch der
IBM DB2-Server, der für den System Console-Server und den Proxy-Server
benötigt wird. Alles läuft unter Microsoft Windows 2008 R2.
Nach dem für die Umbenennung notwendigen Neustart begrüßte mich der DB2-Dienst mit "SQL1042C Ein Systemfehler ist aufgetreten" ("SQL1042C An unexpected system error occurred") und weigerte sich fortan zu starten - weder als Dienst, noch manuell. Sogar das DB2-Befehlsfenster beendete sich immer sofort wieder.
In den Details der Protokolldateien fand ich die - mehr oder wendiger - informative Meldung: "SQL1022C There is not enogh memory available to process the command".
Zu diesem Zeitpunkt waren aber mehr als 6 GB physischer und sogar ungefähr 20 GB logischer Arbeitsspeicher noch frei!?!
Nach langer, ausgiebiger Recherche kam ich zu dem Schluss, dass mich die Fehlermeldung erfolgreich komplett auf die falsche Fährte gelockt hatte.
DB2 speichert unter Windows einige Informationen, u.a. über lokale Benutzer (in der Form ServerName\Benutzername) und den Hostnamen des Server in der Windows-Registrierung unter dem Schlüssel HKEY_LOCAL_MACHINE\SOFTWARE\IBM\DB2\InstalledCopies\DB2COPY1\GLOBAL_PROFILE. (ändere "DB2COPY1", wenn deine Instanz anders heißt).
Wenn ich es vorher gekannt hätte, wäre ich einfach den Anweisungen in diesem Dokument gefolgt: Changing hostname of the DB2 server. Aber ich wollte jetzt nicht erst die Umbenennung rückgängig machen, um alles jetzt einmal "richtig" zu wiederholen.
In diesem Blog-Eintrag How do you rename the Windows machine name for a DB2 v9.1.x database? und vor allem in den zugehörigen Kommentaren fand ich doch noch einen schnellere, einfacheren und direkteren Weg, um das DB2-System zu wieder zum Laufen zu bringen.
Da ich einen Workgroup-Server (WSE = Workgroup Server Edition) habe, entfällt das Anpassen der db2nodes.cfg.
In einer als Administrator gestarteten Eingabeaufforderung habe ich folgende Befehle ausgeführt:
Nach dem für die Umbenennung notwendigen Neustart begrüßte mich der DB2-Dienst mit "SQL1042C Ein Systemfehler ist aufgetreten" ("SQL1042C An unexpected system error occurred") und weigerte sich fortan zu starten - weder als Dienst, noch manuell. Sogar das DB2-Befehlsfenster beendete sich immer sofort wieder.
In den Details der Protokolldateien fand ich die - mehr oder wendiger - informative Meldung: "SQL1022C There is not enogh memory available to process the command".
Zu diesem Zeitpunkt waren aber mehr als 6 GB physischer und sogar ungefähr 20 GB logischer Arbeitsspeicher noch frei!?!
Nach langer, ausgiebiger Recherche kam ich zu dem Schluss, dass mich die Fehlermeldung erfolgreich komplett auf die falsche Fährte gelockt hatte.

DB2 speichert unter Windows einige Informationen, u.a. über lokale Benutzer (in der Form ServerName\Benutzername) und den Hostnamen des Server in der Windows-Registrierung unter dem Schlüssel HKEY_LOCAL_MACHINE\SOFTWARE\IBM\DB2\InstalledCopies\DB2COPY1\GLOBAL_PROFILE. (ändere "DB2COPY1", wenn deine Instanz anders heißt).
Wenn ich es vorher gekannt hätte, wäre ich einfach den Anweisungen in diesem Dokument gefolgt: Changing hostname of the DB2 server. Aber ich wollte jetzt nicht erst die Umbenennung rückgängig machen, um alles jetzt einmal "richtig" zu wiederholen.
In diesem Blog-Eintrag How do you rename the Windows machine name for a DB2 v9.1.x database? und vor allem in den zugehörigen Kommentaren fand ich doch noch einen schnellere, einfacheren und direkteren Weg, um das DB2-System zu wieder zum Laufen zu bringen.
Da ich einen Workgroup-Server (WSE = Workgroup Server Edition) habe, entfällt das Anpassen der db2nodes.cfg.
In einer als Administrator gestarteten Eingabeaufforderung habe ich folgende Befehle ausgeführt:
- db2extsec -r
- db2extsec /a DB2ADMNS /u DB2USERS
- db2set -g DB2SYSTEM={NewHostName}

Quick Tip: Benutzervalidierung durch erneute Passwort-Eingabe
Ich hatte letzte Woche eine "kleine" Anforderung von einem Kunden:
Er ist interessiert an der erweiterten kommerziellen Version unseres
assono Password-Safes. Aber seine
interne Revisionsabteilung hatte noch eine neue Anforderung: Der aktuelle
Benutzer sollte vor dem Öffnen eines Dokuments oder beim Kopieren eines
Passworts in die Zwischenablage direkt aus einer Ansicht vorher noch einmal
sein Notes-Passwort eingeben müssen.
Dafür gibt es ein @Command: ToolsUserLogoff. Wenn es ausgeführt wird, logt es den Benutzer aus. Wenn man dieses Kommando mit etwas Code kombiniert, mit dem man auf den Server zugreift, erscheint der Passworteingabe-Dialog, wie es zum Bespiel hier beschrieben wurde: Forcing user re-entry of passwords for electronic signatures in script.
Dieser Ansatz hat aber für mich drei Nachteile:
1. Er funktioniert nicht offline, also z. B. bei einer lokalen Replik, weil der Serverzugriff notwendig ist, um den Passworteingabe-Dialog zu öffnen.
2. Ich möchte es nutzen, um das Öffnen von existierenden Dokumenten abzusichern. Wenn der Benutzer den Dialog abbricht, wird das Dokument trotzdem geöffnet.
3. Der Formel-Code muss im QueryOpen-Ereignis der Maske eingetragen werden. Dort brauche ich aber zwangsläufig LotusScript.
Also musste ich weiter suchen nach einer Lösung in LotusScript. Und ich wurde fündig bei Eknori, der in 2004 diesen Blog-Eintrag verfasst hat: @Command(ToolsUserLogoff) in Lotus Script.
Diese Lösung muss für die aktuellen Versionen von Notes angepasst werden, weil man sich jetzt nicht mehr mit F5, sondern mit Strg-F5 auslogt.
Ich war immer noch nicht überzeugt, dass dies die bestmögliche Lösung für mein Problem sein sollte. Es ist wegen der Verwendung von Windows-DLLs nicht auf andere Plattformen übertragbar, der Benutzer bleibt ausgeloggt, wenn er den Passworteingabe-Dialog abbricht, es würde wohl auch nicht lokal funktionieren usw.
Ich suchte weiter und fand diese großartige Idee: Mittels Notes C-API auf den privaten Teil der Benutzer-ID-Datei zugreifen, genauer mit der REGGetIDInfoString-Function mit REGIDGetPrivateKey als infoType.
Vielen Dank an Davy Vanherbergen für seinen OpenNTF Code Bin-Beitrag (von 2003!): Call notes password prompt from lotusscript
Ich habe seine Idee genommen und auf "meine Art" neu implementiert. Dabei habe ich unsere C-API-Hilfsfunktionen genutzt, und ich lasse den Benutzer eine andere ID-Datei auswählen, wenn die in der notes.ini eingestellte nicht die seine ist.
Function ValidateCurrentUser As Boolean
'/**
' * validates current user by letting him enter his password
' *
' * @return True, if user has successfully entered his password
' *
' * @author Thomas Bahn/assono <tbahn@assono.de>
' * @version 2014-09-30
' */
Const MAXOUTBUFRLEN% = 4096
Dim idFileName As String
Dim returnCode As Integer
Dim userNameBuffer As String*MAXUSERNAME
Dim actualLen As Long
Dim currentUserName As String
Dim outBufrLen As String*MAXOUTBUFRLEN
If Not IsDebugMode() Then On Error GoTo errorHandler
ValidateCurrentUser = False
idFileName = session.GetEnvironmentString("KeyFileName", True)
returnCode = REGGetIDInfoString(idFileName, REGIDGetName, _
userNameBuffer, MAXUSERNAME, actualLen)
Call ShowCAPIErrorIfAnyAndEnd(returnCode, "REGGetIDInfo", _
NULLHANDLE)
currentUserName = Left(userNameBuffer, actualLen - 1)
Do While session.UserName <> currentUserName
' ID file configured in notes.ini is not the ID file of the
' current user
idFileName = uiws.OpenFileDialog(False, _
"Wählen Sie Ihre ID-Datei:", "*.ID|", _
GetNotesDataDirectory(), idFileName)(0)
returnCode = REGGetIDInfoString(idFileName, REGIDGetName, _
userNameBuffer, MAXUSERNAME, actualLen)
Call ShowCAPIErrorIfAnyAndEnd(returnCode, "REGGetIDInfo", _
NULLHANDLE)
currentUserName = Left(userNameBuffer, actualLen - 1)
Loop
returnCode = REGGetIDInfoString(idFileName, REGIDGetPrivateKey, _
outBufrLen, MAXOUTBUFRLEN, actualLen)
If returnCode = -32355 Then
Exit Function ' user cancelled dialog
Else
Call ShowCAPIErrorIfAnyAndEnd(returnCode, "REGGetIDInfo", _
NULLHANDLE)
End If
' when we get here, the user must have entered his password
' successfully
ValidateCurrentUser = True
Exit Function
errorHandler:
If HandleError() = RESUME_NEXT_LINE Then Resume Next
Exit Function
End Function
GetNotesDataDirectory() ist eine Hilfsfunktion, die das Notes-Datenverzeichnis zurück gibt. Ersetze diese Funktion durch deine eigene oder einfach eine String-Konstante.
Und passe den Fehlerbehandlungscode (IsDebugMode() and HandleError()) entsprechend deinen Standards an.
Ich benötige noch einige Deklarationen (Declarations):
Private Const LIBRARY = "Eintrag utils"
' WORD LNPUBLIC OSLoadString(HMODULE hModule, STATUS StringCode, char far *retBuffer, WORD BufferLength);
Declare Function OSLoadString Lib "nnotes" Alias "OSLoadString" (ByVal hModule As Long, ByVal stringCode As Integer, ByVal retBuffer As LMBCS String, ByVal bufferLength As Integer) As Integer
' STATUS LNPUBLIC REGGetIDInfo(char far *IDFileName, WORD InfoType, void far *OutBufr, WORD OutBufrLen, WORD far *ActualLen);
Declare Function REGGetIDInfoString Lib "nnotes" Alias "REGGetIDInfo" (ByVal idFileName As String, ByVal infoType As Integer, ByVal outBufr As String, ByVal outBufrLen As Integer, actualLen As Long) As Integer
Declare Function REGGetIDInfoBoolean Lib "nnotes" Alias "REGGetIDInfo" (ByVal idFileName As String, ByVal infoType As Integer, ByVal outBufr As Long, ByVal outBufrLen As Integer, actualLen As Long) As Integer
Const REGIDGetName = 7 ' Data structure returned Is char xx[MAXUSERNAME]
Const REGIDGetPrivateKey = 9 ' Data structure returned Is char xx[xx]
' STATUS LNPUBLIC NSFDbClose(DBHANDLE hDB);
Declare Function NSFDbClose Lib "nnotes.dll" (ByVal hDB As Long) As Integer
Const NOERROR = 0
Const NULLHANDLE = 0&
Const MAXUSERNAME = 256
And two support functions for the C API error handling:
Sub ShowCAPIErrorIfAnyAndEnd(errorCode As Integer, functionName As String, hDB As Long)
'/**
' * shows user the C API error and aborts execution.
' *
' * @param errorCode return code of the function's execution
' * @param functionName name of the C API function called
' * @param hDB handle to the open database
' *
' * @author Thomas Bahn/assono <tbahn@assono.de>
' * @version 2014-07-17
' */
If errorCode = NOERROR Then Exit Sub ' exit if no error occured
If hDB <> 0 Then
' if there is a valid handle, try to close database
Call NSFDbClose(hDB)
End If
Error Err, "Fehler in Bibliothek '" & LIBRARY & "'" & Chr$(10) & _
"Ein Fehler ist aufgetreten in der C-API-Funktion'" & _
functionName & "': " & Chr$(10) &_
"Fehler-Code: " & Trim$(Str$(errorCode)) & Chr$(10) & _
"Fehler-Text: " & Chr$(10) & GetCAPIErrorMsg(errorCode)
End Sub
Function GetCAPIErrorMsg(errorCode As Integer) As String
'/**
' * gets error message for the C API error.
' *
' * @param errorCode return code of the function's execution
' * @return error message for the C API error
' *
' * @author Thomas Bahn/assono <tbahn@assono.de>
' * @version 2014-07-17
' */
Dim length As Integer
Dim buffer As String
' initialize a buffer of adequate length to accept the error string
buffer = String$(256, 0)
' get the API error message from the internal Notes/Domino string
' tables
length = OSLoadString(NULLHANDLE, errorCode, buffer, Len(buffer))
If length > 0 Then
' remove any trailing characters from the string and
' return it to the caller
GetCAPIErrorMsg = Left$(buffer, InStr(1,buffer,Chr$(0))-1)
Else
' couldn’t locate the error message in the string tables
GetCAPIErrorMsg = "Unbekannter Fehler"
End If
End Function
Schließlich platziere Code ähnlich dem folgenden in das QueryOpen-Ereignis deiner Maske:
If continue Then
continue = ValidateCurrentUser()
If continue Then
' do some stuff if necessary
End If
End If
Da der Code ausschließlich Notes C-API-Aufrufe benutzt, kann er leicht auf weitere Plattformen erweitert werden. Momentan ist er auf Windows beschränkt.
Dafür gibt es ein @Command: ToolsUserLogoff. Wenn es ausgeführt wird, logt es den Benutzer aus. Wenn man dieses Kommando mit etwas Code kombiniert, mit dem man auf den Server zugreift, erscheint der Passworteingabe-Dialog, wie es zum Bespiel hier beschrieben wurde: Forcing user re-entry of passwords for electronic signatures in script.
Dieser Ansatz hat aber für mich drei Nachteile:
1. Er funktioniert nicht offline, also z. B. bei einer lokalen Replik, weil der Serverzugriff notwendig ist, um den Passworteingabe-Dialog zu öffnen.
2. Ich möchte es nutzen, um das Öffnen von existierenden Dokumenten abzusichern. Wenn der Benutzer den Dialog abbricht, wird das Dokument trotzdem geöffnet.
3. Der Formel-Code muss im QueryOpen-Ereignis der Maske eingetragen werden. Dort brauche ich aber zwangsläufig LotusScript.
Also musste ich weiter suchen nach einer Lösung in LotusScript. Und ich wurde fündig bei Eknori, der in 2004 diesen Blog-Eintrag verfasst hat: @Command(ToolsUserLogoff) in Lotus Script.
Diese Lösung muss für die aktuellen Versionen von Notes angepasst werden, weil man sich jetzt nicht mehr mit F5, sondern mit Strg-F5 auslogt.
Ich war immer noch nicht überzeugt, dass dies die bestmögliche Lösung für mein Problem sein sollte. Es ist wegen der Verwendung von Windows-DLLs nicht auf andere Plattformen übertragbar, der Benutzer bleibt ausgeloggt, wenn er den Passworteingabe-Dialog abbricht, es würde wohl auch nicht lokal funktionieren usw.
Ich suchte weiter und fand diese großartige Idee: Mittels Notes C-API auf den privaten Teil der Benutzer-ID-Datei zugreifen, genauer mit der REGGetIDInfoString-Function mit REGIDGetPrivateKey als infoType.
- Dies würde den Passworteingabe-Dialog erzwingen, aber gleichzeitig den Benutzer nicht abmelden.
- Es würde auch lokal ohne jeden Server-Kontakt funktionieren.
- Und ich könnte sogar feststellen, wenn der Benutzer den Dialog abgebrochen hätte und darauf falls nötig reagieren.
Vielen Dank an Davy Vanherbergen für seinen OpenNTF Code Bin-Beitrag (von 2003!): Call notes password prompt from lotusscript
Ich habe seine Idee genommen und auf "meine Art" neu implementiert. Dabei habe ich unsere C-API-Hilfsfunktionen genutzt, und ich lasse den Benutzer eine andere ID-Datei auswählen, wenn die in der notes.ini eingestellte nicht die seine ist.
Function ValidateCurrentUser As Boolean
'/**
' * validates current user by letting him enter his password
' *
' * @return True, if user has successfully entered his password
' *
' * @author Thomas Bahn/assono <tbahn@assono.de>
' * @version 2014-09-30
' */
Const MAXOUTBUFRLEN% = 4096
Dim idFileName As String
Dim returnCode As Integer
Dim userNameBuffer As String*MAXUSERNAME
Dim actualLen As Long
Dim currentUserName As String
Dim outBufrLen As String*MAXOUTBUFRLEN
If Not IsDebugMode() Then On Error GoTo errorHandler
ValidateCurrentUser = False
idFileName = session.GetEnvironmentString("KeyFileName", True)
returnCode = REGGetIDInfoString(idFileName, REGIDGetName, _
userNameBuffer, MAXUSERNAME, actualLen)
Call ShowCAPIErrorIfAnyAndEnd(returnCode, "REGGetIDInfo", _
NULLHANDLE)
currentUserName = Left(userNameBuffer, actualLen - 1)
Do While session.UserName <> currentUserName
' ID file configured in notes.ini is not the ID file of the
' current user
idFileName = uiws.OpenFileDialog(False, _
"Wählen Sie Ihre ID-Datei:", "*.ID|", _
GetNotesDataDirectory(), idFileName)(0)
returnCode = REGGetIDInfoString(idFileName, REGIDGetName, _
userNameBuffer, MAXUSERNAME, actualLen)
Call ShowCAPIErrorIfAnyAndEnd(returnCode, "REGGetIDInfo", _
NULLHANDLE)
currentUserName = Left(userNameBuffer, actualLen - 1)
Loop
returnCode = REGGetIDInfoString(idFileName, REGIDGetPrivateKey, _
outBufrLen, MAXOUTBUFRLEN, actualLen)
If returnCode = -32355 Then
Exit Function ' user cancelled dialog
Else
Call ShowCAPIErrorIfAnyAndEnd(returnCode, "REGGetIDInfo", _
NULLHANDLE)
End If
' when we get here, the user must have entered his password
' successfully
ValidateCurrentUser = True
Exit Function
errorHandler:
If HandleError() = RESUME_NEXT_LINE Then Resume Next
Exit Function
End Function
GetNotesDataDirectory() ist eine Hilfsfunktion, die das Notes-Datenverzeichnis zurück gibt. Ersetze diese Funktion durch deine eigene oder einfach eine String-Konstante.
Und passe den Fehlerbehandlungscode (IsDebugMode() and HandleError()) entsprechend deinen Standards an.
Ich benötige noch einige Deklarationen (Declarations):
Private Const LIBRARY = "Eintrag utils"
' WORD LNPUBLIC OSLoadString(HMODULE hModule, STATUS StringCode, char far *retBuffer, WORD BufferLength);
Declare Function OSLoadString Lib "nnotes" Alias "OSLoadString" (ByVal hModule As Long, ByVal stringCode As Integer, ByVal retBuffer As LMBCS String, ByVal bufferLength As Integer) As Integer
' STATUS LNPUBLIC REGGetIDInfo(char far *IDFileName, WORD InfoType, void far *OutBufr, WORD OutBufrLen, WORD far *ActualLen);
Declare Function REGGetIDInfoString Lib "nnotes" Alias "REGGetIDInfo" (ByVal idFileName As String, ByVal infoType As Integer, ByVal outBufr As String, ByVal outBufrLen As Integer, actualLen As Long) As Integer
Declare Function REGGetIDInfoBoolean Lib "nnotes" Alias "REGGetIDInfo" (ByVal idFileName As String, ByVal infoType As Integer, ByVal outBufr As Long, ByVal outBufrLen As Integer, actualLen As Long) As Integer
Const REGIDGetName = 7 ' Data structure returned Is char xx[MAXUSERNAME]
Const REGIDGetPrivateKey = 9 ' Data structure returned Is char xx[xx]
' STATUS LNPUBLIC NSFDbClose(DBHANDLE hDB);
Declare Function NSFDbClose Lib "nnotes.dll" (ByVal hDB As Long) As Integer
Const NOERROR = 0
Const NULLHANDLE = 0&
Const MAXUSERNAME = 256
And two support functions for the C API error handling:
Sub ShowCAPIErrorIfAnyAndEnd(errorCode As Integer, functionName As String, hDB As Long)
'/**
' * shows user the C API error and aborts execution.
' *
' * @param errorCode return code of the function's execution
' * @param functionName name of the C API function called
' * @param hDB handle to the open database
' *
' * @author Thomas Bahn/assono <tbahn@assono.de>
' * @version 2014-07-17
' */
If errorCode = NOERROR Then Exit Sub ' exit if no error occured
If hDB <> 0 Then
' if there is a valid handle, try to close database
Call NSFDbClose(hDB)
End If
Error Err, "Fehler in Bibliothek '" & LIBRARY & "'" & Chr$(10) & _
"Ein Fehler ist aufgetreten in der C-API-Funktion'" & _
functionName & "': " & Chr$(10) &_
"Fehler-Code: " & Trim$(Str$(errorCode)) & Chr$(10) & _
"Fehler-Text: " & Chr$(10) & GetCAPIErrorMsg(errorCode)
End Sub
Function GetCAPIErrorMsg(errorCode As Integer) As String
'/**
' * gets error message for the C API error.
' *
' * @param errorCode return code of the function's execution
' * @return error message for the C API error
' *
' * @author Thomas Bahn/assono <tbahn@assono.de>
' * @version 2014-07-17
' */
Dim length As Integer
Dim buffer As String
' initialize a buffer of adequate length to accept the error string
buffer = String$(256, 0)
' get the API error message from the internal Notes/Domino string
' tables
length = OSLoadString(NULLHANDLE, errorCode, buffer, Len(buffer))
If length > 0 Then
' remove any trailing characters from the string and
' return it to the caller
GetCAPIErrorMsg = Left$(buffer, InStr(1,buffer,Chr$(0))-1)
Else
' couldn’t locate the error message in the string tables
GetCAPIErrorMsg = "Unbekannter Fehler"
End If
End Function
Schließlich platziere Code ähnlich dem folgenden in das QueryOpen-Ereignis deiner Maske:
If continue Then
continue = ValidateCurrentUser()
If continue Then
' do some stuff if necessary
End If
End If
Da der Code ausschließlich Notes C-API-Aufrufe benutzt, kann er leicht auf weitere Plattformen erweitert werden. Momentan ist er auf Windows beschränkt.
Quick-Tipp: Script-Bibliothek verhindert Ausführung eines Agenten
Heute konnte ich ein Problem eines meiner Kunden lösen: Ein geplanter Server-Agent
lief einfach nicht!
Eigentlich lief er doch, nur tat er einfach nichts. Wenn man ihn z. B. per Agent Manager aufgerufen hat (z. B. mit tell amgr run "some-db.nsf" 'Test'), erschienen folgende zwei Zeilen im Server-Protokoll:
AMgr: Start executing agent 'Test' in 'some-db.nsf'
AMgr: Agent 'Test' in 'some-db.nsf' completed execution
Aber selbst ein einfaches Print "TEST" in der ersten Zeile der Initialize-Prozedur gab nichts aus - gar nicht erst zu sprechen von der eigentlichen Aufgabe des Agenten.
Wir haben (wirklich!) alles geprüft, was irgendwie Einfluss haben könnte - u.a. die Einstellungen zur Server-Sicherheit, die Anwendungseigenschaften ("Hintergrundagenten in dieser Datenbank nicht ausführen"), Zugriffskontrollliste (ACL), die Eigenschaften des Agenten...
Dann habe ich Schritt für Schritt den Agenten nachgebaut. Erst die Use-Befehle, dann Stück für Stück den restlichen Code, und alles getestet, wieder und wieder: Es lief so wie es sollte.
Schließlich habe ich den vermeintlich "korrupten" Agenten gelöscht und den neue Agenten umbenannt, so dass er hieß, wie der alte. Und in dem Moment hörte er auf zu funktionieren!
Okay, wieder zurück umbennant - läuft. Noch einmal auf den Namen des ursprünglichen Agenten geändert - läuft nicht.
Weiter im Domino Administrator den "Gottmodus" (Full-access administration) aktiviert und die Datenbank neu im Designer inspiziert. Gibt es vielleicht einen bisher unsichtbaren Agenten mit dem gleichen Namen? Nöps, auch nichts.
Aber ich fand eine Script-Bibliothek mit exakt demselben Namen wie der Agent. Naja, vielleicht... Umbenannt ... und ... der ... Agent ... LÄUFT!
Was habe ich daraus gelernt: Eine Script-Bibliothek verhindert die Ausführung eines Agenten, wenn beide gleich heißen!
Eigentlich lief er doch, nur tat er einfach nichts. Wenn man ihn z. B. per Agent Manager aufgerufen hat (z. B. mit tell amgr run "some-db.nsf" 'Test'), erschienen folgende zwei Zeilen im Server-Protokoll:
AMgr: Start executing agent 'Test' in 'some-db.nsf'
AMgr: Agent 'Test' in 'some-db.nsf' completed execution
Aber selbst ein einfaches Print "TEST" in der ersten Zeile der Initialize-Prozedur gab nichts aus - gar nicht erst zu sprechen von der eigentlichen Aufgabe des Agenten.
Wir haben (wirklich!) alles geprüft, was irgendwie Einfluss haben könnte - u.a. die Einstellungen zur Server-Sicherheit, die Anwendungseigenschaften ("Hintergrundagenten in dieser Datenbank nicht ausführen"), Zugriffskontrollliste (ACL), die Eigenschaften des Agenten...
Dann habe ich Schritt für Schritt den Agenten nachgebaut. Erst die Use-Befehle, dann Stück für Stück den restlichen Code, und alles getestet, wieder und wieder: Es lief so wie es sollte.
Schließlich habe ich den vermeintlich "korrupten" Agenten gelöscht und den neue Agenten umbenannt, so dass er hieß, wie der alte. Und in dem Moment hörte er auf zu funktionieren!
Okay, wieder zurück umbennant - läuft. Noch einmal auf den Namen des ursprünglichen Agenten geändert - läuft nicht.
Weiter im Domino Administrator den "Gottmodus" (Full-access administration) aktiviert und die Datenbank neu im Designer inspiziert. Gibt es vielleicht einen bisher unsichtbaren Agenten mit dem gleichen Namen? Nöps, auch nichts.
Aber ich fand eine Script-Bibliothek mit exakt demselben Namen wie der Agent. Naja, vielleicht... Umbenannt ... und ... der ... Agent ... LÄUFT!
Was habe ich daraus gelernt: Eine Script-Bibliothek verhindert die Ausführung eines Agenten, wenn beide gleich heißen!
Auch mit Firefox 31 nun keine selbstsignierten SSL Zertifikate mehr unterstützt
Die Luft für Nutzer selbstsignierter SSL Zertifikate wird immer dünner.
Schon vor einiger Zeit berichteten wir darüber, dass Windows Phone keine selbst signierten Zertifikate unterstützt. Diese Problem ist nun bei einem anderen Kunden wieder aufgetaucht, der nun auch mit einem Nokia Lumia experimentieren wollte. Erschwerend war hier nun aber, dass auch der Zugriff auf die Traveler-Weboberfläche mit Firefox plötzlich nicht mehr möglich war. Statt der erwarteten Anmeldemaske erschien eine Fehlermeldung, die einem mitteilte, dass das Zertifikat ungültig sei. Keine Möglichkeit mehr, die Meldung zu ignorieren bzw. eine Ausnahme hinzuzufügen.
Dazu gibt es seit gestern eine Technote bei IBM.
Es gibt offenbar mit Firefox 31 einen neuen Validierungsalgorithmus für SSL-Zertifikate, der das neue Verhalten zeigt. Diesen kann man über die Konfiguration (about:config) deaktivieren, um das alte Verhalten zurück zu bekommen. Das ist natürlich aus mehreren Gründen keine langfristige Lösung:
BTW: Die von IBM vorgeschlagene "Lösung" funktioniert
.
Schon vor einiger Zeit berichteten wir darüber, dass Windows Phone keine selbst signierten Zertifikate unterstützt. Diese Problem ist nun bei einem anderen Kunden wieder aufgetaucht, der nun auch mit einem Nokia Lumia experimentieren wollte. Erschwerend war hier nun aber, dass auch der Zugriff auf die Traveler-Weboberfläche mit Firefox plötzlich nicht mehr möglich war. Statt der erwarteten Anmeldemaske erschien eine Fehlermeldung, die einem mitteilte, dass das Zertifikat ungültig sei. Keine Möglichkeit mehr, die Meldung zu ignorieren bzw. eine Ausnahme hinzuzufügen.
Dazu gibt es seit gestern eine Technote bei IBM.
Es gibt offenbar mit Firefox 31 einen neuen Validierungsalgorithmus für SSL-Zertifikate, der das neue Verhalten zeigt. Diesen kann man über die Konfiguration (about:config) deaktivieren, um das alte Verhalten zurück zu bekommen. Das ist natürlich aus mehreren Gründen keine langfristige Lösung:
- Der alte Algorithmus wird mit der Zeit aus Firefox ausgebaut werden (laut Mozilla Wiki), so dass man sich dem Problem mittelfristig wieder wird stellen müssen.
- Es ist zu viel Aufwand, wenn man das nicht zentral steuern kann - was vermutlich nicht bei allen Unternehmen der Fall ist
- Es bedeutet einen vermutlich sichereren Algorithmus mutwillig zu deaktivieren, weil man...
- ...nicht das ohnehin sicherere, aber kostenpflichtige/-günstige, vertrauenswürdige Zertifikat kaufen möchte.
BTW: Die von IBM vorgeschlagene "Lösung" funktioniert

Quick-Tipp: ‘Verbergen-Formeln von Aktionen bei jedem Dok’wechsel prüfen’ funktioniert nicht
Es gibt immer mal wieder Tage, wo einem das Offensichtliche nicht ins Gesicht
springt und man sucht und sucht und sucht...
Man kann bei einer Ansicht eine Eigenschaft einstellen, dass die Verbergen-Wenn-Formeln (hide when) jedes mal neu berechnet werden, wenn man auf ein anderes Dokument in der Ansicht geht bzw. darauf klickt.
Das braucht man, wenn man z. B. Ansicht-Aktionen abhängig von Status des Dokuments anzeigen möchte, also die Aktion "Antrag genehmigen" in der Ansicht nur anzeigen möchte, wenn der aktuell ausgewählte Antrag auf "zu genehmigen" steht.
Ich wollte eine Aktion zum Kopieren des Feldinhalts in die Zwischenablage nur dann anzeigen, wenn das Feld "Passwort" ausgefüllt ist:
So einfach funktioniert das - normalerweise. Und die gleiche Aktion in der Maske funktionierte auch, nur eben nicht in der Ansicht.
Nach längerer Suche und viel Ausprobieren bin ich dann auf die Lösung gekommen. Kern des Problems war, dass das Feld "Passwort" verschlüsselt gespeichert wird:
Verschlüsselte Felder werden nicht "normal" im Dokument gespeichert, sondern in $SealData-Items.
Sie können dann weder in Spalten angezeigt, noch in Spalten- oder Aktionsformeln benutzt werden.
Als Lösung habe ich ein neues Feld "PasswortEingegeben" in der Maske hinzugefügt, dass nur die Information speichert, ob das Passwort-Feld gefüllt ist. Im geöffneten Dokument kann man ja noch einfach auf die verschlüsselten Felder zugreifen.
Danach habe ich das neue Feld in der Verbergen-Wenn-Formel der Aktion benutzt:
Und es funktioniert!
Man kann bei einer Ansicht eine Eigenschaft einstellen, dass die Verbergen-Wenn-Formeln (hide when) jedes mal neu berechnet werden, wenn man auf ein anderes Dokument in der Ansicht geht bzw. darauf klickt.
Das braucht man, wenn man z. B. Ansicht-Aktionen abhängig von Status des Dokuments anzeigen möchte, also die Aktion "Antrag genehmigen" in der Ansicht nur anzeigen möchte, wenn der aktuell ausgewählte Antrag auf "zu genehmigen" steht.
Ich wollte eine Aktion zum Kopieren des Feldinhalts in die Zwischenablage nur dann anzeigen, wenn das Feld "Passwort" ausgefüllt ist:
So einfach funktioniert das - normalerweise. Und die gleiche Aktion in der Maske funktionierte auch, nur eben nicht in der Ansicht.
Nach längerer Suche und viel Ausprobieren bin ich dann auf die Lösung gekommen. Kern des Problems war, dass das Feld "Passwort" verschlüsselt gespeichert wird:
Verschlüsselte Felder werden nicht "normal" im Dokument gespeichert, sondern in $SealData-Items.
Sie können dann weder in Spalten angezeigt, noch in Spalten- oder Aktionsformeln benutzt werden.
Als Lösung habe ich ein neues Feld "PasswortEingegeben" in der Maske hinzugefügt, dass nur die Information speichert, ob das Passwort-Feld gefüllt ist. Im geöffneten Dokument kann man ja noch einfach auf die verschlüsselten Felder zugreifen.
Danach habe ich das neue Feld in der Verbergen-Wenn-Formel der Aktion benutzt:
Und es funktioniert!
Quick-Tipp: Wie du die lästigen Fehlermeldungen des Schedulers beim Domino-Server-Start los wirst
Chris
Miller ist sicherlich einer
der besten Domino-Administratoren der Welt. Er schreibt auch regelmäßig
für die SocialBiz
user group und beantwortet dort
schwierige Fragen rund um die IBM Notes und Domino-Administration.
Letzte Woche hat er darüber geschrieben, wie man gezielt Datenbanken vom Scheduler und Rooms and Resources Manager ausnimmt: From the SocialBiz Mailbox: Excluding Calendars from Mail-in Databases
Du kennst wahrscheinlich auch diese "Fehlermeldungen" des Schedulers und des RnRMgr, die jedesmal kommen, wenn einer der beiden Prozess auf eine Datenbank stößt, zu der er den Eigentümer nicht (mehr) im Domino-Verzeichnis findet:
SchedMgr: Error processing calendar profile document (NoteID: NT....) in database...:
Can't find $BusyName field on profile
SchedMgr: Error processing calendar profile document (NoteID: NT...) in database ...:
Cannot find user in Domino Directory
Dass passiert zum Beispiel wenn das Personen-Dokument gelöscht wurde (Mitarbeiter arbeitet nicht mehr für das Unternehmen) und bei Mail-In-Datenbanken.
Die beiden Prozesse sind wichtig, um die Freie-Zeit-Datenbank (busytime.nsf bzw. clubusy.nsf) immer auf dem aktuellen Stand zu halten. Aber bei bestimmten Datenbanken ist das einfach nicht (mehr) relevant. Und da wäre es doch toll, wenn man den beiden Prozessen einfach sagen könnten: "Diese Datenbank bitte ignorieren."
Und Chris kennt eine Lösung: In kleines Programm namens "NoCal".
Leider hat es ein paar Nachteile
Im Kern sieht der Agent so aus:
Sub Initialize
'/**
' * gets current database's options, prints "Has Calendar" option,
' * toggles it and prints new status
' *
' * @author Thomas Bahn/assono <tbahn@assono.de>
' * @version 2014-07-17
' */
Dim session As NotesSession
Dim returnCode As Integer
Dim hDB As Long
Dim retDbOptions As Long
On Error GoTo miniErrorHandler
Set session = New NotesSession
' open the current database
returnCode = NSFDbOpen(session.CurrentDatabase.FilePath, hDB)
Call ShowCAPIErrorIfAnyAndEnd(returnCode, "NSFDbOpen", hDB)
' get current options
returnCode = NSFDbGetOptions(hDB, retDbOptions)
Call ShowCAPIErrorIfAnyAndEnd(returnCode, "NSFDbGetOptions", hDB)
' print current status
If retDbOptions And DBOPTION_HAS_CALENDAR Then
Print "Database has calendar"
Else
Print "Database doesn't have calendar"
End If
' toggle option
returnCode = NSFDbSetOptions(hDB, _
retDbOptions Xor DBOPTION_HAS_CALENDAR, _
DBOPTION_HAS_CALENDAR)
Call ShowCAPIErrorIfAnyAndEnd(returnCode, "NSFDbSetOptions", hDB)
' get current options
returnCode = NSFDbGetOptions(hDB, retDbOptions)
Call ShowCAPIErrorIfAnyAndEnd(returnCode, "NSFDbGetOptions", hDB)
' print new status
If retDbOptions And DBOPTION_HAS_CALENDAR Then
Print "Database now has calendar"
Else
Print "Database now doesn't have calendar"
End If
' close the database
returnCode = NSFDbClose(hDB)
Exit Sub
miniErrorHandler:
MessageBox "Error #" & Err & " occurred in line " & Erl & Chr$(10) &_
"Error message: " & Chr$(10) & Error$, 48, "Error"
If hDB <> 0 Then ' if there is a valid handle, try to close database
returnCode = NSFDbClose(hDB)
End If
End Sub
Er braucht noch einige Deklarationen (Declarations):
' STATUS LNPUBLIC NSFDbOpen(char far *PathName, DBHANDLE far *rethDB);
Declare Function NSFDbOpen Lib "nnotes.dll" (ByVal pathName As LMBCS String, rethDB As Long) As Integer
' WORD LNPUBLIC OSLoadString(HMODULE hModule, STATUS StringCode, char far *retBuffer, WORD BufferLength);
Declare Function OSLoadString Lib "nnotes" Alias "OSLoadString" (ByVal hModule As Long, ByVal stringCode As Integer, ByVal retBuffer As LMBCS String, ByVal bufferLength As Integer) As Integer
' STATUS LNPUBLIC NSFDbGetOptions(DBHANDLE hDB, DWORD far *retDbOptions);
Declare Function NSFDbGetOptions Lib "nnotes.dll" (ByVal hDB As Long, retDbOptions As Long) As Integer
' STATUS LNPUBLIC NSFDbSetOptions(DBHANDLE hDB, DWORD DbOptions, DWORD Mask);
Declare Function NSFDbSetOptions Lib "nnotes.dll" (ByVal hDB As Long, ByVal dbOptions As Long, ByVal mask As Long) As Integer
' STATUS LNPUBLIC NSFDbClose(DBHANDLE hDB);
Declare Function NSFDbClose Lib "nnotes.dll" (ByVal hDB As Long) As Integer
' DBOPTION_HAS_CALENDAR - TRUE if database stores calendar events.
Const DBOPTION_HAS_CALENDAR& = &H00002000
Const NOERROR = 0
Const NULLHANDLE = 0&
Und zwei unterstützende Funktionen für die C-API-Fehlerbehandlung:
Sub ShowCAPIErrorIfAnyAndEnd(errorCode As Integer, functionName As String, hDB As Long)
'/**
' * shows user the C API error and aborts execution.
' *
' * @param errorCode return code of the function's execution
' * @param functionName name of the C API function called
' * @param hDB handle to the open database
' *
' * @author Thomas Bahn/assono <tbahn@assono.de>
' * @version 2014-07-17
' */
If errorCode = NOERROR Then Exit Sub ' exit if no error occured
MessageBox _
"An error occurred in C API function '" & functionName & "'" & Chr$(10) &_
"Error code: " & Trim$(Str$(errorCode)) & Chr$(10) & _
"Error message:" & Chr$(10) & GetCAPIErrorMsg(errorCode), _
48, "Error calling C API"
If hDB <> 0 Then ' if there is a valid handle, try to close database
Call NSFDbClose(hDB)
End If
End
End Sub
Function GetCAPIErrorMsg(errorCode As Integer) As String
'/**
' * gets error message for the C API error.
' *
' * @param errorCode return code of the function's execution
' * @return error message for the C API error
' *
' * @author Thomas Bahn/assono <tbahn@assono.de>
' * @version 2014-07-17
' */
Dim length As Integer
Dim buffer As String
' initialize a buffer of adequate length to accept the error string
buffer = String$(256, 0)
' get the API error message from the internal Notes/Domino string tables
length = OSLoadString(NULLHANDLE, errorCode, buffer, Len(buffer))
If length > 0 Then
' remove any trailing characters from the string
GetCAPIErrorMsg = Left$(buffer, InStr(1, buffer, Chr$(0)) - 1)
Else
' couldn’t locate the error message in the string tables
GetCAPIErrorMsg = "Unknown error"
End If
End Function
Erstelle einfachen einen LotusScript-Agenten, der manuell gestartet wird mit dem Ziel "Keine".
Hier ist eine kleine Datenbank mit dem Agenten - fertig zum Kopieren:
Die Datenbank enthält auch eine erweiterte Version des Agenten, mit dem man andere Datenbanken umstellen kann.
Der Benutzer wählt als erstes die Datenbank aus, bekommt den aktuellen Status angezeigt und kann dann entscheiden, ob er die Einstellung ändern möchte.
Wichtig: Soweit meine Versuche ergeben haben, wird die Änderungen der Datenbank-Option nicht repliziert. Du musst also jede Replik auf jedem Server einzeln umstellen.
Letzte Woche hat er darüber geschrieben, wie man gezielt Datenbanken vom Scheduler und Rooms and Resources Manager ausnimmt: From the SocialBiz Mailbox: Excluding Calendars from Mail-in Databases
Du kennst wahrscheinlich auch diese "Fehlermeldungen" des Schedulers und des RnRMgr, die jedesmal kommen, wenn einer der beiden Prozess auf eine Datenbank stößt, zu der er den Eigentümer nicht (mehr) im Domino-Verzeichnis findet:
SchedMgr: Error processing calendar profile document (NoteID: NT....) in database...:
Can't find $BusyName field on profile
SchedMgr: Error processing calendar profile document (NoteID: NT...) in database ...:
Cannot find user in Domino Directory
Dass passiert zum Beispiel wenn das Personen-Dokument gelöscht wurde (Mitarbeiter arbeitet nicht mehr für das Unternehmen) und bei Mail-In-Datenbanken.
Die beiden Prozesse sind wichtig, um die Freie-Zeit-Datenbank (busytime.nsf bzw. clubusy.nsf) immer auf dem aktuellen Stand zu halten. Aber bei bestimmten Datenbanken ist das einfach nicht (mehr) relevant. Und da wäre es doch toll, wenn man den beiden Prozessen einfach sagen könnten: "Diese Datenbank bitte ignorieren."
Und Chris kennt eine Lösung: In kleines Programm namens "NoCal".
Leider hat es ein paar Nachteile
- Es ist wirklich, wirklich alt: Release: R5, Platform: Windows 95/98/NT, Date Posted: 04.05.2001,
- es muss von der Kommandozeile aus gestartet werden und
- es kann die "Has Calendar"-Datenbank-Option nur ausschalten, nicht wieder anschalten, wenn man aus Versehen die falsche Datenbank erwischt hat.

- Meine Lösung sollte klein und transportabel sein, wie z. B. ein Agent, den man einfach in die Schablone oder jede Datenbank packen kann,
- sie sollte in der Lage sein, die Option an- und auszuschalten und
- sie sollte einfach aus dem Notes-Client heraus aufgerufen werden können.
Im Kern sieht der Agent so aus:
Sub Initialize
'/**
' * gets current database's options, prints "Has Calendar" option,
' * toggles it and prints new status
' *
' * @author Thomas Bahn/assono <tbahn@assono.de>
' * @version 2014-07-17
' */
Dim session As NotesSession
Dim returnCode As Integer
Dim hDB As Long
Dim retDbOptions As Long
On Error GoTo miniErrorHandler
Set session = New NotesSession
' open the current database
returnCode = NSFDbOpen(session.CurrentDatabase.FilePath, hDB)
Call ShowCAPIErrorIfAnyAndEnd(returnCode, "NSFDbOpen", hDB)
' get current options
returnCode = NSFDbGetOptions(hDB, retDbOptions)
Call ShowCAPIErrorIfAnyAndEnd(returnCode, "NSFDbGetOptions", hDB)
' print current status
If retDbOptions And DBOPTION_HAS_CALENDAR Then
Print "Database has calendar"
Else
Print "Database doesn't have calendar"
End If
' toggle option
returnCode = NSFDbSetOptions(hDB, _
retDbOptions Xor DBOPTION_HAS_CALENDAR, _
DBOPTION_HAS_CALENDAR)
Call ShowCAPIErrorIfAnyAndEnd(returnCode, "NSFDbSetOptions", hDB)
' get current options
returnCode = NSFDbGetOptions(hDB, retDbOptions)
Call ShowCAPIErrorIfAnyAndEnd(returnCode, "NSFDbGetOptions", hDB)
' print new status
If retDbOptions And DBOPTION_HAS_CALENDAR Then
Print "Database now has calendar"
Else
Print "Database now doesn't have calendar"
End If
' close the database
returnCode = NSFDbClose(hDB)
Exit Sub
miniErrorHandler:
MessageBox "Error #" & Err & " occurred in line " & Erl & Chr$(10) &_
"Error message: " & Chr$(10) & Error$, 48, "Error"
If hDB <> 0 Then ' if there is a valid handle, try to close database
returnCode = NSFDbClose(hDB)
End If
End Sub
Er braucht noch einige Deklarationen (Declarations):
' STATUS LNPUBLIC NSFDbOpen(char far *PathName, DBHANDLE far *rethDB);
Declare Function NSFDbOpen Lib "nnotes.dll" (ByVal pathName As LMBCS String, rethDB As Long) As Integer
' WORD LNPUBLIC OSLoadString(HMODULE hModule, STATUS StringCode, char far *retBuffer, WORD BufferLength);
Declare Function OSLoadString Lib "nnotes" Alias "OSLoadString" (ByVal hModule As Long, ByVal stringCode As Integer, ByVal retBuffer As LMBCS String, ByVal bufferLength As Integer) As Integer
' STATUS LNPUBLIC NSFDbGetOptions(DBHANDLE hDB, DWORD far *retDbOptions);
Declare Function NSFDbGetOptions Lib "nnotes.dll" (ByVal hDB As Long, retDbOptions As Long) As Integer
' STATUS LNPUBLIC NSFDbSetOptions(DBHANDLE hDB, DWORD DbOptions, DWORD Mask);
Declare Function NSFDbSetOptions Lib "nnotes.dll" (ByVal hDB As Long, ByVal dbOptions As Long, ByVal mask As Long) As Integer
' STATUS LNPUBLIC NSFDbClose(DBHANDLE hDB);
Declare Function NSFDbClose Lib "nnotes.dll" (ByVal hDB As Long) As Integer
' DBOPTION_HAS_CALENDAR - TRUE if database stores calendar events.
Const DBOPTION_HAS_CALENDAR& = &H00002000
Const NOERROR = 0
Const NULLHANDLE = 0&
Und zwei unterstützende Funktionen für die C-API-Fehlerbehandlung:
Sub ShowCAPIErrorIfAnyAndEnd(errorCode As Integer, functionName As String, hDB As Long)
'/**
' * shows user the C API error and aborts execution.
' *
' * @param errorCode return code of the function's execution
' * @param functionName name of the C API function called
' * @param hDB handle to the open database
' *
' * @author Thomas Bahn/assono <tbahn@assono.de>
' * @version 2014-07-17
' */
If errorCode = NOERROR Then Exit Sub ' exit if no error occured
MessageBox _
"An error occurred in C API function '" & functionName & "'" & Chr$(10) &_
"Error code: " & Trim$(Str$(errorCode)) & Chr$(10) & _
"Error message:" & Chr$(10) & GetCAPIErrorMsg(errorCode), _
48, "Error calling C API"
If hDB <> 0 Then ' if there is a valid handle, try to close database
Call NSFDbClose(hDB)
End If
End
End Sub
Function GetCAPIErrorMsg(errorCode As Integer) As String
'/**
' * gets error message for the C API error.
' *
' * @param errorCode return code of the function's execution
' * @return error message for the C API error
' *
' * @author Thomas Bahn/assono <tbahn@assono.de>
' * @version 2014-07-17
' */
Dim length As Integer
Dim buffer As String
' initialize a buffer of adequate length to accept the error string
buffer = String$(256, 0)
' get the API error message from the internal Notes/Domino string tables
length = OSLoadString(NULLHANDLE, errorCode, buffer, Len(buffer))
If length > 0 Then
' remove any trailing characters from the string
GetCAPIErrorMsg = Left$(buffer, InStr(1, buffer, Chr$(0)) - 1)
Else
' couldn’t locate the error message in the string tables
GetCAPIErrorMsg = "Unknown error"
End If
End Function
Erstelle einfachen einen LotusScript-Agenten, der manuell gestartet wird mit dem Ziel "Keine".
Hier ist eine kleine Datenbank mit dem Agenten - fertig zum Kopieren:
Die Datenbank enthält auch eine erweiterte Version des Agenten, mit dem man andere Datenbanken umstellen kann.
Der Benutzer wählt als erstes die Datenbank aus, bekommt den aktuellen Status angezeigt und kann dann entscheiden, ob er die Einstellung ändern möchte.
Wichtig: Soweit meine Versuche ergeben haben, wird die Änderungen der Datenbank-Option nicht repliziert. Du musst also jede Replik auf jedem Server einzeln umstellen.
Quick-Tipp: Domino Console verbindet sich nicht mit lokalem Server
Seit einigen Versionen des Windows-Servers kann man nicht mehr direkt die
Konsole des Domino-Servers sehen - aus Sicherheitsgründen.
Als Ersatz kann man die Domino Console (jconsole.exe) auf dem Server starten, die auch deutlich vielseitiger und leistungsfähiger als die "alte" Serverkonsole ist.
Normalerweise (soll heißen: in meiner Erfahrung bisher immer) findet die Domino Console nach dem Start den lokal laufenden Domino-Server und verbindet sich ohne weitere Authentifizierung mit diesem. Letzten Donnerstag habe ich das erste Mal erlebt, dass es nicht funktioniert hat.
Der Server wurde migriert von Hardware auf VM, die Version von 8.5.3 FP6 auf 9.0.1 FP1 IF1 angehoben. Er ist ein Cluster-Server mit einer zweiten Netzwerkkarte und IP-Adresse für den Cluster-internen Verkehr. Der "Hauptserver" des Clusters wurde vorher schon umgestellt und alles lief auf Anhieb wie erwartet. Bei diesem, zweiten Server jedoch hat sich die Domino Console nicht mit dem laufenden Domino-Server verbunden - weder automatisch beim Start der Console, noch auf "Aufforderung".
Man konnte sich mit der Domino Console auf andere Server über Netzwerk verbinden, indem man Server-Adresse, Name und Passwort angab.
netstat -a -n zeigte, dass der Dienst auf tcp/2050 lauschte und zwar auf der Hauptadresse des Servers. Die Domino Console versucht beim Start sich mit dem lokalen Server zu verbinden und benutzt dazu den Namen des Windows-Servers (der abweicht vom Namen des Domino-Servers).
Ein ping auf den Windows-Namen nutzte IPv6, ein ping -4 zeigte, dass der Windows-Name auf die IP-Adresse des Cluster-Netzwerks aufgelöst wurde. Das ließ sich erstaunlicherweise aber weder über den DNS-Eintrag noch über einen entsprechenden Eintrag in der lokalen hosts-Datei ändern. Windows halt.
Die Lösung fand ich in einer Technote, die sich eigentlich mit etwas anderem beschäftigt: Domino running on Windows 2008, Is there way to set the Lotus Domino Console starts, it would connect to the local Domino server ?
Dort stand in der dconsole.ini im ersten Abschnitt:
LocalHost=Name:2050
Flugs habe ich eine solche Zeile auf dem problembehafteten Server eingetragen - mit dem Domino-Server-Namen, der per DNS-Eintrag auf die "richtige" IP-Adresse übersetzt wird, und nach einem Server-Neustart und dem Aufruf der Domino Console verbindet sich diese automatisch mit dem lokalen Domino-Server! Ich habe fertig!
Als Ersatz kann man die Domino Console (jconsole.exe) auf dem Server starten, die auch deutlich vielseitiger und leistungsfähiger als die "alte" Serverkonsole ist.
Normalerweise (soll heißen: in meiner Erfahrung bisher immer) findet die Domino Console nach dem Start den lokal laufenden Domino-Server und verbindet sich ohne weitere Authentifizierung mit diesem. Letzten Donnerstag habe ich das erste Mal erlebt, dass es nicht funktioniert hat.

Der Server wurde migriert von Hardware auf VM, die Version von 8.5.3 FP6 auf 9.0.1 FP1 IF1 angehoben. Er ist ein Cluster-Server mit einer zweiten Netzwerkkarte und IP-Adresse für den Cluster-internen Verkehr. Der "Hauptserver" des Clusters wurde vorher schon umgestellt und alles lief auf Anhieb wie erwartet. Bei diesem, zweiten Server jedoch hat sich die Domino Console nicht mit dem laufenden Domino-Server verbunden - weder automatisch beim Start der Console, noch auf "Aufforderung".
Man konnte sich mit der Domino Console auf andere Server über Netzwerk verbinden, indem man Server-Adresse, Name und Passwort angab.
netstat -a -n zeigte, dass der Dienst auf tcp/2050 lauschte und zwar auf der Hauptadresse des Servers. Die Domino Console versucht beim Start sich mit dem lokalen Server zu verbinden und benutzt dazu den Namen des Windows-Servers (der abweicht vom Namen des Domino-Servers).
Ein ping auf den Windows-Namen nutzte IPv6, ein ping -4 zeigte, dass der Windows-Name auf die IP-Adresse des Cluster-Netzwerks aufgelöst wurde. Das ließ sich erstaunlicherweise aber weder über den DNS-Eintrag noch über einen entsprechenden Eintrag in der lokalen hosts-Datei ändern. Windows halt.

Die Lösung fand ich in einer Technote, die sich eigentlich mit etwas anderem beschäftigt: Domino running on Windows 2008, Is there way to set the Lotus Domino Console starts, it would connect to the local Domino server ?
Dort stand in der dconsole.ini im ersten Abschnitt:
LocalHost=Name:2050
Flugs habe ich eine solche Zeile auf dem problembehafteten Server eingetragen - mit dem Domino-Server-Namen, der per DNS-Eintrag auf die "richtige" IP-Adresse übersetzt wird, und nach einem Server-Neustart und dem Aufruf der Domino Console verbindet sich diese automatisch mit dem lokalen Domino-Server! Ich habe fertig!
Quick-Tipp: Fehler beim Signieren von Datenbanken mit der Server-ID
Das war mal wieder ein ganz mysteriöser Fehler: Im Domino Administrator
unter dem Reiter Dateien eine Datenbank ausgewählt, im Rechtsklick-Kontextmenü
"Signieren..." und im folgenden Dialog "ID des aktiven Servers"
gewählt. Sofort kommt eine Dialogbox mit der Fehlermeldung "Sie sind
zur Durchführung dieser Operation nicht berechtigt." (You are not
authorized to perform that operation).
Kurze Recherche und viel Überprüfen der aktuellen Einstellungen:
Option Public
Option Declare
Sub Initialize
'/**
' * signs this database by manually creating a request with NotesAdministrationProcess.
' *
' * @author Thomas Bahn <tbahn@assono.de>
' * @version 2014-07-03
' */
Dim uiws As New NotesUIWorkspace
Dim session As NotesSession
Dim currentDB As NotesDatabase
Dim adminDB As NotesDatabase
Dim adminP As NotesAdministrationProcess
Dim noteID As String
Set uiws = New NotesUIWorkspace
Set session = New NotesSession
Set currentDB = session.CurrentDatabase
Set adminDB = New NotesDatabase(currentDB.Server, "admin4.nsf")
Set adminP = session.CreateAdministrationProcess(currentDB.Server)
noteID = adminP.SignDatabaseWithServerID(currentDB.Server, currentDB.FilePath, False)
If noteID <> "" Then
Call uiws.EditDocument(False, adminDB.GetDocumentByID(noteID))
End If
End Sub
Ich nenne ihn "Datenbank per Administration Request signieren lassen via AdminP-Objekt" (SignDatabaseAdminPViaAdminPObject),
Es kommt in der Zeile mit "adminP.SignDatabaseWithServerID" der gleiche Fehler wie im Admin-Client.
Noch gebe ich mich nicht geschlagen, ein anderer Weg, ein anderer Agent:
Option Public
Option Declare
Sub Initialize
'/**
' * signs this database by manually creating a request document in the admin4.nsf.
' *
' * @author Thomas Bahn <tbahn@assono.de>
' * @version 2014-07-03
' */
Dim session As NotesSession
Dim currentDB As NotesDatabase
Dim adminDB As NotesDatabase
Dim requestDoc As NotesDocument
Set session = New NotesSession
Set currentDB = session.CurrentDatabase
Set adminDB = New NotesDatabase(currentDB.Server, "admin4.nsf")
Set requestDoc = adminDB.CreateDocument
Call CreateItem(requestDoc, "Form", 0, "AdminRequest", False, True)
Call CreateItem(requestDoc, "Type", 0, "AdminRequest", False, True)
Call CreateItem(requestDoc, "ProxyProcess", 0, "Adminp", True, True)
Call CreateItem(requestDoc, "ProxyAction", 0, "101", True, True)
Call CreateItem(requestDoc, "ProxyTextItem1", 0, "0", True, True)
Call CreateItem(requestDoc, "ProxyOriginatingTimeDate", 0, Now, True, False)
Call CreateItem(requestDoc, "ProxyServer", 0, currentDB.Server, True, True)
Call CreateItem(requestDoc, "ProxyDatabasePath", 0, currentDB.FilePath, True, True)
Call CreateItem(requestDoc, "ProxyNameList", 0, currentDB.Title, True, True)
Call CreateItem(requestDoc, "ProxyAuthor", 0, session.UserName, True, True)
Call CreateItem(requestDoc, "FullName", AUTHORS, session.UserName, True, False)
Call CreateItem(requestDoc, "$OnBehalfOf", AUTHORS, session.UserName, True, False)
Call CreateItem(requestDoc, "ProxyOriginatingAuthor", AUTHORS, session.UserName, True, False)
Call CreateItem(requestDoc, "ProxyOriginatingOrganization", AUTHORS, StrRightBack(session.UserName, "/O="), True, False)
Call CreateItem(requestDoc, "ProxyOriginatingRequestUNID", 0, requestDoc.UniversalID, False, False)
Call requestDoc.Sign()
Call requestDoc.Save(True, True)
End Sub
Sub CreateItem(doc As NotesDocument, itemName As String, itemType As Integer, itemValue As Variant, signItem As Boolean, protectItem As Boolean)
'/**
' * creates a item in the document.
' *
' * @param doc NotesDocument to create item in.
' * @param itemName name of the item to create.
' * @param itemType type of the item to create.
' * @param itemValue value to assign to item.
' * @param signItem should item be signed?
' * @param protectItem should item be protected?
' *
' * @author Thomas Bahn <tbahn@assono.de>
' * @version 2014-07-03
' */
Dim item As NotesItem
If doc.HasItem(itemName) Then
Call doc.RemoveItem(itemName)
End If
If itemType <> 0 Then
Set item = New NotesItem(doc, itemName, itemValue, itemType)
Else
Set item = New NotesItem(doc, itemName, itemValue)
End If
item.IsSummary = True
item.IsSigned = signItem
item.IsProtected = protectItem
End Sub
Ich speichere diesen Agenten in der gleichen Datenbank unter den Namen "Datenbank per Administration Request signieren lassen via admin4.nsf-Dokument" (SignDatabaseAdminPViaAdmin4.nsfDoc).
Dann starte ich ihn auf der Serverkonsole mit
tell amgr run "datenbank.nsf" 'SignDatabaseAdminPViaAdmin4.nsfDoc'
Er läuft fehlerfrei durch und die Datenbank wird tatsächlich mit der Server-ID signiert (Juhu!).
Eigentlich machen beide Agenten doch das gleiche: Ein Dokument in der admin4.nsf anlegen, das dann vom Administrationsprozess abgearbeitet wird. Trotzdem geht der eine Weg, der andere nicht.
Weitere, ausgiebige Recherchen bringen weitere Ideen:
Jetzt komme ich irgendwie auf die Idee, einige Gruppen aus dem öffentlichen Adressbuch in das persönliche kopieren: LocalDomainAdmins und LocalDomainServers.
ERFOLG!
Das war's.
Offenbar prüft der (Admin-)Client die Berechtigung des Benutzers und verwendet dabei ggf. vorhandene lokale Gruppen?!
Egal, Problem gelöst, Kunde glücklich. Und das ist das Wichtigste.
Kurze Recherche und viel Überprüfen der aktuellen Einstellungen:
- Aktueller Benutzer muss Administrator
auf dem Server sein (Server-Dokument, Sicherheit-Reiter): gecheckt.
Er hat sogar das Recht für Administration mit voller Berechtigung - und selbst wenn diese aktiviert ist, kommt der Fehler. - Er muss mindestens Autor sein auf der Datenbank admin4.nsf (Administrationsanforderungen) und Dokumente erstellen können: gecheckt, sogar Manager mit allen Rechten.
- Er ist Manager auf der zu signierenden Datenbank
- Er kann die Gestaltung der Datenbank mit seiner ID problemlos signieren.
Option Public
Option Declare
Sub Initialize
'/**
' * signs this database by manually creating a request with NotesAdministrationProcess.
' *
' * @author Thomas Bahn <tbahn@assono.de>
' * @version 2014-07-03
' */
Dim uiws As New NotesUIWorkspace
Dim session As NotesSession
Dim currentDB As NotesDatabase
Dim adminDB As NotesDatabase
Dim adminP As NotesAdministrationProcess
Dim noteID As String
Set uiws = New NotesUIWorkspace
Set session = New NotesSession
Set currentDB = session.CurrentDatabase
Set adminDB = New NotesDatabase(currentDB.Server, "admin4.nsf")
Set adminP = session.CreateAdministrationProcess(currentDB.Server)
noteID = adminP.SignDatabaseWithServerID(currentDB.Server, currentDB.FilePath, False)
If noteID <> "" Then
Call uiws.EditDocument(False, adminDB.GetDocumentByID(noteID))
End If
End Sub
Ich nenne ihn "Datenbank per Administration Request signieren lassen via AdminP-Objekt" (SignDatabaseAdminPViaAdminPObject),
- Auslöser: Durch Ereignis - Auswahl im Menü 'Aktionen'
- Ziel: Keine
- Sicherheitsstufe zur Laufzeit: 2. Beschränkte Operationen zulassen
Es kommt in der Zeile mit "adminP.SignDatabaseWithServerID" der gleiche Fehler wie im Admin-Client.
Noch gebe ich mich nicht geschlagen, ein anderer Weg, ein anderer Agent:
Option Public
Option Declare
Sub Initialize
'/**
' * signs this database by manually creating a request document in the admin4.nsf.
' *
' * @author Thomas Bahn <tbahn@assono.de>
' * @version 2014-07-03
' */
Dim session As NotesSession
Dim currentDB As NotesDatabase
Dim adminDB As NotesDatabase
Dim requestDoc As NotesDocument
Set session = New NotesSession
Set currentDB = session.CurrentDatabase
Set adminDB = New NotesDatabase(currentDB.Server, "admin4.nsf")
Set requestDoc = adminDB.CreateDocument
Call CreateItem(requestDoc, "Form", 0, "AdminRequest", False, True)
Call CreateItem(requestDoc, "Type", 0, "AdminRequest", False, True)
Call CreateItem(requestDoc, "ProxyProcess", 0, "Adminp", True, True)
Call CreateItem(requestDoc, "ProxyAction", 0, "101", True, True)
Call CreateItem(requestDoc, "ProxyTextItem1", 0, "0", True, True)
Call CreateItem(requestDoc, "ProxyOriginatingTimeDate", 0, Now, True, False)
Call CreateItem(requestDoc, "ProxyServer", 0, currentDB.Server, True, True)
Call CreateItem(requestDoc, "ProxyDatabasePath", 0, currentDB.FilePath, True, True)
Call CreateItem(requestDoc, "ProxyNameList", 0, currentDB.Title, True, True)
Call CreateItem(requestDoc, "ProxyAuthor", 0, session.UserName, True, True)
Call CreateItem(requestDoc, "FullName", AUTHORS, session.UserName, True, False)
Call CreateItem(requestDoc, "$OnBehalfOf", AUTHORS, session.UserName, True, False)
Call CreateItem(requestDoc, "ProxyOriginatingAuthor", AUTHORS, session.UserName, True, False)
Call CreateItem(requestDoc, "ProxyOriginatingOrganization", AUTHORS, StrRightBack(session.UserName, "/O="), True, False)
Call CreateItem(requestDoc, "ProxyOriginatingRequestUNID", 0, requestDoc.UniversalID, False, False)
Call requestDoc.Sign()
Call requestDoc.Save(True, True)
End Sub
Sub CreateItem(doc As NotesDocument, itemName As String, itemType As Integer, itemValue As Variant, signItem As Boolean, protectItem As Boolean)
'/**
' * creates a item in the document.
' *
' * @param doc NotesDocument to create item in.
' * @param itemName name of the item to create.
' * @param itemType type of the item to create.
' * @param itemValue value to assign to item.
' * @param signItem should item be signed?
' * @param protectItem should item be protected?
' *
' * @author Thomas Bahn <tbahn@assono.de>
' * @version 2014-07-03
' */
Dim item As NotesItem
If doc.HasItem(itemName) Then
Call doc.RemoveItem(itemName)
End If
If itemType <> 0 Then
Set item = New NotesItem(doc, itemName, itemValue, itemType)
Else
Set item = New NotesItem(doc, itemName, itemValue)
End If
item.IsSummary = True
item.IsSigned = signItem
item.IsProtected = protectItem
End Sub
Ich speichere diesen Agenten in der gleichen Datenbank unter den Namen "Datenbank per Administration Request signieren lassen via admin4.nsf-Dokument" (SignDatabaseAdminPViaAdmin4.nsfDoc).
- Auslöser: Durch Zeiplan - Nie
- Ziel: Keine
- Sicherheitsstufe zur Laufzeit: 2. Beschränkte Operationen zulassen
Dann starte ich ihn auf der Serverkonsole mit
tell amgr run "datenbank.nsf" 'SignDatabaseAdminPViaAdmin4.nsfDoc'
Er läuft fehlerfrei durch und die Datenbank wird tatsächlich mit der Server-ID signiert (Juhu!).
Eigentlich machen beide Agenten doch das gleiche: Ein Dokument in der admin4.nsf anlegen, das dann vom Administrationsprozess abgearbeitet wird. Trotzdem geht der eine Weg, der andere nicht.
Weitere, ausgiebige Recherchen bringen weitere Ideen:
- cache.ndk löschen - bringt nichts
- eine neue Arbeitsumgebung anlegen und zu dieser wechseln - bringt nichts
- im persönlichen Adressbuch die Letzten Kontakte (Recent Contacts) löschen - bringt nichts
- im persönlichen Adressbuch einen vorhandenen Eintrag für den aktuellen Benutzer löschen - bringt nichts
- im persönlichen Adressbuch alle Gruppen löschen - bringt nichts
Jetzt komme ich irgendwie auf die Idee, einige Gruppen aus dem öffentlichen Adressbuch in das persönliche kopieren: LocalDomainAdmins und LocalDomainServers.
ERFOLG!
Das war's.
Offenbar prüft der (Admin-)Client die Berechtigung des Benutzers und verwendet dabei ggf. vorhandene lokale Gruppen?!
Egal, Problem gelöst, Kunde glücklich. Und das ist das Wichtigste.
Quick-Tipp: Unter CentOS 6 zusätzliche Repositories benutzen
CentOS
6 ist für mich eine rundherum
gelungene Distribution. In Zusammenhang mit IBM-Software, wie z. B. IBM
Domino, gefällt mir besonders die Kombination aus "fast" offiziell
vom Hersteller unterstützter Plattform und kostenloser Benutzung für beliebig
viele Server. Gerade wenn man ständig neue VMs erstellt zum Testen, für
Demos usw., die man aber auch nach Monaten und Jahren noch nutzen können
möchte, müsste man sonst für andere Betriebssystemlizenzen sehr viel Geld
ausgeben. Nur "fast" weil es (binär-)kompatibel zum RedHat Enterprise
Linux (RHEL) ist, das IBM offiziell als Betriebssystem für IBM Notes, IBM
Sametime, IBM Connections usw. unterstützt.
Eine kleine Einschränkung ist aber, dass die Paketbibliotheken (Repositories), die direkt zu CentOS gehören, relativ klein sind zu anderen Linux-Distributionen. Kürzlich hatte ich auf einem Gentoo-Linux ein kleines Hilfsprogramm gesehen (htop), das mir auf Anhieb gefiel. Heute wollte ich auf eine mit NTFS formatierte, externe Festplatte zugreifen. Beides geht nicht mit den Paketen aus den Standard-Repositories. Aber glücklicherweise gibt es da was vonRatio... RepoForge
(vormals RPMForge), einem weiteren
Repository, das sich leicht einbinden
lässt.
Bei zusätzlichen Repositories gibt es ein prinzipielles Problem: Was ist, wenn die Pakete und/oder Versionen enthalten, die mit denen aus den Standard-Repositories konkurrieren? Bei RepoForge sollte das kaum auftreten, aber wenn man sich trotzdem etwas absichern möchte, kann man das mit dem yum-priorities-Plug-in tun. Hier die notwendigen Schritte in aller Kürze für CentOS 6:
Jetzt kann man das RepoForge-Repository hinzufügen:
Und zum Test kann man jetzt htop installieren:
Eine kleine Einschränkung ist aber, dass die Paketbibliotheken (Repositories), die direkt zu CentOS gehören, relativ klein sind zu anderen Linux-Distributionen. Kürzlich hatte ich auf einem Gentoo-Linux ein kleines Hilfsprogramm gesehen (htop), das mir auf Anhieb gefiel. Heute wollte ich auf eine mit NTFS formatierte, externe Festplatte zugreifen. Beides geht nicht mit den Paketen aus den Standard-Repositories. Aber glücklicherweise gibt es da was von
Bei zusätzlichen Repositories gibt es ein prinzipielles Problem: Was ist, wenn die Pakete und/oder Versionen enthalten, die mit denen aus den Standard-Repositories konkurrieren? Bei RepoForge sollte das kaum auftreten, aber wenn man sich trotzdem etwas absichern möchte, kann man das mit dem yum-priorities-Plug-in tun. Hier die notwendigen Schritte in aller Kürze für CentOS 6:
- im Terminalfenster
yum install yum-plugin-priorities - prüfen von /etc/yum/pluginconf.d/priorities.conf: enthält die Datei enabled=1 im Abschnitt ain/font>?
- in allen Dateien in /etc/yum.repos.d/ Prioritäten eintragen, also priority=n in allen Abschnitten eintragen, wobei ich für die Basis-Pakete in CentOS-Base.repo 1 bzw. 2 vergebe, in CentOS-Vault.repo 3 bis 7 (aufsteigend zu den älteren Versionen hin), in CentOS-Media.repo die 8 und in CentOS-Debuginfo.repo die 9.
Jetzt kann man das RepoForge-Repository hinzufügen:
- Je nach Plattform: 32-bit (i686):
wget http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el6.rf.i686.rpm
bzw. 64-bit (x86_64):
wget http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm
oder einfach:
wget http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el6.rf.`uname -m`.rpm - Signatur-Schlüssel holen:
rpm --import http://apt.sw.be/RPM-GPG-KEY.dag.txt - Paket überprüfen:
rpm -K rpmforge-release-0.5.3-1.el6.rf.*.rpm
Ergebnis sollte etwa so aussehen:
rpmforge-release-0.5.3-1.el6.rf.i686.rpm: (sha1) dsa sha1 md5 gpg OK - und installieren:
rpm -i rpmforge-release-0.5.3-1.el6.rf.*.rpm - in /etc/yum.repos.d/rpmforge.repo Prioritäten eintragen, also priority=n in allen Abschnitten eintragen (ich verwende 10 bis 12 von oben nach unten)
Und zum Test kann man jetzt htop installieren:
- einmalig die Yum-Caches säubern:
yum clean all - htop installieren:
yum install htop - und schließlich ausführen:
htop - für die NTFS-Unterstützung
installiere ich ntfs-3g:
yum install ntfs-3g
Quick-Tipp: Unter CentOS 6 zusätzliche Repositories benutzen
CentOS
6 ist für mich eine rundherum
gelungene Distribution. In Zusammenhang mit IBM-Software, wie z. B. IBM
Domino, gefällt mir besonders die Kombination aus "fast" offiziell
vom Hersteller unterstützter Plattform und kostenloser Benutzung für beliebig
viele Server. Gerade wenn man ständig neue VMs erstellt zum Testen, für
Demos usw., die man aber auch nach Monaten und Jahren noch nutzen können
möchte, müsste man sonst für andere Betriebssystemlizenzen sehr viel Geld
ausgeben. Nur "fast" weil es (binär-)kompatibel zum RedHat Enterprise
Linux (RHEL) ist, das IBM offiziell als Betriebssystem für IBM Notes, IBM
Sametime, IBM Connections usw. unterstützt.
Eine kleine Einschränkung ist aber, dass die Paketbibliotheken (Repositories), die direkt zu CentOS gehören, relativ klein sind zu anderen Linux-Distributionen. Kürzlich hatte ich auf einem Gentoo-Linux ein kleines Hilfsprogramm gesehen (htop), das mir auf Anhieb gefiel. Heute wollte ich auf eine mit NTFS formatierte, externe Festplatte zugreifen. Beides geht nicht mit den Paketen aus den Standard-Repositories. Aber glücklicherweise gibt es da was vonRatio... RepoForge
(vormals RPMForge), einem weiteren
Repository, das sich leicht einbinden
lässt.
Bei zusätzlichen Repositories gibt es ein prinzipielles Problem: Was ist, wenn die Pakete und/oder Versionen enthalten, die mit denen aus den Standard-Repositories konkurrieren? Bei RepoForge sollte das kaum auftreten, aber wenn man sich trotzdem etwas absichern möchte, kann man das mit dem yum-priorities-Plug-in tun. Hier die notwendigen Schritte in aller Kürze für CentOS 6:
Jetzt kann man das RepoForge-Repository hinzufügen:
Und zum Test kann man jetzt htop installieren:
Eine kleine Einschränkung ist aber, dass die Paketbibliotheken (Repositories), die direkt zu CentOS gehören, relativ klein sind zu anderen Linux-Distributionen. Kürzlich hatte ich auf einem Gentoo-Linux ein kleines Hilfsprogramm gesehen (htop), das mir auf Anhieb gefiel. Heute wollte ich auf eine mit NTFS formatierte, externe Festplatte zugreifen. Beides geht nicht mit den Paketen aus den Standard-Repositories. Aber glücklicherweise gibt es da was von
Bei zusätzlichen Repositories gibt es ein prinzipielles Problem: Was ist, wenn die Pakete und/oder Versionen enthalten, die mit denen aus den Standard-Repositories konkurrieren? Bei RepoForge sollte das kaum auftreten, aber wenn man sich trotzdem etwas absichern möchte, kann man das mit dem yum-priorities-Plug-in tun. Hier die notwendigen Schritte in aller Kürze für CentOS 6:
- im Terminalfenster
yum install yum-plugin-priorities - prüfen von /etc/yum/pluginconf.d/priorities.conf: enthält die Datei enabled=1 im Abschnitt ain/font>?
- in allen Dateien in /etc/yum.repos.d/ Prioritäten eintragen, also priority=n in allen Abschnitten eintragen, wobei ich für die Basis-Pakete in CentOS-Base.repo 1 bzw. 2 vergebe, in CentOS-Vault.repo 3 bis 7 (aufsteigend zu den älteren Versionen hin), in CentOS-Media.repo die 8 und in CentOS-Debuginfo.repo die 9.
Jetzt kann man das RepoForge-Repository hinzufügen:
- Je nach Plattform: 32-bit (i686):
wget http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el6.rf.i686.rpm
bzw. 64-bit (x86_64):
wget http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm
oder einfach:
wget http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el6.rf.`uname -m`.rpm - Signatur-Schlüssel holen:
rpm --import http://apt.sw.be/RPM-GPG-KEY.dag.txt - Paket überprüfen:
rpm -K rpmforge-release-0.5.3-1.el6.rf.*.rpm
Ergebnis sollte etwa so aussehen:
rpmforge-release-0.5.3-1.el6.rf.i686.rpm: (sha1) dsa sha1 md5 gpg OK - und installieren:
rpm -i rpmforge-release-0.5.3-1.el6.rf.*.rpm - in /etc/yum.repos.d/rpmforge.repo Prioritäten eintragen, also priority=n in allen Abschnitten eintragen (ich verwende 10 bis 12 von oben nach unten)
Und zum Test kann man jetzt htop installieren:
- einmalig die Yum-Caches säubern:
yum clean all - htop installieren:
yum install htop - und schließlich ausführen:
htop - für die NTFS-Unterstützung
installiere ich ntfs-3g:
yum install ntfs-3g
Quick-Tipp: Optionale berechnete Teilmaske
Manchmal möchte man eine Teilmaske in eine Maske nur einfügen, wenn eine
Bedingung erfüllt ist, z. B. wenn ein bestimmter Status im Workflow erreicht
wurde. Gegenüber dem Verstecken mit "Hide-When"-Formeln hat das
den Vorteil, dass der Notes-Client entsprechend weniger berechnen muss
(Hide-When-Formeln selbst, aber ggf.auch viele @DbLookups, Werte-Formeln,
usw.) und das Dokument von unnötigen Items verschont bleibt. Dafür gibt
es bei Notes die Möglichkeit der Berechneten Teilmaske.
Für eine Anpassung der Mail-Datenbank hatte ich kürzlich wieder mal diesen Fall. Nur wenn in den Mail-Vorgaben die Funktion "Zentrale Ablage" aktiviert wurde, sollte die Teilmaske zur Auswahl der zentralen Ablage und zur Verschlagwortung des Dokuments angezeigt werden. Die Formel der Berechneten Teilmaske in der Memo- und den beiden Reply-Masken sah dann (vereinfacht) so aus:
@If(
@GetProfileField("CalendarProfile"; "CentralFilingStatus") = "1";
"CentralFiling";
""
)
Das funktioniert auch wunderbar.
Einige Tage später kam dann ein Kollege auf mich zu und machte mich darauf aufmerksam, dass in der Statuszeile immer wieder die Meldung "Element des Dokuments nicht gefunden" erschien. Nach kurzer Analyse konnte ich die Meldung mit der Berechneten Teilmaske in Verbindung bringen.
Meine erste Lösung:
Eine leere Teilmaske z. B. "EmptySubform" - erstellen und deren Name (oder Alias) in die Formel einfügen:
@If(
@GetProfileField("CalendarProfile"; "CentralFilingStatus") = "1";
"CentralFiling";
"EmptySubform"
)
Die zweite, etwas elegantere Lösung:
Statt des leeren Strings @Nothing zurück geben:
@If(
@GetProfileField("CalendarProfile"; "CentralFilingStatus") = "1";
"CentralFiling";
@Nothing
)
Für eine Anpassung der Mail-Datenbank hatte ich kürzlich wieder mal diesen Fall. Nur wenn in den Mail-Vorgaben die Funktion "Zentrale Ablage" aktiviert wurde, sollte die Teilmaske zur Auswahl der zentralen Ablage und zur Verschlagwortung des Dokuments angezeigt werden. Die Formel der Berechneten Teilmaske in der Memo- und den beiden Reply-Masken sah dann (vereinfacht) so aus:
@If(
@GetProfileField("CalendarProfile"; "CentralFilingStatus") = "1";
"CentralFiling";
""
)
Das funktioniert auch wunderbar.
Einige Tage später kam dann ein Kollege auf mich zu und machte mich darauf aufmerksam, dass in der Statuszeile immer wieder die Meldung "Element des Dokuments nicht gefunden" erschien. Nach kurzer Analyse konnte ich die Meldung mit der Berechneten Teilmaske in Verbindung bringen.
Meine erste Lösung:
Eine leere Teilmaske z. B. "EmptySubform" - erstellen und deren Name (oder Alias) in die Formel einfügen:
@If(
@GetProfileField("CalendarProfile"; "CentralFilingStatus") = "1";
"CentralFiling";
"EmptySubform"
)
Die zweite, etwas elegantere Lösung:
Statt des leeren Strings @Nothing zurück geben:
@If(
@GetProfileField("CalendarProfile"; "CentralFilingStatus") = "1";
"CentralFiling";
@Nothing
)
Quick-Tipp: FTP-Server auf VMware vSphere Hypervisor (ESXi) einrichten
Wenn man große Dateien kopiert, dauert es eigentlich immer zu lange...
Aber besonders langsam ist das Kopieren in meiner Erfahrung mit dem Datastore Explorer beim VMware vSphere Client. Und da die VMDK-Dateien normalerweise auch ziemlich groß sind - zwei- bis dreistellige Werte in GB - nervt das zumindest mich ganz erheblich.
Wenn man die SSH-Funktion aktiviert hat, dann man mit FTPs und SCP schon deutlich schneller kopieren. Auf Windows benutze ich dann entweder WinSCP oder FileZilla. Je nach Ausstattung des VMware vSphere Hypervisors (ESXi-Servers), macht es auch noch einen Unterschied, welchen Verschlüsselungsalgorithmus man auswählt (Blowfish ist normalerweise schneller als AES ist schneller als 3DES) und ob man die Datenkompression aktiviert.
Wenn man es aber noch schneller haben möchte und sich innerhalb einer sicheren, vertrauenswürdigen Umgebung befindet, könnte man auch das unverschlüsselte FTP nutzen.
Der VMware vSphere Hypervisor bringt auch einen FTP-Client mit, aber leider nur einen ziemlich einfachen, konsolen-basierten. In der ESXi- oder einer SSH-Shell kann man mit ftp zielhost eine FTP-Sitzung starten. help gibt die möglichen Befehle aus.
Komfortabler wäre es, wenn man einen "richtigen", grafischen FTP-Client unter Windows oder Linux nutzen könnte und ein FTP-Server auf dem ESXi-Server liefe. Leider hat VMware dieses Feature bisher nicht vorgesehen, so dass man sich anders behelfen muss.
Zum Glück hat Andreas Peetz sich die Arbeit gemacht, den kostenlosen Open-Source-FTP-Server ProFTPd für den ESXi-Server zu paketieren und teilt mit uns das Ergebnis seiner Arbeit.
Jetzt sind es praktisch nur noch wenige Zeilen in der ESXi- oder einer SSH-Shell:
Die Deinstallation geht dann einfach mit
esxcli software vib remove -n ProFTPD
Man kann den FTP-Server dann mit /etc/init.d/proftpd stop beenden und mit /etc/init.d/proftpd start wieder starten.
Quellen:
Howto add FTP server in ESXi 5.x to support Fast Download/Upload of VM / Images
elease ProFTPD (FTP server) for VMware ESXi 5.x
Unter A Daemon's VIB: Building a software package for VMware beschreibt Andreas Peetz in drei Artikeln, wie der das ProFTPd-Paket erstellt hat.

Aber besonders langsam ist das Kopieren in meiner Erfahrung mit dem Datastore Explorer beim VMware vSphere Client. Und da die VMDK-Dateien normalerweise auch ziemlich groß sind - zwei- bis dreistellige Werte in GB - nervt das zumindest mich ganz erheblich.
Wenn man die SSH-Funktion aktiviert hat, dann man mit FTPs und SCP schon deutlich schneller kopieren. Auf Windows benutze ich dann entweder WinSCP oder FileZilla. Je nach Ausstattung des VMware vSphere Hypervisors (ESXi-Servers), macht es auch noch einen Unterschied, welchen Verschlüsselungsalgorithmus man auswählt (Blowfish ist normalerweise schneller als AES ist schneller als 3DES) und ob man die Datenkompression aktiviert.
Wenn man es aber noch schneller haben möchte und sich innerhalb einer sicheren, vertrauenswürdigen Umgebung befindet, könnte man auch das unverschlüsselte FTP nutzen.
Der VMware vSphere Hypervisor bringt auch einen FTP-Client mit, aber leider nur einen ziemlich einfachen, konsolen-basierten. In der ESXi- oder einer SSH-Shell kann man mit ftp zielhost eine FTP-Sitzung starten. help gibt die möglichen Befehle aus.
Komfortabler wäre es, wenn man einen "richtigen", grafischen FTP-Client unter Windows oder Linux nutzen könnte und ein FTP-Server auf dem ESXi-Server liefe. Leider hat VMware dieses Feature bisher nicht vorgesehen, so dass man sich anders behelfen muss.
Zum Glück hat Andreas Peetz sich die Arbeit gemacht, den kostenlosen Open-Source-FTP-Server ProFTPd für den ESXi-Server zu paketieren und teilt mit uns das Ergebnis seiner Arbeit.
Jetzt sind es praktisch nur noch wenige Zeilen in der ESXi- oder einer SSH-Shell:
- mkdir Zielverzeichnis
- cd Zielverzeichnis
- wget http://esxi-customizer.googlecode.com/files/ProFTPD-1.3.3-8-offline_bundle.zip
- esxcli software vib install --no-sig-check -d /vmfs/volumes/1TB/ftp/ProFTPD-1.3.3-8-offline_bundle.zip
Die Deinstallation geht dann einfach mit
esxcli software vib remove -n ProFTPD
Man kann den FTP-Server dann mit /etc/init.d/proftpd stop beenden und mit /etc/init.d/proftpd start wieder starten.
Quellen:
Howto add FTP server in ESXi 5.x to support Fast Download/Upload of VM / Images
elease ProFTPD (FTP server) for VMware ESXi 5.x
Unter A Daemon's VIB: Building a software package for VMware beschreibt Andreas Peetz in drei Artikeln, wie der das ProFTPd-Paket erstellt hat.