プログラマ38の日記

主にプログラムメモです。

Salesforce: データの共有に関する設定箇所

Salesforceでは、あるレコードを参照できる人・できない人のセキュリティ制御が可能です。

 

標準機能のグローバル検索やビュー・レポートの結果も参照できるレコードのみで表示されるし、URLを直接変更されて参照できないレコードのIDを指定されてもアクセスできないようになっています。

 

そのレコードを参照できる人・できない人の設定は次があります。

 

共有設定

セキュリティ制御の代表です。「セキュリティのコントロール>共有設定」で、

  • 公開・非公開
  • 階層を使用したアクセス許可
  • 非公開にした場合の共有ルール(所有者ベース・条件ベース)

が可能です。

階層を使用したアクセス許可をすると、ロール階層で、より上位の人はアクセスできるようになります。

取引先や、商談、ケースなどは階層を使用したアクセス許可のチェックが外せないので、そのレコードを参照できる人の上位ロールの人にはレコードが参照できるようになっています。

どうしても、取引先や、商談、ケースなどで上位のロールの人にアクセスさせたくない場合、

  1. 公開グループ(グループAとする)を用意する。
  2. グループAの「階層を使用したアクセス許可」のチェックを外す
  3. レコードの参照・編集権をグループAに付与する
  4. あるユーザ(ユーザ甲とする)には参照させたいけど、ユーザ甲の上位ロールのユーザには見せたくない場合、ユーザ甲をグループAのメンバーにする。

という制御が可能です。(これは、取引先や、商談、ケースだけでなく、階層を使用したアクセス許可にしつつ、階層を使用したくない時があるオブジェクトで有効です)

 

ロール

f:id:crmprogrammer38:20180124095125p:plain

商談やケースを非公開にすると、ロール作成時に共有設定とは別のアクセス権の設定が可能です。

最初は商談やケースを公開にした状態で開発を進めていき、途中から非公開に変更した場合に、この設定を忘れやすいので注意です。

 

取引先チーム・商談チーム・ケースチーム

標準オブジェクトには便利なアクセス制御の仕組みが用意されています。

 

公開ボタン(手動設定)

f:id:crmprogrammer38:20180124095653p:plain

ユーザへ委ねる仕組みです。所有者の上位ロールの人にもボタンは表示されます。

ルール化できないようなアクセス制御はこれを使う事になります。

 

キュー

レコードの所有者にキューを指定すると、そのキューのメンバーはアクセス権が付与されます。(引き受けるの機能はキューとワンセットの機能です)

取引先や商談など、キューが使えないようになっているものもあります。

 

Apex開発(バッチやトリガ)

共有オブジェクト(末尾にShareがつく)に直接レコードを操作することで複雑なアクセス権の制御ができます。

f:id:crmprogrammer38:20180125092226p:plain

上のApex共有の理由とワンセットで実装するもので、作成したApex 共有の理由は、共有オブジェクトの、テリトリー割り当て方法(RowCause)の選択リスト値として使うことができます。

 

番外編 共有セット

今までのアクセス権の設定とは別に、コミュニティ向けのデータ共有の仕組みで共有セットが用意されています。(以前は大規模コミュニティは共有セットでしたが、最近はコミュニティのバリエーションの中で共有セットを使うものがあるようです)

共有セットの仕組みは、所有者のプロファイルがそのコミュニティのプロファイルの場合に、指定した内部ユーザへアクセス権を付与する仕組みです。所有者のプロファイルがコミュニティユーザのプロファイルのままでは、内部ユーザ間の共有ルールのアクセス制御は有効にならないので注意が必要です。

 

最後に

プロファイルのすべてのデータの参照や編集などは割愛していますが、Salesforceのアクセス制御はとても充実してるなーと感じています。その代わり設定するのは大変ですが。。

さらにVisualforceなどの開発した画面では、apex class での without sharing で、レコードのアクセス権関係無しにデータを取得することもできます。

用意されてる仕組みを利用すれば複雑な仕様も実現できます。が、誰にでもわかりやすいシンプルなルールにしておくのが一番かなーと思っています。

Salesforce: visualforce「apex:relatedList」のlist属性で指定できる標準の子リレーション名 (api41)

visualforceの 「apex:relatedList」 で標準で用意される関連リストを表示したい時があります。

api41で使えるlist属性の値のメモです。

 

カスタムオブジェクトの標準関連リストで次をvisualforceで個別に表示します。

f:id:crmprogrammer38:20180115141628p:plain

 listで次を指定することで個別に指定できます。

