2010年1月30日土曜日

@DbLookup 式を LotusScript へ変換する

既存アプリケーションを変更する際、式言語ではできないことを実現するため LotusScript へ変換することがあります。

今回は思い出したくない過去の失敗事例を思い出してしまったので、ここにご紹介します。

変換前の式には @DbLookup は下のような式です。

Category2 := @DbLookup("": "NoCache"; @DbName; "test"; Category1; "Category2")

これを Lotus Script へ変換するとします。Category1 はフィールド名です。
Dim ws As New NotesUIWorkspace
Dim doc As NotesDocument
Dim vw As NotesView
Dim dc As NotesDocumentCollection
Dim category2 As Variant
Dim tmpKey As String, init(0) As String

Set vw = ws.CurrentDatabase.Database.GetView( "test" )
set tmpKey = ws.CurrentDocument.Document.Category1(0)
Set dc = vw.GetAllDocumentsByKey( tmpKey, True )
If dc.Count > 0 Then
  category2 = init
  Set doc = dc.GetFirstDocument
  While Not ( doc Is Nothing )
    Category2 = Arrayappend( Category2, doc.Category2 )
    Set doc = dc.GetNextDocument( doc )
  Wend
  category2 = Fulltrim( Category2 )
End If
一見何の問題ないように見えるこのプログラム...実は大きな落とし穴があったんです。

見落としだったのですが...フォーム上の Category1 は"複数値も可"が有効なフィールドでした。

@DbLookup のキーには複数値を設定できます。
複数値で検索した場合、指定した複数の値に一致するすべての値を一度に取り出します。

そのため上記の Category(0) といったように配列の0番目だけを取り出すのではなく、
配列のすべての要素を検索のキーとして指定する必要があります。

ここで注意したいのは、GetAllDocumentsByKey で指定するキーです。

このキーは配列でもよいのですが、@DbLookup では最初のソートされた列だけを対象に検索してくれるのに対して、GetAllDocumentsByKey では複数のソート列を対象にして検索します。

同じように複数値を指定しても検索結果が違うのです。

つまり、次のように変換するのが正しかったのです。
<...宣言は省略...>
Set vw = ws.CurrentDatabase.Database.GetView( "test" )
Category2 = init
Forall o In ws.CurrentDocument.Document.Category1
  Set dc = vw.GetAllDocumentsByKey( o, True )
  If dc.Count > 0 Then
    Set doc = dc.GetFirstDocument
    While Not ( doc Is Nothing )
      Category2 = Arrayappend( Category2, doc.Category2 )
      Set doc = dc.GetNextDocument( doc )
    Wend
  End If
End Forall
Category2 = Fulltrim( Category2 )

2010年1月28日木曜日

文書を印刷できなくする

アプリケーション内の文書をNotesクライアントから印刷させたくない場合、印刷機能を無効にすることができます。

WEBブラウザでアクセスさせる場合、Dominoでは制御できません。ブラウザの種類によって制御方法を変えたり、そもそも制御できないブラウザもある(?)ようで、いろいろ面倒なようです。

ここは専用クライアントを使うことのメリットがある好例でしょう。

Notesの印刷機能を無効にしたからといっても、画面をキャプチャしたりデジタルカメラで撮影することまでは制御できませんから、そういった意味では印刷を完全に防ぐことは不可能です。

でも画面をキャプチャしたものはコピーであり原本ではありません。
重要なのは、原本(と同じもの)を他の媒体へコピーできないことです。

ここではNotesの文書を原本と考え、それを印刷やコピーができないよう制御する方法をご紹介します。


A. アプリケーション内のすべての文書を印刷不可にする

アクセス制御リスト(ACL)で各エントリに対して "文書を複製またはコピー" のチェックを外します。

この権限をもたない(チェックを外した)ユーザーが文書を作成することによる問題が報告されていますので、ご注意ください。


B. 特定のフォームで作成した文書を印刷不可にする

