2009年12月28日月曜日

Excelファイルを読み込んで文書を作成する


マスターファイルとして使っているようなExcelファイルをNotesアプリケーションへ取り込みたいといったことはよくあります。
そんなときに雛型として利用している Lotus Script をご紹介します。

Sub Initialize
Dim ws As New NotesUIWorkspace
Dim db As NotesDatabase
Dim filepath As Variant
Dim xlapp As Variant
Dim xlbook As Variant
Dim xlsheet As Variant
Dim maxrows As Long
 
Set db = ws.CurrentDatabase.Database
 
On Error Goto ERRORHANDLER
 
filepath = ws.OpenFileDialog(False, "取り込むファイルを選択してください。 ", _
"EXCEL 97-2003|*.xls|EXCEL 2007|*.xlsx|CSV|*.csv|TXT|*.txt|すべて|*|")
 
If Isempty(filepath) Then Exit Sub
 
'選択したExcelファイルを開く、ただし画面には表示しない
Set xlapp = CreateObject("Excel.Application")
Set xlbook = xlapp.Workbooks.Open(filepath)
Set xlsheet = xlbook.Worksheets(1)
xlApp.Visible = False
 
'最終行の番号を調べる
maxrows = xlsheet.UsedRange.Rows(xlsheet.UsedRange.Rows.Count).Row
 
For rows = 1 To maxrows 'タイトル行がある場合、開始行を変更する
Set doc = New NotesDocument(db)
'ここから -都合にあわせて書き換える
Call doc.ReplaceItemValue("Form", "MainTopic")
Set item = doc.ReplaceItemValue("Category1", Cstr(xlsheet.Cells(rows, 1).Value))
Set item = doc.ReplaceItemValue("Category2", Cstr(xlsheet.Cells(rows, 2).Value))
'ここまで
Call doc.Save(True, True)
Set doc = Nothing
Next
 
ENDPROC: 'Excelを終了する
If Not xlapp Is Nothing Then
xlapp.quit
Set xlapp = Nothing
End If
Exit Sub

ERRORHANDLER:
Msgbox Str(Err) & ": " & Error$
Resume ENDPROC
End Sub

2009年12月25日金曜日

文書の「閉じかた」には気をつけろ!

ドミノ懇談室のこのスレッドに関連して
返答文書を保存したあと、親文書の値を書き換えて、画面を閉じる、といったアクションボタンの式をテストしていた時のこと、

(アクションボタンへは次のように3行だけ記述されています)
@Command([FileSave]);
@SetDocField($Ref; "LastUpdated"; @Now);
@Command([FileCloseWindow])

上記の「画面を閉じる」部分を @Command([CloseWindow]) に書き換えて実行したところ、NSDを出力してNotesが強制終了されました。
(Notes 8.0.1 Basicで確認)

試しに @PostedCommand([CloseWindow]) に書き換えたところ、これは正常に終了します。

結果です。
× @Command([CloseWindow])
○ @PostedCommand([CloseWindow])

○ @Command([FileCloseWindow])


正常に動作するのは、どれも他の式がすべて評価された後に実行されるものです。
上記3パターンのうち @Command([CloseWindow]) だけがすぐに実行されます。

いずれにしても、式の一番最後に記述されているのに挙動が異なる場合もあるのですね。

2009年12月23日水曜日

全文索引フォルダのサイズを調べる


全文索引を作成すると、アプリケーションのnsfファイルがあるフォルダに.ft で終わるフォルダが作成されます。

アプリケーションのファイル名が names.nsf の場合、フォルダ名は names.ft となります。わかりやすいですね。

これまでサイズを調べるには、アプリケーションのプロパティや Windows の Explorer でフォルダのプロパティを参照していました。
ただサーバー内のすべての全文索引について調べるには、これでは効率が悪すぎます。

そこでツールを作ろうと思い立ったわけです。

「添付ファイルを作成する」を有効にしている場合など.ftフォルダのサイズが大きいものではギガバイト単位に膨れ上がったりします。

Domino6.5.3の頃、そんなアプリケーションで誰かが全文検索した時にサーバーがクラッシュするといった不具合が時々あり、試しに「添付ファイルを作成する」を無効にして.ftフォルダのサイズを抑制したところ、全文検索時のクラッシュが再現しなくなった、といったこともありました。

下のプログラムは.ftフォルダのサイズを調べるツールとして作成したものです。Windows 2000 Serverで稼働しました。

