ログインユーザーの権限をJavaScriptでチェックする

ひさびさの更新になってしまいましたがちゃんと生きてますよ!!
というわけで、生きている証を立てたいと思います。

ログインユーザの権限に応じて、サイトのUIの一部を非表示にしたいといった要望は多いですよね。
そこで、今回はClient Object Modelを使ってユーザー権限をチェックするコードをJavaScriptで実装してみたいと思います。

Client Object Modelでユーザー権限を取得するには、SP.Web.effectiveBasePermissions propertyを使います。
これで、ユーザーがサイトに対して持っている権限の一覧が取れるので、そこからユーザー権限を判断します。
たとえば、「Webサイトの管理」権限を持っていればフルコントロール権限があるだろう、とか。

では、ログインユーザーが「Web サイトの管理」権限を持っているかチェックするサンプルコードを書いてみます。jQueryも使ってます。

$(function(){
    ExecuteOrDelayUntilScriptLoaded(CheckPermissionOnWeb, "sp.js");
});

function CheckPermissionOnWeb() {
    var ctx = new SP.ClientContext.get_current();
    var web = ctx.get_web();

    var currentUser = web.get_currentUser();
    ctx.load(currentUser);
    ctx.load(web,'EffectiveBasePermissions');
    ctx.executeQueryAsync(Function.createDelegate(this, onSuccessMethod), Function.createDelegate(this, onFailureMethod));        

    function onSuccessMethod(sender, args) {
        if (web.get_effectiveBasePermissions().has(SP.PermissionKind.manageWeb)) {
            console.log('You are Site Admin.');
        }
    }

    function onFailureMethod(sender, args) {
        console.log('Error:' + args.get_message());
    }
}

上記で「SP.PermissionKind.manageWeb」の「manageWeb」のところを変えれば、別の権限を持っているかもチェックできます。
たとえば、「manageLists」ならリストの管理権限があることになります。
具体的には、以下をご参考に。
https://msdn.microsoft.com/en-us/library/ee556747.aspx

広告

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を使っていたときよりだいぶシンプルに書けたのではないかと思います。

SharePointのwikiページにページビューカウンタをつけてみた

気が付いたら、今年も残すところあと数日・・・早いものですね。
私らしくどうでもいいネタで今年を締めくくりたいと思います。

もうすっかり前世紀の遺物となってしまった感のあるアクセスカウンターですが、サイトのトップページやお知らせページなんかの場合てっとりばやくどのくらいアクセスがあるのか知りたいって時もあるんじゃないかと思います。

実装するやり方はいろいろあるかと思いますが、ここではアクセスカウントを格納しておくカスタムリストを一つつくっておいて、ページロードの時にJavaScriptでカウンタをインクリメントするようにしてみました。
試した環境はSharePoint2010ですが、2013でもたぶんいけると思います。

下準備:
①サイト内にカスタムリストを作成し、countという一行テキスト列を追加しておきます。
今回は「test」という名前でカスタムリストを作成しました。
②作成したリストに新規アイテムを一つ作成し、countには「0」を入れておきます。

以上で準備は終わりです。
次に、カウンタをつけたいページにコンテンツエディタwebパーツを追加し、そのHTMLソースに以下のコードを記述します。

<div id="pageView"></div>
<script src="http://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery.SPServices/2014.01/jquery.SPServices.min.js" type="text/javascript"></script>
<script type="text/javascript">
(function (){

    var itemId;
    var count;

    $().SPServices({
        operation: "GetListItems",
        async: false,
        listName: 'test',
        completefunc: function (xData, Status) {
            $(xData.responseXML).SPFilterNode("z:row").each(function () {
                itemId= $(this).attr("ows_ID");
                count = Number($(this).attr("ows_count"));
            });
        }
    });

    count++;
    count = String(count);

    $().SPServices({
        operation: "UpdateListItems",
        async: false,
        batchCmd: "Update",
        listName: "test",
        valuepairs: [["count", count]],
        ID: itemId,
        completefunc: function(xData, Status) {
          //
        }
    });

    $('#pageView').empty();
    $('#pageView').append('<p>' + count + ' views</p>' );

})();
</script>

以上でOKです!
こんな感じで控え目にページビューが表示されます。
20141226

ちなみに、上記サンプルコードは、多数アクセスがあったときの排他制御とか、複数ビューカウンタをつけたい場合とか、自分のアクセスや編集モードにしたときのページロードを排除するとかは一切考慮しておりません。
何かのヒントになればうれしいです。

