SharePointの入力フォームで列名の下にDescriptionをもってくる

半年ぶりの更新です(ひゃーーー)。
てゆーか最近SharePoint2007に携わっているので、ネタないんです・・・
はやく2016さわりたい~!!!

というわけで小ネタをちょっとだけ。

SharePointの列にはDescriptionを設定できます。入力例や注意事項を記載したいときに便利です。
pic20160927-1

でも、デフォルトのDescriptionの場合、入力欄の下に配置されるので見落とされがちなんですよね。
また、URLも貼れたりしますが、表示文言を自由に設定してハイパーリンク化はできません。

そこで、JavaScriptでDescriptionに書きたい内容を列名の下に持ってきてみました。
pic20160927-2

ソースコードです。
こちらをコンテンツエディタwebパーツでNewForm.aspxとEditForm.aspxに埋め込んでください。

<script src="https://code.jquery.com/jquery-1.12.4.min.js&quot; integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script>
<script>
$(document).ready( function () {
$('h3:contains("Body")').parent().append('<p><a href="https://servername/sitename/Doclib/manual.pdf&quot; target="_blank" style="font-weight:normal; text-decoration: underline; color:blue">マニュアルへのリンク</a></p>');
});
</script>

コード内の”Body”のところはDescriptionを置きたい列名に適宜変更してくださいね。
ちょっとしたことですが、ユーザビリティーの向上につながる・・・かも?

SharePointのリストフォームに採番機能をJavaScriptで実装する

この世にはようかんぱんなるものがあることを知り気になって仕方ないのですが、そんなことはどうでもいいですね。

さて、だいぶ間が空いてしまいましたが久々の更新です!
いぜん、ページにビューカウンタをつける方法をポストしましたが、意外とよく読まれているようなので、気を良くして応用編(?)です。

SharePointのリストアイテムに、システム的に振られるIDではなく、独自ルールで採番したカスタムIDを振りたい!という要望はよくありますよね。
そこで、ページビューカウンタと同じやり方で、採番機能を実現してみました。
新規フォームで見るとこんな感じ。
20151217

実装方法です。
事前に「counter」という名前の採番用カスタムリストを同じサイト内に作成しておきます。
採番用カウンタは、Titleフィールドをそのまま使います。
新規フォーム作成時に、採番用リストから番号を取得し採番フィールドに表示、保存時のアクションでインクリメントした番号を採番用リストに返す仕組みです。
なので、新規フォームをキャンセルした場合は、インクリメントされません。

以下、ソースコードです。
こちらをNewForm.aspxにコンテンツエディタwebパーツで埋め込んでください。

<script src="//code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery.SPServices/2014.01/jquery.SPServices.min.js" type="text/javascript"></script>
<script type="text/javascript">
var count;
var itemId;
$(document).ready( function () {
var numStr;
$().SPServices({
operation: "GetListItems",
async: false,
listName: "counter",
completefunc: function (xData, Status) {
$(xData.responseXML).SPFilterNode("z:row").each( function () {
itemId= $(this).attr("ows_ID");
count = Number($(this).attr("ows_Title"));
});
}
});
count++;
numStr = "CustomID - " + ("0000000" + count).slice(-8);
$("input[title='CustomID']").val(numStr).attr("readonly", "readonly").css("color", "#6a6a6a");
$(":text:not([readonly])").eq(0).focus();
});
function PreSaveAction(){
$().SPServices({
operation: "UpdateListItems",
async: false,
batchCmd: "Update",
listName: "counter",
valuepairs: [["Title", count]],
ID: itemId,
completefunc: function(xData, Status) {
}
});
return true;
}
</script>

SPServicesjQuery使ってます。

さらにちょっとした小技として、

21行目:カスタムIDの数値部分を桁固定0埋めで表示
23行目:採番フィールドを読み取り専用にする
24行目:採番フィールドがフォームの最初のフィールドでもフォーカスを当てない

などを盛り込んでおります。

なお、EditForm.aspxには以下を埋め込んでおきます。

<script src="//code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
$("input[title='CustomID']").attr("readonly", "readonly").css("color","6a6a6a");
$(":text:not([readonly])").eq(0).focus();
});
</script>

