2010/02/22

XSLT を使用してフォームを XPages に変換する

今回は Paul Calhoun 氏による「Transforming Domino Forms into XPages via XSLT」の紹介です {Original Source}

作者自身は長く XML や XSLT に関わっていますが 既存のノーツフォームを XPages 化する際に気づいた点と自身が作成した XSLT を使ってフォーム設計を XPage に変換する方法を紹介しています。

基本的なコンセプトはフォーム設計も XML (正確には DXL) で表現できるので、もともと XML (JSP/JSF シンタックス) である XPage へは簡単に変換ができるのではないかということです。

この変換のために作者が作成した XSLT はこちらからダウンロードできます。

では作者が示した手順と同じように行って行きましょう。
まずは、ダウンロードした XSLT は <ノーツデータフォルダ>\xsl フォルダ内にコピーしてください。

そして簡単なフォーム設計を作成します。内容はどんなものでも構いませんが、今回私はいろんなフィールド種類のものを定義してみました。
では早速作者が提供している XSLT を使ってフォーム設計を XPages へ変換してみましょう。
先ほどのフォーム設計を選択した状態で Domino Designer のメニューより [ツール] - [DXLユーティリティ] - [トランスフォーマー] を選択します。

次のダイアログで変換するフォームを選択。そして XSL リソース を作者が提供する xsl ファイルを指定します。出力先は「表示」で OK ボタンを押します。
するとブラウザが立ち上がり、変換された設計が表示されます。ここからこのページのソースを表示します。
ソースはだいたい以下のような内容になっていると思います。
ソースコード全体をクリップボードにコピーします。

ここから新しい XPage を新規作成します。そしてソースタブに切り替えてください。

自動的に作成された箇所も含めて、先ほどクリップボードにコピーした内容に置き換えします。
設計タブに切り替えるとフォーム設計から変換された XPage を見ることができます。

変換された結果を見ると、データソース(フォーム設計)が設定されている事。作成された表はフォーム上のフィールドに大してラベルとフィールドと対応したコントロールが、フィールド名と同じ名前で自動作成されています。
省略値は編集可能フィールドに対して有効です。

選択肢として定義したキーワード(値と別名の両方)も有効です。

もちろん変換が完璧に出来上がったわけではなく、100% とは言わないまでも非常に近いレベルまで作成できることがお分かりいただけると思います。短縮できた時間をより開発の核心部分に集中させることができます。私の感覚では確かにスクラッチから XPage を作成する事を考えれば、30 分の 1 ぐらい効率化できそうです。作者の言葉を借りれば「Work smarter not harder」というところでしょうか。

2010/02/19

Web セミナー「Lotus Notes Domino アップグレードソリューション」がオンデマンド(録画)公開

先日行いました Web セミナーがオンデマンド(録画)で公開されました。こちらのリンクでご覧いただけるようになります。
Web セミナー「Lotus Notes Domino アップグレードソリューション」

ご覧いただくためには、再生ボタンと押し、お名前、Emailアドレスなど必要な情報のご入力をお願いしております。




また、前回公開したスライドはこちらで入手いただけます。

2010/02/18

Teamstudio Upgrade Filters でより精度の高い非互換検出結果を出すための工夫

昨日、Web セミナーで Teamstudio Upgrade Filters をご紹介させていただきました。そこで今回は Upgrade Filters を使用して非互換検出をご検討されている方にちょっとした Tips です。

非互換検出の精度を高めるためには考慮すべき点がいくつかありますが、今回は典型的な例をご紹介しながらどういう条件を作成すればよいか見ていきます。

たとえば、R5 以前のユーザーで独自に「Replace」というファンクションを作成していた場合、R6 から同名のファンクションが標準で追加されたため衝突が発生してしまいます。これを例にしてご説明しましょう。

この非互換を検出するため Upgrade Filters では下のような条件が記述されています。
赤い四角で囲ってある部分が条件式ですが、お分かりのとおり、そのままでいけば「Replace」ということばを単に探すということになります。