フォームのプロパティ - [セキュリティ]タブ - "印刷/転送/クリップボードへのコピーを不可" のチェックを付けます。


C. 特定の文書を印刷不可にしたい場合

Bのフォームで作成した文書には $KeepPrivate というアイテムができ、値に "1" が設定されますが、実はBのプロパティを有効にしなくても同様の機能を実現できます。

FIELD $KeepPrivate := "1"

といった式を設定したエージェントで文書に $KeepPrivate アイテムと、その値として "1" を設定するのです。

フォーム上に "印刷不可にする" といった表示のチェックボックスを出しておいて、作成者が当該文書の印刷可否を決められるような仕様も実現できます。



なお、一度 $KeepPrivate を設定した文書は、フォームのプロパティで "印刷/転送/クリップボードへのコピーを付加" のチェックを外した後、文書を保存しなおしても $KeepPrivate アイテムと値はそのまま残ります。

ときどき“このアプリケーションは印刷制御しているからリッチテキストフィールドに貼られた添付ファイルの内容も印刷できない”と勘違いされている方がいらっしゃいますが、添付ファイルを開いた後の印刷機能はファイルに関連付けられたアプリケーション側の制御になります。

2010年1月27日水曜日

フレームにビューが表示されなくなる

フレームセットで画面を分割したアプリケーションで、データベースを開いたときにフレーム内に最初に表示するよう設定しているビューが開かず、そのフレームだけ別のビューが開くようになることがあります。

こうなった場合、Bookmark.nsfのエントリを削除すると解決するのですが、面倒だし一般ユーザーには敷居が高すぎます。

そもそもこんなことにならないよう作っておくべきなのですが、Notesのお作法はよくわからん...
ということで失敗から学んだ事例です。

フレームに別のビューが表示されるようになったDBのフレームセットでは、
横に2分割して、そのうちの右のフレームをさらに縦に2分割した、計3分割の構成でした。
右の上にあるフレームへ最初にで "WhatsNew" というビューを開くよう設定していました。

また、DBのプロパティでは[起動]タブにある"最後に表示していたものを表示する"のチェックを外していました。

このような設定でしばらく運用していたところ、フレームに "WhatsNew" 以外のビューが最初に表示されるようになったという声がちらほらではじめました。

懇談室で相談したところ、フレームの枠をドラッグして幅を調整した後、アプリケーションを再度起動した時から、フレームの内容が表示されなくなることがわかりました。

どうやらフレームセットの構成が影響している...との助言があり下図のように作り変えたところ、枠の幅を変更してもビューが置き換わらなくなりました。

クリックすると大きく表示されます

2010年1月23日土曜日

NotesDirectory クラスをさわってみた

先日の投稿に関連して、ディレクトリアシスタントを使った(複数のアドレス帳で認証する)環境でもグループ検索を可能にするものはないかとDesigner ヘルプを開いたところ、8 から追加された Lotus Script のクラスに NotesDirectory なるものがありました。

これについて学ぼうと記述例なんぞを探してみたのですが、関連するプロパティやメソッドの文書の例の多くが、本文に TBD (= to be determined:将来決定する、未使用)と書かれているだけでした...orz

もっとひどいのは NotesDirectoryNavigator クラスで、プロパティやメソッドの記述例の文書がひとつもありゃしない...

ここはテストして学ぶしかないかぁ~

というわけで、以下、テストの結果です。Notes(Basic)8.0.1で実験してます。

テスト用に作成したエージェント
Dim ss As New NotesSession
Dim nd As NotesDirectory
Dim ndnav As NotesDirectoryNavigator
Dim strServer$, strView$
Dim strName as String
Dim strItem as String

strServer = "server1/org"
strView = "$NamesFieldLookup"
strName = "hoge hoge1/org"
strItem = "FullName"

Set nd = ss.GetDirectory(strServer)
Set ndnav = nd.LookupNames(strView, strName, strItem, False)

If ndnav.NameLocated Then
 If ndnav.MatchLocated Then
  Print "登録済み"
 Else
  Print "未登録"
 End If
