カテゴリ「Visualforce」の記事

2014年9月30日 (火)

意外と知らないSalesforce Tips (8~10)

玉澤です。
意外と知らないSalesforce Tipsの残り3つを紹介します。


8. Salesforce標準のログイン画面をブランディングする

Salesforceの標準のログイン画面を変えたいお客様の会社のイメージでブランディングしたいときに役立つTipsです。

下記の手順で、Salesforce標準のログイン画面をカスタマイズできます。

まず、[管理]→[ドメイン管理]→[私のドメイン]から組織のサブドメインを登録します。
※組織のサブドメインは一度しか登録できないため、慎重に選択してください。

20140930_81

登録したら、↓のような画面になるので、「こちらをクリックしてログインしてください」を押下して、
ドメインのテストを実施します。

20140930_82
テスト後、↓のような画面になるので、「ユーザにリリース」を押下して、リリースします。
※新しいドメイン名をリリースした後は、元に戻せません。リリース後は、すべてのユーザが新しいドメインにリダイレクトされるので、注意してください。

20140930_83
リリース後、ログインページのブランド設定が表示されるので、ロゴ、背景色、右フレームのURLを表示します。

20140930_84
設定後、リリースしたドメインにアクセスすると、↓のように、ログイン画面がカスタマイズされて表示されるようになります。

20140930_85

9. タブのデフォルト表示を「最近使った○○」ではなくする

タブをクリックしたときに、「最近使った○○」(※○○はオブジェクト名)というビューではなく、通常のビュー画面をデフォルト表示したい場合に役立つTipsです。

20140930_91 まず、通常のビュー画面を表示するVisualforceページを作成します。↓のコードを書けばOKです。

<apex:page tabStyle="Rsv2__c"> ・・・オブジェクト名を指定
  <apex:enhancedList type="Rsv2__c" ・・・オブジェクト名を指定
             customizable="false" 
             height="600" 
             rowsPerPage="50" />
</apex:page>


次に、タブのデフォルト表示を変更したいオブジェクトの設定画面を開き、「ボタン、リンク、およびアクション」セクションで、タブの設定編集リンクをクリックします。

20140930_92
タブの設定編集画面で、上書き手段で「Visualforceページ」を選択し、先程作成したVisualforceページを選択します。

20140930_93
すると、タブをクリックしたときに、「最近使った○○」というビューではなく、通常のビュー画面が表示されるようになります。

20140930_94
10. レコード詳細画面にグラフを表示する

レコードの詳細画面にグラフを表示したいときに役立つTipsです。対象のレコードに関連するレポート結果だけをグラフ表示できます。

ここでは、取引先レコードの詳細画面で、その取引先の商談をフェーズ毎に集計したグラフを表示する方法を例に説明します。

まず、レポートをグラフ付きで作成します(今回の例では、標準の商談レポートタイプで、フェーズ毎に集計したサマリレポートを作成します)。

20140930_101

20140930_102
次にグラフを表示したいオブジェクトのページレイアウトの設定画面を表示します。レポートグラフのセクションに、先程作成したレポートが表示されているので、ドラッグ&ドロップでレイアウトに追加します(今回の例では、取引先のページレイアウトを表示し、先程作成したレポートグラフを追加します)。

20140930_103
追加後、プロパティ設定で、表示サイズ、タイトル表示の有無、絞り込み条件を設定します。
グラフは、元々のレポートの集計結果から、更に絞り込み条件で指定した項目で絞り込んだ結果が表示されます(今回の例では、取引先IDを指定して、その取引先の商談だけが表示されるようにします)。

20140930_104設定すると、↓のように、レコード詳細画面でグラフが表示されるようになります(今回の例では、取引先レコードの詳細画面で、その取引先の商談をフェーズ毎に集計したグラフが表示されます)。

20140930_105

2014年8月20日 (水)

意外と知らないSalesforce Tips (6~7)

またもやこのシリーズです。
今回は6つめと7つめをお送りします。

6. VisualforceをRead-Onlyモードで実行する

Apexの実行には実はRead-Onlyモードというものがあります。
これ、何か良いことがあるのかというとガバナ制限が緩和されるというメリットがあります。

どうしても大量のデータを扱いたいといったケースなどには、特別な細工をせずに扱えるデータ件数が増えるので利用価値がありそうです。

Read-Onlyモードの設定の仕方には2通りの方法があります。

(1) Visualforceページ全体をRead-Onlyモードにする
(2) コントローラークラスのメソッドをRead-Onlyモードにする

どちらの方法でモード設定するかによって緩和される制限に差があります。

具体的な緩和されるガバナ制限の内容は下記になります。

・1トランザクションで取得可能なレコード数上限が緩和される

50,000件 → 1,000,000件

・繰り返しコンポーネントでの使用可能コレクションサイズの上限が緩和される

1,000件 → 10,000件
対象の繰り返しコンポーネントは<apex:repeat><apex:dataTable><apex:dataList>の3つ
※Visualforceページ全体をRead-Onlyモードにした時のみ適用されます。

ここからはRead-Onlyモードの設定の仕方について書いていきます。

まずは、Visualforceページ全体をRead-Onlyモードにする方法から。
これはすごく簡単で、<apex:page>タグにパラメータ「readOnly="true"」を追加するだけです。

