"SharePoint Designer " にカテゴリー登録されている32 投稿

SharePoint の 日付選択コントロールにスタイルが適用されない

前回の投稿では、SharePoint Designer 2007 を使用してカスタム作成ページにSharePoint の 日付選択コントロールを追加して利用する例をご紹介しました。

しかし、この日付選択コントロールは既定では SharePoint のスタイルが適用されずレイアウトが崩れてしまいます。

この場合の解決方法は次の通りです。

  1. SharePoint ハイブ(%programfiles%\common files\microsoft shared\web server extensions\12\)内のtemplates\layoutsにある "iframe.aspx" をメモ帳などで開く
  2. <Head>タグの直後に次のタグを追加する。
    <link rel="stylesheet" type="text/css"href="/_layouts/1041/styles/datepicker.css"/>
    ※ 2008.3.28 修正しました。ディレクトリ指定をうっかり "1033" と記載しておりました。 "1041" の間違いでした。

以上、Tips でした。


【参考】
http://blog.richfinn.net/2006/12/10/SharePointDateTimeControlDoesNotRenderStyles.aspx


特定の期間でフィルタを設定する : データビューとクエリ文字列

ドキュメント ライブラリなどのファイルにメタデータ(プロパティ)を持たせることができるのは周知のことと思います。

たとえば、契約書などを保存する際に契約満了日などのプロパティを持たせておくと管理がしやすそうです。しかし、折角こうしたプロパティを持たせたのであれば、ユーザーが自由に期間を指定して特定のファイルだけを表示させたいところです。

さて、このようなフィルタを比較的手軽に行うにはどうしたらいいかが問題です。カスタムビューでのフィルタでは固定値になります。そんなときにうってつけなのがこちらのブログで何度もご紹介している "データビュー" です。 SharePoint Designer 2007 を使用すると既定のリストビューを データビューに変更できます。データビューに変換すると、フィルタが設定できるようになりますが、このフィルタに "クエリ文字列" が利用できます。

そこで次のようなソリューションを考えました。既定のリストビューをデータビューに変換し、クエリ文字列で期間を指定できるようにする。これで決まりです! あとはコントロールなどを配置してうまいこと JavaScript を組み込んでやればかなり使い勝手がよくなります。

次の図は 日付コントロールなどを挿入してスタイルシートを適用して見栄えを整えてWeb パーツでも挿入したような見た目にしていますが、実際には JavaScript のコードしか書いていません。

[図.期間指定でフィルタを設定する]
Dataviewadvancedfilter
Dataviewadvancedfilter2

作業概要

  • データビューに変換しフィルタを設定する
  • クエリ文字列を渡すためのコントロールやスクリプトを用意する

用意したライブラリ

ドキュメントライブラリを用意し、そこに "契約満了日" というプロパティを持たせています。

データビューに変換しフィルタを設定する

データビューの変更方法は既出の記事でご紹介していますので、詳しくは以下を参考にしてください。

  1. リストの view.aspx 内のリストビューを XSLT データビューに変換します。
  2. データビューの部分を右クリックし "コモン コントロール タスクの表示" をクリックします。
  3. "フィルタ" をクリックします。
  4. フィルタ条件として、フィールド名: "契約満了日" 、条件式: "次の値以上" 、値: "新しいパラメータの作成" を指定します。
  5. パラメータの作成画面で、パラメータ ソース: "クエリ文字列"、クエリ文字列変数: start というエントリを作成します。同様に、パラメータ ソース: "クエリ文字列"、クエリ文字列変数: end というエントリを作成します。
    Dataviewfilterquerystring1
  6. [OK] をクリックします。
  7. 一つ目の条件の値が "[start]" になっていることを確認します。
  8. 再びフィルタ条件として、フィールド名: "契約満了日" 、条件式: "次の値以下" 、値: "[end]" を追加します。

    Dataviewfilterquerystring2

  9. [OK] をクリックします。

※ソースコードを確認するとわかりますが、フィルタを設定すると、DataSource 内の select 属性にフィルタ内容が CAML として生成されます。CAMLは SharePoint 用のクエリ言語のようなものです。ここに書かれている条件を元にデータを取得してきます。

以上で 「リストのURL/view.aspx?start=開始日&end=終了日」と指定することで、フィルタが適用できるようになります。

クエリ文字列を渡すためのコントロールやスクリプトを用意する

データビューコントロールのタグの直前に次のようなコードを挿入します。今回は日付をコントロールを利用して指定できるよう、SharePointDateTimeControl を挿入しています。

+++++++++++++++++++++++

