REST APIでログインユーザー情報を取得し、フォームに表示する

SharePoint2013のお話です。

SharePointのフォームにログインユーザーの情報をデフォルト値として設定したいというのはよくある要望だと思います。

私も過去にいくつかポストしてきましたし、
列に現在のユーザー名を表示【2010】 | Me & SharePoint.
列に現在のユーザー名を表示【2013】 | Me & SharePoint.

CSOMでユーザー情報を取得するやり方については、いっつもお世話になっているMVP 太田さんのブログにくわしい解説があります。
ログインしているユーザーのプロファイルを JavaScript で取得する | idea.toString();.

まあ、上の記事だけでも十分なんですが、試しにREST APIを使って実装してみましたのでメモとして残しておきます。

新規フォームの「投稿者」というユーザー列にログインユーザーの表示名、「所属」という一行テキスト列に所属名を表示します。

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
    $(document).ready(function () {
        SP.SOD.executeFunc('sp.js', 'SP.ClientContext', SetCurrentUserInfo);
    });

    function SetCurrentUserInfo() {
        $.ajax({
            url: _spPageContextInfo.webAbsoluteUrl + "/_api/SP.UserProfiles.PeopleManager/GetMyProperties",
            contentType: "application/json;odata=verbose",
            headers: { "accept": "application/json;odata=verbose" },
            success: onSuccess,
            error: onError
        });
    }

    function onSuccess(data, request) {
        // アカウント名
        var loginName = data.d.AccountName;
        // 表示名
        var dispName = data.d.DisplayName;
        // 所属情報
        var arr = data.d.UserProfileProperties.results;
        var dept;
        for (var i = 0, len = arr.length; i < len; i++) {
            if (arr[i].Key == "Department") {
                dept = arr[i].Value;
            }
        }

        //PeoplePickerに設定
        var PeoplePickerId = $("div[title='投稿者']").attr('id');
        var PeoplePickerInput = $("input[title='投稿者']");
        PeoplePickerInput.val(dispName);
        var PeoplePicker = SPClientPeoplePicker.SPClientPeoplePickerDict[PeoplePickerId];
        PeoplePicker.AddUnresolvedUserFromEditor(true);

        //所属を設定
        $("input[title='所属']").val(dept);

    }

    function onError(error) {
        console.log(error);
    }
</script>

こちらのコードを、NewForm.aspxにスクリプトエディタで埋め込めばOKです!
コード的にはCSOMのほうがシンプルなように思いますが、もしRESTを使いたいシチュエーションがあったとき、参考にしていただけるとうれしいです。

SharePoint RESTサービスとKnockout.jsで遊んでみた

いぜん、この辺のポスト
SharePoint2013 & Knockout.js で遊んでみた(その3)
でKnockout.jsを使ってSharePointリストのCRUDを試みましたが、その時心残りだったのが、jQuery Library for SharePoint Web Servicesというライブラリを使っていたこと、アイテム更新時に差分だけの更新ができなかったことでした。

で、かねてよりRESTサービスを使って何とかできないかなぁ・・・と考えていたのですが、先日こちらの記事を読みまして、
SharePoint REST サービスを使用したアイテムの CRUD 方法 – Japan SharePoint Support Team Blog – Site Home – TechNet Blogs
ここに記載のサンプルコードを元にトライしてみることにしました。(SharePoint2013のオンプレ環境です)

画面イメージはこんな感じです。
20150109

事前準備として、サイト内に「testList」という名前のカスタムリストを作成し、「Body」という一行テキスト列を追加しておきます。

ではソースコードです。
以下を、ページのスクリプトエディタ内にぺちょっと貼っていただけばOKです。

<script type="text/javascript" src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js"></script>
<script langauge="JavaScript">
var weburl = "http://[your host]/[your site]";
var listTitle = "testList";
var myDigest = null;