使用例: Visualforce側

<apex:page readOnly="true" controller="ReadOnlyController" action="{!init}" >
    <apex:repeat value="{!accountList}" var="ac">
        <apex:outputField value="{!ac.Id}"/>
        <apex:outputField value="{!ac.Name}"/>
        <hr />
    </apex:repeat>
    <apex:form>
        <apex:commandButton action="{!save}" value="Save"/>
    </apex:form>
</apex:page>

使用例: コントローラー側

global class ReadOnlyController {

    public List<Account> accountList {get; set;}
    
    public PageReference init(){
        accountList = [SELECT Id, Name FROM Account];
        return null;
    }
    
    // Read-Onlyモードだとこの処理は失敗する
    public PageReference save(){
        update [SELECT Id, Name FROM Account LIMIT 1];
        return null;
    }
}

コントローラー側は特に何もする必要はありません。
ちなみに、Read-Onlyモードの時にコントローラー側でデータの更新をしようとすると「Too many DML statements」の例外が発生します。

次はコントローラーのメソッド単位でRead-Onlyモードを設定する方法です。
コントローラーのメソッドにRead-Onlyモードを設定するには「@ReadOnly」あのテーションを付与します。
ただ、どんなメソッドにもアノテーションを付けられる訳ではなく、次の条件を満たしている必要があります。

・ global もしくは public である
・ static である
・ @RemoteAction アノテーションが付いている

ちなみにVisualforceということを抜きにすれば「@ReadOnly」自体はwebserviceなどでも使用できます。
詳細はDeveloper's Guideを見てください。

Force.com Apex Code Developer's Guide - ReadOnly Annotation
https://www.salesforce.com/us/developer/docs/apexcode/Content/apex_classes_annotation_ReadOnly.htm

Visualforce Developer's Guide - Working with Large Sets of Data
https://www.salesforce.com/us/developer/docs/pages/Content/pages_controller_readonly_context.htm

では、実際の使用例です。

使用例: Visualforce側

<apex:page controller="ReadOnlyController2">
    <apex:includeScript value="//code.jquery.com/jquery-1.11.0.min.js" />
    <apex:includeScript value="//code.jquery.com/jquery-migrate-1.2.1.min.js" />
    <apex:includeScript value="//cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js" />

    <div data-bind="foreach: accountList">
        <span data-bind="text: Id" /> / <span data-bind="text: Name" />
        <hr/>
    </div>
    
    <button data-bind="click:save1">Save1</button><br/>
    <button data-bind="click:save2">Save2</button><br/>
    
    <apex:form>
        <apex:commandButton action="{!save3}" value="Save3"/>
    </apex:form>

    <script>
        jQuery.noConflict();    
        jQuery(document).ready(function($){
            var viewModel = new (function(){
                var self = this;
                self.accountList = ko.observableArray();
                
                self.find = function(){
                    Visualforce.remoting.Manager.invokeAction(
                        '{!$RemoteAction.ReadOnlyController2.find}',
                        function(result, e){
                            if(!e.status){
                                alert('error:' + ko.toJSON(e));
                                return;
                            }
                            self.accountList(ko.utils.arrayMap(result, function(o){
                                return {
                                    Id: ko.observable(o.Id),
                                    Name : ko.observable(o.Name)
                                };
                            }));
                        },
                        {escape :false}
                    );
                };
                
                self.save1 = function(){
                    Visualforce.remoting.Manager.invokeAction(
                        '{!$RemoteAction.ReadOnlyController2.save1}',
                        function(result, e){
                            alert((e.status?"success":("failed"+ko.toJSON(e))));
                        },
                        {escape :false}
                    );
                };
                
                self.save2 = function(){
                    Visualforce.remoting.Manager.invokeAction(
                        '{!$RemoteAction.ReadOnlyController2.save2}',
                        function(result, e){
                            alert((e.status?"success":("failed"+ko.toJSON(e))));
                        },
                        {escape :false}
                    );
                };
                
                //initialize
                self.find();
            })();
            ko.applyBindings(viewModel);
        });
    </script>
</apex:page>

使用例: コントローラー側

global class ReadOnlyController2 {
   
    @ReadOnly @RemoteAction global static List<Account> find() {
        return [SELECT Id, Name FROM Account];
    }
    
    // Read-Onlyモードなのでこの処理は失敗する
    @ReadOnly @RemoteAction global static void save1() {
        update [SELECT Id, Name FROM Account LIMIT 1];
    }
    
    @RemoteAction global static void save2() {
        update [SELECT Id, Name FROM Account LIMIT 1];
    }
    
    public PageReference save3(){
        update [SELECT Id, Name FROM Account LIMIT 1];
        return null;
    }
}

ページ全体がRead-Onlyモードになるわけではないので通常のメソッドを呼び出してデータを更新することもできます。

いかがだったでしょうか。
機会があったら活用してみてください。

7. 変更セットへ楽に項目を追加する

変更セット使ってますか。
変更セットは便利といえば便利なのですが、そのUIはお世辞にも使い易いとは言えません。
特にカスタム項目数が数千件ある時に、そのなかから必要なものを選択する場合は一苦労です。

こんなときに次の手順を踏むと少しだけ項目を選ぶのが楽になります。