それではみなさま、よいお年を!

SharePoint2010にコンテンツエリア拡大ボタンをつけてみた

SharePointのヘッダー部分や、サイドリンクバーって意外と場所をとりますよね。
なんで、コンテンツがもりだくさんのページの場合、もっと大きな画面で見せたい時があると思います。

そういうニーズを受けてかなのか、SharePoint2013なら、コンテンツエリアを拡大して見せるボタン(*)が標準であるのですが、残念ながら2010にはありません。

* コレ↓です
20141128

というわけで、自前で作ってみました。
動作イメージはこんな感じです。画面右下、「全画面」ボタンを押すとコンテンツエリアがぐぐっとひろがります。
20141128

ソースコードです。これをコンテンツエディタwebパーツでページにぺたっと貼ればOKです。

<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.1.min.js"></script>
<style type="text/css">
  .screen-change
  {
      position: fixed;
      bottom: 20px;
      right: 80px;
  }

  .screen-change a
  {
      background: #666;
      text-decoration: none;
      color: #fff;
      width: 120px;
      padding: 8px 0;
      text-align: center;
      display: block;
      border-radius: 3px;
      -webkit-border-radius: 3px;
      -moz-border-radius: 3px;
      opacity: 0.8;
      filter: alpha(opacity=80); /* IE lt 8 */
      -ms-filter: "alpha(opacity=80)"; /* IE 8 */
  }

  .screen-change a:hover
  {
      text-decoration: none;
      background: #222;
  }
</style>
<script>
  $(function(){
    $(document.body).append('<p id="only-content-area" class="screen-change"><a href="#">全画面表示</a></p><p id="default-screen" class="screen-change"><a href="#">元に戻す</a></p>');
    $("#default-screen").hide();
    $(".screen-change").click(function() {
      $('#s4-titlerow, #s4-leftpanel, #RibbonContainer-TabRowLeft, .ms-cui-tts').toggle('slide', '', 500);
      var hide = $('#default-screen').css('display');
      if(hide == 'none'){
        $('.s4-ca').css('margin-left', '0px');
      } else {
        $('.s4-ca').css('margin-left', '155px');
      };
      $("#only-content-area, #default-screen" ).toggle();
    });
  });
</script>

リボンメニューが表示されている状態で拡大するとあやしい動きになってしまうので、リボンメニューを表示させられるツールバーのボタンやタブも軒並み隠してますが、それならいっそツールバー自体を隠しちゃってもよかったかも。
そのへんはお好みで調整してください。

ふと、思ったのですが、これ応用したら印刷用画面としても使えるかも・・・?

ドキュメントライブラリのファイルがPDFだったら別窓で開く

ひさびさの更新になってしまいました。気が付けば、今年も残すところあと2か月、巷には早くもクリスマスのイルミが・・・という季節になってしまいました。早すぎるだろ。

さて、SharePoint2010のお話です。

ドキュメントライブラリでPDFを入れている場合、該当のファイル名をクリックするとブラウザの同じウィンドウ内で開いてしまいます。
(ブラウザでPDFを開く設定にしていれば、ですが。)
その時、うっかりタブを閉じたりすると元のライブラリに戻れなくなっちゃったりします。
地味に不便ですよね。

で、PDFファイルの場合だけ選択的に別窓で開かせるスクリプトを書いてみました。jQuery使ってます。

// これだとグルーピングしたビューではダメ
$('a[href$=".pdf"]').each(function(){
	$(this).removeAttr('onclick');
});
//無理やりだけど・・・
$(document).on('mouseover', 'a[href$=".pdf"]',
	function(eo) {
		$(eo.target).removeAttr('onclick');
	}
);
//クリックイベントを設定
$(document).on('click', 'a[href$=".pdf"]',
	function(eo) {
		window.open(eo.target.href, '_blank');
		return false;
   	}
);

ドキュメントライブラリのリストビューだと、クリック時 onclick属性に設定されてるファンクションが先に動いてしまうので、まずそいつを削除する、しかもmouseoverイベントで・・・という荒ワザをやっております。
こんなことしていいかよくわかりませんけど、よくない気もしますけど、もっといいやり方があったら教えていただけるとうれしいです。

