logo

サイト内検索
ココログ最強検索 by 暴想

最近のトラックバック

無料ブログはココログ

« 2011年6月 | トップページ | 2011年8月 »

2011年7月

VisualForcePDFの改ページについて_3:レコード表示制御編

Creating Professional PDF Documents with CSS and Visualforce
※基本的に上記リンクに描いてあることを噛み砕んでいる物です。

前回は1ページはラッパー表現推奨~という話をしました。
今回はデータ表示、レコード表示のテーブルについてかこうと思います。

例えばこのような帳票があるとします。

これを表現するにはどうしたら良いでしょう。


前回指摘した通り、通常手段ではループカウントは利用できません。

またグループ化した1レコード目のみデータ表示を行う事や、
ボーダーの表示非表示を制御する必要もありそうです。

つまりここでもラッパーを利用します。

Tips: つまり List<ページラッパー<レコードラッパー>> という構成になります。
  またレコードラッパーはSObjectを格納します





 テーブルのヘッダー部分を動的に変えたいケースも出てくるでしょう。
この場合apex:PageBlockTableでは上手く行かない場合があります。

Tips:具体的にはタイトル表示に使う apex:facetタグ に<apex:outputText>が入れ込めない。
  また後述する「ナナメ線の表現」が出来ない。

ここでは<Table>+apex:repeatを使って説明していきます。
ただし利用時は最低限必ずスタイル:「border-collapse:cllapse;」を適用してください。

Tips:border-collapse:cllapse;の適用について
Ws000001

Tips:各種利点と欠点について

・PageBlockTable
       
  • 利点        
                  
    • 構築が分かりやすく作り易い
    •             
    • ヘッダと表示データのコード位置が
      近しいため、メンテナンスしやすい
    •             
    • スタイルクラスに[.even][.odd]
      というモノを用意すれば
      色違いの表が簡単にできる。
    •        
       
  •    
  • 欠点        
                  
    • ヘッダーにapex:outputTextが使えない
    •             
    • Image挿し込みによるスラッシュ表示が不可能
    •             
    • 動的にヘッダーを作り込めない
    •        
       

・<Table>+apex:repeat
       
  • 利点        
                  
    • 素のテーブルなのでカスタマイズ性が非情に高い
    •             
    • [A\B]という表現が可能
    •             
    • 動的なヘッダ作成も思うがまま
    •        
       
  •    
  • 欠点        
                  
    • 構築上必須CSSが存在する
    •             
    • ヘッダ行とデータ表示行が離れるため、
      構築上注意が必要
    •        

       

ちなみに両者ともにColspanおよびRowspanは利用しません。
全てフラグ制御でボーダー表示などを行います。
(Excelでセル結合せず表を作るイメージ)

構築は可能ですが、若干分かりづらいものになると思います。
またコーディングも難しくなり相対的にコード量が増えると思われます。






=============================

注意点1:データ表示限界について

1つセルに表示されるデータのWidthは、設定したWidthを大きく超えると致命的な問題が発生します。

例えば横幅100Pxのフィールドがあったとします。
そこに100pxを大きく超える2,000Px相当のインライン要素をいれこむと…

※顧客名に注目してください
Ws000002_3

これはひどい

そうならないよう、意図的に表示文字数を制御することをおすすめします。

方法はApex:outputTextのフォーマット機能か、
String.substringを利用するかのどちらかでしょう。

Tips:スタイルシート「Overflow」による表示制御は試しましたが、
  PDF化すると効かなくなるようです



また動的に改行(<br />)するケースも同じような問題があります。

1ページあたりのデータ表示件数はHeightではなく行数で見ています

行数とは「テーブル行要素内に文字表示をしたときのテーブル行Height」になります。
動的にとは、この文字表示部分を改行したケースを指します。
Kaigyoimg

この時 テーブルの1行のHeight ≠ 1文字のHeight であるため、表示が狂います。