function ItemsModel() {
  var self = this;
  self.Items = ko.observableArray([]);
  GetItems();

  function GetItems() {
      $.ajax({
      url: weburl + "/_api/Web/Lists/GetByTitle('" + listTitle + "')/Items",
      type: "GET",
      headers: {
                "accept": "application/json;odata=verbose",
                "Content-Type": "application/json;odata=verbose",
                "x-requestforceauthentication": true
              },
      success: function(data){
                if (data.d.results) {
                  self.Items(data.d.results);
                }
              },
      error: function (xhr) { alert(xhr.status + ": " + xhr.statusText) }
    });
  }

  function runWithFormDigest(fn){
    if (myDigest == null){
      $.ajax({
        url: weburl + "/_api/contextinfo",
        type: "POST",
        contentType: "application/x-www-url-encoded",
        dataType: "json",
        headers: {
                  "accept": "application/json;odata=verbose",
                },
        contentLength: 0,
        beforeSend: function (xhr) { xhr.withCredentials = true; },
        success: function (data) {
                  if (data.d) {
                    myDigest = data.d.GetContextWebInformation.FormDigestValue;
                    fn(); 
                  }
                }
      });
    } else {
      fn();
    }
  }

  self.AddRow = function() {
    self.Items.push({
      ID: "New",
      Title: "",
      Body: "",
    });
  };

  self.DelItem = function(data){
    var id = data.ID;
    if (id !== "New") {
      runWithFormDigest(function(){
        $.support.cors = true;
        $.ajax({
          url: weburl + "/_api/Web/Lists/GetByTitle('" + listTitle + "')/Items(" + id + ")",
          type: "POST",
          headers: {
                    "X-HTTP-Method":"DELETE",
                    "accept": "application/json;odata=verbose",
                    "Content-Type": "application/json;odata=verbose",
                    "x-requestforceauthentication": true,
                    "X-RequestDigest": myDigest,
                    "IF-MATCH": "*"
                  },
          success: function(xhr){ alert("completed.")},
          error: function (xhr) { alert(xhr.status + ": " + xhr.statusText) }
        });
      });
    };
    self.Items.remove(data);
  };

  self.SaveItem = function(data){
    var id = data.ID;
    var title = data.Title;
    var body = data.Body;
    if(id!= "New"){
      runWithFormDigest(function(){
        $.support.cors = true;
        $.ajax({
          url: weburl + "/_api/Web/Lists/GetByTitle('" + listTitle + "')/Items(" + id + ")",
          type: "POST",
          data: JSON.stringify({ '__metadata': { 'type': 'SP.Data.TestListListItem' }, 'Title': title, 'Body': body }),
          headers: {
                    "X-HTTP-Method":"MERGE",
                    "accept": "application/json;odata=verbose",
                    "Content-Type": "application/json;odata=verbose",
                    "x-requestforceauthentication": true,
                    "X-RequestDigest": myDigest,
                    "IF-MATCH": "*"
                  },
          success: function(xhr){ GetItems(); alert("completed.");},
          error: function (xhr) { alert(xhr.status + ": " + xhr.statusText) }
        });
      });
    } else {
      runWithFormDigest(function(){
        $.support.cors = true;
        $.ajax({
          url: weburl + "/_api/Web/Lists/GetByTitle('" + listTitle + "')/Items",
          type: "POST",
          data: JSON.stringify({ '__metadata': { 'type': 'SP.Data.TestListListItem' }, 'Title': title, 'Body': body }),
          headers: {
                    "accept": "application/json;odata=verbose",
                    "Content-Type": "application/json;odata=verbose",
                    "x-requestforceauthentication": true,
                    "X-RequestDigest": myDigest
                  },
          success: function(xhr){ GetItems(); alert("completed.")},
          error: function (xhr) { alert(xhr.status + ": " + xhr.statusText) }
        });
      });
    }
  }
}

$(document).ready(function () {
  ko.applyBindings(new ItemsModel());
});
</script> 
<div id="mainContent">
  <table>
    <thead>
      <tr>
        <th>ID</th>
        <th>Title</th>
        <th>Body</th>
        <th></th>
        <th></th>
      </tr>
    </thead>
    <tbody data-bind='foreach: Items'>
      <tr>
        <td data-bind="text:ID"></td>
        <td><input data-bind='value: Title' /></td>
        <td><input data-bind='value: Body' /></td>
        <td><input type='button' value='Save' data-bind='click: $root.SaveItem' /></td>
        <td><input type='button' value='Delete' data-bind='click: $root.DelItem'/><td>
      </tr>
    </tbody>
  </table>
  <input type="button" value="Add New" data-bind='click: AddRow' />
</div>

なお、上で引用したSharePoint Support Teamさんのブログではアイテムの追加と更新のとき、「type」に「SP.ListItem」を渡していましたが、私の環境ではうまくいきませんでした。
なんでだろう・・・と悩みつつ、こちらを読むと
REST を使用したリスト アイテムの操作

この操作を実行するには、リストの ListItemEntityTypeFullName プロパティを知っていて、それを HTTP 要求本文の type の値として渡す必要があります。

とあったので、上記ソースコードのような書き方になっています。
私のソースコードでうまくいかない場合は、Support Teamさんのブログを参照してください。

