SharePoint Online のモダン ページを利用すると、ユーザーが自由にページのレイアウトを作成したり必要な Web パーツを配置したりといったことが簡単にできます。

しかし一方で、何かしら “ちょっと” カスタマイズしたいなと思ったときには SharePoint Framework を利用して Web パーツの作成が必要だったりなど、その “ちょっと” が難しくなってもいます。

従来であればコンテンツ エディタ Web パーツやスクリプト エディタ Web パーツを利用して JavaScript などで “ちょっと” のカスタマイズができたのですが、残念ながらこうした機能はモダン ページでは提供されていません。

というわけで、これはどうにかならないものかなーと思い、調べたり試行錯誤したりと模索しています。

1. SharePoint Framework

とは言え大本命はやはり SharePoint Framework の利用です。こちらを利用して行う方法が、ユーザーにとっては最も再利用性が高く、また、カスタマイズできる範囲も広くなります。

SharePoint Framework の概要
https://docs.microsoft.com/ja-jp/sharepoint/dev/spfx/sharepoint-framework-overview

というわけで、皆さん SharePoint Framework を利用しましょう!という結論ではつまらないので、もう少し調べてみます。

2. スクリプト エディタ Web パーツ

実は GitHub で公開されている SharePoint Framework のサンプルにスクリプト エディタ Web パーツがあり、ソースコードが公開されています。

Script editor web part for modern pages built in React
https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-script-editor

こちらのソースコードを基に SharePoint Framework のスクリプト エディタ Web パーツをパッケージ化し環境に展開することで、モダン ページ上でもスクリプト エディタ Web パーツが利用可能になります。

SharePoint Online のモダン ページで JavaScript によるカスタマイズを動作させたいといった場合、こちらの Web パーツを試す価値は十分にあります。

3. 標準機能のみで行う方法

それも難しいといった場合、標準機能の組合せで何かできないかを試行錯誤してみました。今回ご紹介する方法は、自身で作成した JavaScript を埋め込んだページをドキュメント ライブラリに保存しておき、それを標準の「埋め込み」Web パーツでページ上に表示する方法です。今回の投稿ではこの方法をメインで紹介します。

サイト コレクションの設定変更

まず、この方法で JavaScript を埋め込んだページを SharePoint Online 上で動作させるためには、サイト コレクションの設定を変えておく必要があります。SharePoint Online ではユーザーが好き勝手にカスタマイズができないようにいくつかのカスタマイズ方法を制限しているためです。

設定変更を行うためには、SharePoint Online Management Shell でテナントに接続し、対象のサイト コレクションの DenyAddAndCustomizePages プロパティを変更します。

コマンド例です。

Set-SPOSite https://contoso.sharepoint.com/sites/sitename -DenyAddAndCustomizePages $false

これを実行することで、サイト コレクションで制限されているいくつかのカスタマイズの機能が開放されます。

JavaScript を埋め込んだページを作成する

つぎに、埋め込み Web パーツで表示したいページをテキスト エディタで作成します。今回は下記のようなページを作成してみました。(このサンプルは Google Chrome でしか動作を確認していないのでご注意を)