従って、その他の関数、たとえば「ReplaceItemValue」もヒットしたり、英語圏のユーザーの中にはコメントとして良くつかわれることが充分に想定されます。
ではどうしたらいいでしょうか?

Replace 関数は
ret = Replace(array1, array2, array3)
のように記述されるため上記の問題をそれを避けるために検証式を
TMSRTContains( Rich Text; 0; "Replace(" )
と記述し直すとより ReplaceItemValue 関数がヒットしなくなり、より Replace 関数のみにフォーカスが当たるようになります。

実はこれでもまだノーツ式の @Replace との切り分けができていないのですが、本来であれば
TMSRTContains( Rich Text; 0; "Replace(" )
とReplace の前にスペース(△)を一文字入れたいとこです。しかし、
ret=Replace(array1, array2, array3)
のようにスペースがなく保存されているスクリプトも考えられますので、ここでスペースを入れると見落としが発生してしまう可能性が出てきます。

このようにどこまで精度を高くしたいか、コーディングのクセなども考慮して適切と思われる条件にすることが重要になってきます。

新たに追加されたクラスのプロパティやメソッドも同じことが言えます。

可能であれば、一度出た結果に基づいてあまりにも検出された内容が多く関係のないところが検出された場合には、もう一度条件を見直して再度解析できるような余裕がほしいものです。

2010/02/17

Web セミナー「Lotus Notes Domino アップグレードソリューション」のスライド公開

前々回のポストでご紹介しました Web セミナー『Lotus Notes/Dominoアップグレードソリューション』のスライドをアップします。
今回この Web セミナーをレコーディングしましたので、Webcast では後日 Teamstudio のサイトでご覧いただけるようになると思いますが、とりあえず先行してスライドだけアップします。

今回は合計で30名弱の方にご参加いただいたそうです。今後も継続してこのような Webinar も増やしていければと思っています。

Teamstudio Notes/Domino アップグレードソリューション

2010/02/16

Lotusphere 2010 のセッションより Part 2

今回は Lotusphere 2010 で行われた AD107 Enhance your existing applications with XPages の内容からエッセンスだけをご紹介します。残念ながら実際見聞きしたわけではなくセッション資料のみからの内容になります、その点はご容赦ください。

このセッションのテーマは、「既存のアプリケーションを XPages を使用して拡張する」というタイトルから分かるように、クライアントベースのアプリケーション、従来の Domino Web アプリケーションをどうやって XPages アプリケーションにするのか、それぞれのテクノロジーにの比較から始まり、各設計要素単位の考慮点をまとめています。

この作業に取り掛かる前に、まず現在のアプリケーションの設計分析が重要で、どのような動きをするのか、どのようなコードが書かれているかを事前に検証しなければなりません。

  • UI で使用されるコードの量
    • フィールドの Enter/Exit イベント
    • UI のクラス
    • 非表示式
  • 暗号がどれくらい使用されているか
  • Sub や Function の複雑さ
  • リッチテキストフィールドの使い方

次の課題はそれぞれのアーキテクチャーの違いを理解することで、それぞれの比較は以下の通りです。
IBM Lotus®

Notes®Client

Application
Classic Lotus

Domino® web

Application
XPages

application
セッション 恒久 時間ベース 時間ベース
ステート フルステート ステートレス 部分的に可
プログラミング LotusScript

JavaScript
LotusScript

JavaScript

Java™
JavaScript

Java™
UI リッチテキスト リッチテキスト
から

変換されたHTML
HTML /
Dojo
リッチテキストサポート フル 弱い 部分的
ページ更新

フルと部分更新
そして分散可
フル更新のみ フルと部分更新
(ひとつの領域のみ)
暗号化/署名 不可 検討中
レイアウト



自由、

スクリーン内にビューのための2つフレームで表示するのが典型的
自由 自由

OneUI 推奨
セクション



標準とアクセス
制御可
標準のみ 標準のみ(アク
セス制御は検討中)
非表示式

段落単位 段落単位

+ HTML DOM
要素単位