(1) 一覧で「▼増やす」のリンクをクリック

(2) URLに追加されるパラメータ「rowsperpage」を1000に書き換えたURLに移動

これで一気に一覧の表示件数を上限の1000件に変更することができます。
あとはソートするなり、ページ内検索するなりすればほんの少し楽に項目追加ができるようになります。

さらに手動でURLを書き換えるのすら面倒だという方のために一覧表示を1000件にするブックマークレットを用意してみました。
(やっていることは表示しているURLにパラメータ「rowperpage」があれば値を1000に変更し、なければ値を1000で追加して遷移させているだけです。)

javascript:(function(){var l=location;var h=l.href;if(!(/entityType=/i).test(h)){h+=('&entityType='+document.getElementById('entityType').value);} l.href=((/rowsperpage=[0-9]+/i).test(h))?h.replace(/rowsperpage=[0-9]+/ig,'rowsperpage=1000'):(h+'&rowsperpage=1000');})();

一応、Chromeで動作確認してあります。
もし良かったら使ってやってください。

それでは。

2014年8月18日 (月)

意外と知らないSalesforce Tips (4~5)

前回からの続きです。
今回は4つめと5つめをお送りします。

4. オブジェクトでトピックを利用する

Spring'14からChatterのトピック機能がオブジェクトでも利用できる様になりました。
トピックはTwitterで言うところのハッシュタグみたいなもので、レコードの整理に使うことができます。

・使うための準備
オブジェクトに対してトピック機能を有効化する必要があります。
Spring'14以降に作成した組織の場合はトピックを使用可能なすべての標準オブジェクトは、デフォルトで機能が有効になっている様です。

トピックの有効化は「カスタマイズ>トピック>オブジェクトのトピック」から行います。

Mame20140818_01

オブジェクトのトピックが有効になると、そのオブジェクト種別のレコードで公開タグが無効になるので、公開タグを使っている場合は注意が必要です。

・使ってみる
トピックをレコードに追加するには詳細画面を開いて、タイトルの下にある「クリックしてトピックを追加」リンクをクリックします。
(すでにトピックが追加済みの場合はリンク名が「トピック」になります)

Mame20140818_02

トピック名を入力して「完了」をクリックするとトピックが追加されます。

Mame20140818_03

追加されたトピック名をクリックすると、対象のトピックが付与されているレコードが一覧で表示されます。

Mame20140818_04

Mame20140818_05

また、トピックはビューの絞り込み条件に指定することもできます。
トピックを絞り込み条件に指定する場合は、絞り込みの項目で「トピック」を選びます。

Mame20140818_06

次はApexからトピックを操作してみます。
ConnectApiネームスペースのTopicsクラスがこの辺りの処理の為のメソッドを持っています。

トピックを追加するレコードのIDとトピック名を指定してトピックを追加します。
(第1引数はコミュニティのIDでコミュニティに対してトピックを追加する時でなければ、nullを指定します。)

String communityId = null;
String recordId = 'a04U000000Jj29h';
String topicName = 'トピックテスト';
ConnectApi.Topic objTopic = ConnectApi.Topics.assignTopicByName(communityId, recordId, topicName);

トピックのIDを指定して削除します。
(第1引数のコミュニティIDの扱いは追加の時と同じです。)

String communityId = null;
String topicId = '0TOU00000008ePB';
ConnectApi.Topics.deleteTopic(communityId, topicId);

トピックが追加されているレコードの一覧はTopicAsssignmentオブジェクトから取得できます。

Select Id, EntityId, TopicId FROM TopicAssignment WHERE TopicId = '0TOU00000008eP6OAI'

EntityIdにレコードのID、TopicIdにトピックのIDが入っています。
(組織でコミュニティが有効化されている場合はオブジェクトにNetworkIdカラムが追加され、ここにコミュニティのIDが入ります。)

・権限
トピック機能を扱うにはトピックの権限とレコードに対する権限が必要です。

一般ユーザ権限説明
トピックを割り当てる レコードへのトピックの追加・削除に必要です。
トピックを作成 トピックそのものを作成するのに必要です。
トピックを削除 トピックそのものを削除するのに必要です。
トピックを編集 トピックそのものを編集するのに必要です。

トピックを割り当てるには対象のレコードを更新できる必要があります。
参照権限のみの場合は、参照はできますが、割り当てはできません。

トピック機能は種類の異なるオブジェクトを関連づける事もできるので、うまく使って効果的にレコードを整理してみてください。

5. レコードの閲覧日時と参照日時を参照する

最近使ったXXX的な一覧を作ってみたくなったことはありませんか。
まさにそのためだけに作られたような項目が Summer'13 から各オブジェクトに追加されています。

「LastReferencedDate」と「LastViewedDate」がその項目です。

この2つの項目はどちらも読み取り専用の項目で、違いは項目の値が更新されるタイミングです。
詳細は次の表にまとめましたのでご覧ください。

 LastReferencedDateLastViewedDate
レコードの詳細画面を見たとき 更新される 更新される
レコードを画面からルックアップして参照項目に設定したとき 更新される 更新されない

このように画面で操作したときは上のタイミングで項目が更新されるのですが、Apexからの操作の場合は何もしなければこの2項目は更新されません。
Apexから更新したい場合は次のオプションを付けたSELECT文を発行することでこれらの項目を更新することができます。