<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">
<!-- 以下にコードを追加-->
<br/><p></p>
<table style="font-family:'MS UI Gothic'" class="ms-quickLaunch" align="center">
<tr class="ms-quicklaunchheader"><td colspan="5" align="left">契約満了日でフィルタする</td></tr>
<tr>
<td>
<SharePoint:DateTimeControl runat=server id="DateTimeControl_Start"
DateOnly="true" ToolTip="検索を開始する日付"/>
</td>
<td>から</td>
<td>
<SharePoint:DateTimeControl runat=server id="DateTimeControl_End"
DateOnly="true"  EnableViewState="true"/>

</td><td>まで</td><td>

<input type="button" value="フィルタの実行" onclick="return goFilter()" style="background-color:#FFCC00" name="filterButton"/>
</td></tr>
<tr><td colspan="4" style="color:red;font-size:x-small" id="kikan"></td></tr></table>
<p></p>

<!-- ここまで -->
  <WebPartPages:WebPartZone runat="server" FrameType="None" ID="Main" Title="loc:Main">

++++++++++++++++++++++++++++++

あとはスクリプトを組み込むだけです。日付データであることの妥当性検証などは省いていますが、たとえば以下のようなスクリプトを上記に追加してやればよいです。

+++++++++++++++++++++++++++++

<script type="text/javascript">
<!--

//クエリ文字列を取得し、設定した期間を画面上に表示する
_spBodyOnLoadFunctionNames.push("getFilterCondition");

function getFilterCondition(){
var startDate,endDate;
var message;
var values = getQueryString();
if((values["start"]!= null)&&(values["end"]!=null))
{
  startDate=values["start"];
  endDate=values["end"];
  message= startDate + " から " + endDate  + " までの期間でフィルタしています";
}else{
  message="フィルタの条件が指定されていません。条件を指定してください。";
}
document.getElementById("kikan").innerText=message;
}