※ListItemEntityTypeFullName プロパティは、ブラウザのアドレスバーに以下を入力してEnterすると取得できます。

http://[your host]/[your site]/_api/lists/getbytitle('testList')?$select=ListItemEntityTypeFullName

RESTサービスだと値をJSON形式で取れるので、SPServicesを使っていたときよりだいぶシンプルに書けたのではないかと思います。

カスタムリストの中身をコンテンツスライダーで表示してみた

夏休みボケがまだ直らない感じなので、お遊びネタを一つ。

若干時代遅れ感ありつつも、まだまだ世の中のwebサイトでよく見かけるコンテンツスライダー。
これをSharePointのページに表示させたりしたらかっちょいいのでは・・・と、SharePoint2013で息抜きがてらに作ってみました。

BHvfSMH3t5K
コンテンツスライダーは、こちらのjQueryプラグインを利用しています。設置がとても簡単なうえ、オプションも多数あり、とても使い勝手がよいです。
jQuery Content Slider | Responsive jQuery Slider | bxSlider.

表示させる中身は以下のようなカスタムリストに登録し、RESTサービスを利用して引っ張ってきています。
リスト名:SliderContents
列:
Title(1行テキスト)
Caption(1行テキスト、画像にオーバーレイで表示させるキャプション)
contentURL(1行テキスト、画像をクリックしたら飛んでいく先)
imageURL(1行テキスト、スライダーに表示させる画像のURL)

ソースコードです。

JavaScript

$(function () {
	$("#main-slider").empty();
	$.ajax({
		url: "/[your site]/_api/web/lists/getbytitle('SliderContents')/items?$select=*", 
		method: "GET", 
		headers: { "Accept": "application/json; odata=verbose" },
		success : function(data){
			var liHtml = "<ul class='bxslider'>";
			$.each(data.d.results, function(index, item){
				liHtml += "<li><a href='" + item.contentURL + "'>";
				liHtml += "<img src='" + item.imageURL + "' alt='" + item.Title + "' title='" + item.Caption + "' />";
				liHtml += "</a></li>";
			}); //end each
			liHtml += "</ul>";
			$("#main-slider").append(liHtml);
			$(".bxslider").bxSlider({
				captions: true,
				auto: true,
				autoControls: true,
				autoHover: true,
				pager: false
			});
		},
	});
});

HTML(こちらをスクリプトエディタwebパーツでページに埋め込みます)

<!-- jQuery library (served from Google) -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<!-- bxSlider Javascript file -->
<script src="/[your site]/Lib/jquery.bxslider/jquery.bxslider.min.js"></script>
<!-- bxSlider CSS file -->
<link href="/[your site]/Lib/jquery.bxslider/jquery.bxslider.css" rel="stylesheet" />
<script type="text/javascript" src="/[your site]/Lib/slider/SliderSample.js" ></script>
<div id="main-slider">
</div>

ほとんどSharePointとは関係ない世界にはいってるような気もしますが・・・ご参考まで。

ページ最下部までスクロールすると自動で次のアイテムを読み込む何かを作ってみた

Facebookのように、ページの最後までスクロールすると次のアイテムが自動で読み込まれるみたいなサイト、最近ほんと増えましたよね。
で、SharePointでもこれできないかな?と考えていたところ、こちらの記事

jQueryでページ最下部までスクロールした際に外部HTML(JSON形式)化した要素を追加する方法 | BlackFlag

を読みまして、SharePoint2013でトライしてみることにしました。

scroll

サイト内に作成したカスタムリスト(ここではtestというリスト名で作成、列はTitleとBodyだけ)からRESTサービスでリストアイテムを呼び出し、読み込んでいます。
(ほんとは標準のリストビューで無限スクロールできればカッコよかったのですが、私の技量ではどうにも無理なのでこの形になりました・・・)

なお、下記ソースは上記BlackFlagさんの記事からほとんどいただいておりますので、ぜひ元記事もご参照ください。
※CSSは、ほぼそのまま拝借させていただいております。ありがとうございます!

[追記]
最初のソースでは、アイテムへのリンクをクリックして表示したのち前のページに戻ると、スクロール位置が最初に戻ってしまい、読み込んでいた内容が消えてしまって「ああああ・・・」という感じだったので、リンクをクリックしたらアイテムをダイアログで表示させるようにしてみました。これで、少しは実用に近づいてきたでしょうか・・・?