詳細画面を見たときと同じ更新を行う
  ・・・ 「FOR VIEW」オプションを付与します。

SELECT Id,Name,AccountId,LastViewedDate,LastReferencedDate FROM Contact
WHERE ID='003U00000024dJgIAI' FOR VIEW

レコードを画面からルックアップして参照項目に設定したときと同じ更新を行う
  ・・・ 「FOR REFERENCE」オプションを付与します。

SELECT Id,Name,AccountId,LastViewedDate,LastReferencedDate FROM Contact WHERE
ID='003U00000024dJgIAI' FOR REFERENCE

機会があったら使ってみてください。

それでは。

2014年6月 1日 (日)

Visualforce Remote Objectsを使ったデータ更新

エンジニアの谷隈です。

Spring'14からパイロットリリースされている新機能「Visualforce Remote Objects」でのデータ取得について前回検証しました。
今回は更新処理について検証します。

ちなみに「Visualforce Remote Objects」というのはVisualforce上からJavaScript経由でセールスフォース上のオブジェクトデータを操作(取得・作成・更新・削除)する仕組みです。

○使い方

「Visualforce Remote Objects」を使用する際の大まかな手順は次の2ステップになります。
データ更新をする場合でも大まかな手順はデータ取得の場合と同じです。

Step

まずはイメージを掴むための、簡単なサンプルから。

コード例:Visualforce

<apex:remoteObjects >
  <apex:remoteObjectModel name="Test__c" fields="Id, Name, DateField__c, DateTimeField__c, CreatedDate"/>
</apex:remoteObjects>

「Visualforce Remote Objects」でオブジェクトのデータを操作する場合には、どのオブジェクトのどのフィールドを使用するかどうかをapexタグで宣言する必要があります。
上のコードはVisualforceページにTest__cオブジェクトを使用するためのタグを記載したコード例です。
Test__cオブジェクトのId, Name, DateField__c, DateTimeField__c, CreatedDateフィールドへの使用を宣言しています。
apexのタグの使い方はデータ取得の場合と同じです。

apexタグのドキュメントは下記です。
apex:remoteObjects
http://www.salesforce.com/us/developer/docs/pages/Content/pages_compref_remoteObjects.htm
apex:remoteObjectModel
http://www.salesforce.com/us/developer/docs/pages/Content/pages_compref_remoteObjectModel.htm
apex:remoteObjectField
http://www.salesforce.com/us/developer/docs/pages/Content/pages_compref_remoteObjectField.htm

コード例 JavaScript

var obj = new SObjectModel.Test__c();
obj.set('Id', 'a00U0000006eU8fIAE');
obj.set('DateField__c', new Date());

obj.update(function(error, result, event){
    // 更新完了後の処理
});

JavaScript側のコードは、apexタグにより生成されたモデルを利用してデータの操作を行います。
データの更新には「update」functionを使用します。
上の例は最も使うであろう呼び出し方で書いていますが、「update」function にはいくつか呼び出し方にバリエーションがあります。

○JavaScriptコードについての詳細

「Visualforce Remote Objects」利用時のJavaScriptコードの基本的な流れは次の2ステップです。

1. apexタグにより生成されたモデルのオブジェクトを生成
2. 生成したオブジェクトのメソッドを呼び出す。

データの更新にはapexタグで生成されたモデルの「update」functionを使います。

Updating Records with Remote Objects
https://www.salesforce.com/us/developer/docs/pages/Content/pages_remote_objects_using_update.htm

パターン① シンプルなupdate

「update」functionの一番シンプルな呼び出し方は、引数無しでfunctionを呼び出すだけの形になります。

var obj = new SObjectModel.Test__c();
obj.set('Id', 'a00U0000006eU8fIAE');
obj.set('DateField__c', new Date());

obj.update();

ドキュメントには上の形の変形パターンとして引数でセットする値をオブジェクトで渡す形の説明がありましたが、実際に試してみたところこちらは正しく動作しませんでした。
何か試し方が悪かったのかもしれません。

var obj = new SObjectModel.Test__c();
obj.update({
	Id: 'a00U0000006eU8fIAE',
	DateField__c: new Date()
});

いずれにしろ、シンプルな呼び出しのパターンではupdate処理が成功したかどうかが確認できないので使うケースはあまり無さそうです。

パターン② updateの結果をコールバックfunctionで処理する

「update」functionは引数に実行結果を処理するコールバックfunctionを指定して呼び出すこともできます。

var obj = new SObjectModel.Test__c();
obj.set('Id', 'a00U0000006eU8fIAE');
obj.set('DateField__c', new Date());

obj.update(function(error, result, event){
    // 更新完了後の処理
});

使うとしたらこれが一番活躍するパターンだと思われます。
引数にコールバックfunctionを指定します。

更新処理完了後、ここで指定したfunctionが呼び出されるので、処理結果に応じた後処理を行うことができます。