Dim FSO, FLD, TF
Set FSO = WScript.CreateObject("Scripting.FileSystemObject")
Set FLD = FSO.GetFolder(".")
Set TF = FSO.CreateTextFile(FLD.Path & "\ftlist.csv", True)
ScanFolder FSO.GetFolder(FLD.Path), FSO, TF
TF.Close()
Set TF = Nothing
Set FLD = Nothing
set FSO = Nothing
WScript.Echo("全文索引フォルダのリストを出力しました。")

Sub ScanFolder(objFolder, objFSO, objFile)
 Const DQ = """"
 For Each o in objFolder.subFolders
  if Ucase(Right(o.Name, 3)) = ".FT" Then
   objFile.WriteLine DQ & o.Path & DQ & "," & DQ & o.Size & DQ
  Else
   ScanFolder objFSO.GetFolder(o), objFSO, objFile
  End If
 Next
End Sub

上記プログラムを ftlist.vbs などとして Notes\Data フォルダに保存します。

コマンドプロンプトから実行すると、notes\dataフォルダ配下の .ft フォルダの名前とそのサイズを ftlist.csv へ出力します。

ただ、ディレクトリリンクがあっても、その先まで追いかけませんが、そこは即席手抜きツールなのでご勘弁。

2009年12月21日月曜日

文書を編集できないよう制御する

アクセス制御リスト(ACL)上では便宜上、編集者としなければならないけれど、特定の条件下では閲覧できるけれども編集モードにさせたくない、といった場合がときどきあります。

編集モードで表示するには、表示モードで開いている状態から移行する他に、ビューから Ctrl + E 等で直接編集モードで開くといったパターンが考えられます。

表示モードから編集モードへ切り替える時、フォームのイベント Querymodechange で判定できます。
Sub Querymodechange( Source As Notesuidocument, Continue As Variant )
    If Not Source.EditMode Then
        If Isnull( Arraygetindex( Evaluate( "@UserRoles" ), "[Admin]", 5 ) ) Then Continue = False
    End If
End Sub

上のコードでは、Evaluate の戻り値が配列になるので Arraygetindex で配列のどこに管理者のロールがあるかを調べています。配列のどこにもなければ Null となり判定できます。


ビューから直接、編集モードで開く時、フォームのイベント QueryOpen で判定できます。
Sub Queryopen( Source As Notesuidocument, Mode As Integer, Isnewdoc As Variant, Continue As Variant )
    If Not Isnewdoc Then
        If Mode = 1 Then
            dbadmin = Evaluate(|@IsMember( "[Admin]"; @UserRoles )|)
            If dbadmin( 0 ) = 0 Then Continue = False
        End If
    End If
End Sub

上のコードでは、@IsMember で管理者のロールが付与されているかどうかを調べています。ロールが付与されていなければ戻り値(配列)の0番目には0が入ります。

なお、新規文書の作成時には判定しないよう制御しています。
判定方法はいろいろあると思いますが、上のどちらも@関数とロールを活用してコードを簡単にしています。

2009年12月20日日曜日

しょっちゅう変更されるトップページの実現について


アプリケーションを開いたときに特定の文書をトップページ(ホーム/初期画面)として開きたい、といった要望がときどきあります。
 
変更頻度がほとんどないなら「アプリケーションについて」などで代用することを提案するのですが、中には「毎日更新する」という精力的な方々もいらっしゃいます。
 
けれど、アクセス制御リストにおいてアプリケーションの利用者を編集者以下として設定している場合、ページのような設計要素を変更することができません。
 
そういった時はトップページ専用のフォームを用意して、アプリケーションの利用者が文書を作成できるようにしています。
 
フォームにはトップページを変更するためのアクションボタンがあり、それをクリックすると文書のUNIDをプロフィール文書へ設定します。
このプロフィール文書は特定の個人専用ではなく、皆が共通で利用することを想定しています。
 
アプリケーションを開いたときに必ず開くフレームセットでは、その中のトップページを表示するフレームでURLの式を次のように設定しています。
 
serv := @Left(@Name([Abbreviate];@Subset(@DbName;1));"/");
dbn := @ReplaceSubstring(@Subset(@DbName; -1); "\\"; "/");
docid := @Text(@GetProfileField("CommonProfile"; "MessageDocumentUNID"; "Common"));
tmp := @GetDocField(docid; "Subject");
"notes://" + serv + "/" + dbn + @If(@IsNull(tmp) | @IsError(tmp); "/BLANKPAGE?OpenPage"; "/0/" + docid + "?OpenDocument")

万一、トップページ文書が削除された場合でもフレームセットがキチンと表示されるよう "BLANKPAGE" という名前のページを作成しています。 

2009年12月19日土曜日

カレンダーの現在の日付を新規文書へ反映させる


カレンダー形式のビューで新規文書を作成するアクション(日付の枠内をダブルクリック、あるいはビューのアクションボタンをクリック)したとき、アクティブになっている日付を、新規文書の日付欄へ自動で設定したい場合があります。
 
ダブルクリックした日付は NotesUIView.CalendarDateTime で取得することができます。
これをカレンダービューのイベント Regiondoubleclick で使います。
 
Sub Regiondoubleclick(Source As Notesuiview)
 Dim ws As New notesuiworkspace
 Dim uidoc As NotesUIDocument
 Dim doc As NotesDocument
 Set uidoc = ws.ComposeDocument("", "", "Schedule")
 Set doc = uidoc.Document
 doc.StartDate = Datevalue(Source.CalendarDateTime)
End Sub

 
アクティブな日付をカレンダービューのアクションボタンで取得するには、NotesUIWorkspace.CurrentCalendarDateTime を使います。
 
Sub Click(Source As Button)
 Dim ws As New notesuiworkspace
 Dim uidoc As NotesUIDocument
 Dim doc As NotesDocument
 Dim varDate As Variant
 varDate = ws.CurrentCalendarDateTime
 Set uidoc = ws.ComposeDocument("", "", "Schedule")
 Set doc = uidoc.Document
 doc.StartDate = varDate
End Sub
 
ちなみに、最後の行を
doc.StartDate = ws.CurrentCalendarDateTime
とすると値が入らないし、
doc.StartDate = Datevalue(ws.CurrentCalendarDateTime)
とするとすんなりいきません(Notes 8.0.1で確認)

2009年12月17日木曜日

文書リンクから非表示フィールドへアクセスする


フォームのプロパティで「印刷/転送/クリップボードへのコピーを不可」が有効な場合、そのフォームで作成した文書をプロパティで覗くと、各アイテムの値等がグレーアウトされます。
その状態ではスクロールもできないので、隠しフィールドにある値が確認できない、といったことがあります。
 
そこで、アイテムの値を簡単に確認できるものを作ってみました。
 
下記はフォーム上のホットスポットボタンに書いた Lotus Script です。
フォームにはこのボタンの他にリッチテキスト・フィールド "Body" があります。
 
ユーザーはこのリッチテキスト・フィールドへ対象文書のリンクを貼り付けた後、ボタンをクリックします。
すると文書内にある読者フィールドまたは作成者フィールドのアイテム名と値をステータスバーへ表示します。
 
Sub Click(Source As Button)
 Dim ws As New NotesUIWorkspace
 Dim db As New NotesDatabase("", "")
 Dim doc As NotesDocument, tdoc As NotesDocument
 Dim rti As NotesRichTextItem
 Dim rtnav As NotesRichTextNavigator
 Dim rtlink As NotesRichTextDocLink
 Dim DUMMYID As String, tmpType As String
 DUMMYID = String$(32, "0")

 Call ws.CurrentDocument.Refresh(True) '保存せずリンクへアクセスするための Refresh

 Set doc = ws.CurrentDocument.Document
 Set rti = doc.GetFirstItem("Body")
 Set rtnav = rti.CreateNavigator
 If rtnav.FindFirstElement(RTELEM_TYPE_DOCLINK) Then
  Set rtlink = rtnav.GetElement
  If rtlink.DocUnID <> DUMMYID Then
   If db.OpenByReplicaID(rtlink.ServerHint, rtlink.DbReplicaID) Then
    Set tdoc = db.GetDocumentByUNID(rtlink.DocUnID)
    If Not (tdoc Is Nothing) Then
     Forall item In tdoc.Items
      If item.IsAuthors Or item.IsReaders Then
       If item.IsAuthors Then
        tmpType = "AUTHORS"
       Else
        tmpType = "READERS"
       End If
       Print "Item : " & item.Name & " , Type : " & tmpType & " , Value : " & item.Text
      End If
     End Forall
    End If
   End If
  End If
 End If
End Sub

上記では文書にあるすべてのアイテムのタイプを調べていますが、アイテム名がわかっている場合はフォーム上で指定できてもいいかもしれませんね。
 
まあ、結果をPrint 文で表示しているあたり、いいかげんに作ったことがバレバレですが....失礼。
 
上記フォームのフィールド SaveOptions へ "0" をセットしておくと、すんなりとフォームを閉じることができます。

2009年12月16日水曜日

すべてのアプリケーションのACLエントリを書き出す

あるサーバー内に設置されたすべてのアプリケーションでアクセス権がどのように設定されているのか、ということを一気に書き出すエージェント(Lotus Script)を紹介します。

ACL取得専用のDBをひとつサーバーに設置して、エージェントを二つ登録します。

ひとつは、下に示すエージェントをサーバー上で実行する指示を出すエージェント、

もうひとつは、以下のとおりです。

処理のおおまかな流れとしては、

1. サーバー内のアプリケーションのリストを取得する
2. 各アプリケーションのACLエントリを書き出す

となっています。

サーバー内のアプリケーションのリストは、NotesSession.GetDbDirectory で取得できます。
リストからアプリケーションを1件取り出してはACLのエントリを書き出す、という処理を繰り返すわけです。

ひとつのACLエントリにつき1文書を書き出していきます。
Sub Initialize
    '処理を起動する
    Dim ss As New NotesSession
    Call dblist(ss)
End Sub

Sub dblist(ss as NotesSession)
    'サーバー上にあるアプリケーションのリストを取得、処理させる
    Dim dbdir As NotesDbDirectory
    Dim db As NotesDatabase
    Set dbdir = ss.GetDbDirectory(ss.CurrentDatabase.Server)
    Set db = dbdir.GetFirstDatabase(DATABASE)
    While Not (db Is Nothing)
        Call getaclentries(db, ss)
        Set db = dbdir.GetNextDatabase()
    Wend
End Sub

Sub getaclentries(db As NotesDatabase, ss as NotesSession)
    'アプリケーションのACLに登録されたエントリを書き出します。
    Dim notesacl As NotesACL
    Dim aclentry As NotesACLEntry
    Dim doc As NotesDocument

    If Not db.IsOpen Then flag = db.Open("", "")
    Set notesacl = db.ACL
    Set aclentry = notesacl.GetFirstEntry()
    While Not (aclentry Is Nothing)
        Set doc = New NotesDocument(ss.CurrentDatabase)
        With doc
            .Form = "ACLEntry"
            .Server = db.Server
            .DBFilePath = db.FilePath
            .DBTitle = db.Title
            .DBReplicaID = db.ReplicaID
            .CreateDate = CreateDate.LSLocalTime
            .CanCreateDocuments = OFFON(aclentry.CanCreateDocuments)
            .CanCreateLSOrJavaAgent = OFFON(aclentry.CanCreateLSOrJavaAgent)
            .CanCreatePersonalAgent = OFFON(aclentry.CanCreatePersonalAgent)
            .CanCreatePersonalFolder = OFFON(aclentry.CanCreatePersonalFolder)
            .CanCreateSharedFolder = OFFON(aclentry.CanCreateSharedFolder)
            .CanDeleteDocuments = OFFON(aclentry.CanDeleteDocuments)
            .CanReplicateOrCopyDocuments = OFFON(aclentry.CanReplicateOrCopyDocuments)
            .IsAdminReaderAuthor = OFFON(aclentry.IsAdminReaderAuthor)
            .IsAdminServer = OFFON(aclentry.IsAdminServer)
            .IsGroup = OFFON(aclentry.IsGroup)
            .IsPerson = OFFON(aclentry.IsPerson)
            .IsPublicReader = OFFON(aclentry.IsPublicReader)
            .IsPublicWriter = OFFON(aclentry.IsPublicWriter)
            .IsServer = OFFON(aclentry.IsServer)
            .Level = aclentry.Level
            .Name = aclentry.Name
            .Roles = aclentry.Roles
            .UserType = aclentry.UserType
            flag = .Save(True, True)
        End With
        Set aclentry = notesacl.GetNextEntry(aclentry)
        Set doc = Nothing
    Wend
End Sub

Function OFFON(flag As Variant) As String
    'True/Falseの値を扱いやすくするため、Trueを"1"、Falseを"0"に置き換えます
    If flag Then
        OFFON = "1"
    Else
        OFFON = "0"
    End If
End Function

ところで、サーバー管理者と言えど全てのアプリケーションへのアクセスが許可されていない場合があります。
そこで、NotesAgent.RunOnServer でエージェントをサーバー側で実行する際、サーバー側で実行するエージェントのプロパティにある「実行時セキュリティレベルの設定」は"3. フルアドミニストレータ権限で制限された操作を許可する"を選択します。


当方では、上記で書き出したリストを簡易な変更履歴として保管する仕組みを追加しています。といっても、前回までに取得したACLエントリ文書へフラグをたてて、最新のACLエントリ文書と区別できるようにしているだけですけどね。

2009年12月15日火曜日

日付の範囲をカレンダービューで表示する


日付の範囲が入力されたアイテムをプロパティ等でみてみると、ハイフンで区切られた二つの日付が見えます。

2009/12/15 - 2009/12/17

このアイテムをカレンダービューの1列目にそのまま設定しても、最初の日(上の例では12/15)だけにしか表示されません。

カレンダービューの12月15日、16日、17日の3日間すべてに文書を表示する場合、範囲をひとつひとつの日付に分解します。

そこで登場するのが @Explode 関数です。

@Explode 関数は特定の区切り文字で区切られた単一の値をリストに分解するものとして利用しますが、日付の範囲の場合、複数値に分割してくれます。

例えば次のような式の場合、"2009/12/15", "2009/12/16", "2009/12/17" の3つの値に分解されます。

@Explode([2009/12/15 - 2009/12/17])

ここで注意しなけれならないのは、戻り値が日付のリストではなくテキストのリストであることです。

そんな訳でカレンダービューの1列目に設定する場合、上記式の戻り値を @TextToTime 関数で日時形式へ変換します。

例えば StartEndDate フィールドに日付の範囲が入力されている場合、列式へは

@TextToTime(@Explode(StartEndDate))

と指定します。

そしてビューの1列目で、ソートを昇順、「複数値を別のエントリで表示」をオンにすればできあがりです。

2009年12月10日木曜日

Field: 'FIELDNAME': Too many arguments for database function

日本語版Notesではこのメッセージが


フィールド: FIELDNAME: データベースの関数に対して引数が多すぎます。

と表示されます。

これはR6.5で作られたDBの文書をR5のクライアントを使って開こうとしたときに表示されたメッセージです。

@DbLookup のパラメータとして使った [FAILSILENT] がR5では使えないのです。

Notesに限らず開発する場合には、利用者の中でも一番低いバージョンにあわせて開発するのがキホンです。

今回は知らない間にアクセス許可された海外事業所の利用者のことまで考えがおよばなかったという(言い訳するところも含めて)お粗末な見本でした。

2009年12月9日水曜日

アプリケーション管理者の扱い


ワークスペースからアイコンを削除するつもりでnsfファイルを削除してしまう...

単純な操作ミスですが、そんなトラブルを経験したことはないですか?

削除されたnsfファイルをバックアップテープからリストアするのは時間もかかるし面倒なんですよね。

過去に沢山そんな経験をしました。

アプリケーションへのアクセス権を変更するにはACLを変更できなければいけない、とするとACL上は「管理者」として登録しておかなければいけません。

でも管理者はnsfファイルもろとも削除することができてしまう。

そこでACLを触らずにアクセス権を変更できるようにするため、ドミノディレクトリのグループ文書を利用することにしました。

アプリケーション管理者へは権限の種類ごとに用意したグループ文書を編集できる権限を付与します。

そしてACLではアプリケーションの管理者へ付与する権限は「設計者」以下としました。

こうすることで、現在はリストア作業がほとんどなくなりました。

2009年12月8日火曜日

フレームセットの表示内容が表示されない


フレームセットを使ったアプリケーションで、ときどきフレームの中に何も表示されなくなる場合があります。

このような時、ブックマーク(bookmark.nsf)に格納された情報を削除すると元通りになることがあります。

削除方法
  1. アプリケーションのプロパティの情報タブにあるレプリカIDをメモします。
  2. ワークスペースからブックマークのアイコンを右クリックして、Ctrl キーと Shift キーを押しながら「アプリケーション - 移動」をクリックします。
  3. フォルダとビューのリストの中にある (ByURL) をクリックして[OK]ボタンをクリックします。
  4. するとNotes URL列に"Notes:///レプリカID" といった感じでリストされたビューが開きます。
  5. Notes:///の直後に、メモしたレプリカIDが表示された文書のすべての行へチェックマークを付けます。
  6. Delete キーを押すと「O001 オブジェクト変数が設定されていません」とメッセージが表示されますが、気にせず[OK]ボタンをクリックします。
  7. F9 キーを押し「n 文書をデータベース ブックマーク (8) から削除しますか?」のようなプロンプトが表示されるので[はい]ボタンをクリックします。
この後、念のためLotus Notesを再起動します。

2009年12月7日月曜日

プリビューペインは名前をこうするとこうなるの


フレームセットを使ったアプリケーションで、ビューで選択した文書をプリビューウインドウで表示したい場合があります。

そんな時はプリビューペインとするフレームに付ける名前を "NotesPreview" とするべきです。

しないと後悔しますよ~

それはどうしてでしょうか?

プリビューペインの名前を "Preview" とか "DocumentFrame" のようにしていても、ビューで選択した文書はプリビューペインへ表示されます。

ただ、プリビューペインを開いたり閉じたりするコマンド @Command([ShowHidePreviewPane]) が利かない、Notesのメニューに「表示(V) - プリビューペイン(P)」が表示されないといった事態になるのです。

でも "NotesPreview" だったら大丈夫!

きちんとプリビューとして機能してくれますよ。

2009年12月5日土曜日

アクセス権があるのにアクセスできない理由


ドミノディレクトリのグループ文書へユーザーを登録した後、当該アプリケーションのアクセス制御リストの画面にあるボタン[有効なアクセス権]を使い、該当するグループの権限が追加したユーザーに付与されたことを確認しました。

その後ユーザーがアプリケーションリンクをクリックすると、アクセスする権限がないと叱られてしまい、アクセスできないことがあります。

どうしてでしょうか?

ドミノサーバーは、ユーザーが初めてサーバーにアクセスした時点でドミノディレクトリを参照し、ユーザーがどのグループに所属しているかを確認します。
その後ユーザーのセッションが切れるまで、アクセスの際にグループに所属するかどうかの確認はしないのです。

したがって、Notesを起動中にドミノディレクトリのグループのメンバーへ追加しても、そのユーザーは当該データベースへアクセスできないのです。

この場合、ユーザー側でログインしなおす、あるいは Lotus Notes を再起動することで一旦セッションが切れ、アクセスできるようになります。

管理者側で操作する場合、サーバーのコンソールから "Drop NotesID" あるいは "Drop All" を投入してセッションを切ることでアクセスできるようになります。

2009年12月2日水曜日

ビューを開いたとき最後に閉じた文書にフォーカスする


データベースを開いた時に、前回最後に開いた文書をビュー上で選択した(フォーカスした)状態にしたい場合、データベースのプロパティにて「最後に表示していたものを表示する」を有効にすれば「ほとんどの場合」問題ないのですが、最後に閉じた文書をフォーカスしたい、といわれることもあります。

細かいことは気にするな! といいたいところですがそこをグッとこらえて...

ビュー上で文書を選択するには NotesUIView クラスの SelectDocument メソッドを利用します。

最後に開いていた文書を示す値をプロフィール文書へ保存することにします。ここで「文書を示す値」としてユニバーサルID(UNID)を利用したいと思います。
プロフィール文書も複製されますから、このようにしておくことでレプリカを開いたときにも機能しますね。

最後に開いていた文書のUNIDをプロフィール文書へ保存するための式をフォームのイベント Queryclose へ記述します。

@If(@IsNewDoc; @Success; @SetProfileField("PrivateProfile"; "LastOpenDoc"; @Text(@DocumentUniqueID); @UserName))

フォームのイベント Queryclose へ記述した式や Lotus Script はフォームを閉じる前に処理されます。

プロフィール文書のフィールド LastOpenDoc へUNIDを保存します。ただし文書が保存されていない(@IsNewDoc の戻り値が True の)場合はUNIDがないため保存しません。

ビューを開いた後に最後に開いていた文書にフォーカスを当てる処理(Lotus Script)をビューのイベント Postopen へ記述します。

Sub Postopen(Source As Notesuiview)
 Dim db As NotesDatabase
 Dim prof As NotesDocument, target As NotesDocument

 Set db = Source.View.Parent
 Set prof = db.GetProfileDocument("PrivateProfile", db.Parent.UserName)
 If prof.HasItem("LastOpenDoc") Then
  Set target = db.GetDocumentByUNID(prof.GetItemValue("LastOpenDoc")(0))
  If Not (target Is Nothing) Then
   Call Source.SelectDocument(target)
  End If
 End If
End Sub