Uncategorized

SharePoint Add-ins : List の開発と Client Side Rendering (CSR)

最新の SharePoint 開発については SharePoint Framework を参照してください。(この開発手法は、古い情報です。)

SharePoint Add-ins 開発

こんにちは。

今回は、SharePoint Add-ins (SharePoint アドイン, 旧 App for SharePoint) のカスタム リスト定義 (List Definition) とリスト インスタンス (List Instance) の開発 (プログラミング) を解説します。

まず、ご存じない方のために、基本事項を補足します。
リスト定義とは、例えば、SharePoint に既に含まれている「タスク」、「お知らせ」、「ディスカッション掲示板」などのリスト (ドキュメント ライブラリーを含む) のひな形 (テンプレート) であり、リスト インスタンスは、このリスト定義から実際に作成された「リスト」 (または、「ドキュメント ライブラリー」) です。(厳密には、リスト定義とは別に「リスト テンプレート」があり、SharePoint ではこれらもテンプレートとして表示されますが、ここでは細かな説明は省略します。)
SharePoint Add-ins でも、従来の SharePoint 開発同様、リスト定義とリスト インスタンスを開発 (プログラミング) でき、考え方のベースは同じですが、いくつか SharePoint 2013 独自の注意点や新機能もあるので以下に補足しながら解説します。

なお、今回から、ちゃんと日本語版を使用します !

 

基本は従来と同じ、しかし … (App Event Receiver の活用)

では、さっそくリスト定義とリスト インスタンスを構築してみましょう。

Visual Studio 2012 を起動し、[Apps for SharePoint 2013] (SharePoint Add-ins) のプロジェクトを新規作成します。
リスト定義 (List Definition) のような SharePoint の artifacts の場合には、SharePoint-hosted (SharePoint ホスト型) でホストされます。(SharePoint-hosted については、「SharePoint Add-ins の動作と概要」参照。) このため、今回は、ウィザードで、[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='' ...>  <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/">  <ListInstancetitle=''OnQuickLaunch="TRUE"TemplateType="10000"Url="Lists/List1"Description="マイ リスト インスタンス">  </ListInstance></Elements>

ただ、この設定をおこなっても、そもそもリスト定義やリスト インスタンスが別サイト (サブ サイト) に反映されるという点に注意してください。例えば、ユーザーが SharePoint Online (Office 365) を使っていて、https://contoso.sharepoint.com/sites/test1 のサイトで SharePointApp1 の Add-ins を追加した場合、以下のサブ サイトが作成され、ここにリスト定義やリスト インスタンスが追加されます。(「SharePoint Add-ins の動作と概要」の解説を参照。)

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 開発同様、カスタム コードを使って設定をおこなうことができます。SharePoint Add-ins では、従来使用していた Feature Receiver は使用できず、App Event Receiver を使用します。
例えば、Host Web (ユーザーが使用しているサイト) に Quick Launch を追加するには、(Visual Studio の) ソリューション エクスプローラーで Add-ins のプロジェクトをクリックし、下図の通り [アプリのハンドルがインストールされました] (Handle App Installed) を「True」に設定します。

アプリがインストールされた際のハンドラーが、Remote App (マニフェストの入っている Add-ins とは別プロジェクト) として作成されます。(Provider-hosted で作成されます。) この作成された 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;  }  . . .

なお、この場合、Add-ins に「サイト コレクション」への Manage のアクセス許可 (Permission) を設定してください。(アクセス許可の考え方については、「SharePoint Add-ins の動作と概要」を参照してください。)

補足 : Remote Event Receiver のデバッグ実行をおこなう際は注意してください。普通にデバッグ実行すると、Office 365 のサーバーから localhost のサービスへの接続に失敗します。
Remote App を Azure にホストするなどして動作させます。

この Remote Event Receiver の動作とプログラミングについては、次回、解説します。

また、[サイト コンテンツ] から、この Add-ins をクリックした場合に、リスト (上記の 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 は、Add-ins のサイトを表すトークンです。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="pagesform.aspx"WebPartZoneID="Main"JSLink="~site/Scripts/jquery-1.7.1.min.js|~site/Scripts/List1Sample.js" />  <Form Type="EditForm"Url="EditForm.aspx"SetupPath="pagesform.aspx"WebPartZoneID="Main" />  <Form Type="NewForm"Url="NewForm.aspx"SetupPath="pagesform.aspx"WebPartZoneID="Main" /></Forms>. . .

つぎに、この List1Sample.js を作成します。SharePoint Add-ins の 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 name="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 '';elsereturn '<a name="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 Callbackvar validators = new SPClientForms.ClientValidation.ValidatorSet();formCtx.registerClientValidator(formCtx.fieldName, validators);// Init CallbackformCtx.registerInitCallback(formCtx.fieldName, function () {  inputObj = $('#' + selectorEscape(inputId));});// Focus CallbackformCtx.registerFocusCallback(formCtx.fieldName, function () {  if (inputObj != null)inputObj.focus();});// ValidationError CallbackformCtx.registerValidationErrorCallback(formCtx.fieldName, function (errorResult) {});// GetValue CallbackformCtx.registerGetValueCallback(formCtx.fieldName, function () {  if (inputObj == null)return '';  elsereturn inputObj.val();});formCtx.updateControlValue(formCtx.fieldName, '');// original : <span dir="none"><input title='' 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 の変更が反映されないことがあります。)

 

※ 変更履歴 :

2015/05/05  App for SharePoint (SharePoint 用アプリ) を SharePoint Add-ins (SharePoint アドイン) に名称変更

Categories: Uncategorized

Tagged as: ,

10 replies»

Leave a Reply