React Revisited after version 16.8

React というのは DOMの内容を簡単に操作しようという JavaScriptのライブラリーの一つだが、Shadow DOMという概念を導入し、JQueryのようにコマンドごとの実行でDOMを書き換えることはせず、Shadow DOMにある程度書き込んでおいてから描画する直前にまとめてDOMに書き込む。なので軽快に動作する、というのが売り。

数年前に覚えようとしたのだが、 二点腑に落ちないところがあって、結局、Vue.jsという他のLibraryを愛用(溺愛)するに至った。

その二点というのが

1.JSXという新しい概念の導入。 一見するとHTMLなのだがHTMLではなく、XML書式をJavaScriptで理解できるようにしたプリプロセッサ。この一見HTMLというのがくせもので、class= はJSの予約用語ゆえに使えずclassName=にしなければならないなど、細かいところで違っていて気持ちがわるい。(これに比べてVueのTemplateはまごうことなきHTMLの拡張)

2.コンポーネントで操作できる変数(State)を使うためにはコンポーネントをClass表記してsetState()という関数を使って管理しなければならない。

というもの。

その後 「JSXは単なるJavaScriptの関数表現」という開発チームの解説をYoutubeでみて ストンと納得できるものがあったものの、 二つ目のJavaScriptにClassが必要というのがなんとも納得いかない(個人の意見です)身としては今いち手が出せずにいた。 Class表記をするあまり、 thisの多用を行い、しかもそのContextを明示するためにClass Constructorに this.function=this.function.bind(this) を書きまくるというのがなんとも切ない。

しかしながら、 Reactの勢いは侮りがたいものがある。 特にMicrosoftのOffice開発系の方たちは Office UI GraphicsのComponentに「Reactは正義!」という感じで使いまくっている。 SharePoinitの JavaScript Frame workも Reactびいきがすごい。

で、最近React-client-appというCLIでReactのScaffoldingが簡単にできます、という記事をよく見るので、実際どれくらい簡単なんだいと試してみたら、 生成されるScaffoldingのComponentがClass表記ではなくfunction表記になっている。 あれ、これって、Stateful Componentは Class表記しなければいけないんじゃなかったっけ、と思ったが、 実はReactも進化していた。

Version 16.8から Hooksという機能が追加されて Functional ComponentでもStateの管理ができるようになっていた。 そして今まで、 Functional Component = Stateless Component, Class Componnet=Stateful Componentとなっていたものが、 どちらを使ってもよいようになっている。 Reactのサイトにも将来の開発にはFunctional Componentを使うのがおすすめと書いてある。 なので、React-client-appもDefaultで生成するコードはクラスレスになっていたわけだ。

紹介ページ https://reactjs.org/docs/hooks-intro.html にあるコードをみれば一目瞭然だが、クラス表記のようなConstructorもなければ、this. でメンバー指定をすることもない。 非常に簡単。 ただ、このHookを使ったクラスレスのステートフルコンポーネントの作り方、オフィシャルサイト以外で解説しているサイトはまだ少ない。

この

const [ var1, setVar1]=useState('this is variable one');

で気をつけなければいけないのは Var1が複数メンバーから構成されるオブジェクトだった場合、

const [obj1,setObj1]=useState({member1:"this is member 1", member2:"this is member 2"})

のように Obj1を宣言したときに、

setObj1({member2:"this is member 2 modified"})

とやってもsetState()と違って、オブジェクトの他のメンバー(Key)とマージしてくれない。上の例ではmember1が消えてしまう。

セット関数には元のstateがパラメータとして渡されているのを利用して、

setObj1(s=>({ ...s, member2:"this is member 2 modified"}))

とスプレッド表記を使ってマージしてあげる必要がある。(上のサイトにはObject.createを使うのも可と書いてある)

また、いわゆる ComponentWillMountなどのLife Cycle hookの代わりには描画毎に発火するuseEffect()を使えと書いてある。 このHookは発火を限定するために二つ目のパラメータ―で発火する条件を指定できる。 ファイルデータを読み込むなど、あるいはイベントリスナーを設定するなど、一回だけ発火させたい場合、 このパラメーターを空のアレイで渡してあげることで実現できる。(なんとなくハックっぽいが)

useEffect(()=>{Data を読み込む云々}, [])

他にも何種類かのHooksが用意されているが、 演習用にアプリを書いてみると、上の二つのHookだけで、それなりのものができる。

すでにJSXへのアレルギーはなくなっているので、クラスを使わなくてよいReactという選択は結構魅力的です。