そして、アプリケーションを XPages 化する際の、それぞれの設計要素の XPages コンポーネントや手法についてのサマリ―です。これから XPages に取り組みたいを思っている人の中にはクライアントベースの開発がベースにあるので、こういった対応表があると XPages 開発のイメージが直感的に分かるのではないでしょうか。

Notes XPages
フォーム XPageとJSオブジェクトとカスタムコントロールを組み合わせたサーバーサイドJSライブラリ
サブフォーム カスタムコントロール
ビュー 埋め込みビューを持つXPage
ビューアクション アクションのリンクをUnordered list(ul) とサーバーサイドJSで記述したカスタムコントロール
フィールド データバインディングされた様々なコントロール
表コントロール
タブ付きの表 パネルを組み合わせたタブ付きパネルコントロール
非表示式 ページの中の様々なオブジェクトの中で表示式を記述可能
リッチテキスト MIMEベースのリッチテキスト
名前(作成者、読者) Name Pickerを使用したカスタムコントロール
フレームセット AJAXのUIのため不可
アウトライン CSSを使用したリンクのリスト
埋め込みビュー XPageの中のビュー
ページ XPage
エージェント XPageのイベントからエージェントの呼び出し

2010/02/12

Teamstudio Webセミナー 「Notes/Dominoアップグレードソリューション」開催

大阪、名古屋、東京で先月行ったセミナーをWebinarで行います。既にお客様には Email ニュースでご連絡しておりますが、その他どなたでもご参加いただけます。
詳細は以下の通りです。

Webセミナー『Lotus Notes/Dominoアップグレードソリューション』

1月に大阪、名古屋、東京の三都市で開催された内容を、 Webセミナーにてお届けいたします。

アプリケーションのアップグレード作業でリソースの確保と予算取りの難しさから、ほとんどの企業ではアップグレードへの新しいアプローチを模索し始めています。
「アップグレード作業=突貫工事的なプロジェクト」という方程式はもう終焉を迎えているのです。
チームスタジオが提唱するアップグレードへの新しいアプローチは、今の開発サイクルに少しの追加で実現できる効率的で有効な方法です。アップグレードのためだけに余分なリソースやコストを追加する必要はまったくありません。
アップグレードの情報を集めておきたい、という方はもちろん、 Teamstudioのセミナーに出たことが無い、 Teamstudioの製品がどんなものなのか情報が欲しい、といったお客様もお気軽にご参加ください。

また、時間内でご質問にもお答えいたしますので、事前に質問があれば内容もお知らせください。

●開催日時
2010年2月17日(水)14:00~15:00

●お申込
下記より必要情報をご登録ください。別途受講のご案内メールをご登録先に送付いたします。

【お申し込み登録はこちら】
https://teamstudio.webex.com/teamstudio-jp/onstage/g.php?t=a&d=485606410

受講に必要なシステム環境
――――――――――――――――――――――――
・Windows 2000, XP, 2003 and Vista
・ブラウザ:Internet Explorer 6/7/8. Firefox 2/3, Chrome 1
・JavaScriptおよびCookieの有効化
・Internet Explorerの場合ActiveXを有効
・メモリ:512 MB以上 (Vistaの場合は2 GB以上)
・56Kbps以上のインターネット接続
――――――――――――――――――――――――
注)音声はストリーミング配信でお聞きいただきます。当日はヘッドフォン等をご利用の上、ご参加ください。

2010/02/09

XPages でのカレンダーコントロール (Part 1)

Classic Notes アプリケーションやこれまでの Domino Web アプリでカレンダーを表示させ、日付を入力したり、選択したりすることは少なくなかったと思います。
ところが XPages の開発においてこのカレンダーのコントロールがないことがしばしば話題に上ったりしています。当然コアコントロールとして装備されていてもおかしくはないのですが・・・・

今回ご紹介するのは、カレンダーをカスタムコントロールとして作成する方法を Keith Strickland 氏の keithstric.com  SnTT: XPages Blank Calendar Control (Part 1) からご紹介します。