検証環境は2010ですが、2007、2013でも動くと思います。
参考にしていただけるとうれしいです!

SharePointサイトをレスポンシブ・デザインにするときの情報源

こんにちは。世間は夏休み真っ盛りですが、私は絶賛仕事中です!!
電車が空いててうれしいぞ!!!

さて、ここのところSharePointサイトをモバイルでも美しく表示できるようにするべく、いろいろと実験していたのですがそのとき参考にした情報を備忘録として載せておきます。

基本的な前提と方針は、
・バージョンはSharePoint2013
・SharePoint Onlineへのマイグレーションも見据え、カスタムマスタページは作らない
(参考:Latest Advice on Office 365 Branding | Microsoft Trends.)
・よって、デバイスチャネルも使わず、レスポンシブデザインで設計
・とはいえ、JavaScriptは使いたいのでマスタページでのJavaScriptの参照は(しぶしぶ)許容する
といったところです。

デフォルトマスタページを使い、CSSだけで何とかするやり方はもうほぼこれでOKではないかと。
Making seattle.master Responsive | Our SharePoint Experience.

レスポンシブなCSSグリッド・システムを導入するやり方。まだ試してないけど、そのうちやってみたいと思います。
Apply grid system to SharePoint using SUSY | Stefan Bauer – n8d

マスタページをいじくらずにviewportを設定する裏ワザ。
Add viewport meta without editing the master page | Stefan Bauer – n8d.

グローバルナビゲーションをモバイルではアコーディオンメニューにしたい!
巷にあふれるプラグインをしつこくいろいろ試した結果これがよさそうです。
レスポンシブメニューを作成できるjQueryプラグイン「MeanMenu」 | たすデザイン.
具体的な導入方法は、いずれ別のエントリで書きます。

JavaScript内でユーザーエージェントを判別したいとき。
2015年版JavaScriptユーザエージェント判別・判定.

viewportの設定になやんだら?
もう逃げない。HTMLのviewportをちゃんと理解する – Qiita.

マスタページをいじくらずともサイト全体にカスタムJavaScriptを適用するにはこんなやり方もあるです。
JavaScript を使用して SharePoint サイト UI をカスタマイズする.
MSの推奨はたぶんこれ。私はまだ試してないです。アドイン作れとか言われると、とたんにハードルが・・・(汗)

新たにいい情報が出てきたら、今後も追加していく予定です。
また、こんなのも参考になるよ!なんてのがあったらTwitterでもなんでもお知らせください。

SharePointのページに下部固定のフッターをつけてみた

SharePointのページにフッターをつけたい場合、通常はマスタページをカスタマイズすることになると思います。
しかし、マスタページをいじれないエンドユーザー・デベロッパーとか、SharePoint Onlineでカスタムマスタページを作りたくない!という場合はちょっと工夫が必要となります。

以下で紹介するのは、JavaScriptでフッター要素をページに追加し、CSSでスタイリングする方法です。
ページのコンテンツが少ない場合はウィンドウの最下部、それ以外はページ最下部に固定で表示されます。
検証した環境はSharePoint2013オンプレミスのチームサイト、ブラウザがIE11、マスタページはデフォルトのseattle.masterです。

できあがりはこんな感じ。
20150624

ソースコードです。

<style>
#s4-bodyContainer {
    position:relative;
    min-height: 100%;
    padding-bottom: 30px;
    box-sizing: border-box;
}
/* footer */
.footer-container{
    position:absolute;
    bottom: 0;
    left: 0;
    text-align: center;
    width: 100%;
    padding: 5px;
    background-color:#29629C;
    color: #fff;
    box-sizing: border-box;
    overflow: hidden;
}
</style>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
    var footerHtml = "<div class='footer-container ms-dialogHidden'>";
    footerHtml += "<footer>© Marineko. All rights reserved.</footer></div>";
    $("#s4-bodyContainer").append(footerHtml);
});
</script>

マスタページはさわりたくないんだよね~、というときに、参考にしてみていただければと思います。

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を使いたいシチュエーションがあったとき、参考にしていただけるとうれしいです。