関連リスト listの値
活動予定 OpenActivities
活動履歴 ActivityHistories
メモ AttachedContentNotes
ファイル AttachedContentDocuments
メモ & 添付ファイル CombinedAttachments
承認履歴 ProcessSteps
グループ RecordAssociatedGroups
履歴 Histories  ※項目変更履歴を取得した際の変更履歴一覧

 

レイアウトをそのまま出したいなら「apex:detail」を使えばいいのですが、個別に関連リストを出したい時は上記のlistで指定することになります。「apex:detail」の関連リストには表示可能なものが、「apex:relatedList」で表示できないものもあります。

 

 

 

最後に

メモ & 添付ファイル のlistの値が以前使っていた値「NotesAndAttachments」が使えなくない事に気づき、その他調べた結果をメモしてみました。

 

 

雑記: Sony 「Music Center for PC」をインストール後、「x-アプリ」を削除したら「Music Center for PC」が起動しなくなった

普段、Walkmanで音楽を聞いています。

CDの取り込み用のアプリケーション「x-アプリ」をインストールしていたPCに「Music Center for PC」をインストールしました。

その後「x-アプリ」をコントロールパネルから削除したら「Music Center for PC」が起動しなくなりました。

※「x-アプリ」は終了して、「Music Center for PC」に移行されています。

 

インストールされていたバージョンや、削除する際の手順(すべてのコンポーネントを削除するとかの違い)で発生したのだと思います。

 

削除しなければいいだけなのですが、不要なファイルを残しておくのが嫌なんですよね。。プログラマなので職業病だと思います。

 

「Music Center for PC」をインストールし直そうと思ったら、次のエラーが出てインストールできなくなりました。

Windowsのアップデートが完了していません。」(うろ覚え)

 

結論としては、「x-アプリ」の削除に問題があったようで、コントロールパネルから「x-アプリ」だけを削除するのではなく、もう1つsony製のソフトウェア「Sony Media Library Earth」を削除する必要がありました。

 

そのアプリケーションも削除した後に、「Music Center for PC」を再インストールすることができ事なきを得ました。

 

今後、同じ目にあった時のためにメモしておこうと思います。(ただ、エラーメッセージもうろ覚えで、役に立つのかという疑問もありますが)

 

Walkmanが使えなくなるかと思い本当に焦りました。ノイズキャンセルは本当に便利に使っています。周りの音を遮断できるのはいいですよね。

Java: いまさらになってStream APIをさわってみた

今さらですが、java8で追加されたStream APIをさわってみました。

自分でコードを書く際には使いませんが、別の人のコードを読むときや、Stream APIを使って書かれたコードを修正する時には知ってたほうがいいかなーと思っていて、ざっくりと理解しようとした時のメモです。

※なので、全てを把握してはいません。

 

  • リスト、マップを便利に使える仕組み、forを使って色々処理していることをメソッドとしてコードが書ける
  • メソッドには、各要素に対して操作するもの、要素全体に対して操作するものがある

 

forで定型的なプログラムを書く手間を省くための仕組みだと理解しました。

よくあるリストを使った処理を想定してかき分けてみたいと思います。

 

処理1:

リストの要素をSQLの in の検索条件として使う

    

//このリストを検索条件にしたい

List<String> searchlist = Arrays.asList("レキシ","B'z","サカナクション");





//streamを使った場合

String searchstr = searchlist.stream()

         .map(s-> "'" + s.replaceAll("'", "''") + "'")

         .collect(Collectors.joining(", ", "searchfield in (", ")"));







//今まで通りのfor文を使った場合

StringJoiner tempjoiner = new StringJoiner(", ", "searchfield in (", ")");

for(String s : searchlist) {

    tempjoiner.add("'" + s.replaceAll("'", "''") + "'");

}
String searchstr2 = tempjoiner.toString();

どちらで書いても「searchfield in ('レキシ', 'B''z', 'サカナクション')」の、SQLのWhere句の文字列となります。

上記のポイントとしてシングルクオートはエスケープしています。値の中にシングルクオートを持つ名詞を探したらB'zを見つけたので、ミュージシャンでリストを作成してみました。

streamを使っても使わなくてもあまり変わらないように思います。StringJoinerを使ってるせいかもしれませんが。

 

処理2:

リストの要素をフィルタして別のリストを作る

// 事前準備 start ********************************** 

//こんなクラスを用意

public class UserInfo {

    String username;

    boolean isActive;

    

    public UserInfo(String username, boolean isActive) {

        this.username = username;

        this.isActive = isActive;

    }

}



//こんなリストを用意

UserInfo u1 = new UserInfo("A", true);

UserInfo u2 = new UserInfo("B", false);