End If

NotesSession.GetDirectory
指定したサーバーが存在しなくても NotesDirectory オブジェクトはできますが、その後
NotesDirectory.LookupNames を実行すると、サーバーを検索した後、次のように最後が文字化けしたようなメッセージが表示されました...orz

----------------------
LookupNames が失敗しました:
サーバーへのパスが見つかりません。ネットワーク接続を確認してください。
接続に問題がなければ、[プリファレンス] - [Notes のポート] - [トレース]をクリックして、問題を特定してく■
----------------------

NotesDirectory.LookupNames("ビュー名", 検索する名前, アイテム名, 部分一致)
"検索する名前"と"アイテム名"は単一の文字列、または配列です。一度に複数の名前を検索することができ、複数のアイテム値を戻すことができます。

指定したビューが存在しない場合も NotesDirectoryNavigator オブジェクトが作成されました。
隠しビュー(ビュー名が括弧でかこまれたビュー)は括弧をつけなくても機能しました。

一致する名前が見つかると次のようになりました
MatchLocated が True (みつからないと False)
CurrentMatches が 1以上の値 (みつからないと 0)
CurrentMatch は見つかるかどうかにかかわらず 1 (よくわからん...)

当方ではディレクトリアシスタントを利用して2つのドミノディレクトリで運用していますが、どちらのドミノディレクトリからも検索できました。
どちらのドミノディレクトリにもある名前で検索してみたところ、検索結果が複数になり、NotesDirectoryNavigator のメソッドで読み出すことができました。

詳しくは後日、今日はココでご勘弁ください m(_ _)m

2010年1月21日木曜日

暗号化の操作はこのバージョンの Lotus Notes ではサポートされていません。

アプリケーションリンクをクリックすると、標記のメッセージが表示され、開かないユーザーがいらっしゃいました。

アプリケーションリンクのサーバーヒントには確かに当方のサーバーが設定されていましたが、この方は当該データベースのレプリカを Local に持っていて、そのアイコンがワークスペース上にありました。

ユーザーに聞いたところ、Localのレプリカは出張先にて作成したもので、レプリカを作成した時に利用していたNotes IDは出張先のドメインで発行されたものだったとのこと。
作成時には(デフォルトで)暗号化のオプションが有効だったのを、おそらく意識しなかったのでしょう。

そこで、「データベースを開く」の画面を表示させ、当方のサーバー上の当該データベースを指定したところ、無事に開きました。

表記のメッセージじゃ見当もつかないですよね?今回に限らずですが、もっとわかりやすい文言にならないものでしょうか?>IBMさん


実はアプリケーションリンクをクリックしてもアプリケーションが開かないといった問い合わせは非常に多いのです。

多くは海外(の出張先)から帰国して間もないユーザーで、ロケーションに絡むものです。

今回のように自分がアクセスしようとしても開かないが他のメンバーはアクセスできる、他のアプリケーションは開く、といったユーザー固有の問い合わせがあると、なんだかワクワクしてしまう自分がいます。

2010年1月20日水曜日

グループの入れ子を洗い出す

ドミノディレクトリのグループ文書では、メンバーの値としてユーザーのほか、グループを設定できます。

このように入れ子(ネストとも呼ばれます)になったグループを Domino は(アクセス認証の場合) 6 つのレベルまでたどってくれます。
※メール配信の場合は20のレベルまで
※8のAdministratorヘルプには「メール配信は5つのレベル」といった記載がいくつか残っています。R4.xの頃の名残りと思われ...orz

でもグループのメンバーにさらにグループが設定されていると、実際のアクセス権限がどうなっているのかがわかりづらいですよね。

そこで入れ子のグループを階層表示するアプリケーションを作ってみました。

選択したアプリケーションのACLからグループのエントリだけ取り出し、そのメンバーに設定されたグループを掘り出していきます。
そして掘り出したグループをビューで階層表示します。

下記は "グループの入れ子を調べる" エージェントの Lotus Script です。