最悪ページの底が突き抜けるケースがあり得るでしょう。
なので固定改行ではない動的改行は、改行=次のレコード行として作ったほうが良いかと思います。

Kugirikaigyo

Tips:レコード表示を空で表示するときも、
  Heightを合わせるために最大Heightを確保した空レコードをつくるようにしてください。
Tips:br 改行数の最大値が決まっているのであれば、
  そのサイズだけ事前にから文字改行でHeightを取るのも手です。





=============================

注意点2:画像の利用について

基本的にHTMLをPDFを変換しているため、画像を表示すればそのままPDFに反映されます。

Imgpdf

※使用した画像はこちら
Test

実装例
<apex:image value="{!$Resource.イメージ画像名}" widht="82px" height="30px" />

しかし見たとおり、ドットのアラ(ジャギー)が目立って多少ではなく見苦しい面があります。

よって画像は「大きく」作り、表示するときはサイズ指定で小さく表示することを是非おすすめします。

 

またこれを利用し、HTMLでは出来ない次のような表現が可能です。

Slashpdf

 

実装は以下のように行います。

1.次のような\を描いた大きい画像を用意。

Sla

おおむね300px*300pxあれば充分。
Backgroundなどで背景色が変わる場合、画像の透過処理もお忘れなく。

2.静的リソースとしてアップロード

3.以下のように実装

※画像を300*300 から 180*60にリサイズ
<div style="position: relative; border:1px solid black; width:180px; height:60px;">
   <apex:image value="{!$Resource.slash}" width="180px" height="60px" /><br />
   <span style="position:absolute; top:3px; right:3px;">cd</span>
   <span style="position:absolute; bottom:3px; left:3px;">ab</span>
</div>

※divはtdに置き換えてもOK

注意点としては、Widthが固定されてしまう事。
全体的なWidth修正時はこの部分も対象になるので留意してください。

またapex:pageBlockTableでこの表現は利用できないので注意してください。

Tips:画像表示したPDF印刷でCromeを使う場合は注意が必要です。
  Version12において、画像の印刷がうまくいかないケースが発生しました。







=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=

実際にレコード表示用ラッパーを作る

レコード表示用ラッパーの要点は以下のとおりです

  ・行のデータをピンポイントで表示非表示を制御可能
  ・ボーダーラインの制御が可能
  ・文字制限を行う(ソース上で行う場合)

ここで動的にボーダーラインを作るため、実装の形は以下のようにします。
Tableに対して設定するCSSは以下のような形が良いかと思います。

A.Tableに対するスタイルシート設定
  border-collapse: collapse;     //項番1で説明した物
  border-left:1px solid black;   //左にボーダー
  border-bottom:1px solid black; //下にボーダー

Ws000004

B.1セルごとのスタイルシート設定
  border-top:1px solid black;    //上にボーダー
  border-right:1px solid black;  //右にボーダー

Ws000005

AとBを組み合わせると、調度良い形で表が形作れるとおもいます。
またB項目のセル設定を外部から設定する形にすれば、表示の問題も解決できます。

これを表現するラッパーは以下のように構築すると良いかと思います。

public class RecordWrapper{
    private SObject record;       //実データを格納
    private Boolean dummy = false;     //ダミーレコードかどうか
    private Boolean lineFlag = false;  //線表示に関するフラグ   ←←←
    private Integer strlen = 15;       //文字数制限用。置き場はドコデモ。


    /*ダミーレコード用*/
    public RecordWrapper(){
        record = new SObject();  //Nullは絶対回避
        dummy = true;            //ダミー証明
    }


    /*通常レコード用*/
    public RecordWrapper(SObject record){
        this.record = record;  //実データ格納
        dummy = false;         //ダミー解除
    }


    /* 実データは必ず参照可能にする */
    public SObject getRecord(){
        return record;
    }


    /*ダミーはコンストラクタで定義するため、Getterのみ用意*/
    public Boolean getDummy(){
        return dummy;
    }