UserInfo u3 = new UserInfo("C", true);



List<UserInfo> users = Arrays.asList(u1,u2,u3);

// 事前準備 end  ********************************** 





//streamを使った書き方

List<UserInfo> activeUsers = users.stream()

        .filter(u-> u.isActive == true)

        .collect(Collectors.toList());

List<UserInfo> inactiveUsers = users.stream()

        .filter(u-> u.isActive == false)

        .collect(Collectors.toList());





//for文を使った書き方

List<UserInfo> activeUsersF = new ArrayList<>();

List<UserInfo> inactiveUsersF = new ArrayList<>();



for(UserInfo u : users) {

    if( u.isActive == true ) activeUsersF.add(u);

    if( u.isActive == false ) inactiveUsersF.add(u);                

}    

プログラムでこういうフィルタがしたいということが明確で、streamだとわかりやすさがあります。

 

最後に

上記のメモでは、Streamのmap, collect, filterメソッドだけを使っていますが、Streamにはたくさんのメソッドが用意されていて、例えば、ソート用のsorted、それぞれの要素で処理を行うforEachなどがあります。

さらにcollectメソッドで使うCollectorsクラスには、返却するリストやマップ、文字列をさらに細かく指定できるメソッドが用意されています。StreamとCollectorsを抑えればStream APIで困ることはないかなと考えています。

 

あと、なんでもそうですが、できるからといって技を駆使してプログラムを書くとメンテナンスできないものになります。(perlとかは最たる例だと思います。)

streamは仕様変更には弱そうだなと直感的に感じていて、普通の人が普通にプログラム修正するには癖が強いと思いました。

実際に書く際には、ここまでは利用してここから先は利用しない、もしくはこういった処理だけ利用するなどの線引きが必要だなーと思いました。

 

雑記: 個人環境でhttpプロキシを使いたい時はSquidを使おう

Excel VBAでプログラムを書いておきたいなと思い、今までにいくつかExcel VBAのプログラムを書いてきました。

せっかくなのでSalesforceAPIを操作して楽ができるツールがいいなと思い書いてきたのですが、通信制御のところで不具合があることがわかりました。もう今は全て修正したのですが、不具合は次のものです。

 

エラー内容

「httpプロキシで、Basic認証をしている環境で、システムエラーが発生する。」

 

具体的には次のコードが駄目です。(少しシンプルにしています)

    Set http = CreateObject("WinHttp.WinHttpRequest.5.1")

    http.setProxy 2, proxyhost & ":" & proxyport, ""

    http.setCredentials proxyusername, proxypassword, 1

    http.Open "POST", sfendpoint, False

上の「setCredentials 」で、httpプロキシのBasic認証のためのユーザ、パスワードを指定しているのですが、この関数は「Open」関数の後に呼び出しをしないとエラーとなります。なので、正しくは、次のようになります。

    Set http = CreateObject("WinHttp.WinHttpRequest.5.1")

    http.setProxy 2, proxyhost & ":" & proxyport, ""

    http.Open "POST", sfendpoint, False

    http.setCredentials proxyusername, proxypassword, 1

いつもhttpプロキシを使わずに通信する環境で作っているためhttpプロキシを経由して通信するテストができていなかったことが原因です。

なので環境を用意してテストできるようにしたいと思いました。

 

 

ローカル環境のhttpプロキシ

ローカルにhttpプロキシを立てるには「Squid」というツールが便利で、情報も揃っています。(ただバージョンの違いで設定も多少違うようです)

インターネットにアクセスするプログラムをローカルで開発する際に、httpプロキシ経由、そしてBasic認証のテストをする時にとても便利です。

使い方は検索すればたくさん出てくると思いますが、Windows10でインストールした時のメモです。

  • Squid.msiをダウンロード(Squid-3.5 64bit)してインストール
  • インストール後、[インストールフォルダ]etcsquidsquid.conf を変更(※1)
  • ユーザ/パスワード用のファイルを用意(※2)
  • インストール後、タスクトレイから「Start Squid Service」 (既にStartしていた場合は、StopしてからStart)
    f:id:crmprogrammer38:20180105194310p:plain
  • ブラウザのプロキシ設定をしてインターネットアクセス時にユーザ/パスワードを求められればOK

※1  とりあえずこれで動いている詳細を把握して設定していないです。

#

# Recommended minimum configuration:

#



auth_param basic program /lib/squid/basic_ncsa_auth /etc/squid/.htpasswd

auth_param basic children 5

auth_param basic realm Squid proxy-caching web server

auth_param basic credentialsttl 2 hours

auth_param basic casesensitive off