(Options)
Option Base 1
(Declarations)
Dim ss As NotesSession
Dim vw As notesview
Type aclentrytype
Level As Integer
Name As String
End Type
Dim entrytype() As aclentrytype
Sub Initialize
Set ss = New NotesSession
Dim db As NotesDatabase

'アプリケーションのACLからグループのエントリだけ抽出する
Dim ws As New NotesUIWorkspace
Dim notesacl As NotesACL
Dim aclentry As NotesACLEntry
Dim numEntry As Integer

varDB = ws.Prompt( 13, "選択", "サーバー、アプリケーションを選択します" )
If Isempty( varDB ) Then Exit Sub
Set db = ss.GetDatabase( varDB( 0 ), varDB( 1 ) )
If db Is Nothing Then Exit Sub

If Not db.IsOpen Then flag = db.Open( "", "")
Set notesacl = db.ACL
Set aclentry = notesacl.GetFirstEntry
While Not (aclentry Is Nothing)
If aclentry.IsGroup Then
numEntry = numEntry + 1
Redim Preserve entrytype( numEntry )
entrytype( numEntry ).Level = aclentry.Level
entrytype( numEntry ).Name = aclentry.Name
End If
Set aclentry = notesacl.GetNextEntry( aclentry )
Wend

'レベルとグループ名を書き出す
Dim ndb As NotesDatabase
Dim parentdoc As NotesDocument
Dim i As Integer

Set ndb = ss.GetDatabase( db.Server, "names.nsf")
Set vw = ndb.GetView( "Groups" )
Set parentdoc = maketreedoc( db.Title, 99, Nothing )

For i = 1 To Ubound( entrytype )
Call grpmember( entrytype( i ).Name, i, 1, parentdoc )
Next
End Sub
Sub grpmember(grpname As String, idx As Integer, nest As Integer, pdoc As NotesDocument)
If nest > 6 Then Exit Sub

Dim nam As New NotesName( grpname )
Dim entry As NotesViewEntry
Set entry = vw.GetEntryByKey( nam.Abbreviated, True )
If entry Is Nothing Then Exit Sub

Dim treedoc As NotesDocument
Set treedoc = maketreedoc( grpname, entrytype( idx ).Level, pdoc )

Dim doc As NotesDocument
Set doc = entry.Document
Forall o In doc.GetItemValue( "Members" )
Call grpmember( Cstr( o ), idx, nest + 1, treedoc )
End Forall
End Sub
Function maketreedoc(title As String, idx As Integer, parent As NotesDocument) As NotesDocument
Set maketreedoc = New NotesDocument(ss.CurrentDatabase)
With maketreedoc
If Not (parent Is Nothing) Then Call .MakeResponse(parent)
.Form = "GroupTree"
.Group = title
.Level = idx
Call .Save(True, True)  
End With
End Function

上記エージェントを実行すると、アプリケーション内に文書を書き出します。

これらの文書は親子関係になっていて、以下のような3列のビューを作成すると階層構造で表示できます。

1列目の値として次の式を記述します。
@Replace(@Text(LEVEL); "0":"1":"2":"3":"4":"5":"6":"99"; "なし":"投稿者":"読者":"作成者":"編集者":"設計者":"管理者":"")

2列目は列のプロパティで「返答文書のみ表示」と「展開できる行に三角アイコンを表示する」にチェックを付けます。列の値は Group とします。

3列目は列のプロパティで「展開できる行に三角アイコンを表示する」にチェックを付けます。列の値は Group とします。
ビューのプロパティで「返答文書を階層表示する」にチェックを付けます。

以上でできあがりです。

ディレクトリアシスタントを使って別のアドレス帳にあるグループで認証させている環境では物足りないかもしれませんね。
なんとか対応できるようにいろいろ調べてみたいと思います。

それから、ビュー上で文書をダブルクリックしてもフォームが無いのでエラーが表示されますが、気にしないでください。

2010年1月19日火曜日