    /*線表示用Boolean設定*/
    public void setLineFlag(Boolean lineFlag){
        this.lineFlag = lineFlag;
    }
    public Boolean getLineFlag(){
        return lineFlag;
    }


    /* コード上で表示文字数を制限:こちらで通す場合、Staticメソッド化推奨 */
    public String getSeigenString(){
        String str = record.STRING__c;
        if(str == null && str == ''){
            return '';
        }
        if(str.length() =< strlen){
            return str;
        }
        return str.substring(0, strlen);
    }


//--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--


    /*スタイルシートクラスを指定し、ボーダーラインを制御*/
    public String getLineStyle(){
        if(lineFlag){
            return 'topSolid';    //上部に直線
        }
        if(dummy){
            return 'topDotted';   //上部に点線
        }
        return '';                //表示しない
    }
}

これをデータのリストの代わりに利用し、表示制御を行えばOKです。




ではよきSalesForceライフを。

VisualForcePDFの改ページについて_2:ページラッパー制御編

Creating Professional PDF Documents with CSS and Visualforce
※基本的に上記リンクに描いてあることを噛み砕んでいる物です。

前回VisualForceの基本構成について解説しました。
今回はその続きで1ページ当たりの情報を保持するListについてTipsを書いてみます。
同時に最初のページの頭ににヘッダーを付けて、最後のページの最後にフッターをつける方法についてもメモしておきます。




ヘッダー・フッターの表示は基本的にテーブルレイアウトで記述します。
ただしHTMLであるため、特にフッターの位置には気を使う必要があります。

Tips:レコード表示制御によって、データ部では多少Heightがずれる可能性がある。
  理由は後日配信予定のVisualForceの作りで解説

 

Tips:問題なければapex:pageblock内部でDivを三つ切り、
  ヘッダ用・フッタ用・データ用と分けたほうがいいかもしれません。


またヘッダー・フッターの表示制御はDivをApexタグで描けばrenderedで表示制御が可能です。
ちなみにApexタグでのDiv表現は「<apex:outputPanel layout="block">」です。

Tips:layout="inline"にすると、Spanになります。


<apex:repeat value="{(ラッパーのList)}" var="page">
    <div style="page-break-after:always">
        <apex:outputPanel layout="block" rendered="{!XXX}">
            <-- ヘッダー部 -->
        </apex:outputPanel>

        <-- データ部 -->         <apex:pageBlockTable value="{!page.records}" var="SObject" >             ~データ表示~         </apex:pageBlockTable>
        <apex:outputPanel layout="block" rendered="{!XXX}">             <-- フッター部 -->         </apex:outputPanel>     </div> </apex:repeat> ※XXXは真偽値。

ではRenderedのXXX部分をどのように制御し、ヘッダー・フッターの表示/非表示を表現したら良いでしょうか?

「ループカウントをrepeatからとって、制御したら良くない?」

という意見があるかもしれませんが、取れません
apex:repeat等ループ系タグにはループカウントを取得可能な変数やメソッドはありません。


つまり、自分でループカウントを作らないといけません。


なので上↑のテキスト上、「ラッパーのリスト」と書かれていますが、このラッパーとは、
実際は「SObjectリストを格納できる1ページを表現するラッパー」で、このような別クラスが必要です。

同時にこのラッパーに対してカウンタを設定してあげれば、ページカウントも可能となります。

Tips:またラッパーにする利点は最後ページの改ページキャンセルにも使えます。


以下はラッパーの例です。

public class Report {
    private List<SObject> records = new List<SObject>();  //データのリスト
    private Integer pageNumber;                           //ページナンバー
    private Boolean lastPageFlag;                         //最後ページかどうか