# Example rule allowing access from your local networks.

# Adapt to list your (internal) IP networks from where browsing

# should be allowed



acl localnet src 10.0.0.0/8	# RFC1918 possible internal network

acl localnet src 172.16.0.0/12	# RFC1918 possible internal network

acl localnet src 192.168.0.0/16	# RFC1918 possible internal network

acl localnet src fc00::/7       # RFC 4193 local private network range

acl localnet src fe80::/10      # RFC 4291 link-local (directly plugged) machines

acl localnet src 192.168.1.0/255.255.255.0



acl SSL_ports port 443

acl Safe_ports port 80		# http

acl Safe_ports port 21		# ftp

acl Safe_ports port 443		# https

acl Safe_ports port 70		# gopher

acl Safe_ports port 210		# wais

acl Safe_ports port 1025-65535	# unregistered ports

acl Safe_ports port 280		# http-mgmt

acl Safe_ports port 488		# gss-http

acl Safe_ports port 591		# filemaker

acl Safe_ports port 777		# multiling http

acl CONNECT method CONNECT

acl password proxy_auth REQUIRED





visible_hostname PCNAME



#

# Recommended minimum Access Permission configuration:

#



# Only allow cachemgr access from localhost

http_access allow localhost manager

http_access deny manager

http_access allow localnet

http_access allow password



# Deny requests to certain unsafe ports

http_access deny !Safe_ports



# Deny CONNECT to other than secure SSL ports

http_access deny CONNECT !SSL_ports



# We strongly recommend the following be uncommented to protect innocent

# web applications running on the proxy server who think the only

# one who can access services on "localhost" is a local user

#http_access deny to_localhost



#

# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS

#



# Example rule allowing access from your local networks.

# Adapt localnet in the ACL section to list your (internal) IP networks

# from where browsing should be allowed

http_access allow localnet

http_access allow localhost



# And finally deny all other access to this proxy

http_access deny all



# Squid normally listens to port 3128

http_port 3128



# Uncomment the line below to enable disk caching - path format is /cygdrive/, i.e.

#cache_dir aufs /cygdrive/d/squid/cache 3000 16 256





# Leave coredumps in the first cache dir

coredump_dir /var/cache/squid



# Add any of your own refresh_pattern entries above these.

refresh_pattern ^ftp:		1440	20%	10080

refresh_pattern ^gopher:	1440	0%	1440

refresh_pattern -i (/cgi-bin/|?) 0	0%	0

refresh_pattern .		0	20%	4320



dns_nameservers 8.8.8.8 208.67.222.222



max_filedescriptors 3200



 ※2 ユーザ/パスワードをroot/rootとした時のBasic認証のファイル(.htpasswd)の中身

root:/0ZQd5ttqDR8w

 

最後に

ローカルにhttpプロキシの環境が作成できてとても有難いなと思いました。

本番の通信環境とは異なる環境で開発することの方が多いので本番の通信環境に近い形でテストができるのはとても助かります。

雑記: Windowsのバッチ開発で便利なコマンド

Windowsサーバでバッチ処理を開発したり、バッチ処理の稼動を確認する時に便利だなと思ったコマンドのメモです。

 

よく使うコマンド(引数込み) コマンドで実現できること 利用したい時 備考
CERTUTIL -decode  [intputfile] [outputfile] base64形式のテキストファイル[intputfile]からバイナリファイル[outputfile]へ変更する。 Salesforceのデータローダで添付ファイルをCSV出力した後、CSV内の添付ファイル内容(base64形式)をファイルに変換する。

Windodws標準コマンド

 

もちろんCSV出力時にはヘッダやダブルクオテーションなど不要な文字が含まれているので純粋にbase64部分だけのテキストにする必要はあります。

xcopy /T /E /I [inputdirectory] [outputdirectory] ディレクトリの構成だけをコピーする。 中に含まれているファイルのサイズが大きい、またはファイルの数が多い場合に、ディレクトリの構造だけコピーする。 Windodws標準コマンド
[標準出力に表示するコマンド] | clip コマンドの標準出力をクリップボードにコピーする。 例えば「 dir /b | clip」などでファイルの一覧をクリップボードへコピーする。 Windodws標準コマンド
tail -n50 -f [inputfile] ファイルの末尾を表示する。 ログファイルの確認をする。 gnuコマンド
cygwinなどでコマンドのインストールが必要

利用する際には環境変数「LANG」に「ja_JP.SJIS」を指定するとShift_JISのファイルが文字化けしません。
[標準出力に表示するコマンド] | tee -a [outputfile] 標準出力をそのまま表示しつつ、ファイルにも出力する。 コマンドの実行結果を標準出力で確認しつつ、ログファイルにも保存する。 gnuコマンド
cygwinなどでコマンドのインストールが必要

 