オリジナルのタイトルにもある通り、今回はまずブランクのカレンダー、つまりカレンダーの表示だけについて見ていくことになります。

最初は、表を使ってカレンダーの外観を作成するわけですが、まずはカレンダーの曜日が表示される部分にあたるヘッダーのコードです。
<xp:table styleClass="calTable" id="calTable">
<thead styleClass="calTableHead">
<th>Sun</th>
<th>Mon</th>
<th>Tue</th>
<th>Wed</th>
<th>Thu</th>
<th>Fri</th>
<th>Sat</th>
</thead>
<xp:text escape="false" id="startRows">
<xp:this.value><![CDATA[#{javascript:"<tr>"}]]></xp:this.value>
</xp:text>

カレンダーの始まりは日曜日になっていますが、その月の始まりの日が日曜日ではない場合ブランクのセルを表示させるといった処理をするため、まず何年何月の1日の位置がどこから始まり、その月が何日あるのかをページがロードされる際に把握しておかなければなりません。これらを「beforePageLoad」イベントで viewScope の変数を使って実現します。
<xp:this.beforePageLoad><![CDATA[#{javascript:if (!viewScope.containsValue("dispDate")) {
viewScope.dispDate = @Now();
viewScope.dispCalYear = @Year(viewScope.dispDate);
viewScope.dispCalMonth = @Month(viewScope.dispDate);
viewScope.daysInMonth = new Date(viewScope.dispCalYear,viewScope.dispCalMonth,0).getDate();
viewScope.firstDayInMonth = new Date(viewScope.dispCalYear,viewScope.dispCalMonth -1,1).getDay();
}}]]></xp:this.beforePageLoad>