Lotus Script でキャッシュをクリアする

アプリケーションを開発中に Notes や Designer が何かおかしい...と感じるときがあります。

そんなときには迷わず再起動して、ついでにキャッシュもクリアします。

キャッシュは Notes のデータディレクトリに cache.ndk というファイル名で存在します。

こいつは Notes アプリケーションの拡張子である nsf とは違いますが、NotesDatabase クラスでアクセスできます。

中にある文書を片っ端から削除してあげましょう。

Sub Initialize
Dim ws As New NotesUIWorkspace
Dim cache As NotesDatabase
Dim dc As NotesDocumentCollection
Dim doc As NotesDocument, ndoc As NotesDocument
Dim intNum As Integer

On Error GoTo ERRORTRAP

Set cache = New NotesDatabase("", "cache.ndk")
Set dc = cache.Alldocuments
If dc.Count > 0 Then
Set doc = dc.Getfirstdocument
While Not doc Is Nothing
intNum = intNum + 1
Set ndoc = dc.Getnextdocument(doc)
Call doc.Removepermanently(True)
Set doc = ndoc
Wend
End If
Call cache.Compact
Call ws.Prompt(1, "情報", "キャッシュをクリアしました。再起動してください。")
Exit Sub

ERRORTRAP:
Resume
Call ws.Prompt(1, "エラー", "キャッシュはクリアできませんでした。")
End Sub

文書の削除が済んだら、Lotus Notes を再起動しましょう。

って再起動するくらいなら Cache.ndk をOSレベルで削除すればいいじゃない....orz

キャッシュファイルがなくなっても、Lotus Notes が勝手に作成しますのでご心配なく。

2010年1月18日月曜日

セクションを制限つきアクセスにする

文書を編集モードで開いた時、一部分は特定の人にだけ編集させたい場合があります。

ドラフトの時だけ特定の人にコメントを書かせたい場合、制限つきアクセスセクションの中にコメントフィールドを設置します。

セクションを編集できる人を導き出す式を「フォームのセクション」プロパティの「式」タブにある「アクセス式」に書き込み、この式の実行結果として、ユーザー、グループ、ロールが導き出されるようにします。

結果としてヌル "" を設定すると、編集モードにできる全員がこのセクション内を編集できます。要するに「制限なし」ですね。

たとえば....

Statusフィールドが"Draft"の場合、チェッカーだけ編集可能、"Draft"でなくなったら管理者だけ編集可能
チェッカーと管理者にはそれぞれ[CHECKER]と[ADMIN]というロールを割り当てている

とすると、式は

@If(Status="Draft"; "[CHECKER]"; "[ADMIN]")

となります。

誰にも編集できないようにすることはお勧めしませんが、したい場合はいいかげんな値("DUMMY"等)を設定するといいのではないかと...


ちなみに「式」タブの「種類」に"表示用の計算結果"以外の値を選択した場合、そのフォームを使って文書保存した後の文書のプロパティを見ると、「セクションタイトルと境界線」タブで「セクションフィールド名(R3 互換)」なる項目で設定した名前でアイテムができます。

セクションを誰が編集できるかは、そのアイテムの値を見るとわかります。

また「式」タブの「種類」を"編集可能"としたセクションを編集モードでダブルクリックした時、編集させる人を選択できる「セクションの編集者を指定」画面を表示します。

競合文書のトラウマがあるのでひとつの文書をつつきあう仕様はあまり好きではありません...ボソボソ

2010年1月15日金曜日

リンク付きメールには署名が邪魔なんです

LotusScript でフロントエンドに文書リンクが付いたメール作成画面を表示する場合、バックエンドで作成した NotesDocument に RichtextItem の "Body" を作り AppendDocLink で文書リンクを付けた後、NotesUIWorkspace.EditDocument でフロントエンドに登場させる、といった手法をとることもできます。

しかし"署名を自動的に追加する"が有効の場合、署名の後に文書リンクが付いてしまいます。