    //必ずページナンバーをセットする。     public Report(Integer pageNumber){         this.pageNumber = pageNumber;         this.lastPageFlag = false;     //基本は最後ページでないのでFalse     }
    /* データリストと中継してやり取り。 */     public void add(SObject record){         records.add(record);     }     /* データリストと中継してやり取り。実装によってはあると便利。 */     public void addAll(List<SObject> record){         records.addAll(record);     }
    /* データリストを返す */     public List<SObject> getRecords(){{         return records;     }
    /* ページナンバーを返す */     public Integer getPageNumber(){         return pageNumber;     }
    //最後ページかどうか。     public void setLastPageFlag(Boolean lastPageFlag){         this.lastPageFlag = lastPageFlag;     }     public Boolean getLastPageFlag(){         return lastPageFlag;     }
//--*--*--*--*--*--*--*--*--*--*--*--*--*--*--     //page-break-after:always;設定の例     public String getLastPageStyleSheet(){         if(!lastPageFlag){             return 'page-break-after:always;';             //alwaysかavoidを切り替える形でも良いと思います。         }         return '';     } }



これを加味してヘッダー制御を行うと…

//1ページ目のみヘッダーを表示する
<apex:outputPanel layout="block" rendered="{!XXX}">
  ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓
<apex:outputPanel layout="block" rendered="{!page.pageNumber == 1}">
//ページNo=1の時だけTrue = 表示する。

という形になります。

ただしこの実装をする場合は2ページ目以降ヘッダー分Heightが大きく開きます。
レコード表示数が拡張されるので、実装上その点に留意してください。

では良きSalesForceライフを。

VisualForcePDFの改ページについて_1:基本編

全国三千万…もいるかは分かりませんが、SFDC技術者の皆様、こんにちは。

SalesForceを利用したシステム構築をするとき、帳票作成に躓いてはいませんか?
実はSalesForce、HTMLをPDFに変換する機能が標準で組み込まれています。

とりあえず<apex:page>タグに、「renderAs="pdf"」とつけてみましょう。


<普通のVisualForce画面>
Kihon1before

<renderAs="pdf"を設定>
Kihon1after

わおすごい、何の変哲もないHTMLがいとも簡単にPDF化することが出来ました!!

おしまい。


……だったらとてもとても幸せなのですが、事はそう上手くは行きません。

理由は実際に見たほうが早いでしょう。
以下のDIV要素を、該当のVisualForceに貼りつけて保存してください。

<div style="height:1200px; border:1px solid black; background-color:#ccccff;" />

するとこのようになります。
Kihon2break

Divが底を突き抜けて、次のページまで貫いてしまいました。

1ページに収まらなかった分は上手いこと改ページしてくれないのです。
また厳密なHeight調整もかなり難しい上ほぼ不可能です。

つまりrenderAs="pdf"だけでは、1ページ帳票しかPDF化を保証してくれません。

実際利用するなら…
Kihon3risoukei

こんな形が理想型なのですが、これでは諦めるしか…。




と、嘆いていたのも昔の話。

先日以下のような情報が米SalesForceに投稿されました。
Creating Professional PDF Documents with CSS and Visualforce

簡単にいえば『VisualForcePDFを改ページ表示できた!』というもの。
今回はこれについて、掻い摘んで説明してみようと思います。

※基本的に上記リンクに描いてあることを噛み砕んでいる物です。







さて、上記リンクのソースを利用すると概ね次のような表現ができると思います。
Ws000006






これはスタイルシート『page-break-after:always;』によるものです。

PDFにこれを仕込むと、仕込んだ要素直後に空白のページが1つ出来ます。
Ws000001






ではこの空白部分に再度『page-break-after:always;』を当てはめていけば・・・?
最終的に次のようになります。
Ws000002






これが改ページの基本的考え方です。

ただし改ページするのはpage-break-after:always;が指定された要素の直後です。
つまり1ページの表示データが、1ページのHeightを上回る形で1ページを表現すると…
Ws000003






このようになります。
page-break-after:always;要素直後に空白のページが1つ出来るのであって、
はみ出したページを自動的に改ページするものではありません。

なので1ページに収める実装は、自ら行う必要があります。

これらは指示通り、1ページあたりのレコード表示数を事前に限定することで可能としています。




ちなみに最後ページの空白を消す方法は2パターンあります。
  1.最後のページだけpage-break-after:always;を適用しない
  2.最後のページだけpage-break-afterを always から avoid に設定する。

どちらにしろ『最後のページに』というのが肝になります。




===========================ー
さて実際にデータを作る場合、そのレコードの構成は List<List<SObject>> となります。
これは…

  List<SObject>は実データのリスト。1ページあたりの表示データ
  List<List<SObject>>はページのリスト。Addされるのは1ページのデータ=List<SObject>。

という形になっています。

検索後1ページあたりの表示件数で検索結果をList<SObject>に切り出し、
これを集めたものを画面上に表示します。

また先述のとおり1ページ当たりの表示件数は自身で調整し、
画面からはみ出さないようにしましょう。






VisualForceは上記リストを利用するため、ループ系を2重に設置します。

ソース例

<apex:repeat value="List<List<SObject>>" var="List<SObject>">
    <div style="page-break-after:always;">
        ~ヘッダーが必要な場合はここに記述~
        <apex:pageBlockTable value="List<SObject>" var="SObjcect">
            ~実装~
        </apex:pageBlockTable>
    </div>
</apex:repeat>

基本形はこのようになります。



では良きSalesForceライフを。

Facebook APIで出来ない事:Pageによる他のPageへの投稿

Salesforceから、Facebookへの連携を研究していて、しばらくFacebook APIというものを扱ってみましたが、どうも現時点では、あまり出来の良いものでは無いという印象です。
とにかく、

これが出来るなら当然、こうすれば、これも出来るよね!

と思うようなところのあちこちで、理不尽に出来ない事が多すぎる。

やっているうちに何が出来て何が出来ないのか訳がわからなくなってきたので、ここで整理もかねて、Facebookでやりたいのに出来ないことをいくつか書いておきます。

まず、今回の出来ないことは、

Facebook Page名による他のPageへの投稿

Facebookページを運営するとして、Salesforce側のDBにある情報をもとに、自分のFacebookページや、他のFacebookページに投稿する機能を考えています。

まず、Facebookページへの投稿は、以下のようなURLを使用して、graph APIで出来ます。

 curl -F 'access_token=<アクセストークン>' -F 'link=xxxx' -F 'picture=xxxx' -F 'caption=xxxx' -F 'description=xxx'  https://graph.facebook.com/<ページのID>/feed  ---①

なお、投稿はPOSTメソッドを使用する必要があるので、curlコマンドで書いておきます。
ここで、アクセストークンには、publish_stream権限を持たせたものである必要があります。

しかし、この場合、投稿された記事は私自身の投稿となって、私の顔が記事に載ることになります。しかし、ビジネスを目的として投稿する以上、私的な投稿ではまずいので、ここはやはり、自分の運営するFacebook Pageの名前で投稿する必要があるわけです。
実は、「誰から投稿されたか」という情報は<アクセストークン>の中に持っています。そこで、自分のFacebook Pageのアクセストークンを入手することが必要になります。
それは、以下のようなURLを使用して、graph APIで出来ます。

https://graph.facebook.com/me/accounts?access_token=<アクセストークン>  ---②

ここでのアクセストークンには、publish_stream,offline_access,manage_pages権限を持たせます。

上記、URLの結果、自分の運営するPage(それ以外にアプリ等も)の一覧が入手でき、その中に、各Pageのアクセストークンがあります。

ここから、使用したいPageのアクセストークンを取って、①のURLに使用してPOSTすれば、Facebook Pageの名前で、その同じFacebook Pageに投稿する事は出来ます。

ここで、自分のFacebookページ以外にも、同様のジャンルのページに記事を投稿して、ファンを増やしたいとします。そこで、①のURLに自分のFacebookページのアクセストークンを使用して、他のPageのID宛に投稿しようとすると、これが出来ないのです!

 "error":{"type":"OAuthException","message":"(#200) Posts where the actor is a page cannot also include a target_id"}}

というエラーになります。

当然、対象ページには、投稿権限はあります。実際、手作業では投稿できるページです。

エラーメッセージの内容もわかったようなわからないような?「target_id」というのが使えないと言っているが、そんなパラメーターは使っていない。つまりは、Pageから投稿する場合は、ターゲットが他のものじゃだめだ、と言っているようにも思えるが、その理由が、セキュリテイ上の理由で、わざとそうしているのかも不明確。

この件に、関しては以下のフォーラムのページ議論されています。

http://forum.developers.facebook.net/viewtopic.php?id=95325

これは、バグだということで、報告したにもかかわらず、再現情報不足?とかの理由で勝手にCloseされたと言っている。

ここで、あきらめずに別の方法を探したところ、Feed Dialog という機能を見つけました。

http://developers.facebook.com/docs/reference/dialogs/feed/

以下のようなURLでダイアログを開いて投稿します。