また、グルーピングしたビューの場合アイテムが動的に追加されるため hoge.click(function… みたいな書き方は通用しません。
で、こちらなど参考にしつつ、
外部リンクを別窓で開くJavaScriptを改善した – rakugaki-box.net.
上記コードに落ち着きました。

リンクリストで、外部リンクは別窓で開かせたい!なんて場合にも応用できそうですね。

SharePointのページ内リンクでスムーススクロール

またしてもSharePointの機能とはさして関係のないネタです。当ブログ、こんなんばっかりですがお許しください・・・

以前、こんな記事を書きましたが、
SharePointでスクロールするとトップへ戻るボタンを表示してみた
こちらの姉妹編のエントリです。

最近のwebサイトでは、ページ内リンクへのジャンプはアニメーションでスルスル~っとスクロールしてくのがもう定番ですよね。
これをSharePointのページにも実装してみました。
smoothScroll

スムーススクロールは巷にプラグインなどあふれてますが、余計なものを入れたくなかったのでCSS-Tricksにあったこちらを元ネタにしました。
Smooth Scrolling | CSS-Tricks
jQueryさえあれば動かせます。

ソースコードです。SharePoint2010、2013で動作確認しています。
JavaScript

$(function() {
  $('a[href*=#]:not([href=#])').click(function() {
    var def = $('div[id*=titlerow]').offset().top;
    if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
      var target = $(this.hash);
      target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
      if (target.length) {
        $('#s4-workspace').animate({
        	scrollTop: target.offset().top - def
        }, 500);
        return false;
      }
    }
  });
});

HTMLサンプル

<h3>​​​​​​<a href="#section1">Section1</a></h3>
<h3><a href="#section2">Section2​</a></h3>

<h1 id="section1">Section1</h1>
<div>Vivamus suscipit tortor eget felis porttitor volutpat. Pellentesque in ipsum id orci porta dapibus.
 Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. 
Quisque velit nisi, pretium ut lacinia in, elementum id enim. Pellentesque in ipsum id orci porta dapibus. 
Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. 
Pellentesque in ipsum id orci porta dapibus. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. 
Cras ultricies ligula sed magna dictum porta. Proin eget tortor risus.</div>

<h1 id="section2">Section2</h1>
<div>Vivamus suscipit tortor eget felis porttitor volutpat. 
Pellentesque in ipsum id orci porta dapibus. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. 
Quisque velit nisi, pretium ut lacinia in, elementum id enim. 
Pellentesque in ipsum id orci porta dapibus. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. 
Pellentesque in ipsum id orci porta dapibus. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. 
Cras ultricies ligula sed magna dictum porta. Proin eget tortor risus.</div>

SharePointで実装するときの注意点ですが、windowオブジェクトではスクロール位置がとれないので、idが”#s4-workspace”のdiv要素を動かします。
また、リンク元の要素がページの半ばにあることも考慮して、サイトのタイトルエリアがwindowのトップからどれだけ上にふっ飛んでるかでスクロール幅を調整しています。
HTML側は、サンプルのように基本飛ばしたい先の要素にハッシュと同じID名を振ればよいのですが、スクリプト的にはアンカーにも対応してます。

余談ですが、SharePointでページにアンカーをつけたい時は、リンクツールでBookmark欄に任意のアンカー名をいれればつけられます。
20141003

なので、上記コードをパッケージ化(コンテンツエディタwebパーツに仕込んでExportするなど)すれば、HTMLを書けないユーザでもスムーススクロールできるようになりそうですね!

グルーピングの見出しに色をつけて簡易ダッシュボード的なものをつくってみた

SharePoint2010で、進捗管理にカスタムリストを利用したときのお話です。

サイトのトップページでパッと見て進捗がわかるようにできるといいよね~、というので作ってみたものです。
20140827
リストwebパーツをステータスでグループ化したビューで表示し、グループ化の見出しにJavaScriptで色をつけています。

ソースコードです。

JavaScript : Indicator.js

$(document).ready(function () {
    $("td.ms-gb:contains('Not Started')").css({ "color": "#ffff00", "background-color": "red" });
    $("td.ms-gb:contains('In Progress')").css({ "color": "#0000ff", "background-color": "yellow" });
    $("td.ms-gb:contains('Completed')").css({ "color": "#ffffff", "background-color": "green" });
});

HTML(※こちらをtxtファイルでライブラリに格納し、リストwebパーツを配置したのと同じページにコンテンツエディタwebパーツでリンクします)

<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="/[your site]/Lib/Indicator.js"></script>

こういったwebパーツをいくつかトップページに配置しておけば、簡易ダッシュボード的なものが作れるのではないかと思います。