(個人的には署名なんて手動でも使いたくないが...orz)こいつが非常に気になります。

Memoフォームの設計を見るとPostOpenで署名を加えているようですが、まさか設計を変えるわけにもいかない...

どうにか署名の前に文書リンクをつけられないか、フロントエンドの文書へ直接文書リンクを付ける方法がなかろうかと試行錯誤を繰り返した末、実現できたのが次のような仕組みです。


アクションボタンの式(ビュー、またはフォームに設置できます。新規文書の場合もうひと手間かけること)

@Command([EditMakeDocLink]);
@Command([RunAgent]; "(MakeDocument)")


エージェント"MakeDocument"のスクリプト

Sub Initialize
Dim ss As New NotesSession
Dim doc As NotesDocument
Dim strSubject As String, strSendTo As String
Dim varSendTo As Variant
Dim nam As NotesName

'メモリ内文書を取得
Set doc = ss.DocumentContext

'件名を取得
strSubject = doc.GetItemValue("Subject")(0)

'宛先を取得
varSendTo = doc.GetItemValue("Attention")

'階層なしの値に置換 (複数値に対応)
For i = 0 To Ubound(varSendTo)
Set nam = New NotesName(varSendTo( i ))
varSendTo( i ) = nam.Abbreviated
Next

'単一の文字列に変換。値はカンマ区切り
strSendTo = Join(varSendTo, ", ")
Dim db As New NotesDatabase("", "")

'現在のユーザーのメールアプリケーションを開く
Call db.OpenMail
If db Is Nothing Then
Messagebox "メールが開きません。",,"警告"
Exit Sub
End If

Dim ws As New NotesUIWorkspace
Dim uidoc As NotesUIDocument

'フロントエンドでMemoフォームを開く
Set uidoc = ws.ComposeDocument(db.Server, db.FilePath, "Memo")
With uidoc
Call .FieldAppendText("EnterSendTo", strSendTo)
Call .FieldAppendText("Subject", strSubject)
Call .GotoField("Body")
Call .InsertText("下のリンクをクリックして確認してください。")
Call .InsertText(Chr(10))
Call .Paste 'クリップボードの文書リンクを張付
End With
End Sub


でもこれでは本文で文字のスタイルを変更したり整った表を作るといったことがやりづらいのです。やっぱりバックエンドでメール文書を作りたいのですよ...

もう、署名なんてなければいいのに!

ところでメールアプリケーションにはリンク付きメールを作る際に便利な Bookmark フォームがあります。

Bookmark では現在開いている(あるいはビューでフォーカスが当たっている)文書のリンクがついたメールを作成できますが、フロントエンドで使うにはタイトルが勝手に設定されたりとちょっと曲者なのです。

でも署名が付かないところはステキです

2010年1月13日水曜日

別ドメインに不正アクセス

外部のNotesドメインのユーザーが当方のサーバーアクセスしようとして権限なしではじかれた旨、ログ(log.nsf)に記録された。

2010/01/13 10:00:17 ATTEMPT TO ACCESS SERVER by hogehoge/org was denied

気になったのは夜間のアクセスはなく、日中に限っていること。ほぼ1時間おきに10行程度記録されていること。

hogehoge/org はもともと当社の社員で関連会社へパソコンを持ち出している。

本人へ直接問い合わせたが、まったく認識がないという。

そこでNotesクライアントの設定をいくつか変更してもらった。

ロケーションの設定で複製を無効に変更 .... NG
ユーザープリファレンスで「定期ローカルエージェントの有効化」を無効に変更 .... NG
ユーザープリファレンスで「購読の確認」を無効に変更 .... OK!

ヘルプによると、購読が有効の場合、購読するDBに対して定期的にチェックを行うが、その間隔はユーザープリファレンスに設定された「新規メールの通知」の間隔の2倍の頻度でチェックされるとのこと。

本人によると、そもそも購読に登録したことすら覚えていなかったとさ...orz

2010年1月12日火曜日