 http://www.facebook.com/dialog/feed?
   app_id=<アプリケーションID>&
   link=xxxx&
   picture=xxxx&
   name=xxxx&
   caption=xxxx&
   description=xxxx&
   message=xxxxx&
   redirect_uri=<リダイレクトURL>&
   from=<自分のページID>&
   to=<投稿先ページID>

これだと、いちいち、ダイアログを開いてボタンを押す必要があるのですが、一応、Page to Pageの投稿が出来ました。

ところが!

ある日突然、これも出来なくなりました。

現在、toを指定すると、以下のようなエラーになります。

エラーが発生しました。

We're working on getting this fixed as soon as we can.

なんか、エラーメッセージ的には「今対応中、なるべく早く直します」と言っているようなので、バグと認めた感はあるが....それにしても、発見してからもう2日以上も直っていない。

少なくとも、7/11時点でこの機能で投稿した記録があるので、そこまでは動いていたはず。

Feed Dialogのマニュアルページにあるディスカッションの欄でも、ここ数日で突然動かなくなったという意見があるので、私の勘違いではなさそう。

やってられない..というのが感想です。

さて、これだけじゃなく、他にも出来るはずと思ったことが出来ないのが、あるんだよ..

それは、おいおい、書いていくつもりです。

ApexCode:楽観的排他制御について

SalesForceで排他制御……? え?!    となる方も居るでしょう。


SalesForceではたしかに排他制御を標準でサポートしています。
ただしそれは標準の画面利用に限り、VisualForce等自分で書いたコード上Updateする場合は効かないようです。

なので、VisualForceでUpdateを利用する場合は、自分で排他制御を組み込む必要があります。


方法としては二通りあり、
1.レコードSelect時に for UPDATE オプションを取り付け、悲観的排他制御を用いる
  >Locking Statements
2.Update直前にDB上から対象レコードをSelectし、LastModifyDateと比較を図る
  ※今回はこちらを利用します





楽観的排他制御の基本的流れは次のようになります。