すべてにVue.jsを使いたいのはやまやまなれど、SharePoint関連においてMicroSoft が Reactありきの開発を推しているというのはやはり大きい。

SharePoint Column Formatter の使い方

SharePoint Onlineのテナントで一ヶ月くらい前からPushされはじめた機能なのだが、リストのコラムセッティングに”Column Formatting”というのが追加されている。 Text Entry Field になっており、説明は”JSON書式でフォーマットを記入してください”とかなりそっけない。 詳細はOffice Devのほうにある。 ここ 。

さらに書式サンプルのDepositoryがGitHUBにおいてある。 ここ

 

で、上の記述を参考にListViewのカスタム化を自分で試してみたのがこれ。

ここではCheckedのColumn Formatting に以下を指定

{
  "debugMode": true,
  "elmType":"div",
  "children":[
    {
      "elmType": "img",
      "attributes": {
        "src": {
          "operator":"?",
          "operands":[
            "@currentField",
            "/_layouts/images/CNSAPP16.GIF",
            "/_layouts/images/CbUnChecked.gif"
          ]
        },
        "aria-hidden":"true"
      },
      "style": {

      }
    }
  ]

}


これでYes/Noの文字表示をアイコンイメージに置き換えている。

Delivery Statusのコラム書式はちょっと長くなる。(参照する各Columnの内部名称をColumn settingsでそれぞれ確認しておく必要があることに注意 下の例では CheckedはCheckedだがDue DateはTaskDueDateになっている)