未知のドメインのサーバーへの初回アクセスで「検索するサーバーの選択」画面を表示させない方法

クライアント上でアプリケーション・リンクをクリックするなどして、これまでに接続したことのないドメインのサーバーにあるアプリケーションへアクセスさせようとした時、「検索するサーバーの選択」というタイトルの画面が表示される場合があります。

当然その画面にあるサーバーリストには未知のドメインのサーバーなどあるはずもない。

Notesの名前解決では、DNSやhostsファイルと連携した名前解決はしないのです...orz

※ここではIPネットワーク的な接続はできていて、相互認証があり、サーバーやアプリケーションへのアクセス権もあります

こんなとき、Notes上で Ctrl + O をタイプして「データベースを開く」画面を開き、サーバー入力欄へIPアドレスをタイプしてもらうのも手です。
しかしながら IPアドレスのような数字の羅列は間違えやすいし、将来変わる可能性もあるので、なるべくなら入力させたくありませんね。

実はローカルの names.nsf にあるロケーション文書には、これまで接続できたサーバーの情報がキャッシュされていまして、ここへ未知のサーバーの情報を書き込んであげると...アプリケーションがすんなり開くのです。

サーバー $SavedServers
ポート  $SavedPorts
アドレス $SavedAddresses

下記にLotus Scriptのサンプルを示します。
メールのホットスポットボタンに組み込んで(Notes R6.5.3にて)利用したことがあります。

Sub Click(Source As Button)
Dim ss As New NotesSession
Dim db As New NotesDatabase("", "")
Dim dc As NotesDocumentCollection
Dim doc As NotesDocument
Dim item1 As NotesItem, item2 As NotesItem, item3 As NotesItem
Const savedServers = "CN=hogehoge/O=org"
Const savedPorts = "TCPIP"
Const savedAddresses = "hogehoge"

'現在のロケーション名を取得する
vLocation$ = ss.GetEnvironmentString("Location", True)
vLocation$ = Strtoken(vLocation$, ",", 1)

'ロケーション文書を取得する
Call db.Open("", "names.nsf")
sFormula$ = |Form="Location" & Name="| & vLocation$ & |"|
Set dc = db.Search(sFormula$, Nothing, 0)
If dc.Count = 0 Then
Messagebox "ロケーション「" & vLocation$ & "」が存在しません。",16,"失敗"
Exit Sub
End If

'ロケーション文書を変更する
Set doc = dc.GetFirstDocument
If doc.HasItem("$SavedServers") Then
Set item1 = doc.GetFirstItem("$SavedServers")
If Not Isnull( Arraygetindex(item1.Values, savedServers, 1) ) Then
Call item1.AppendToTextList(savedServers)

If doc.HasItem("$SavedPorts") Then
Set item2 = doc.GetFirstItem("$SavedPorts")
Call item2.AppendToTextList(savedPorts)
Else
Call doc.ReplaceItemValue("$SavedPorts", savedPorts)
End If

If doc.HasItem("$SavedAddresses") Then
Set item3 = doc.GetFirstItem("$SavedAddresses")
Call item3.AppendToTextList(savedAddresses)
Else
Call doc.ReplaceItemValue("$SavedAddresses", savedAddresses)
End If
End If
Else
Call doc.ReplaceItemValue("$SavedServers", savedServers)
End If
Call doc.Save(True, True)

Dim ws As New NotesUIWorkspace
Call ws.OpenDatabase("hogehoge/org", "hoge.NSF")
End Sub

上記でポート名を "TCPIP" としていますが、各クライアントの環境により異なることがある場合にはロケーション文書のアイテム "EnabledPorts" や @GetPortsList([Enabled]) コマンドで取得するといいと思います。

なお $SavedAddresses へ設定する値はIPアドレスでも良いようです。

2010年1月8日金曜日

@Commandでカレンダーを開く

アクションボタンから自身のメールアプリケーションにあるカレンダービューを開きたいと考え、試してみました。