  1.DBから更新対象のレコード取得
  2.レコードを編集
  3.更新ボタンをおし、処理スタート
  4.1のレコードを再取得
  5.1と2のレコードのLastModifyDateを比較
  6A.同一  :updateに2のレコードをセット
  6B.差異あり:updateを流さず戻る

6Bのエラー時、弊社では「エラーページへ遷移する」という仕様にしています。

 

    //Update実装例
    public Pagereference goUpdate(){
        PageReference pr = checkSave(Sobject);
        if(pr != null){
            return pr;
        }
    }


    //楽観的排他制御付更新。エラーが発生した場合はエラーページへのPagereferenceが返る
    public static Pagereference checkSave(SObject so){
        if(so==null){
            return null;
        }
        String tblname = so.getSObjectType().getDescribe().getLocalName();
        String ql = 'select id,LastModifiedDate,LastModifiedBy.Name from ' +
            tblname + ' where id =\'' + so.id + '\'';
        Sobject[] wksos = Database.query(ql);
        if(wksos.isEmpty()){
            //削除エラー
            //Page.ConcurrencyErrorは、SalesForceエラーページのソースを利用して作ったVisualForce。
            return Page.ConcurrencyError;  //排他エラー:誰かが削除していた
        } else if(wksos[0].get('LastModifiedDate')!=so.get('LastModifiedDate')){
            //makeErrPrはエラー生成用のメソッド。後述
            return makeErrPr(wksos[0]);    //排他エラー:レコードが編集されていた
        }
        update so;  //問題なければUpdate
        return null;
    }