サイドリンクバーをアコーディオンメニューにする

前回に引き続き、SharePoint2013のサイドリンクバーのプチカスタマイズです。

サイドリンクバーもエントリ数や第2階層のリンクがふえてくると、画面から見切れてしまうことがあります。
そんなとき、第1階層でアコーディオンで開閉可能にできるといいですよね。

で、jQueryを使ってサイドリンクバーをアコーディオンにする以下のポストを見つけ、
Simple Expand/Collapse for the Quick Launch in SharePoint 2013 or SharePoint Online | Marc D Anderson’s Blog.

これでOK!と思ってたんですが、このコードだとサイドリンクバーのエントリをクリックして他ページに遷移すると、開閉状態がチャラになって全部閉じてしまうという・・・
ちょっと使い勝手がよくないです。

そこで、クッキーで開閉状態を覚えさせるようにしてみました。
クッキーの操作には、jquery.cookie.jsというプラグインを使用しています。

※jquery.cookie.jsの使い方などはこちらをご参考に・・・
jquery.cookie.jsの使い方と簡単なサンプルを紹介します|Webpark.

ソースコードです。

<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
<style>
.ms-core-listMenu-verticalBox li > span.menu-item {
    cursor: pointer;
}
</style>
<script>
$(document).ready(function(){
    var topLevelLinks = $("div[id$='QuickLaunchMenu'] > ul > li:has('ul') > span, div[id$='QuickLaunchMenu'] > ul > li:has('ul') > a");
    topLevelLinks.prepend("<span class='accordion'><i class='fa fa-caret-right'></i></span>");
    topLevelLinks.closest("li").find("> ul").hide();

    var idx = 0;
    var key;
    topLevelLinks.each(function () {
        key = "expandable" + idx;
        $(this).attr("id", key);
        if ($.cookie(key) == "open") {
            $(this).closest("li").find("> ul").show();
            $(this).find(".accordion").replaceWith("<span class='accordion'><i class='fa fa-caret-down'></i></span>");
        }
        idx = idx + 1;
    });

    topLevelLinks.click(function(e) {
        e.preventDefault();
        var childUl = $(this).closest("li").find("> ul");
        var isVisible = childUl.is(":visible");        
        if(isVisible) {  
            $(this).find(".accordion").replaceWith("<span class='accordion'><i class='fa fa-caret-right'></i></span>");
            childUl.slideUp();
            $.cookie($(this).attr('id'), "close", { path: '/' });
        } else {
            $(this).find(".accordion").replaceWith("<span class='accordion'><i class='fa fa-caret-down'></i></span>");
            childUl.slideDown();
            $.cookie($(this).attr('id'), "open", { path: '/' });
        }
    });
});
</script>

上記のコードを、スクリプトエディタでページに埋め込みます。
できあがりはこんな感じ。
20150608

参考にしていただけるとうれしいです♪

SharePointのナビゲーションで「最近使った項目」を隠す

GWはひどい鼻風邪で引きこもっていたにもかかわらず、いまだに風邪が抜けません。ずぴー。やれやれ。

前回のポストで、ちょろっとふれたナビゲーションの「最近使った項目」。
図1
これ、表示したくないって時も結構あるんでないかと思います。

しかしながら、2010の時みたいにCSSで隠すのはちょっと難しい(クラス名とか、IDとかついてないのですよ・・・)。
仕方ないので奥の手・JavaScriptで無理やり隠します。

$(document).ready(function() {
    $(".ms-core-listMenu-item:contains('最近使った項目')").parent().hide();
});

コード見ていただければわかりますが表面的に隠しているだけですので、気がむいたときにサイトの設定のナビゲーションから削除してあげたほうがいいと思います。

ついでに、2010から引き続きwikiページライブラリに出てきてしまう例のアレ
図2
も、JavaScriptで隠せます。

$(".ms-core-listMenu-item:contains('更新済みのページ')").parent().hide();
$(".ms-core-listMenu-separatorLine").hide();

サイトカスタマイズの参考になれば・・・。

ログインユーザーの権限を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

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

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