R8.0.1 では次のどれでもカレンダービューが開きましたが、既にメールアプリケーションが開いているか否かで表示結果が違ったり、フレームセットが違ったりします。

@Command([FileOpenDatabase]; @MailDbName; "Calendar")
@Command([OpenCalendar]; @UserName)
@Command([OpenCalendar]; @UserName; [UseFrameset])
@Command([OpenCalendar]; @UserName; [UseMailFrameset])
@Command([OpenCalendar]; @UserName; [UseToDoFrameset])
@Command([OpenCalendar]; @UserName; [UseContactsFrameset])

もしかするとNotesのバージョンにより動かないかもしれませんので、ご自身の環境で確認してみてください。

ちなみに @Command([OpenCalendar]) の2番目のパラメータはヘルプには記載されていません。使わないほうが無難です。

2010年1月7日木曜日

Notes を強制終了する

Windows 上の Notes で手動で実行したエージェントを強制終了する場合 Break キーを押しますが、
Notes が出したエラーダイアログに[OK]ボタンで答えても繰り返し同じエラーが表示される等、Break キーが効かないようなどうしようもないループ状態に陥ったときに Notes を強制終了するコマンドがあります。

コマンドプロンプトを開き、次のコマンドを投入します。

cd \Program Files\lotus\notes
nsd -kill


nsd は Notes のプログラムディレクトリにあるので、コマンド投入前にチェンジディレクトリ(cd)します。

2010年1月6日水曜日

ブックマークのSubjectを変更する(デフォルト値を任意の値で置換する)

メールDBには、文書リンクを送信する専用のフォーム「ブックマーク」があります。

メールDB以外のデータベースからこのフォームを利用して誰かに通知したい、といった場合にも使うことができます。

ブックマークの件名はデフォルト値として現在選択されている文書の Subject フィールドの値に"リンクメッセージ(TM): "を自動的に付加されるよう設計されています。

その件名を変えたい場合は、フォームを開いた後に Subject フィールドに設定された値を置換するよう仕込まなければなりません。

次の例では、文書のタイトルに"[Information]:"を付けた文字列をブックマークの件名へセットします。

tmp := Subject;
@Command([Compose]; @MailDbName; "Bookmark");
@UpdateFormulaContext;
@Command([EditGotoField]; "Subject");
@Command([EditSelectAll]);
@Command([EditInsertText]; "[Information]: " + tmp)

ちなみに、上の @Command([EditSelectAll]) の直後に @Command([EditClear]) を実行すると変なメッセージが表示されました。
ただ、すべての文字列を選択した後に値を挿入することは、結果的に選択した値を消去され挿入した文字列で置換することと同意なので EditClear は不要なのでした。

2010年1月5日火曜日

ビューで選択した文書を、ビューのソート順に従って処理する

このトピックについて興味があり、遊んでみました。

ここでの問題は、ビューで選択したいくつかの文書を NotesUIView.Documents や NotesDatabase.UnprocessedDocuments で文書コレクションを取得しても、ビューの表示順(ソート順、並び順)のとおりに取り出すことができないことです。

そこで、ビューのエントリを最初から順になめていき、文書コレクションに一致する文書があれば処理する、といった手法で解決する例が紹介されていて感心したのですが、少々ややこしかったので簡単に書きなおしてみました。

下の例はビューのアクションボタンに記述しています。

Dim ws As New NotesUIWorkspace
Dim dc As NotesDocumentCollection
Dim doc As NotesDocument
Dim entry As NotesViewEntry
Dim nav As NotesViewNavigator

Set dc = ws.CurrentView.Documents '選択文書を取得する(ここは作成日の昇順になる)

Set nav = ws.CurrentView.View.CreateViewNav
Set entry = nav.GetFirstDocument
While Not entry Is Nothing
    Set doc = dc.GetDocument(entry.Document)
    If Not doc Is Nothing Then
        'ここへ処理を記述します。例) Print Format$(doc.Created, "yyyy/mm/dd")
    End If
    Set entry = nav.GetNextDocument(entry)
Wend