ブランクの数が分かったら、繰り返しの内部にある計算結果フィールドを使って、そのカレンダーに必要な数だけのブランクを作成する処理を記述します。
<xp:repeat id="calBlanks" rows="7" value="#{javascript:viewScope.firstDayInMonth;}" var="calBlankVar">
<xp:text id="blankCells" contentType="html">
<xp:this.value><![CDATA[#{javascript:'<td class="calBlank"> </td>';}]]></xp:this.value>
</xp:text>
</xp:repeat>

ここでその月の日数分だけ回して、表の TD の記述の中に日の数を埋めていく処理に移ります。ここで注意しなければいけないのは、

  1. 計算結果フィールドにはその行のタグの開始と終了も追加して記述すること。そして週の最初のエントリの後ではなく前に置くこと。
  2. セルの中に埋め込む日数の数の計算結果には、実際の日にプラス 1 すること。これは最初の日がゼロから始まるためです。
  3. セルの内容についてロジックを考える必要はありません。新しい行が必要かどうかは繰り返しの中の最初の計算結果フィールドで決定します。
<xp:repeat id="calDay" rows="31"
value="#{javascript:viewScope.daysInMonth;}" var="calVar"
indexVar="calIndex">
<xp:text escape="false" id="rowEndBegin">

<xp:this.value><![CDATA[#{javascript:"</tr><tr>"}]]></xp:this.value>
<xp:this.rendered><![CDATA[#{javascript:if (calIndex != 0) {
if (((calIndex + viewScope.firstDayInMonth) % 7) == 0) { 
return true; 
}else{ 
return false; 
}
}}]]></xp:this.rendered>
</xp:text>
<xp:text escape="false" id="dayCells">
<xp:this.value><![CDATA[#{javascript:var calDayTD = '<td class="calDay"><span class="dayNum">' + (calVar+1) + '</span></td>';
return calDayTD;}]]></xp:this.value>
</xp:text>
</xp:repeat>

最後に、その月の最終日が土曜日でない場合は必要なセルにブランクを設定し、表はこの行で最後だということで終了される必要があります。これを別の繰り返しコントロールと計算結果フィールドを使って実現しています。
<xp:repeat id="calBlanksEnd" rows="14">
<xp:this.value><![CDATA[#{javascript:var lastRow = 42 - (viewScope.daysInMonth + viewScope.firstDayInMonth);
if (lastRow > 13) {
lastRow = lastRow - 14;
}else if (lastRow > 6) {
lastRow = lastRow - 7;
}
return lastRow;}]]></xp:this.value>
<td class="calBlank"></td>
</xp:repeat>
<xp:text escape="false" id="lastRow">
<xp:this.value><![CDATA[#{javascript:"</tr>"}]]></xp:this.value>
</xp:text>

最後にこのカレンダーの上部にタイトルヘッダーを追加して、何年何月が表示されているかと、前後の月に移動できるようリンクを追加します。
これは 2 つのリンクとひとつの計算結果フィールドで実現できます。
計算結果フィールドを使って表示される月は、すべての月の情報が入っている配列の中のコンテンツから決定され、viewScope.dispCalMonth の変数から 1 をマイナスした値が使われます。それぞれのリンクは viewScope の変数を変更する処理を行います。これらをひとつのパネルの中に記述します。

<xp:panel id="calendar">
  <xp:panel styleClass="calMonthTitle" id="Header">
   <xp:link escape="true" id="monthBack"><xp:this.text><![CDATA[<<]]></xp:this.text>
    <xp:eventHandler event="onclick" submit="true"
     refreshMode="complete">
     <xp:this.action><![CDATA[#{javascript:var curMonth = @Month(viewScope.dispDate);
var curYear = @Year(viewScope.dispDate)
curMonth = (@Month(viewScope.dispDate)-1);
var curDate = new Date(curYear,curMonth -1,1);

viewScope.put("dispDate",curDate);
viewScope.put("dispCalYear",@Year(viewScope.dispDate));
viewScope.put("dispCalMonth",@Month(viewScope.dispDate));

var monthDays = new Date(viewScope.dispCalYear,viewScope.dispCalMonth,0).getDate();
var monthFirstDay = new Date(viewScope.dispCalYear,viewScope.dispCalMonth -1,1).getDay();
viewScope.put("daysInMonth",monthDays);
viewScope.put("firstDayInMonth",monthFirstDay);}]]></xp:this.action>
    </xp:eventHandler></xp:link> 
     
   <xp:text escape="false" id="dispMonth">
    <xp:this.value><![CDATA[#{javascript:var months = new Array("January","Feburary","March","April","May","June","July","August","September","October","November","December");
var curMonth = months[(viewScope.dispCalMonth-1)];
return curMonth + " " + viewScope.dispCalYear;}]]>
</xp:this.value >
   </xp:text>
     
   <xp:link escape="true" id="monthForward">
    <xp:this.text><![CDATA[>>]]></xp:this.text>
    <xp:this.onclick><![CDATA[#{javascript:var curMonth = @Month(viewScope.dispDate);
var curYear = @Year(viewScope.dispDate)
curMonth = (@Month(viewScope.dispDate)+1);
var curDate = new Date(curYear,curMonth -1,1);

viewScope.put("dispDate",curDate);
viewScope.put("dispCalYear",@Year(viewScope.dispDate));
viewScope.put("dispCalMonth",@Month(viewScope.dispDate));

var monthDays = new Date(viewScope.dispCalYear,viewScope.dispCalMonth,0).getDate();
var monthFirstDay = new Date(viewScope.dispCalYear,viewScope.dispCalMonth -1,1).getDay();
viewScope.put("daysInMonth",monthDays);
viewScope.put("firstDayInMonth",monthFirstDay);}]]></xp:this.onclick>
    <xp:eventHandler event="onclick" submit="true"
     refreshMode="complete">
     <xp:this.action><![CDATA[#{javascript:var curMonth = @Month(viewScope.dispDate);
var curYear = @Year(viewScope.dispDate)
curMonth = (@Month(viewScope.dispDate)+1);
var curDate = new Date(curYear,curMonth -1,1);

viewScope.put("dispDate",curDate);
viewScope.put("dispCalYear",@Year(viewScope.dispDate));
viewScope.put("dispCalMonth",@Month(viewScope.dispDate));

var monthDays = new Date(viewScope.dispCalYear,viewScope.dispCalMonth,0).getDate();
var monthFirstDay = new Date(viewScope.dispCalYear,viewScope.dispCalMonth -1,1).getDay();
viewScope.put("daysInMonth",monthDays);
viewScope.put("firstDayInMonth",monthFirstDay);}]]></xp:this.action>
    </xp:eventHandler>
   </xp:link></xp:panel>