{
  "debugMode": true,
  "elmType": "div",
  "attributes": {
    "class": {
      "operator": "?",
      "operands": [
        "[$Checked]",
        "sp-field-severity--good",
        {
          "operator": "?",
          "operands": [
            {
              "operator": "<=",
              "operands": [
                "[$TaskDueDate]",
                {
                  "operator": "-",
                  "operands": [
                    "@now",
                    86400000
                  ]
                }
              ]
            },
            "sp-field-severity--severeWarning",
            {
              "operator": "?",
              "operands": [
                {
                  "operator": "<=",
                  "operands": [
                    "[$TaskDueDate]",
                    "@now"
                  ]
                },
                "sp-field-severity--blocked",
                {
                  "operator": "?",
                  "operands": [
                    {
                      "operator": "<=",
                      "operands": [
                        "[$TaskDueDate]",
                        {
                          "operator": "+",
                          "operands": [
                            "@now",
                            604800000
                          ]
                        }
                      ]
                    },
                    "sp-field-severity--warning",
                    "sp-field-severity--low"
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  },
  "children": [
    {
      "elmType": "span",
      "style": {
        "display": "inline-block",
        "padding": "0 4px"
      },
      "attributes": {
        "iconName": {
          "operator": "?",
          "operands": [
            "[$Checked]",
            "CheckMark",
            {
              "operator": "?",
              "operands": [
                {
                  "operator": "<=",
                  "operands": [
                    "[$TaskDueDate]",
                    {
                      "operator": "-",
                      "operands": [
                        "@now",
                        86400000
                      ]
                    }
                  ]
                },
                "ErrorBadge",
                {
                  "operator": "?",
                  "operands": [
                    {
                      "operator": "<=",
                      "operands": [
                        "[$TaskDueDate]",
                        "@now"
                      ]
                    },
                    "Warning",
                    {
                      "operator": "?",
                      "operands": [
                        {
                          "operator": "<=",
                          "operands": [
                            "[$TaskDueDate]",
                            {
                              "operator": "+",
                              "operands": [
                                "@now",
                                604800000
                              ]
                            }
                          ]
                        },
                        "Error",
                        "Forward"
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "elmType": "span",
      "txtContent": {
        "operator": "?",
        "operands": [
          "[$Checked]",
          "Delivered",
          {
            "operator": "?",
            "operands": [
              {
                "operator": "<=",
                "operands": [
                  "[$TaskDueDate]",
                  {
                    "operator": "-",
                    "operands": [
                      "@now",
                      86400000
                    ]
                  }
                ]
              },
              "Past Due",
              {
                "operator": "?",
                "operands": [
                  {
                    "operator": "<=",
                    "operands": [
                      "[$TaskDueDate]",
                      "@now"
                    ]
                  },
                  "Due Today",
                  {
                    "operator": "?",
                    "operands": [
                      {
                        "operator": "<=",
                        "operands": [
                          "[$TaskDueDate]",
                          {
                            "operator": "+",
                            "operands": [
                              "@now",
                              604800000
                            ]
                          }
                        ]
                      },
                      "Nearing Due Date",
                      "Upcoming"
                    ]
                  }
                ]
              }
            ]
          }
        ]
      },
      "style": {
        "color": "black"
      }
    }
  ]
}

それぞれのAttribute (Text, Class, iConName)に対して出力は異なるものの条件自体は同じ式をを繰り返していれてやらなければならないので記述がやたらに長くなる。 また簡単な条件式を入れ子状に繰り返し使うので、どんどん括弧がふえていく。

条件式のロジックだが、 due date 当日には

@now – 1 day < Due Date < @now

になっていることを利用して 以下のIf構文をJasonにて記述している。

If checked == True then “Delivered 納品済み”
else if Due Date < = @now – 1 day then “Over Due 納期遅れ”
else if Due Date < = @ now then “Due Today 本日納期”
else if Due Date < = @now + 7 days then “Nearing Due Date 納期近し”
else “Upcoming 今後の納入”

ページにJavaScript を埋め込まずにこういうことができるのは画期的だが、 普通のNotepadでエラーを出さずに記述するのはまず不可能。JSON構文のチェック機能とエレメントブロックの折りたたみ機能がしっかりついているProgramming Editorは必須。(無料で使えるVisual Studio Codeがいまどきの旬です。)

なお、このフォーマッターはModern View のみで有効。 Classic Viewでは何も起こらないので CSRを使ったJavaScriptコードの必要性がなくなったというわけではない。 また、 将来への計画はされているようだが、WebPartを使ってリストViewをページに埋め込んだ場合も現時点では機能しない。

最初の行の”debugmode”: trueは記述しておくとエラーの内容がブラウザーのデベロッパーコンソールとコラム表示に吐き出されるのでデバッグ用に便利だがもちろん削除してもかまわない。

SharePoint Framework

去年の暮れぐらいだったか、MSのお知らせでSharePoint Frameworkという新しい開発環境を使った SharePoint SiteのCustomizationが可能になりました。という連絡があった。  自分がやりたいSPサイトのCustomizationは 基本的にJavaScriptをページに埋め込むJavaScript Injectionという手法だ。 これでクライエントサイドの描画をコントロールするCSRフックとかリストやファイルのアイテムをAjaxで読み書きするREST APIなどを使ってカスタムページを作るという方法で用が足りてしまうので、あまり気にしていなかったのだが、この方法、最近、その進化が目立つModern view  pageでは使えない。 これは開発するのにVisual Studioが必須であるSharePoint Add-Inでも同じ様で、 どうも今の時点でModern Look componentのCustomizationをサポートしているのはSharePoint Frameworkのみのようなのだ。

で、Dev.Office.comのDocumentationを眺めてみると、なんとこれ、 Node.JS上で全部動いているのだ。

NPMで yo,  gulpと @microsoft/generator-sharepoint を導入し Project folderを作って以下を実行

yo @microsoft/sharepoint

これで必要なファイル群がフォルダーにインストールされる。 hello worldのテンプレートになっているので、これでもうnode.js 上のローカルPCで動くSharePointのページを模したWorkbench上での実行が可能(下記コマンドでDefaultブラウザが勝手に立ち上がる)

gulp trust-dev-center

gulp serve

ここまで、SharePointサイトにアクセスする必要すらない。 Step by Stepはdev.office.comに詳しいので割愛 (Build your first SharePoint client-side web part (Hello World part1)

SharePointのサイトでもすでにDevelopper siteが例えば”https://mysite.com/sites/dev”などとして設定されているなら、 ”https://mysite.com/sites/dev/_layouts/workbench.aspx” にアクセスすると、開発中のwebPart のWorkbenchがSharePoint上で作動する。

そしてこのTool Chainによる開発、Visual Studioが必要ない、というか使えない。dev.office.comのお勧めEditorはVS Code. IDEの例としてJetBrainのWebStormがあげられているなどVisual Studioへの依存性が一切ない。(C#もドットnetも使わないなら当たり前というべきか)

ただ、使われているコードが通常のJavaScriptではなく、TypeScriptなのだ。 TypeScriptのコードは実行時にはJavaScriptにばらされるのでそんなに大きなLearning Curveにはならない、と予測はしているのだが、これは学習する必要があるんだろう。

Node.jsは言語というより開発環境のベースとして使われるので、これ自体を学ぶ必要はない。

VSで使っていたNugetだとかMS buildーtoolなどのツールが、下のようにNode.JS上で動くツールにおきかわっているわけで、今わかっている範囲でまとめると

  1. Node.js: Install するだけ、普通のJavaScriptの知識で十分のはず。
  2. NPM :DependencyのUpdateなど、使い方は学んだほうがよさそう。yarnでもいいのかな
  3. yeoman(yo):Install するだけ、学習の必要性はなさそう
  4. gulp: コマンドをいくつか覚えるだけで学習の必要性はなさそう
  5. typescript: 学習の要あり
  6. webpack:@microsoft モジュールを導入したときに含まれている。さわらなくてよい。何本ものファイルを、一本に圧縮する、というような作業をやっているらしい。
  7. VS CodeまたはWebStorm:Dev.office.comはVS Codeを使った用例を挙げているが、自分は従来からつかっているWebStormを使う。

というわけで、なんと開発環境的には、2,3,4,5,6は1のNodeJSが動けば動くのでなんでもよく、VS codeもWebStormもマルチOS環境で動くので 要するに Linux上で開発できてしまうのだ。

というわけで、javaScriptをかじっている人間にはハードルが低そうな予感。 Linux環境でぼちぼちやってみようかな?

SharePoint Change Page Title without changing site address ページタイトルをサイトアドレスを変えずに変更する。

これってSharePointのこの部分がちゃんと動いていないだけなのではないかとおもうのだが、今現在のOffice365(SharePoint Online)のインプリメンテーションだと、Page Attributeでページタイトルを変更するとaspx ページの名前自体が変わってしまう。

たとえば https://mysite.sharepoint.com/sites/mysite/SitePages/thispage.aspx

というページがあるとして、これだと表示されるページタイトルが”thispage”となってしまう。これを Edit pageからタイトル変更して ”This Page”とすると、 サイトアドレスが

https://mysite.sharepoint.coms/sites/mysite/SitePages/This%20Page.aspx

となってしまう。サイトアドレスをなるべく短くしたいのでページタイトルとサイトアドレスの一部になるページの名前を別けることができればよいのだが、今の時点ではできない。

世の中には同じことを不便と考える人がいるわけで、何件かの記事ではSharePoint Designerを使ってマスターページを変更する方法が述べられている。すでにSharePointにある機能をターンオンするだけの簡単な修正ではあるが、

1)マスターページを変更する必要がある。
2)SharePoint Designerが必要

すなわち、サイトオーナーでかつSharePoint Designerを使えるという条件を満足していなければならない。

ブラウザーのdebug Toolで確認してみたら以外と簡単にタイトルだけ変更できることが判ったのでここに書いておく。 サイトのエディター権限があればできるというのが利点。

ターゲットのページでエディットモードに移行し、適当なところに Insert ->Embed Codeでコードエディターを開く。

以下を入力する

<script>
document.getElementById('DeltaPlaceHolderPageTitleInTitleArea').innerHTML="My Page";
</script>

これだけである。 ページタイトルが”My Page”となる。

SharePointの場合、CSRというメカニズムがあって、表示する内容の大部分は一度ブラウザーがスクリプトを実行したのち、データを描画するため、 ページへの書き込みのタイミングを遅らせなければならないのが普通だが、タイトル部分は最初の読み込み時に描かれてから変更がないため、上のようにブラウザがスクリプトを読み込んで即実行、という単純な動作でも有効なんである。

ちなみにWiki PageとWeb Part Pageの両方で有効。 Site Pageはためしていない。

 

 

exploring SharePoint Online

Our office is soon moving to SharePoint online. Currently we are using SP2007 and Microsoft does not support content migration from 2007 to 2013 or SP online. This is good and bad. Good because we can build sites from scratch, bad because there is no easy way to move our contents from SP2007 to SP online. We should be able to copy files and folders from 2007 to online but all the metadata will be lost. lists will be even tougher. we have the third party solution called ShareGate but IT guys are telling us it is faily slow we can not possibly migrate the whole sites (that is in the size of the order of Terabytes.) and would like to selectively use the tool. Document Management is tricky under the SharePoint at best.

UI has been greatly improved. It is now much easier to interact with contents with JavaScript. I am having tremendous fun with technique like CSR (client side rendering) and REST API that can be used with CSR or as a conetnt of CEWP as usual.  Furthermore , attaching JavaScript code to web part is made less painful by the introduction of JsLink option and inclusion of script web part.

It is also good to have started late in the game. Internet is filled with power user tips. (I can use most of the technique meant for SharePoint 2013 with SharePoint online.)

One of my favorite technique called “calculated column” is still possible with much less hassle.  Again CSR can be used for this with literally one line of code.  Even better, I can forego with calculated columns at all and directly manipulate the target columns and show bar graph or traffic signal instead, using CSR’s context override.

Filter/connection of WebParts on the page is now performed on the list and not view. In another word, the filtering column do not need to be a part of the view.  This is tremendous for me. I no longer need to hide a column that is used for my filter because I don’t need to include the column in the view to start with.

EasyTabs that we used and found so useful for SharePoint 2007 does not work as is.  Again somebody has modified it for SharePoint 2013 and it is  working. Christophe seems to have developed another tool package that includes EasyTabs functionality but my inquiry to him went unanswered so I am using this version.

There is another code called Hillibily Tabs which essentially do the same thing as EasyTabs.  This one uses jQuery and jQuery UI (and CSS) to manipulate the tab, so how do I say this..  It’s flashy and probably more maintainable.

Excellent write up on CSR by Andrei Markeev is here be sure to check other articles by him.

This one by Kaptyn is also good

Many CSR examples are on Office Dev Center, uploaded by Muawiyah Shannak.

The above examples contains calculated column like effect witihout using the calculated column.    However, if you insist Calculated columns to be used, here is Christoph’s explanation of how it can be done.

List Roll up

I was first thinking maybe we need to purchase third party package to do this.  It turned out this is doable with home made JavaScript code with REST API. I am able to Roll up the list (read only at this time, because I only feel comfortable with “GET” potion of the API) in my sandbox, using a couple of sub-sites.  I will try and see how scalable this technique is.

Minimum Download Strategy

Sooner or later, people will hit this “MDS” *issues*  where the view changes back to its default look when switching the view,  or code breaks because it can not find jQuery that was supposed to have been loaded.  I am still learning this but found some blog entries very helpful.

  1. Article by Kalmstrom.com talks about what happens with MDS in very short text and also give pointers to how to deal with it
  2. SharePoint 2013 – A Better Pattern for Client Side Rendering Scripts shows a script pattern that can be used for site with or without MDS feature enabled.

However, my observation is that once you introduce your own code to the mix, then SharePoint engine with MDS seems to try render first with MDS way, then try to render again for “foreign” code.  Because of this, some of the pages does load faster with MDS off.  So turning this feature is not as bad as it may sound.

 

Most of the link here are from various blog entries.  Again, it must be much easier now than two years ago to find the information I need.  MSDN also have many documents I can refer to but there coverage is broader (C#, XML ,.aspx and JavaScript code).  As a business side power user, I (can) only use JavaScript and find those individuals’ entries priceless. My appreciation goes to those who spent many nights to figure out and then took a time to write about it!

SharePoint online :: modifying “New item or Edit this list” to “Edit this list”

This is my first “SharePoint Online” related javaScript.(I am still experimenting with nuance of SP online)
This script changes “New item or Edit this list” links to simpler “Edit this list link.


/**
* Created by A2life on 3/24/2016.
* Modify new or edit div to "edit this list" only
* modify "New items or edit" elements on entire page (not just webPart this is attached to)
* */
(function () {

var thisContext = {};
thisContext.OnPostRender = modifynewEdit;
SPClientTemplates.TemplateManager.RegisterTemplateOverrides(thisContext);

})();

function modifynewEdit() {
// jQuery library is required in this sample
// Fallback to loading jQuery from a CDN path if the local is unavailable
(window.jQuery || document.write('<script src="//ajax.aspnetcdn.com/ajax/jquery/jquery-1.11.3.min.js"><\/script>'));

jQuery("td.ms-list-addnew:contains('new')").each(function (index) { //only respond to "New" elements
var edit = $(this).find('a:contains("dit")'); // because I am looking for "[E|e]dit"
edit = edit.text("Edit").add("<span> this list</span>"); // reconstruct link with proper words
$(this).html(edit);
});
}

attach this to list webPart as jsLink parameter. If uploaded to Site Assets folder as “modifyEdit.js” , it should look like “~site/SiteAssets/modifyEdit.js” . “~site” is a token for the site that sharePoint uses to resolve the url reference.

Note on this code.

  • The document.write line dynamically writes a script tag for jQuery. This appears to be more reliable way of loading jQuery rather than referencing to the file within script tag.
  • The code creates a context override for onPostRender SP event for the webPart (of item list view) being attached. This event seems to fire even when other list view on the same view is changed  (and the code invoked will affect other list views on the same page as expected) so the function modifynewEdit is attached to this event.

Hiding SharePoint list column from list view

This article is about a JavaScript snippet that hides a specific column from a SharePoint list view. If I google this subject, most of the articles found on the Internet is about hiding a specific column from new, display or edit form. Although Javascript can do this, you can do the same thing without use of JavaScript by enabling list content-type as well. See here.

Not many articles talks about hiding columns from the list view though. (This one is an exception)
The link above shows the solution for toggling the particular columns. I only needed to hide a column from the list. But why do I even want to do that when one can simply create a view without that specific column you don’t want to see?
The reason is that I want to show the list based on filtered value from the column in question. I can add the filter webpart that I can connect to the target list, then specify the column to which the filter webpart will be connected to. In another word, for the filter to work, the column need to be a part of the list view (This is no longer the case with SharePoint 2013 or SharePoint online.  See below). Since the column will always show the same value for all displayed items(filtered value) I want to hide this column. If your company allows the use of SharePoint Designer, then you can use the Dataview webpart where you can use the column that is not shown to filter the items list. But Dataview creation and editing requires SPD. I wanted a easier solution, hence this javascript snippet.

if (typeof jQuery === 'undefined') { throw new Error('hide column snippet requires jQuery') }

//This function gets targetTable object and columnindex to hide
//targetTable is a jQuery object with single element.
function doHideColumn(targetTable,col) {
    targetTable.find(".ms-viewheadertr >*[class^='ms-v']").eq(col).hide();
    targetTable.find(" >tbody >tr").each(function(){$(this).find(">td:eq("+col+")").hide();});
}
$(function() {//this hide the colName column from all table in the page
    var    colName="colToHide";
    var    tarray=$("tr.ms-viewheadertr th:contains('"+ colName +"')");
    var    tlength=tarray.length;
    var    colindex;
    for (var i=0; i<tlength; i++) {
        colindex=$(tarray[i])[0].cellIndex;
        doHideColumn($(tarray[i]).closest('.ms-listviewtable'), colindex);
    }
});

 


This code snippet requires jQuery. You can use content editor Web Parts (CEWP) to include this code (don’t forget to wrap the code with script tag.) It works with SP2007 (My office’s current version) I may need to look at the code again when our IT upgrades the server to either 2013 or O365.  *Edit*  On SharePoint 2013 and SharePoint online filter can be applied to any list column regardless of if the target liist view contains the filtered column.  Therefore, solution explained here is unnecessary. *end Edit *
The code above scans the entire page for column headers with “colToHide” name and hide the column. (or columns if there are multiple lists in the page and if they contains the column with same name)
Since this code uses jQuery array object to hold the column information, it wont be difficult to modify the code to accept more than one column names to hide multiple columns from single list. For now, this snippet achieves what I need.

Filter Web parts are nice. For instance, URL query web parts let me create a page that contents are based on the filtered value in url query. For instance,
http://projectpage.aspx?projphase=1
http://projectpage.aspx?projphase=3
both link goes to the same page but the first link will show project phase 1 list items where the next link will show project phase 3 list.

SharePoint のDocument LibraryをWindows Explorerで見る

Document library created in the SharePoint can be opened in the Windows Explorer. This is handy for bulk copying files from the network drive to SharePoint document library, for instance.  Procedure is quite simple. You just need to click “Actions” in the standard view menu and then select “Show in windows Explorer”  The separate file window will open.  You can copy, move paste etc., in this window.

People sometimes complained that this does not work for him/her.  It took me sometime to understand the reason. It turned out that this only worked at the root folder level in our installation of SharePoint 2003.  In aother word, if you create a subfolder in the document library and you “desend” to this subfolder, you can not switch the view to explorer view. Solution to this is to always start at the root folder level. With the 2007 installation, this limitation seems gone.

SharePointクロスサイトのリスト表示をJQueryで行う

いきなりSharePointの話題なのであるが、同時にかなりマニアックである。

仕事場では4年前からSharePointを使っており自分の部署のアドミンなどをなぜか押し付けられ、とはいうもののねっこのところはしっかりITのかたがたが管理しているわけで、仏様の手のひらにのった孫悟空のようなカスタマイズしかできないわけではあるが、それはさておき

SharePointの不満のひとつにクロスサイトで使っているリストパーツを表示できない、というのがある。 同じサイトコレクションの下であればリストの共有もできる、ということだが、我がITチームはそのようには設定してくれなかった。 もちろん、それなりの理由が4年前にはあったわけで愚痴は言うまい(←言ってる).

Christophe Humbert氏が主催している Path to SharePoint ではこれに対する 解決法が載っている. 彼のサイトにある online tools のひとつが吐き出すJavaScriptを CEWPに落とし込んであげるだけで、他のサイトからのリストやカレンダーの表示が可能になる。 このスクリプトは  JQueryを使ってターゲットページのリストを抜き出し、中身をCEWPのあるところに表示するような仕掛けになっている。

この “Cross site list Snapshot tool” 、使ってみると快適に作動する。 ただし、この方法で抜き出されたリストはそのままではTextToHTML script が使えない。

この TextToHTML というのもChristopheさんの作なのだが、リスト表示の中身を計算式HTMLを生成したコラムをスクリプトを使って処理してカラーテキストや背景色を変えたり、果てはバーグラフやKPIに使うような赤・青・黄の信号灯を表示したりそのほかにも色々できる便利ものである。 もし、SharePointを使っていて、まだ見たことがなければ百聞は一見にしかず、 これとか,これ, あるいは こんな表示もできる、ということで、SharePointの表現力が一気に高まる。 同じ作者の Easy Tabs なども使ってサイトの設計を行えば、苦労せずに同僚から尊敬のまなざしで見られること間違いなしである。

ではなぜ、この二つのツールが組み合わせではうまくうごかないかというと  TextToHTML はページがロードしている最中に実行されてしまうが、そのときにはまだ Cross site list snapshot のスクリプトは他のページを読みこんでいる最中でリストの表示が間に合わないのである。

やはりおなじようなことを彼のブログで質問した人がいて、 Christopheさんが言うにはJQueryのcallback functionを使えば動くはず、ということであった。.

つまりJQuerryの load function に textToHTML scriptをコールバックさせて、順序正しくスクリプトを実行させればよいわけ。

早速試してみて、うまくいったので、ここに書いておく。 2010年の11月1日時点でPathToSharePointサイトにおいてあるスクリプトで作動を確認済み。

まずは TextToHTML のほうを修正する。.  このスクリプトをChristopheさんのサイトからダウンロードしたら、テキストエディターを使って以下の部分を修正する。 (ここでは “light version”を使用)

// Calendar views
var regexpA = new RegExp("\\s*<([a-zA-Z]*)(.|\\s)*/\\1?>\\s*");
TextToHTML(document.getElementsByTagName("a"),regexpA);

// List views
var regexpTD = new RegExp("^\\s*<([a-zA-Z]*)(.|\\s)*/\\1?>\\s*$");
TextToHTML(document.getElementsByTagName("TD"),regexpTD);

上の部分を下のように修正する。基本的には実行コードをファンクションとして定義する。

// Start of modifications
//Calendar views
function calendarT2H() {
var regexpA = new RegExp("\\s*<([a-zA-Z]*)(.|\\s)*/\\1?>\\s*");
TextToHTML(document.getElementsByTagName("a"),regexpA);
}
// List views
function listT2H(){
var regexpTD = new RegExp("^\\s*<([a-zA-Z]*)(.|\\s)*/\\1?>\\s*$");
TextToHTML(document.getElementsByTagName("TD"),regexpTD);
}
//maintains the original functionality of the script.
// Omit below two lines if no other part of the page uses TexttoHtML
calendarT2H();
listT2H();
//end of modifications

で、この修正されたスクリプトを TextToHTMLとして保存

次に Path To SharePoint’s  Tool のCross-Site-List-Snapshotページ を使って、自分の使いたいリストのスクリプトを作成。

このスクリプトをtext editorで開き、以下の文字列を探す。

..(“href”,$(this).attr(“href”)+s+”Source=”+location.href);});});};LoadView…

ここの

“location.href);});});};”

この部分を

“location.href);},listT2H());});};”

このように変更してコールバックを追加。  [ listT2H() はリスト表示の場合。カレンダーの場合は calendarT2H() に変更.]

[おまけ]JQuery load() 関数の使いかた。
Load (url,parameters,callback)
ここで url は参照されるURL, parameters は Javascript object で、サーバーにおくる値を プロパティーとして保持。callback はAjaxが終了した時点でjQueryが呼び出す関数。SnapshotのコードではurlがターゲットリストのあるページのURL. parametersはクリストフさんが処方したマジックパラメーターで、これでリスト成分をぬきだしている。ただし、オリジナルスクリプトではコールバックが空白になっている。 (実は昔JQueryの参考書を読んだときに、このCallback機能の具体的な使い方がわからなかったのだが今回の経験で必要性を実感した次第)

このスクリプトをセーブする.

われわれの環境はSharePoint2007で、一番綺麗にCEWPを使ってスクリプトを実行させる方法は、ソースとして書き込むのではなく、スクリプトファイルをリンクすることだと思っているので、サイト内に “webpart”というドキュメントライブラリーを作成し、ここに二つのスクリプトファイルをアップロードする。

リストを表示したい Web Part ZoneにはふたつのCEWPを埋め込むことになる。

WepPart 1 – CEWP  linked to ../webpart/CrossSiteListSnapshot-modified.txt
WebPart 2 – CEWP (Hidden) linked to ../webpart/TexttoHTML-modified.txt

これで、他のサイトのリストやカレンダーが色つきカラーバー付きで表示されるようになる。

めでたしめでたし!

Showing a list from another SharePoint Site

Showing a list from another SharePoint site (or cross-site listing)

One of the frustration about developing SharePoint page is that you can not use a list view from another Site. If two sites are under the same Site collection, then it will be possible but our IT people created each division’s site as independent site and not as a site collection (and there was a good reason for doing this way, 4 years ago. I am aware of that too.)

Christophe Humbert from Path to SharePoint has a solution to this. One of his online tools will generate a JavaScript snippet that you can drop into CEWP to show a list or calendar from another site page.  He uses JQuery to extract a list out of the target page and insert where the CEWP is located.

This “Cross site list Snapshot tool” works pretty well.  However, the list “extracted” in this manner can not take advantage of TextToHTML script.

The TextToHTML is another gem from Christophe that uses calculated columns to display colored text, background or even bar charts and also traffic signals (as KPI). If you have not seen his solution, take a look at this,this and this.  Along with Easy Tabs (another Christophe’s creation)  my colleagues in the office think I am a SharePoint wizard (far from the truth)

The reason the combination does not work is because  TextToHTML will execute as soon as the browser read the script. Unfortunately at that time, the Cross site list snapshot script is still busy working on extracting the list so TexttoHTML does not see the list.

In his response to one of the comments in his blog, Christophe mentioned a use of callback function that is available for JQuery so that two scripts can talk to each other.

The idea is for the JQuerry load function to “call back” textToHTML script once the load function finishes its primary job.

I have experimented with this concept and came up with (my) solution so I am presenting it here.  This works with scripts available from PathToSharePoint site as of November 1, 2010

First we have to modify the TextToHTML script.  Download the script from Christophe’s site and look for the following section of the code. (I use “light version” of the script)

// Calendar views
var regexpA = new RegExp("\\s*<([a-zA-Z]*)(.|\\s)*/\\1?>\\s*");
TextToHTML(document.getElementsByTagName("a"),regexpA);

// List views
var regexpTD = new RegExp("^\\s*<([a-zA-Z]*)(.|\\s)*/\\1?>\\s*$");
TextToHTML(document.getElementsByTagName("TD"),regexpTD);

Change  the code above as follows. In essense, we are turning the executable codes to function declarations.

// Start of modifications
//Calendar views
function calendarT2H() {
var regexpA = new RegExp("\\s*<([a-zA-Z]*)(.|\\s)*/\\1?>\\s*");
TextToHTML(document.getElementsByTagName("a"),regexpA);
}
// List views
function listT2H(){
var regexpTD = new RegExp("^\\s*<([a-zA-Z]*)(.|\\s)*/\\1?>\\s*$");
TextToHTML(document.getElementsByTagName("TD"),regexpTD);
}
//maintains the original functionality of the script.
// Omit below two lines if no other part of the page uses TexttoHtML
calendarT2H();
listT2H();
//end of modifications

Save the above script as your TextToHTML.

Now generate your list snapshot script using Path To SharePoint’s  Tool -Cross-Site-List-Snapshot page

Open the Script with text editor. Search for the following string toward the end of the script.

..(“href”,$(this).attr(“href”)+s+”Source=”+location.href);});});};LoadView…

Change

“location.href);});});};”

to

“location.href);},listT2H());});};”

What I did was to add a callback function parameter to JQuery’s  load() function.  [Replace listT2H() with calendarT2H() if the target is calendar.]

The JQuery load() function works like this
Load (url,parameters,callback)
Here, url is the URL of the resource being fetched, parameters is a Javascript object whose properties hold values to send to the server, and callback is a callback function that jQuery will call when the Ajax operation is complete. In the snapshot code, url is our list page url, parameters is a magic that Christophe is passing on to modify the list output. However,
as supplied by Christophe’s code generator, There is no callback.

Save the Script text.

I am using SharePoint2007 and I found the cleanest way to use CEWP for scripting is to link a file to CEWP, rather than entering source code directly in CEWP.  I create “webpart” document library and upload both text files to the library.

So, in the above set up,   A Web Part Zone has two webparts. Both are CEWP.

WepPart 1 – CEWP  linked to ../webpart/CrossSiteListSnapshot-modified.txt
WebPart 2 – CEWP (Hidden) linked to ../webpart/TexttoHTML-modified.txt

The page now shows a list from another site with calculated columns correctly rendered.