Windows Azure 入門
Windows Azure How-To 集
WCF / WF 入門
環境 :Office 365 (Preview)Visual Studio 2012Microsoft Office Developer Tools for Visual Studio 2012 (Preview2)
SharePoint 2013 Apps 開発
こんにちは。
このシリーズをすっかり放置していたところ、「.NET 技術の断捨離」の集まりで参加者の方からツッコミをいただきましたので、再開したいと思います。。。(すみません、今年の初ポストです。)
予定では Remote Event Receiver の解説が先でしたが、その前に、カスタム リスト定義 (List Definition) とリスト インスタンス (List Instance) を理解しておいたほうが良いので、順番を変更しました。
まず、ご存じない方のために、基本事項を補足します。リスト定義とは、例えば、SharePoint に既に含まれている「タスク」、「お知らせ」、「ディスカッション掲示板」などのリスト (ドキュメント ライブラリーを含む) のひな形 (テンプレート) であり、リスト インスタンスは、このリスト定義から実際に作成された「リスト」 (または、「ドキュメント ライブラリー」) です。(厳密には、リスト定義とは別に「リスト テンプレート」があり、SharePoint ではこれらもテンプレート���して表示されますが、ここでは細かな説明は省略します。)Apps for SharePoint 2013 でも、従来の SharePoint 開発同様、リスト定義とリスト インスタンスを開発 (プログラミング) でき、考え方のベースは同じですが、いくつか SharePoint 2013 独自の注意点や新機能もあるので以下に補足しながら解説します。
なお、今回から、ちゃんと日本語版を使用します !
基本は従来と同じ、しかし ... (App Event Receiver の活用)
では、さっそくリスト定義とリスト インスタンスを構築してみましょう。
Visual Studio 2012 を起動し、[Apps for SharePoint 2013] のプロジェクトを新規作成します。リスト定義 (List Definition) のような SharePoint の artifacts の場合には、SharePoint-hosted (SharePoint ホスト型) でホストされます。(SharePoint-hosted については、「Apps for SharePoint 2013 の動作と概要」参照。) このため、今回は、ウィザードで、[SharePoint ホスト型] (SharePoint-hosted) を選択してプロジェクトを作成してください。
作成されたプロジェクトをマウスで右クリックして、[追加] - [新しい項目] を選択すると、下記の通り、SharePoint の artifacts を追加する画面が表示されます。ここで、「List1」という名前のリストを追加します。リストを追加すると、リスト定義と、そのリスト定義から派生したリスト インスタンスが自動作成されます。
以降の開発 (プログラミング) の基本は、SharePoint 2010 の頃と同様です。ですので、事前に、「SharePoint のリスト定義の作成」を読んで理解しておいてください。
例えば、作成された「List1」をダブルクリックして表示される下図で、[列] タブを選択して列の追加や変更が可能です。(下図では、「Address」という名前の列を追加しています。)
この編集内容は、下図の通り、schema.xml に反映されます。(リスト定義の schema.xml については、「SharePoint のリスト定義の作成」を参照してください。)
<?xml version="1.0" encoding="utf-8"?><List xmlns:ows="Microsoft SharePoint" Title="List1" ...> <MetaData> . . . <Fields> <Field ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Type="Text" Name="Title" DisplayName="$Resources:core,Title;" Required="TRUE" ... /> <Field ID="{56ec54d2-3c20-4284-9d74-564ceced0e82}" Type="Text" Name="Address" DisplayName="Address" /> </Fields> . . .
また、上図で [リスト] タブを選択して、表示される画面で [リストをサイド リンク バーに表示する] (Display list at Quick Launch) のチェックを選択すると、リスト インスタンスは SharePoint サイトの左のバー (Quick Launch) に表示されます。
この内容は、これまで (SharePoint 2010 まで) と同様、リスト インスタンスのフィーチャーの Elements.xml に下記の通り反映されます。(Elements.xml についても、「SharePoint のリスト定義の作成」を参照してください。)
<?xml version="1.0" encoding="utf-8"?><Elements xmlns="http://schemas.microsoft.com/sharepoint/"> <ListInstance Title="List1" OnQuickLaunch="TRUE" TemplateType="10000" Url="Lists/List1" Description="マイ リスト インスタンス"> </ListInstance></Elements>
ただ、この設定をおこなっても、そもそもリスト定義やリスト インスタンスが別サイト (サブ サイト) に反映されるという点に注意してください。例えば、ユーザーが SharePoint Online (Office 365) を使っていて、https://contoso.sharepoint.com/sites/test1 のサイトで SharePointApp1 の App (SharePoint-hosted の Apps for SharePoint 2013) を追加した場合、以下のサブ サイトが作成され、ここにリスト定義やリスト インスタンスが追加されます。(「Apps for SharePoint 2013 の動作と概要」の解説を参照。)
https://contoso-<atbitary identity>.sharepoint.com/sites/test1/<sub site with project name>(例えば、https://contoso-3202b4c1a25bf7.sharepoint.com/sites/test1/SharePointApp1)
つまり、リスト インスタンスをサイド リンク バー (Quick Launch) に表示しても、ユーザーが使っている https://contoso.sharepoint.com/sites/test1 のサイド リンク バー (Quick Launch) ではなく、https://contoso-<atbitary identity>.sharepoint.com/sites/test1/<sub site with project name> のサイド リンク バー (Quick Launch) に設定されるので、意味がありません。
このような場合、従来の SharePoint 開発同様、カスタム コードを使って設定をおこなうことができます。Apps for SharePoint 2013 では、従来使用していた Feature Receiver は使用できず、App Event Receiver を使用します。例えば、Host Web (ユーザーが使用しているサイト) に Quick Launch を追加するには、(Visual Studio の) ソリューション エクスプローラーで Apps のプロジェクトをクリックし、下図の通り [アプリのハンドルがインストールされました] (Handle App Installed) を「True」に設定します。
アプリがインストールされた際のハンドラーが、Remote App (App とは別プロジェクト) として作成されます。(既定では、これは Autohosted で作成されます。) この作成された Web アプリケーションの AppEventReceiver.svc.cs に、下記の通りコードを記述します。ここではインストール時の処理のみ作成していますが、同様に、アンインストール時の処理も記述しておきましょう。
. . .using System.ServiceModel;using System.ServiceModel.Channels;using System.Net;. . .public class AppEventReceiver : IRemoteEventService{ public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties properties) { SPRemoteEventResult result = new SPRemoteEventResult(); if (properties.EventType != SPRemoteEventType.AppInstalled) return result; HttpRequestMessageProperty requestProperty = (HttpRequestMessageProperty)OperationContext.Current.IncomingMessageProperties[HttpRequestMessageProperty.Name]; SharePointContextToken contextToken = TokenHelper.ReadAndValidateContextToken( properties.ContextToken, requestProperty.Headers[HttpRequestHeader.Host]); string accessToken = TokenHelper.GetAccessToken( contextToken, properties.AppEventProperties.HostWebFullUrl.Authority).AccessToken; using (ClientContext clientContext = TokenHelper.GetClientContextWithAccessToken( properties.AppEventProperties.HostWebFullUrl.ToString(), accessToken)) { Web site = clientContext.Web; NavigationNodeCollection collQuickLaunchNode = site.Navigation.QuickLaunch; NavigationNodeCreationInformation ciNavicationNode = new NavigationNodeCreationInformation(); ciNavicationNode.Title = "List1"; Uri navUri = new Uri( properties.AppEventProperties.AppWebFullUrl, "SharePointApp1" + "/" + "Lists/List1"); ciNavicationNode.Url = navUri.ToString(); ciNavicationNode.AsLastNode = true; collQuickLaunchNode.Add(ciNavicationNode); clientContext.Load(collQuickLaunchNode); clientContext.ExecuteQuery(); } return result; } . . .
なお、この場合、App に「サイト コレクション」への Manage のアクセス許可 (Permission) を設定してください。(アクセス許可の考え方については、「Apps for SharePoint 2013 の動作と概要」を参照してください。)
補足 : デバッグ実行をおこなう際には、「Office and SharePoint Apps team blog : Debugging Remote Event Receivers with Visual Studio」に記載されている内容に注意してください。(環境によって、Office 365 のサーバーから、localhost のサービスへの接続に失敗します。) Autohosted の場合、「Apps in Testing」(テスト中のアプリ) のライブラリーに直接登録して追加すると Windows Azure にホストされるので、この方法で動作させることもできます。
この Remote Event Receiver の動作とプログラミングについては、次回、解説します。
また、[サイト コンテンツ] から、この App をクリックした場合に、リスト (上記の List1) が表示されるように、AppManifest.xml を下記の通り編集しておくと良いでしょう。
<?xml version="1.0" encoding="utf-8" ?><App . . .> <Properties> <Title>SharePointApp1_Remote</Title> <StartPage>~appWebUrl/Lists/List1</StartPage> </Properties> . . .</App>
Client Side Rendering (CSR) と List View、List Form のカスタマイズ
カスタム リストを使った現実のアプリケーションでは、リスト ビューやリスト フォーム (DisplayForm, EditForm, NewForm) のカスタマイズが頻繁に必要となります。しかし、これまでは、「SharePoint 2010 : リスト定義における XSL を使用したビューのカスタマイズ」を読んでいただくとおわかりの通り、SharePoint 独自の XML (CAML) や XSL を使用して編集をおこなう必要があり、結構面倒でした。(例えば、JavaScript を埋め込む場合には、XSLT の中に埋め込み、文字列をエスケープするなど煩雑なコードの記述が必要でした。)
SharePoint 2013 では、Client Side Rendering (CSR) と呼ばれる方式で、SharePoint が作成するリスト ビューやリスト フォームの UI を JavaScript を使用してカスタマイズ (クライアント側で Overrides) できるようになっており、こうした Pain が解消されています。ここでは、この SharePoint 2013 からの Client Side Rendering (CSR) について補足しておきます。
まず、特定のビューに CSR を適用するには、schema.xml を開いて、下記 (太字) の通り、使用する JavaScript ファイル (.js ファイル) を挿入します。(下記で、~site は、App のサイトを表すトークンです。List1Sample.js は、このあとで作成します。clienttemplates.js は CSR に必要なので、挿入しておいて��ださい。)
. . .<View BaseViewID="1" Type="HTML" WebPartZoneID="Main" . . ."> <Toolbar Type="Standard" /> <XslLink Default="TRUE">main.xsl</XslLink> <JSLink>clienttemplates.js|~site/Scripts/jquery-1.7.1.min.js|~site/Scripts/List1Sample.js</JSLink> <RowLimit Paged="TRUE">30</RowLimit> . . .
また、特定のフォーム (DisplayForm、EditForm、NewForm) に CSR を適用するには、schema.xml に下記の通り記述して .js ファイルを挿入します。(下記は、Display Form に適用した場合の例です。)
. . .<Forms> <Form Type="DisplayForm" Url="DispForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" JSLink="~site/Scripts/jquery-1.7.1.min.js|~site/Scripts/List1Sample.js" /> <Form Type="EditForm" Url="EditForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" /> <Form Type="NewForm" Url="NewForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" /></Forms>. . .
つぎに、この List1Sample.js を作成します。「Apps for SharePoint 2013」の SharePoint-hosted (SharePoint ホスト型) のプロジェクトでは、下図の通り、既に、Scirpts フォルダーとその下の js ファイルがモジュールとして追加されています。(SharePoint のモジュールとは、SharePoint に配置されるイメージ ファイル、スタイル シートなどのファイルやフォルダーのことです。) 今回は、ここに、List1Sample.js を追加します。これをおこなうには、下図の「Scripts」フォルダーを右クリックして、[追加] - [新しい項目] メニューを選択して、「List1Sample.js」という名前で [JavaScript ファイル] を追加します。(下図の Elements.xml には、この追加されたファイルの設定も自動的に記述されます。)
例えば、リスト ビューで、Address 列に、Bing Maps へのハイパーリンク (href) を設定するには、List1Sample.js を以下の通りプログラミングします。
function List1Overrides() { var overrideCtx = {}; overrideCtx.Templates = {}; overrideCtx.BaseViewID = '1'; overrideCtx.ListTemplateType = 10000; // Note1 : Override field rendering overrideCtx.Templates.Fields = { 'Address': { 'View': FieldsOverrides_Address }, }; // Note2 : Register overrides SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideCtx);};function FieldsOverrides_Address( ctx, field, listItem, listSchema) { var value = listItem[field.Name]; return '<a href="http://www.bing.com/maps/?v=2&where1=' + encodeURI(value) + '">' + value + '</a>';}// Note3 : Execute override using CSRRegisterModuleInit("List1Sample.js", List1Overrides); // for MDSList1Overrides(); // for non-MDS// Note4 : Needed for ScriptOnDemandif (typeof (Sys) != "undefined" && Boolean(Sys) && Boolean(Sys.Application)) { Sys.Application.notifyScriptLoaded();}if (typeof (NotifyScriptLoadedAndExecuteWaitingJobs) == "function") { NotifyScriptLoadedAndExecuteWaitingJobs("List1Sample.js");}
上記の Note1 では、「Address」という Field のリスト ビューの表示 (View) の際の動作を FieldsOverrides_Address 関数で Override しています。また、Note3、Note4 は、削除しないでください。MDS (Minimal Download Strategey) モードで表示している場合には Note3 の前者が、それ以外の場合には Note3 の後者が呼ばれます。(RegisterModuleInit は、/_layouts/15/init.js にあります。SharePoint 2013 では、通常、MDS モードが有効ですが、SPWeb の EnableMinimalDownload を設定することで Disable にできます。)
実行結果は、下図の通りになります。Address 列のリンクをクリックすると、Bing Maps に飛び、その住所が表示されます。
また、OnPreRender、OnPostRender によって、レンダリング (描画) の前後で呼ばれる処理を作成 (Override) できます。例えば、下記は、リスト ビューで、偶数行のみ背景色を変更するサンプル コードです。レンダリング (描画) の後の処理として PostRender_RowColoring を登録していますが、function の配列を渡して複数の処理を登録することもできます。また、GenerateIIDForListItem は、ListItem から IID を取得する関数で、前述の clienttemplates.js で定義されています。
function List1Overrides() { var overrideCtx = {}; overrideCtx.Templates = {}; overrideCtx.BaseViewID = '1'; overrideCtx.ListTemplateType = 10000; overrideCtx.Templates.Fields = { 'Address': { 'View': FieldsOverrides_Address }, }; overrideCtx.OnPostRender = PostRender_RowColoring; SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideCtx);};. . .function PostRender_RowColoring(ctx) { //// without jquery ... //for (var i = 0; i < ctx.ListData.Row.length; ++i) { // if (i % 2) { // var iid = GenerateIIDForListItem(ctx, ctx.ListData.Row[i]); // var row = document.getElementById(iid); // row.style.backgroundColor = '#aaaaff;'; // } //} // with jquery ... $.each(ctx.ListData.Row, function (i, val) { if (i % 2) { var iid = GenerateIIDForListItem(ctx, val); $(selectorEscape(iid)).css('background-color', '#aaaaff'); } });}// Escape jquery selector// (SharePoint uses comma and dollar mark for iid and id.)function selectorEscape(arg) { return arg.replace( new RegExp('(,|\\.|\'|\\$|&|\\/|\\\\|!|\\||\\+|\\*|~|=|>|;|:|#|"|\\^|\\(|\\)|\\[|\\])', 'g'), '\\$1'); //var result = iid; //result = result.replace(/,/g, '\\,'); //result = result.replace(/(\$)/g, '\\\$'); //return result;}. . .
実行結果は、下記の通りになります。
また、フォームのカスタマイズをおこなうには、以下の通り記述します。例えば、DisplayForm で、上記同様、Address の内容をハイパーリンクにして Bing Maps に飛ばすには、以下の通り記述します。
. . .function List1FormsOverrides() { var overrideCtx = {}; overrideCtx.Templates = {}; overrideCtx.ListTemplateType = 10000; overrideCtx.Templates.Fields = { 'Address': { 'DisplayForm': DisplayFormOverrides_Address }, }; SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideCtx);};function DisplayFormOverrides_Address(ctx) { var formCtx = SPClientTemplates.Utility.GetFormContextForCurrentField(ctx); if (formCtx.controlMode != SPClientTemplates.ClientControlMode.DisplayForm) return ''; else return '<a href="http://www.bing.com/maps/?v=2&where1=' + encodeURI(formCtx.fieldValue) + '">' + formCtx.fieldValue + '</a>';}. . .RegisterModuleInit("List1Sample.js", List1FormsOverrides); // for MDSList1FormsOverrides(); // for non-MDS. . .
実行結果は、下記の通りになります。(リンクをクリックすると Bing Maps に飛び、住所が表示されます。)
なお、NewForm、EditForm を Override する場合は、下記の通り、必要な Callback を登録しておいてください。特に、GetValue Callback が登録されていない場合、リストに値が設定されないので注意してください。(2013/02/25 追記)
. . .function NewFormOverrides_Address(ctx) { var formCtx = SPClientTemplates.Utility.GetFormContextForCurrentField(ctx); var inputId = formCtx.fieldName + '_' + formCtx.fieldSchema.Id + '_$TextField'; var inputObj = null; if (formCtx.controlMode == SPClientTemplates.ClientControlMode.NewForm) { // ClientValidator Callback var validators = new SPClientForms.ClientValidation.ValidatorSet(); formCtx.registerClientValidator(formCtx.fieldName, validators); // Init Callback formCtx.registerInitCallback(formCtx.fieldName, function () { inputObj = $('#' + selectorEscape(inputId)); }); // Focus Callback formCtx.registerFocusCallback(formCtx.fieldName, function () { if (inputObj != null) inputObj.focus(); }); // ValidationError Callback formCtx.registerValidationErrorCallback(formCtx.fieldName, function (errorResult) { }); // GetValue Callback formCtx.registerGetValueCallback(formCtx.fieldName, function () { if (inputObj == null) return ''; else return inputObj.val(); }); formCtx.updateControlValue(formCtx.fieldName, ''); // original : <span dir="none"><input title="Address" class="ms-long ms-spellcheck-true" id="Address_afb7686f-7d8a-4f53-9643-ad4c6e40bc6f_$TextField" type="text" maxlength="255" value="" /><br/></span> return '<span dir="none"><input title="' + formCtx.fieldSchema.Address + '" ' + 'class="ms-long ms-spellcheck-true" ' + 'id=' + STSHtmlEncode(inputId) + ' ' + 'type="text" ' + 'maxlength="' + formCtx.fieldSchema.MaxLength + '" ' + 'value="" ' + '/><br/></span>'; } return '';}. . .
CSR は、 従来のリスト UI のカスタマイズの手法 (「SharePoint 2010 : リスト定義における XSL を使用したビューのカスタマイズ」を参照) と組み合わせても良いでしょう。実際、SharePoint が生成する HTML は膨大なため、よりシンプルな HTML (必要最小限の HTML) を生成し、さらに高度な (動的な) UI カスタマイズは CSR を使った JavaScript で実装する方法も考えられます。
補足 : 例えば、カスタム リスト ビュー (スタイル) と CSR を併用する場合、「SharePoint 2010 : リスト定義における XSL を使用したビューのカスタマイズ」で紹介している <Xsl> を使った手法を使用してください。(「SharePoint 2010 : リスト定義における XSL を使用したビューのカスタマイズ」の CAML を使った手法は、<JSLINK> とは併用できません。)また、CSR を呼び出し元は、/_layouts/15/vwstyles.xsl の RenderListView です。Custom XSL を使用する場合は、vwstyles.xsl に記述されている CSR の処理 (RenderListView の前後) を XSL に含めてください。
補足 : schema.xml を変更する際には、こちら に記載した通り、キャッシュ (cache) されることがあるので注意してください。(schema.xml の変更が反映されないことがあります。)