unix系でバッチ処理の開発を行う際には当たり前のように使うコマンドがWindowsだと使えなくて不便なのですが、cygwinなどでgnuコマンドを使えばもどかしい思いをしなくてすみます。(もちろん完全に問題なく動作するわけではないので、確認しながらになりますが)

cygwin使うならbase64コマンドもあるからCERTUTILもいらないんですけど、標準で使えるのでいいかなと思います。

 

最後に

PowerShell使えばいいんだと思いますが、今さら感が強くて覚える気にならないんです。PowerShell駆使したいなら、最初からunixでいいのでは?と思ってしまいますし。

Java: webserviceクライアント別のWSDL complexTypeのany要素の使い方

SalesforceのPartner WSDLSOAP APIを使う時、sObjectのフィールド値をセットする時の書き方が各ライブラリによって大分違っています。

 

書き方の違いは、WSDLでの、complexType で anyの要素が各ライブラリで使い方が違うところに起因します。

 

各ライブラリでの書き方は次の通りとなります。ライブラリは、axis1.4、axis2、 wsimport、apache cxf、wscを対象とします。

 

axis1.4

import org.apache.axis.message.MessageElement;
import com.sforce.soap.partner.sobject.SObject;
import javax.xml.namespace.QName;
-------------------------------------------------

SObject sobject = new SObject();
sobject.setType("Sample__c");

MessageElement f1 = new MessageElement();
f1.setQName(new QName("Field1__c"));
f1.setValue("SampleValue1");

MessageElement f2 = new MessageElement();
f2.setQName(new QName("Field2__c"));
f2.setValue("SampleValue2");

sobject.set_any(new MessageElement[]{f1, f2});

axis2 (xmlbeans)

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import com.sforce.soap.partner.sobject.SObject;
----------------------------------------------------

SObject sobject = SObject.Factory.newInstance();
sobject.setType("Sample__c");

Document sobjdoc = sobject.getDomNode().getOwnerDocument();

Element f1 = sobjdoc.createElement("Field1__c");
Node v1 = sobjdoc.createTextNode("SampleValue1");
f1.appendChild(v1);

Element f2 = sobjdoc.createElement("Field2__c");
Node v2 = sobjdoc.createTextNode("SampleValue2");
f2.appendChild(v2);

sobject.getDomNode().appendChild(f1);
sobject.getDomNode().appendChild(f2);

wsimport・apache cxf

import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.sforce.soap.partner.sobject.SObject;
--------------------------------------------------------------

SObject sobject = new SObject();
sobject.setType("Sample__c");

Document doc = null; 
try{
  doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
} catch(Exception ignored){}

Element f1 = doc.createElement("Field1__c");
f1.appendChild(doc.createTextNode("SampleValue1"));

Element f2 = doc.createElement("Field1__c");
f2.appendChild(doc.createTextNode("SampleValue1"));

sobject.getAny().add(f1);
sobject.getAny().add(f2);

wsc

import com.sforce.soap.partner.sobject.SObject;
------------------------------------------------------

SObject sobject = new SObject();
sobject.setType("Sample__c");

sobject.addField("Field1__c" , "SampleValue1");
sobject.addField("Field2__c" , "SampleValue2");

 

最後に

各ライブラリによってだいぶ書き方が違うなーと思います。

 

wscはSalesforceに最適化されているので、Salesforceの項目の型に合わせてセットするJavaの型が異なります。

Salesforce:date・datetime  ⇔ Java:Calendar

Salesforce:int ⇔ Java:Integer

Salesforce:boolean ⇔ Java:Boolean

Salesforce:base64Java:byte[]

それ以外は文字列となります、文字列中にxmlで使用不可の文字(制御コードなど)はライブラリ側が自動で取り除いてくれます。

 

wsc以外は、全ての項目は文字列となりますが、Salesforceの型に合わない文字列を指定すると、apiコールそのものがエラーとなります。

Salesforceの各型へセットする文字は、次の通りとなります。

Salesforce : date ⇒ yyyy-MM-dd

Salesforce : datetime ⇒ yyyy-MM-dd'T'HH:mm:ss'.000'Z

Salesforce : 数値の項目 ⇒ 数値に変換可能な数字

Salesforce : booelan ⇒ true or false

Salesforce : base64base64変換後の文字列

それ以外は文字列となりますが、文字列中にxmlで使用不可の文字(制御コードなど)は自動で取り除いてくれないので、自身で取り除く必要があります。