以下はこれまでのコードの最終完成形です。
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.beforePageLoad><![CDATA[#{javascript:if (!viewScope.containsValue("dispDate")) {
viewScope.dispDate = @Now();
viewScope.dispCalYear = @Year(viewScope.dispDate);
viewScope.dispCalMonth = @Month(viewScope.dispDate);
viewScope.daysInMonth = new Date(viewScope.dispCalYear,viewScope.dispCalMonth,0).getDate();
viewScope.firstDayInMonth = new Date(viewScope.dispCalYear,viewScope.dispCalMonth -1,1).getDay();
}}]]></xp:this.beforePageLoad>

<xp:panel id="calendar">
<xp:panel styleClass="calMonthTitle" id="Header">
<xp:link escape="true" id="monthBack"><xp:this.text><![CDATA[<<]]></xp:this.text>
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:var curMonth = @Month(viewScope.dispDate);
var curYear = @Year(viewScope.dispDate)
curMonth = (@Month(viewScope.dispDate)-1);
var curDate = new Date(curYear,curMonth -1,1);

viewScope.put("dispDate",curDate);
viewScope.put("dispCalYear",@Year(viewScope.dispDate));
viewScope.put("dispCalMonth",@Month(viewScope.dispDate));

var monthDays = new Date(viewScope.dispCalYear,viewScope.dispCalMonth,0).getDate();
var monthFirstDay = new Date(viewScope.dispCalYear,viewScope.dispCalMonth -1,1).getDay();
viewScope.put("daysInMonth",monthDays);
viewScope.put("firstDayInMonth",monthFirstDay);}]]></xp:this.action>
</xp:eventHandler></xp:link> 
  
<xp:text escape="false" id="dispMonth">
<xp:this.value><![CDATA[#{javascript:var months = new Array("January","Feburary","March","April","May","June","July","August","September","October","November","December");
var curMonth = months[(viewScope.dispCalMonth-1)];
return curMonth + " " + viewScope.dispCalYear;}]]></xp:this.value>
</xp:text>
  <xp:link escape="true" id="monthForward"><xp:this.text><![CDATA[>>]]></xp:this.text>
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:var curMonth = @Month(viewScope.dispDate);
var curYear = @Year(viewScope.dispDate)
curMonth = (@Month(viewScope.dispDate)+1);
var curDate = new Date(curYear,curMonth -1,1);

viewScope.put("dispDate",curDate);
viewScope.put("dispCalYear",@Year(viewScope.dispDate));
viewScope.put("dispCalMonth",@Month(viewScope.dispDate));