[さらに追記]
超・有名ブログSharePoint Maniacsの著者、シンプレッソ・コンサルティング中村様から、このようにAjaxによる非同期通信でリストアイテムを表示するのがSharePointのパフォーマンス改善に効果があり、今年のSharePoint カンファレンス(米国)でもレスポンスアップに関するセッションで紹介されていた、とのコメントをいただきました。
私がこの記事を書いたときは、パフォーマンス改善のことはまったく念頭になかったので追記させていただきます。
なお、上記SharePointカンファレンスのセッションはこちらです。
http://channel9.msdn.com/events/SharePoint-Conference/2014/SPC3993
ぜんぜんSharePointの機能じゃないじゃんよ~、というツッコミはさておき、こういうやり方もあるよ、ということで。

HTML

<link rel="stylesheet" type="text/css" href="/[your site]/[your library]/jsonSample.css" />
<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="/[your site]/[your library]/jasonSample.js"></script>
<div id="loadarea"></div>

jsonSample.js

$(function(){
	var setArea = $('#loadarea'),
	loadNum = 1, // 読み込む個数
	loadTxt = 'Now Loading...', // Loading中の表示テキスト
	fadeSpeed = 500; // フェードスピード

	var onclickStr;
	var txt;
	$.ajax({
		url: "/[your site]/_api/web/lists/getbytitle('test')/items?$select=*", 
		method: "GET", 
		headers: { "Accept": "application/json; odata=verbose" },
		success : function(data){
			for (var i=0; i<5; i++) {
				onclickStr = "openInDialog('/[your site]/Lists/test/DispForm.aspx?ID=" + data.d.results[i].ID + "',500,500,true,true,false); return:false;";
				txt = data.d.results[i].Body;
				if(txt.length > 200){txt = txt.substr(0, 200) + "...";}
				$('<div id="' + data.d.results[i].Title + '" class="loadItem"><h3>'
					+ data.d.results[i].Title + '</h3><br>'
					+ txt + ' <a href="javascript:void(0)" onclick=' 
					+ onclickStr + '>' + '[read more]'+ '</a></div>').appendTo(setArea);
			}
		}
	});

	$("#s4-workspace").scroll(function(){
		var winHeight = $("#s4-workspace").height(); 
		var posBottom = $("#s4-bodyContainer").height() - winHeight;
		if($("#s4-workspace").scrollTop() >= posBottom) {
			var adjScrTop = $("#s4-workspace").scrollTop();
			$("#s4-bodyContainer").animate({scrollTop:(adjScrTop)-1},0);
			$.ajax({
				url: "/[your site]/_api/web/lists/getbytitle('test')/items?$select=*", 
				method: "GET", 
				headers: { "Accept": "application/json; odata=verbose" },
				success : function(data){
					var dataLengh = data.d.results.length,
					loadItemLength = setArea.find('.loadItem').length,
					setAdj = (dataLengh)-(loadItemLength),
					setBeg = (dataLengh)-(setAdj);
					if(!(dataLengh == loadItemLength)){
						setArea.append('<div id="nowLoading">' + loadTxt + '</div>');
						if(loadItemLength == 0){
							for (var i=0; i<loadNum; i++) {
								onclickStr = "openInDialog('/[your site]/Lists/test/DispForm.aspx?ID=" + data.d.results[i].ID + ",500,500,true,true,false'); return:false;";
								txt = data.d.results[i].Body;
								if(txt.length > 200){txt = txt.substr(0, 200) + "...";}
								$('<div id="' + data.d.results[i].Title + '" class="loadItem"><h3>'
									+ data.d.results[i].Title + '</h3><br>'
									+ txt + ' <a href="javascript:void(0)" onclick=' 
									+ onclickStr + '>' + '[read more]'+ '</a></div>').appendTo(setArea)
								.css({opacity:'0'}).animate({opacity:'1'},fadeSpeed);
							}
						} else if(loadItemLength > 0 && loadItemLength < dataLengh){
							if(loadNum < setAdj){
								for (var i=0; i<loadNum; i++) {
									v = i+setBeg;
									onclickStr = "openInDialog('/[your site]/Lists/test/DispForm.aspx?ID=" + data.d.results[v].ID + "',500,500,true,true,false); return:false;";
									txt = data.d.results[v].Body;
									if(txt.length > 200){txt = txt.substr(0, 200) + "...";}
									$('<div id="' + data.d.results[v].Title + '" class="loadItem"><h3>'
										+ data.d.results[v].Title + '</h3><br>'
										+ txt + ' <a href="javascript:void(0)" onclick=' 
										+ onclickStr + '>' + '[read more]'+ '</a></div>').appendTo(setArea)
									.css({opacity:'0'}).animate({opacity:'1'},fadeSpeed);
								}
							} else if(loadNum >= setAdj){
								for (var i=0; i<setAdj; i++) {
									v = i+setBeg;
									onclickStr = "openInDialog('/[your site]/Lists/test/DispForm.aspx?ID=" + data.d.results[v].ID + "'500,500,true,true,false); return:false;";
									txt = data.d.results[v].Body;
									if(txt.length > 200){txt = txt.substr(0, 200) + "...";}
									$('<div id="' + data.d.results[v].Title + '" class="loadItem"><h3>'
										+ data.d.results[v].Title + '</h3><br>'
										+ txt + ' <a href="javascript:void(0)" onclick=' 
										+ onclickStr + '>' + '[read more]'+ '</a></div>').appendTo(setArea)
									.css({opacity:'0'}).animate({opacity:'1'},fadeSpeed);
								}
							}
						} else if(loadItemLength == dataLengh){
							return false;
						}
					} else {
						return false;
					}
				},
				complete : function(){
					$('#nowLoading').each(function(){
						$(this).remove();
					});
					return false;
				}
			});
			return false;
		}
	});

    //add button go back to page top
    var topBtn = $('#page-top');
    topBtn.hide();
    $('#s4-workspace').scroll(function () {
        if ($(this).scrollTop() > 400) {
            topBtn.fadeIn();
        } else {
            topBtn.fadeOut();
        }
    });
    topBtn.click(function () {
        $('#s4-workspace').animate({
            scrollTop: 0
        }, 500, "swing");
        return false;
    });
});