function callback(error, results, event) { // ... }

基本、retrieveの時と同じですが一部格納されるデータに違いがあります。

コールバックfunctionの引数

引数説明
error エラーメッセージが格納されます。正常終了時はnullです。
results 更新処理で影響を受けたオブジェクトのIDが配列で返されます。
event DML操作の詳細情報が格納されます。

Remote Objects Callback Functions
http://www.salesforce.com/us/developer/docs/pages/Content/pages_remote_objects_callback_functions.htm

パターン③ 複数レコードを一括で更新する

最後は複数レコードを一括で更新する呼び出し方です。
第1引数に更新するオブジェクトのIDの配列、第2引数に更新する値を指定したオブジェクト、第3引数にコールバックfunctionを指定します。

var obj = new SObjectModel.Test__c();
obj.update(
    ['a00U0000006eU8fIAE','a00U0000006eU8eIAE']
    ,{DateField__c: new Date(2020,8,1)}
    ,function(error, result, event){
        // 更新完了後の処理
    }
);

この一括更新は指定した全オブジェクトを同じ値に更新することしかできないので、使いどころは限られてくると思います。

○その他気になるポイント

・バリデーションエラー

バリデーションエラー発生時の動きは必須項目設定、入力規則、その他の場合、すべて同じです。
コールバックfunctionのerrorオブジェクトにエラーメッセージが設定されて帰ってきます。

function callback(error, results, event) { // ... }

簡易にチェックするならerrorがnullでないかで確認が可能です。

また、同様な情報はeventオブジェクトにも含まれています。

・トランザクション

トランザクションはDML操作のfunction呼び出し単位になります。
トランザクションを考慮した処理を実装したい場合は別の方式を考える必要がありそうです。

Best Practices for Using Remote Objects
https://www.salesforce.com/us/developer/docs/pages/Content/pages_remote_objects_considerations.htm

・オブジェクト権限と項目レベルセキュリティ

オブジェクト権限に読み取りしかない場合、updateの呼び出し結果はエラーになります。
項目レベルセキュリティについては読み取りのみの項目の場合も更新できました。
これは裏側の実装がapexだからということなのかもしれません。

Visualforce Remote Objects
http://www.salesforce.com/us/developer/docs/pages/Content/pages_remote_objects.htm

・日付/日時型の扱い

日付/日時型の値は取得時にはローカルタイムゾーンでDate型に変換されて表示されます。
ここまでは問題ありません。
ところが保存時はJavaScript上のDateの時間がGMT+0000扱いされている様で、SF上で9時間加算されます。
結果、日本環境だと取得してそのまま保存するだけで9時間加算されてしまいます。
今がパイロットだからということであれば良いのですが、修正されない様であれば注意が必要です。

○おまけ

Summer'14のリリースでVisualforce Remote Objectsに機能追加がされる様です。
主な追加機能。
・検索処理時に指定できる条件の追加(lte, gte, ne, order by)
・upsert()操作の追加
・デフォルトの処理のオーバーライドが可能になる

Visualforce Remote Objects Enhancements
https://developer.salesforce.com/releases/release/Summer14/Visualforce+Remote+Objects+Improvements

2014年5月28日 (水)

Visualforce Remote Objectsを使ったデータ取得

エンジニアの谷隈です。

Spring'14からパイロットリリースされている新機能「Visualforce Remote Objects」を試してみました。
「Visualforce Remote Objects」というのはVisualforce上からJavaScript経由でセールスフォース上のオブジェクトデータを操作(取得・作成・更新・削除)する仕組みです。
APIコール数を消費しない、Apexコードの実装が不要といった特長があります。

今回はこの機能のうちデータ取得についての検証を行いました。

○使い方

「Visualforce Remote Objects」を使用する際の大まかな手順は次の2ステップになります。

Step

まずはイメージを掴むための、簡単なサンプルから。

コード例:Visualforce

<apex:remoteObjects >
  <apex:remoteObjectModel name="Test__c" fields="Id, Name, DateField__c, DateTimeField__c, CreatedDate"/>
</apex:remoteObjects>

「Visualforce Remote Objects」でオブジェクトのデータを操作する場合には、どのオブジェクトのどのフィールドを使用するかどうかをapexタグで宣言する必要があります。
上のコードはVisualforceページにTest__cオブジェクトを取得するためのタグを記載したコード例です。
Test__cオブジェクトのId, Name, DateField__c, DateTimeField__c, CreatedDateフィールドへの使用を宣言しています。

コード例 JavaScript

var som = new SObjectModel.Test__c();
som.retrieve({limit:22}, function(err, records){
	// データ取得後の処理
});

JavaScript側のコードは、apexタグにより生成されたモデルを利用してデータの操作を行います。
データの取得には「retrieve」functionを使用します。
retriveには第1引数に取得条件、第2引数に取得したデータを処理するコールバックfunctionを渡します。

実際にはこの後、画面に表示するための処理が必要になりますがSF上からデータを取得するという部分はこれだけです。

○apexタグについての詳細

サンプルはミニマムな例でしたが、「Visualforce Remote Objects」のapexタグにはいくつかオプションがあります。
ここでは指定できるオプションについて見ていきたいと思います。

・apex:remoteObjectsの属性

属性説明
jsNamespace タグにより生成されるJavaScriptの名前空間を指定できます。省略時はSObjectModelになります。
id 他のapexタグと同じです。
rendered 他のapexタグと同じです。

・apex:remoteObjectModelの属性

属性説明
name 利用するオブジェクトのAPI参照名を指定します。(必須)
jsShorthand 利用するオブジェクトのJavaScriptコード上での名前を指定します。省略時はnameと同じになります。
fields 利用するフィールドのAPI参照名をカンマ区切りで指定します。ここで指定しない場合はapex:remoteObjectFieldタグで利用するフィールドを指定します。
id 他のapexタグと同じです。
rendered 他のapexタグと同じです。

・apex:remoteObjectFieldの属性

属性説明
name 利用するフィールドのAPI参照名を指定します。(必須)
jsShorthand 利用するフィールドのJavaScriptコード上での名前を指定します。省略時はnameと同じになります。
id 他のapexタグと同じです。
rendered 他のapexタグと同じです。

各タグのドキュメントは下記です。
apex:remoteObjects
http://www.salesforce.com/us/developer/docs/pages/Content/pages_compref_remoteObjects.htm
apex:remoteObjectModel
http://www.salesforce.com/us/developer/docs/pages/Content/pages_compref_remoteObjectModel.htm
apex:remoteObjectField
http://www.salesforce.com/us/developer/docs/pages/Content/pages_compref_remoteObjectField.htm

○JavaScriptコードについての詳細

「Visualforce Remote Objects」利用時のJavaScriptコードの基本的な流れは次の2ステップです。

1. apexタグにより生成されたモデルのオブジェクトを生成
2. 生成したオブジェクトのメソッドを呼び出す。

データの取得にはapexタグで生成されたモデルのretrieve functionを使います。

Retrieving Records with Remote Objects
https://www.salesforce.com/us/developer/docs/pages/Content/pages_remote_objects_using_retrieve.htm

retieve function

retrieve functionは検索条件と取得結果を処理するコールバックfunctionの2つの引数を受け取ります。

RemoteObjectModel.retrieve({criteria}, callback_function)

・retieve functionの第1引数

第1引数にはデータを取得する条件をオブジェクトに格納して指定します。

プロパティ説明
limit データ取得件数を指定します。省略時は20で最大が100です。
offset データを取得する際に何件目から取得するかを指定します。省略時は1です。
Where データの取得条件を指定します。現在はeq,lt,gt,like,and,orが指定できます。

※limitに100以上の値を指定した場合の動きについて
エラーにはなりませんが100件までしかデータを取得できませんでした。

※Where条件の指定イメージ

{
  limit: 100,
  where: {
    or: {
      DateField__c: {gt: new Date(2014,05,01)},
      Name : {like: '田中%'}
    }
  }
}

Where条件の指定は上のような形式で記述します。
ちなみにドキュメントで利用可能な比較条件にnotが無かったのが気になったので、ダメ元で試してみましたがやはりnot条件は利用できない様です。

Format and Options for Remote Objects Query Criteria
http://www.salesforce.com/us/developer/docs/pages/Content/pages_remote_objects_using_retrieve_query_object.htm

・retieve functionの第2引数

第2引数には取得した結果を処理するコールバックfunctionを指定します。

function callback(error, results, event) { // ... }

コールバックfunctionの引数

引数説明
error エラーメッセージが格納されます。正常終了時はnullです。
result 取得結果レコードがモデルオブジェクトの配列で返されます。
event DML操作の詳細情報が格納されます。

※コールバックfunctionの第3引数について
コールバックfunctionの第3引数には処理結果がJavaScriptのオブジェクトとして格納されます。
retrieve()の結果を確認したところ次のものが格納されていました。
(statusCode, type, tid, ref, action, method, result, status)
ドキュメントには通常は第1,第2引数でエラーチェックを行い、第3引数はデバッグもしくは細かいエラー管理用として使用するとの記載があるのみで詳細には触れられていなかったので通常利用は想定されていないのかもしれません。

Remote Objects Callback Functions
http://www.salesforce.com/us/developer/docs/pages/Content/pages_remote_objects_callback_functions.htm

・モデルオブジェクトのフィールドへのアクセス

取得した結果レコードのオブジェクトのフィールド値へのアクセスはget() / set() functionを利用します。

var name = obj.get('Name');
obj.set('Name', name);

また、日付型、日付/日時型については共にJavaScriptのDateに変換された形で取得することができます。(タイムゾーンはローカルです。)

・オブジェクト権限と項目レベルセキュリティ

オブジェクト、項目レベルどちらの場合も権限がなければ呼び出し結果はエラーになります。
項目レベルセキュリティについてはその項目だけ取得できないという動きではないので注意が必要です。

Visualforce Remote Objects
http://www.salesforce.com/us/developer/docs/pages/Content/pages_remote_objects.htm

・その他制限事項

基本的にVisualforceの制限事項はすべて適用されます。
また、地図情報は取得できません。取得しようとしても何事もなかったのかのように無視されます。

Limitations of Remote Objects
http://www.salesforce.com/us/developer/docs/pages/Content/pages_remote_objects_limitations.htm

2013年6月26日 (水)

VisualforceでCSVファイルとExcelファイルを出力するには

今回は、VisualforceでCSVファイルとExcelファイルを出力する方法をご紹介。

○CSVファイル

CSVファイルを出力するには、↓のように、pageタグのcontentTypeに「application/vnd.ms-excel」を指定します。contentTypeに「#<ファイル名>」と記述することで、ファイル名を指定できます。後は、カンマ区切りでヘッダ及びデータを出力するだけです。

・Visualforceページ

<apex:page contentType="application/vnd.ms-excel;charset=Windows-31J;#test.csv" controller="CsvController">
  <apex:outputText escape="true" value="{!csvHeader}" />
  <apex:repeat value="{!recordList}" var="record" id="csvList">{!record}</apex:repeat>
</apex:page>

○Excelファイル(表だけの場合)

Excelファイルに表を出力するには、↓のように、pageタグのcontentTypeに「application/vnd.ms-excel」を指定します。後は普通にHTMLで表を出力すればOKです。表は<table>タグで書いても、<apex:pageBlockTable>タグで書いても構いません。

・Visualforceページ 例1

<apex:page contenttype="application/vnd.ms-excel;charset=Windows-31J#test1.xls" controller="ExcelController">
  <table>
    <tr><td colspan="2">タイトル</td></tr>
    <tr><td>列1</td><td>列2</td></tr>
  </table>
</apex:page>

・Visualforceページ 例2

<apex:page contenttype="application/vnd.ms-excel;charset=Windows-31J#test2.xls" controller="ExcelController">
  <apex:pageBlock title="取引先責任者一覧">
    <apex:pageBlockTable value="{!account.Contacts}" var="item">
      <apex:column value="{!item.LastName}"/>
      <apex:column value="{!item.FirstName}"/>
    </apex:pageBlockTable>
  </apex:pageBlock>
</apex:page>

・出力されるExcelファイル

test1.xlsをダウンロード

○Excelファイル(表以外にも細かく指定したい場合)

複数のシートを出力したい、印刷範囲を指定して出力したいなど、表だけではなく、少し凝ったExcelファイルを出力したい場合は、Offirce2007以降で採用されているOpen XMLファイル形式で出力しますOffice2003以前では開けないので注意!)。

Open XMLファイルで出力するには、↓のように、pageタグのcontentTypeに「text/xml」を指定し、Open XMLファイル形式に則って記述します(Visualforceページの2~3行目のXMLバージョンとMSO アプリケーションの部分は、Visualforceページの構文の都合上、Visualforceページに直接書けないため、コントローラに記載しています)。同様の方法で、Wordファイルも出力可能です。

・Visualforceページ

<apex:page contenttype="text/xml;charset=UTF-8#test.xml" controller="Excel2Controller">
<apex:outputText escape="true" value="{!xmlVer}" />
<apex:outputText escape="true" value="{!xmlApp}" />
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
 xmlns:o="urn:schemas-microsoft-com:office:office"
 xmlns:x="urn:schemas-microsoft-com:office:excel"
 xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
 xmlns:html="http://www.w3.org/TR/REC-html40">
 <Worksheet ss:Name="Sheet1">
  <Table ss:ExpandedColumnCount="2" ss:ExpandedRowCount="4" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="54" ss:DefaultRowHeight="13.5">
   <Row ss:AutoFitHeight="0">
    <Cell><Data ss:Type="String">表1</Data></Cell>
    <Cell><Data ss:Type="String"></Data></Cell>
   </Row>
   <Row ss:AutoFitHeight="0">
    <Cell><Data ss:Type="String">列1</Data></Cell>
    <Cell><Data ss:Type="String">列2</Data></Cell>
   </Row>
   <Row ss:AutoFitHeight="0">
    <Cell><Data ss:Type="String">あ</Data></Cell>
    <Cell><Data ss:Type="String">A</Data></Cell>
   </Row>
   <Row ss:AutoFitHeight="0">
    <Cell><Data ss:Type="String">い</Data></Cell>
    <Cell><Data ss:Type="String">B</Data></Cell>
   </Row>
  </Table>
 </Worksheet>
 <Worksheet ss:Name="Sheet2">
  <Table ss:ExpandedColumnCount="2" ss:ExpandedRowCount="4" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="54" ss:DefaultRowHeight="13.5">
   <Row ss:AutoFitHeight="0">
    <Cell><Data ss:Type="String">表2</Data></Cell>
    <Cell><Data ss:Type="String"></Data></Cell>
   </Row>
   <Row ss:AutoFitHeight="0">
    <Cell><Data ss:Type="String">列1</Data></Cell>
    <Cell><Data ss:Type="String">列2</Data></Cell>
   </Row>
   <Row ss:AutoFitHeight="0">
    <Cell><Data ss:Type="String">ア</Data></Cell>
    <Cell><Data ss:Type="String">a</Data></Cell>
   </Row>
   <Row ss:AutoFitHeight="0">
    <Cell><Data ss:Type="String">イ</Data></Cell>
    <Cell><Data ss:Type="String">b</Data></Cell>
   </Row>
  </Table>
 </Worksheet>
</Workbook>
</apex:page>

・Apex(コントローラー)

public with sharing class Excel2Controller {
    public String getXmlVer() {
		return '<?xml version="1.0"?>' + '\r\n';
    }

    public String getXmlApp() {
		return '<?mso-application progid="Excel.Sheet"?>' + '\r\n';
    }
}

・出力されるXMLファイル

test.xmlをダウンロード

2011年8月 3日 (水)

ボタン押下時にPDFファイルを添付する方法

今日は、Visualforceで作成したPDFファイルを、ボタン押下時に「メモ&添付ファイル」に保存する方法をご紹介。

○PDFファイル

まず、PDFファイルをVisualforceで作成します。

名前:PdfPage

This is PDF File.

○添付ファイル保存Apex

次に、PDFファイルを「メモ&添付ファイル」に保存するApexを作成します。

カスタムボタンのJavaScriptから呼び出せるようにWebServiceメソッドにし、PDFファイルを保存するレコードのIDを引数で受け取るようにします。

Page.<Visualforceページ>.getContent()で、Visualforceページの本文のデータが取得できます。

global class PdfUtil {
  WebService static void create(ID id) {
    // 添付ファイルに登録
    Attachment attachment = new Attachment();
    attachment.Name = 'Test.pdf';  // ファイル名
    attachment.Body = Page.PdfPage.getContent();  // 本文
    attachment.ParentId = id;  // 関連先
    insert attachment;
  }
}

○カスタムボタン

最後に、上記で作成したApexを呼び出すカスタムボタンを作成し、ページレイアウトに表示します。ここでは、Test__cというオブジェクトにカスタムボタンを作成しています。

表示の種類:詳細ページボタン
動作:JavaScriptを実行
内容のソース:OnClick JavaScript

{!REQUIRESCRIPT('/soap/ajax/20.0/connection.js')} 
{!REQUIRESCRIPT('/soap/ajax/20.0/apex.js')}

sforce.apex.execute(
  'PdfUtil', 'create', {id:'{!Test__c.Id}'});
document.location.reload();
alert('PDFファイルを作成しました。')



動作はこんな感じ↓

ボタン押下前
20110803_1

ボタン押下後

20110803_2

2011年4月26日 (火)

SOQLインジェクションとXSSへの対策方法

○SOQLインジェクション

Salesforceでは、一般のWebサイトと同様に、SOQL内でバインド変数を使用することで、SOQLインジェクションの対策が可能です。

// :name がバインド変数
result = [Select Id From Contact Where Name Like :name];

動的にSOQLを記述する必要がある場合は、StringのescapeSingleQuotesメソッドで、シングルクォーテーションをエスケープして対策します。

// OK
String query = 'Select Id From Contact Where Name Like \'' + String.escapeSingleQuotes(name) + '\'';
result = Database.query(query);

// NG
String query = 'Select Id From Contact Where Name Like \'' + name + '\'';
result = Database.query(query);

○XSS

Salesforceでは、<apex>タグで記述した部分はXSS対策がされます
<apex>タグのescape属性がデフォルトでtrueに設定されているため、自動的にHTMLエスケープされます。
※ escape属性をfalseに指定した場合は、XSS対策がされなくなります。

<apex>タグで記述していない部分のHTMLやJavaScriptについては、一般のWebサイトと同様の方法で、自分でXSS対策を行う必要があります。

<参考>
Developerforce 「Security Tips for Apex and Visualforce Developers」

2010年8月25日 (水)

Visualforceで数値や日付のフォーマットを指定して表示するには?

Visualforceで数値型の項目を、{!ItemNumber}のように差し込み項目で表示しようとすると、「10.0」のように小数で表示されてしまいます。

整数で表示したいんだけど。。。

これは、Visualforceで意外と(?)ハマりがちな問題だと思います。

最初に思い浮かぶのは、項目の設定ですが、
項目の設定で小数点の位置を0にしても、小数で表示されてしまいます。

Visualforceでは、<apex:outputText>タグと<apex:param>タグを使用して、フォーマット指定できます。↓に代表的な例を列挙しますので、参考にして下さい。



	




	




	

フォーマットの設定方法は、JavaのMessageFormatクラスと同じです(こちら)。

採用情報

株式会社フレクトでは、事業拡大のため、
・Salesforce/Force.comのアプリケーション開発
・HerokuやAWSなどのクラウドプラットフォーム上
でのWebアプリケーション開発
エンジニア、マネージャーを募集中です。

未経験でも、これからクラウドをやってみたい方、
是非ご応募下さい。

フレクト採用ページへ

会社紹介

  • 株式会社フレクトは、
      ・認定コンサルタント
      ・認定上級デベロッパー
      ・認定デベロッパー
    が在籍している、セールスフォースパートナーです。
    また、heroku partnersにも登録されています。
    herokuパートナー
    株式会社フレクト
    Salesforce/Force.com
    導入支援サービス
    弊社の認定プロフェッショナルが支援致します。
    ・Visualforce/Apexによるアプリ開発
    ・Salesforceと連携するWebアプリ開発
    も承っております。
    セールスフォースご検討の際は、
    お気軽にお問合せください。

Twitter

リファレンス

■Developer's Guide(リファレンス)
・Apex  HTML | PDF | 日本語PDF | ガバナ制限
・Visualforce  HTML | PDF
・Web Services API  HTML | PDF | 日本語PDF
・Bulk API  HTML | PDF
・REST API  HTML | PDF | 日本語PDF
・Metadata API  HTML | PDF
・Migration Tool  HTML | PDF
・AJAX Toolkit  HTML | PDF
・Data Loader PDF | 日本語PDF

■早見表 (日本語)
数式
Apex
Visualforce
Web Services API
Chatter