var monthDays = new Date(viewScope.dispCalYear,viewScope.dispCalMonth,0).getDate();
var monthFirstDay = new Date(viewScope.dispCalYear,viewScope.dispCalMonth -1,1).getDay();
viewScope.put("daysInMonth",monthDays);
viewScope.put("firstDayInMonth",monthFirstDay);}]]></xp:this.action>
</xp:eventHandler></xp:link></xp:panel>
<xp:table styleClass="calTable" id="calTable">
<thead styleClass="calTableHead">
<th>Sun</th>
<th>Mon</th>
<th>Tue</th>
<th>Wed</th>
<th>Thu</th>
<th>Fri</th>
<th>Sat</th>
</thead>
<xp:text escape="false" id="startRows">
<xp:this.value><![CDATA[#{javascript:"<tr>"}]]></xp:this.value>
</xp:text>
<xp:repeat id="calBlanks" rows="7" value="#{javascript:viewScope.firstDayInMonth;}" var="calBlankVar">
<xp:text id="blankCells" contentType="html">
<xp:this.value><![CDATA[#{javascript:'<td class="calBlank"> </td>';}]]></xp:this.value>
</xp:text>
</xp:repeat>
<xp:repeat id="calDay" rows="31"
value="#{javascript:viewScope.daysInMonth;}" var="calVar"
indexVar="calIndex">
<xp:text escape="false" id="rowEndBegin">

<xp:this.value><![CDATA[#{javascript:"</tr><tr>"}]]></xp:this.value>
<xp:this.rendered><![CDATA[#{javascript:if (calIndex != 0) {
if (((calIndex + viewScope.firstDayInMonth) % 7) == 0) { 
return true; 
}else{ 
return false; 
}
}}]]></xp:this.rendered>
</xp:text>
<xp:text escape="false" id="dayCells">
<xp:this.value><![CDATA[#{javascript:return '<td class="calDay"><span class="dayNum">' + (calVar+1) + '</span></td>';}]]></xp:this.value>
</xp:text>
</xp:repeat>
<xp:repeat id="calBlanksEnd" rows="14">
<xp:this.value><![CDATA[#{javascript:var lastRow = 42 - (viewScope.daysInMonth + viewScope.firstDayInMonth);
if (lastRow > 13) {
lastRow = lastRow - 14;
}else if (lastRow > 6) {
lastRow = lastRow - 7;
}
return lastRow;}]]></xp:this.value>
<td class="calBlank"></td>
</xp:repeat>
<xp:text escape="false" id="lastRow">
<xp:this.value><![CDATA[#{javascript:"</tr>"}]]></xp:this.value>
</xp:text>
</xp:table>
</xp:panel>
</xp:view>


最後に、CSS を使って、カレンダーの中に情報を表示されるためのコントロールのサンプルがダウンロードできます。
ダウンロード

2010/02/01

Discussion Next Gen を試してみる

OpenNTF プロジェクトの中には普段の開発で参考になるコードをたくさん見つけることができます。その中から今回は「Discussion Next Gen」を紹介したいと思います。

このプロジェクトの趣旨はプロジェクトの名前からも想像できるように IBM の提供するディスカッションテンプレートの先を行くといったところとでしょうか。プロジェクトでは標準にはない機能を追加しフィードバックを得てテストしていくことで、いづれ IBM の標準テンプレートへ追加されることになるでしょう。ある意味、機能の予行演習的なプロジェクトでもあります。

今回注目する機能はクライアントサイドと XPages での「今開いている文書はどこか」を見つけるための工夫をご紹介します。

現在のディスカッション標準テンプレートでは主文書と返答文書の結びつきが分からないといったことが発生しているのでこのプロジェクトでは下のスクリーンのように今現在自分がどのスレッドにいるかを一目でわかるように工夫されています。
もちろん主テーマに関していくつ返答があるのか数を事前に把握できるようなビューの作りにもなっています。

クライアントでの動作では文書を開くと Topic Navigator のフレームが左に表れ、自分が今開いている文書と親文書が色分けされて表示されます。主文書の他の返答文書のタイトルも同時に階層表示されますのでエンドユーザーにとっても非常に分かりやすいものとなっています。


今度は XPages 側の Web ブラウザからのアクセスです。文書を選択すると、文書の内容の下にスレッドが階層表示されます。
このようにクライアント側で実現できていることを XPages でも実現しています。
ここで見るようなスレッドの階層表示の方法「繰り返しコントロール」を使用していることは想像がつきますが返答文書の場合さらに段を下げるなどの工夫もされており、一度ソースをくまなく見てみる価値のある内容だと思います。
Web 2.0 のサイトでは良く見かけるこの典型的な表示方法を参考にするだけでも価値があるアプリケーションだと思います。

ちなみにこのアプリケーションも OneUI を使用していますが、標準テンプレートとは違い、「blue」のテーマしか使えません。ブラウザ対応は IE6、IE7 と 8、その他のブラウザと CSS が記述されています。