<html dir="ltr" lang="ja-JP">
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <title>サンプル</title>
    <script type="text/javascript" src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-3.3.1.min.js"></script>
    <link rel="stylesheet" href="//static2.sharepointonline.com/files/fabric/office-ui-fabric-js/1.4.0/css/fabric.min.css" />
    <link rel="stylesheet" href="//static2.sharepointonline.com/files/fabric/office-ui-fabric-js/1.4.0/css/fabric.components.min.css" />
    <script type="text/javascript" src="//static2.sharepointonline.com/files/fabric/office-ui-fabric-js/1.4.0/js/fabric.min.js"></script>
    <script type="text/javascript">
      var config = {
        "sitePath":"/sites/sitename"
      }
      function getLists(){
        $.ajax({
          type:"GET",
          url:config["sitePath"]+"/_api/web/lists",
          dataType:"json"
        }).done(function(data,status,xhr){
          for(var index in data.value){
            $("#lists")
              .append(`<li class="ms-ListItem is-unread" tabindex="0">
                    <span class="ms-ListItem-primaryText">
                      ${data.value[index].Title}
                    </span>
                    <span class="ms-ListItem-secondaryText">
                      ${data.value[index].LastItemModifiedDate}
                    </span>
                    <span class="ms-ListItem-tertiaryText">
                      ${data.value[index].Description}
                    </span>
                  </li>`);
          }
        });
      }

      function getFormDigest(){
        $.ajax({
          type:"POST",
          url:config["sitePath"]+"/_api/contextinfo",
          dataType:"json",
          data:""
        }).done(function(data,status,xhr){
          $("#message").append(`<div class="ms-MessageBar ms-MessageBar--success">
            <div class="ms-MessageBar-content">
              <div class="ms-MessageBar-icon">
                <i class="ms-Icon ms-Icon--Info"></i>
              </div>
            <div class="ms-MessageBar-text">
              ${data.FormDigestValue}
            </div>
          </div>`);
        });
      }
    </script>
  </head>
  <body>
    <button onclick="getLists()" class="ms-Button ms-Button--primary">
      <span class="ms-Button-label">Get All Lists</span>
    </button>
    <button onclick="getFormDigest()" class="ms-Button">
      <span class="ms-Button-label">Get Form Digest</span>
    </button>
    <div id="message">
    </div>
    <ul id="lists" class="ms-List">
    </ul>
  </body>
</html>

ポイントのひとつ目は、日本語を利用する場合に文字化けが起こらないように 3 行目の meta タグを指定しておくことと、ファイル保存時のエンコードは UTF-8 としておくことです。

<meta http-equiv="Content-type" content="text/html; charset=utf-8" />

ふたつ目は、この方法では SharePoint Online の JavaScript Object Model(JSOM)は利用できないため、SharePoint Online のデータを利用する場合には REST API を利用することです。

そして最後が、ページ上への表示に iframe が利用されるため、CSS も自分で作成しておく必要があることです。今回の例では、Office UI Fabric JS を利用しています。

Office UI Fabric JS
https://developer.microsoft.com/en-us/fabric-js

この Office UI Fabric を基に自分で CSS のカスタマイズを行っても良いかもしれませんね。

こうして作成したページを拡張子「.aspx」として保存して、任意のドキュメント ライブラリにアップロードしておきます。

埋め込み Web パーツで表示する

これを SharePoint Online モダン ページの埋め込み Web パーツで呼び出すわけですが、SharePoint Online のドキュメント ライブラリに保存されているページを呼び出すには、単にページを直接参照できる URL を指定するだけです。

これで、ページ上に先ほど作成したページが埋め込み Web パーツによって iframe のコンテンツとして表示されます。表示される高さなどを指定したい場合には、埋め込み Web パーツの設定に iframe タグを記述することもできます。

動作確認

埋め込まれたページでは、作成した通りに表示され、また、SharePoint Online の REST API を利用して SharePoint Online のデータを表示できていることが確認できると思います。

また、この状態ではじめに設定変更した DenyAddAndCustomizePages プロパティを元に戻しても、アップロード済みのページに関してはそのまま動作するようですが、プロパティを元に戻した後にアップロードしたページはエラーになり動作しません。そのため、必要に応じて DenyAddAndCustomizePages プロパティの設定を元に戻しておいても良いかもしれませんね。

さいごに

というわけで、SharePoint Online のモダン ページにカスタマイズを埋め込む方法をいくつか調べてみました。モダン ページを利用することでユーザーが自由にページのレイアウトを作成したり必要な Web パーツを配置したりといったことが簡単にできます。一方で、何かしらの “ちょっと” のカスタマイズが難しくなっています。クラシック ページでは、その “ちょっと” をコンテンツ エディタ Web パーツやスクリプト エディタ Web パーツを利用して JavaScript などを駆使して工夫されていた方も多くいるかと思います。

真っ当な方法としては SharePoint Framework を使ってカスタマイズすることになるのですが、今回紹介した方法も利用できる場合があるかもしれませんね。