//Open item in dialog
function openInDialog(pageUrl, dlgWidth, dlgHeight, dlgAllowMaximize,dlgShowClose,needCallbackFunction){
	var options = {
		url: pageUrl,
		width: dlgWidth,
		height: dlgHeight,
		allowMaximize: dlgAllowMaximize,
		showClose: dlgShowClose
	};
	if(needCallbackFunction){
		options.dialogReturnValueCallback = Function.createDelegate(null, CloseDialogCallback);
	}
	SP.SOD.execute('sp.ui.dialog.js', 'SP.UI.ModalDialog.showModalDialog', options);
}

function CloseDialogCallback(dialogResult, returnValue){
	if(dialogResult == SP.UI.DialogResult.OK){
		//do something
		;
	}else if(dialogResult == SP.UI.DialogResult.cancel){
		;
	}else{
		;
	}
}

jsonSample.css

/* #loadarea
--------------------------- */
#loadarea {
	margin: 0 auto;
	width: 500px;
	text-align: left;
}
#loadarea .loadItem {
	padding: 10px 0;
	width: 500px;
	line-height: 160%;
	border-bottom: #666 1px dotted;
}
/* #nowLoading
--------------------------- */
#nowLoading {
	padding: 5px 0;
	width: 100%;
	text-align: center;
	color: #fff;
}

/* =======================================
	ClearFixElements
======================================= */
#loadarea .loadItem:after {
	content: ".";
	height: 0;
	clear: both;
	display: block;
	visibility: hidden;
}

#loadarea .loadItem {
	display: inline-block;
	overflow: hidden;
}

/* =======================================
	Page Top Button
======================================= */
#page-top
{
    position: fixed;
    bottom: 20px;
    right: 50px;
}

#page-top a
{
    background: #666;
    text-decoration: none;
    color: #fff;
    width: 50px;
    padding: 8px 0;
    text-align: center;
    display: block;
    border-radius: 3px;
    -webkit-border-radius: 3px;
    -moz-border-radius: 3px;
}

#page-top a:hover
{
    text-decoration: none;
    background: #999;
}

Gistにも置きました。
JS
https://gist.github.com/marineko/7a1061a633f5e21560ee
CSS
https://gist.github.com/marineko/8c4dcfddf7fa45b59956

手前味噌ですが、こちらの記事で実装した「TOPへ戻る」ボタンも併用しています。ページが長くなってもにゅるっと最初に戻れていい感じです。
スクロールするとトップへ戻るボタンを表示してみた

そのほか、プログラムを書くにあたり参考にさせていただいた記事です。

☆RESTサービスの使い方

SharePoint 2013 で REST サービスを使用したプログラミング方法 – Japan SharePoint Support Team Blog – Site Home – TechNet Blogs.

SharePoint 2013 – CRUD on List Items Using REST Services & jQuery | Plus Consulting Blog.

☆アイテムをダイアログで開く

SharePoint 2010 ダイアログで開く input タグ | クリエ・イルミネート ブログ.