    //エラー情報を添付したエラーページへの遷移情報作成。
    private static Pagereference makeErrPr(SObject wkso){
        SObject anone =  wkso.getSObject('LastModifiedBy');
        Pagereference pr = Page.ConcurrencyError;
        Map

pmap = pr.getParameters();
        pmap.put('retUrl','/'+wkso.get('id'));        //戻り先のURL
        pmap.put('unmae',(String)anone.get('Name'));  //先にレコードを書き換えたユーザーの名前
        pmap.put('uid',(String)anone.get('id'));      //先にレコードを書き換えたユーザーのID
        return pr;
    }

-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
<エラー表示用VisualForce:ConcurrencyError>
<apex:page controller="ConcurrencyError">
<apex:outputPanel rendered="{!uid!=null}">
<!--更新エラー-->
<table cellspacing='10'>
  <tbody><tr>
    <td><span style='font-weight: bold; font-size: 12pt;'>変更を保存できません</span></td>
  </tr>
  <tr>
    <td>
編集していたレコードは、編集セッション中に<a href='/{!uid}'>{!unmae}</a>によって変更されました。
<br/><br/>
        もう一度編集する前に、<a href='{!retUrl}'>レコードを再表示</a>してください。
    </td>
  </tr>
</tbody></table>
</apex:outputPanel>
<apex:outputPanel rendered="{!uid==null}">
<!--削除エラー-->
<table cellspacing="10">
  <tbody><tr>
    <td><span style="font-weight: bold; font-size: 12pt;">レコードは削除されました。</span></td>
  </tr>
  <tr>
    <td>
アクセスを試みた情報は削除されました。このレコードを削除したユーザは、ごみ箱から情報を復活させることができます。 削除データはごみ箱の中に 30 日間保管されます。(一部の情報を除く。)
    </td>
  </tr>
</tbody></table>
</apex:outputPanel>
</apex:page>

<コントローラ>
public with sharing class ConcurrencyError {
  public ConcurrencyError(){
    Map

pmap = System.currentPageReference().getParameters();
    this.retUrl = pmap.get('retUrl');
    this.unmae = pmap.get('unmae');
    this.uid = pmap.get('uid');
  }
    public String retUrl { get; set; }
    public String unmae { get; set; }
    public String uid { get; set; }
    static testMethod void ConcurrencyErrorTest() {
      ConcurrencyError cr=new ConcurrencyError();
    }
}





また複数オブジェクトを別個に、かつ同一処理内でUpdateする場合、
ロールバックを考慮して以下のように実装します。

    //SObject1とSObject2の場合
    //Update実装例
    public PageReference goUpdate(){
        System.Savepoint sp = Database.setSavepoint();
        //SObject1のレコード処理
        PageReference pr = checkSave(SObject1);
        if(pr != null){
            return pr;
        }
        //SObject2のレコードを処理
        //ここで失敗したとき、RollBack処理をしないとSObject1の処理が確定してしまう。
        PageReference pr = checkSave(SObject2);
        if(pr != null){
            Database.rollback(sp);
            return pr;
        }
        return null;
    }

このような具合に実装すれば、自作のVisualForce&Updateでも排他制御が働きます。

では良きSalesForceライフを。

« 2011年6月 | トップページ | 2011年8月 »