//クエリ文字列を取得する
function getQueryString(){
var qs = location.search.substring(1, location.search.length);   
var args = qs.split("&");
var vals = new Object();

for (var i=0; i < args.length; i++) {
     var nameVal = args[i].split("=");
  var temp = unescape(nameVal[1]).split('+');
  nameVal[1] = temp.join(' ');
  vals[nameVal[0]] = nameVal[1];

return vals;
}

//ユーザーが指定した範囲をクエリ文字列として渡す
function goFilter()
{
var startDateTag,endDateTag;
var startDate=new Date();
var endDate=new Date();

//SharePointDateTimeControl のオブジェクトを取得
startDateTag=document.getElementById("ctl00$PlaceHolderMain$DateTimeControl_Start$DateTimeControl_StartDate");
endDateTag=document.getElementById("ctl00$PlaceHolderMain$DateTimeControl_End$DateTimeControl_EndDate");

if(startDateTag.value=="")
{
alert('フィルタで使用する開始日を指定してください');
return;
}

if(endDateTag.value=="")
{
alert('フィルタで使用する終了日を指定してください');
return;
}

startDate=startDateTag.value;
endDate=endDateTag.value;

if(startDate>endDate)
{
alert('日付の指定が不正です。正しい範囲を指定してください');
return;
}

startDate=replaceDate(startDate);
endDate=replaceDate(endDate);

window.location="view.aspx?start="+startDate + "&end=" + endDate;
}

//日付は "yyyy-mm-dd" 形式で渡す必要がある
function replaceDate(str)
{
while(str.indexOf("/",0)!=-1)
{
  str=str.replace("/","-");
}
return str;
}
//-->
</script>
+++++++++++++++++++++++++++++++++

以上、SharePoint Designer 2007 のみでのカスタマイズでも、以外にここまでできると便利だなぁと改めて実感した次第です。

実は今回使用した日付コントロールですが、そのまま使用するとカレンダー表示時のレイアウトが崩れます。次回は、その修正方法をご紹介します。


【参考】
Microsoft SharePoint Designer Team Blog (英語)

続きを読む "特定の期間でフィルタを設定する : データビューとクエリ文字列" »


SharePoint Designer 2007 で default.master のコピーを削除できない

MOSS の UI を変更するために、SharePoint Designer 2007 を使用してカスタムのマスタページを作成するとき、default.master を直接カスタマイズしたものを再び「サイト定義にリセット」すると default.master のコピーが作成されます。もしくは、単純に default.master をコピーして名前を変更して利用している場合もあると思います。

このように default.master のコピーを作成したあと、コピーが不要になったからといざ削除しようとすると次のようなエラーメッセージが表示され削除できません。

Oldmasterpagedeleteerror

実際にはどこにも参照しているページがないのにも関わらず、このようなメッセージがでます。とはいえ、不要なファイルは削除したいです。正式な方法ではないでしょうが、てっとり早く削除する方法を記しておきます。

操作は単純です。master となっている拡張子を削除し、ファイル名だけにします。この状態だと削除できてしまいます。

UI カスタマイズをする際のちょっとした裏ワザ(?) でした。


他のリストデータ値を参照取得する

先日実施しましたセミナーでご質問いただいたところからヒントを得て、ちょっとしたリストデータ連携を実装してみましたのでご紹介します。

たとえば、顧客データを管理するリストとして「顧客リスト」があり、契約書を保管するドキュメントライブラリとして「契約書ライブラリ」があるとします。契約書ライブラリに契約書を保管してきますのが、プロパティとして顧客リストに格納されているIDを参照する「顧客ID」列を追加しようと思いますが、このときユーザーにはあくまで顧客名で選択させ、実際に格納する値は「顧客ID」にしたいと思います。

さて、SharePoint 上でできるだけ手軽に実装するにはどうしたらよいでしょうか。

追加する「顧客ID」列のデータの種類を参照列にする方法も考えましたが、これでは直接、顧客リストの顧客名を選択させるだけで同時にIDを取得することはできず要件を満たせません。

そこで SharePoint Designer 2007 を利用することにしました。既存のコントロールと JavaScript の組み合わせだけで対応できそうです。

[図.ライブラリ内でのリストデータの参照]

Listlibrary

作業概要

  1. 顧客リストからデータソースを生成する
  2. ASP.NET のドロップダウンリスト コントロールを配置し、1.で生成した顧客リストのデータソースをバインドする
  3. ドキュメントライブラリ上の顧客ID列に値を格納するようなコードをJavaScriptで記述する

顧客リストからデータソースを生成する

  1. SharePoint Desinger 2007 を使用し、契約書ライブラリ内の EditForm.aspx をコピーし "New_EditForm.aspx" として EditForm.aspx と同じ場所に保存します(オリジナルの EditForm.aspx は念のため温存しておきます)。
  2. 「契約書ライブラリ」のプロパティを表示し、 「関連ファイル」タブから「ドキュメントのプロパティを編集するフォーム」を "New_EditForm.aspx" に変更します。
  3. メニューバーにある「データビュー」メニューから「データソースの管理」をクリックします。
  4. デザインビュー内のリストフォームWebパーツ(ListFormWebPart)の直前あたりにカーソルを挿入した状態で、データソースの管理ウィンドウ内の顧客リストのドロップダウンメニューから「データソース コントロールの挿入」をクリックします。

    Datasourcecustomerlist

ASP.NET のドロップダウンリスト コントロールを配置し、1.で生成した顧客リストのデータソースをバインドする

  1. メニューバーにある「作業ウィンドウ」から「ツールボックス」をクリックします。

  2. リストフォーム Web パーツ(ListFormWebPart)の直前あたり(ただし、<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">
    タグよりあと) に ツールボックス内に表示されるASP.NETコントロールの DropDownList を追加します。

  3. デザインビューに表示されるDropDownList の右側に表示される ">" をクリックし、"コモン DropDownList タスク" に表示される 「データソースの選択」をクリックします。

  4. データソース構成ウィザードが表示されます。

  5. 「データソースの選択」にて先ほど追加したデータソースを指定します。既定では"spdatasource1" という名前です。続いて「DropDownListで表示するデータ フィールドの選択」にて表示したい列を選択します。私の環境ではタイトル列を顧客名フィールドとして利用していますので"Title" を指定しています。「DropDownList の値のデータフィールドの選択」にて実際に取得したいフィールドの値を指定します。今回は最終的に顧客IDを取得したいので私の環境では"_x9867__x5ba2_ID"という列を指定しています(ここで選択する列名は環境によって異なることになると思いますので適宜状況に応じたものを選択するようにしてください。なお、最初からわかりやすい列名にしておきたい場合は列作成時に列のタイトルを英語表記で指定しておきあとから日本語のタイトルに変更しておくとよいです)。
    Datasourceconfigwiz

  6. [OK] をクリックします。

  7. "AutoPostBack を有効にする" チェックボックスをオンにします。

以上で契約書ライブラリのプロパティ編集画面に顧客リストの一部が表示できるようになります。今回利用した DropDownList はユーザーからアクセスされたときには Select Option タグとして出力されるようになります。

JavaScript を記述する

<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">タグの直後あたりに、たとえば次のような JavaScript を記述します。

+++++++++++++++++++++++++++++++++++

<script type="text/javascript">
<!--
_spBodyOnLoadFunctionNames.push("getCustomerID");

function getCustomerID()
{
var sourceTag,targetTag;
var index;

//"ctl00_PlaceHolderMain_DropDownList1"の部分はDropDownList の ID値です。
sourceTag=document.getElementById("ctl00_PlaceHolderMain_DropDownList1");

//"ctl00_m_g_79887cea~" の部分は 「顧客ID」列となっている input タグの IDです
targetTag=document.getElementById("ctl00_m_g_79887cea_b0ac_4eac_87ea_f0895d24288a_ctl00_
ctl02_ctl00_ctl01_ctl00_ctl00_ctl00_ctl00_ctl00_ctl04_ctl00_ctl00_TextField
");

//ドロップダウンリストで選択されている項目の値を顧客ID列に格納します
index=sourceTag.selectedIndex;
targetTag.value=sourceTag.options[index].value;

//顧客ID列は直接は編集できないよう、読み取り専用にしておきます
targetTag.readOnly=true;
}

+++++++++++++++++++++++++++++++++++

データビューの連携

今回はついでに、顧客リストのデータをデータビューを使って同時に表示するようにしています。このあたりは前回の記事の応用です(以前の記事)。構成の概要だけご紹介しておきます。

  1. DropDownList の隣に顧客リストのデータビューを挿入します。
  2. データビューのフィルタ設定で、ドロップダウンリスト コントロールの値をパラメータとしてフィルタします。(例) "顧客ID" が "次の値に等しい" "param1" ※ param1 はドロップダウンリスト コントロールの値

補足

今回はデータビューと連携させるために ドロップダウンリストの AutoPostBack を 有効にしましたが、特にデータビューと連携させないのであれば AutoPostBack は有効にせず、直接ドロップダウンリストの onchanged イベントで 顧客ID 列に値を格納するようにしてももちろん構いません。

onchanged イベントを利用して即座に顧客ID列に値を格納する場合のコードサンプル
+++++++++++++++++++++

_spBodyOnLoadFunctionNames.push("addOnchangeFunction");

function addOnchangeFunction()
{
var sTag=document.getElementById("ctl00_PlaceHolderMain_DropDownList1");
sourceTag.onchange=function getCustomerID(){
               var sourceTag,targetTag;
              targetTag=document.getElementById("ctl00_m_g_79887cea_b0ac_4eac_87ea_f0895d24288a_ctl00_ctl02_
ctl00_ctl01_ctl00_ctl00_ctl00_ctl00_ctl00_ctl04_ctl00_ctl00_TextField");
             sourceTag=document.getElementById("ctl00_PlaceHolderMain_DropDownList1");
              var index=sourceTag.selectedIndex;
             targetTag.value=sourceTag.options[index].value;
             targetTag.readOnly=true;
}
}

+++++++++++++++++++++

以上、参考になれば幸いです。

続きを読む "他のリストデータ値を参照取得する" »


タイトル列の非表示 (JavaScript を用いるアプローチ)

以前の記事で、タイトル列の非表示を例にあげ、SharePoint Designer 2007 のユーザー設定のリストフォームを利用する方法をご紹介しました。http://shanqiai.weblogs.jp/sharepoint_technical_note/2007/12/newformaspx_edi_d88b.html

便利な機能ではあるのですが、使い方によっては不具合も多く見られるようです。たとえば、2929さんが何度かコメントされていますがテンプレート化して利用する際にエラーが表示されるなどです。

そこで、タイトル列を非表示にする別のアプローチを探っていたのですが、JavaScript を使って HTML DOM を操作するアプローチが現実的である場合があるように思います。もちろん、ブラウザ依存の部分を考えなければならず、本来はそうした差異を吸収してくれるのがASP.NETのコントロールであったりするわけですが、ある意味逆転の発想です。

参考までに、以下に任意のリストで「タイトル列を非表示にする」スクリプトコードの例を記述しておきます。

*****************************************

<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">
<!-- 以下に、JavaScript を追加-->

<script type="text/javascript">
<!--
_spBodyOnLoadFunctionNames.push("hideTitle");

function hideTitle()
{

var targetTag=getTagFromIdenfifierAndTitle('input','TextField','タイトル');

while(targetTag.parentNode!=null)
{
  tag=tag.parentNode;
  if(tag.tagName=='TR')
  {
   tag.style.display='none';
   return;
  }
}

}//function

function getTagFromIdentifierAndTitle(tagName, identifier, title) {
  var len = identifier.length;
  var tags = document.getElementsByTagName(tagName);

  for (var i=0; i < tags.length; i++) {
    var tempString = tags[i].id;
    if (tags[i].title == title && (identifier == "" || tempString.indexOf(identifier) == tempString.length - len)) {
      return tags[i];
    }//if
  }//for

  return null;
}//function

//-->
</script>

****************************************

上記のコードでは、"タイトル" と書かれているノードの親となる "TR" 要素を取得し、スタイル属性に "display:none" を指定しています。これで、テーブル内のタイトル行が非表示になるわけです。

このように JavaScript から DOM を操作するアプローチはロジックを考えるのが少々面倒なところはありますが、既存のWebパーツの邪魔をせずに必要なタグを修正したり、追加できるのでそれなりに使える場面は多いように思います。

開発者の方でも意外と JavaScript でコーディングする機会は少ないかも知れませんが、MOSS のエンジニアはJavaScript による DOM 操作の基本は押さえておきたいですね。


参考: SharePoint Designer 2007 Team Blog (英語)