Ajax Toolkitを使用したonClick JavaScriptボタンをできるだけコピペでLEXに移行する

SalesforceをClassicからLightning Experienceに移行するときに一番厄介なのがonClick JavaScriptボタンです。

# Lightning Experience Configuration Converter

Winter'19でリリースされた[Lightning Experience Configuration Converter](https://help.salesforce.com/articleView?id=lcc_overview.htm&type=0)でJavaScriptボタンをLightningコンポーネントのクイックアクションとかに自動変換してくれると言うので、まず試してみました。
しかし、私の観測範囲では**JavaScriptボタンは一つも変換できませんでした**:sob:
管理パッケージ内のオブジェクトに追加したカスタムボタンについては**スキャン対象にすらなりませんでした**:sob:

# 手動変換

というわけで手動変換を模索しました。

Lightningコンポーネントのクイックアクションに変換するのが王道なんだと思いますが、それはかなり大変。

結論から言うと、Ajax Toolkitを使用したonClick JavaScriptボタンはVisualforceアクション/ボタンにするとほとんどコピペで移行できそうです。
VisualforceはAjax Toolkitが使えるので、そのまま動きます。もちろんAjax Toolkit使ってないボタンもいけます。

## 例1: JavaScriptだけで何か処理をして特に画面遷移はしないボタン

### コード例

例えば、商談を更新するボタン

```js
{!REQUIRESCRIPT("/soap/ajax/44.0/connection.js")} 
var opp = new sforce.SObject("Opportunity");
opp.id ="{!Opportunity.Id}";
opp.Name = "{!Opportunity.Name}!";
var result = sforce.connection.update([opp]);
window.location.reload();
```

こんなボタンは、以下のようなVisualforceページを作り、Visualforceアクションにすることで、LEXでも普通に動きます。

```html
<apex:page standardController="Opportunity" showQuickActionVfHeader="false">
    <script type="text/javascript">
    var __sfdcSessionId = '{!GETSESSIONID()}';
    </script>
    <script src="../../soap/ajax/44.0/connection.js" type="text/javascript"></script>
    <script src="../../soap/ajax/44.0/apex.js" type="text/javascript"></script>
    <script type='text/javascript' src='/canvas/sdk/js/publisher.js'></script>
    <script type="text/javascript">
var opp = new sforce.SObject("Opportunity");
opp.id ="{!Opportunity.Id}";
opp.Name = "{!Opportunity.Name}!";
var result = sforce.connection.update([opp]);
Sfdc.canvas.publisher.publish({ name: "publisher.close", payload:{ refresh: "true" }});
    </script>
</apex:page>
```

JavaScriptのコード以外のところは、だいたい以下のような定形でOK。

```html
<apex:page standardController="<配置先のオブジェクト名>" showQuickActionVfHeader="false">
    <script type="text/javascript">
    var __sfdcSessionId = '{!GETSESSIONID()}';
    </script>
    <script src="../../soap/ajax/44.0/connection.js" type="text/javascript"></script>
    <script src="../../soap/ajax/44.0/apex.js" type="text/javascript"></script>
    <script type="text/javascript">
// ここにonClickJavaScriptのコードをコピペ
    </script>
</apex:page>
```

`showQuickActionVfHeader="false"`はクイックアクションのダイアログのヘッダー/フッターを消す設定。必要に応じて追加。

`window.location.reload()`のところだけ、以下を追加する。ダイアログを閉じ、閉じたときにレコードページを再読み込みする。

このコードには存在しないが、`OwnerEmail`とかはonClick JavaScriptボタンとVisualforceで差込項目の書き方が違うので、そういう箇所は修正が必要です。

```html
    <script type='text/javascript' src='/canvas/sdk/js/publisher.js'></script>
```

```js
Sfdc.canvas.publisher.publish({ name: "publisher.close", payload:{ refresh: "true" }});
```

### 設定/動作例

カスタムVisualforceアクションを作成して、

![image.png](https://qiita-image-store.s3.amazonaws.com/0/27267/75bb5021-b522-b166-32c3-147060187c00.png)

ページレイアウトに追加して、実行してみると、

![update-opp-name-lex.gif](https://qiita-image-store.s3.amazonaws.com/0/27267/7edc5e2e-1e84-8e50-24d1-095bbb126fb4.gif)

ちゃんと商談名が更新されます（わかりやすいように「####<元の商談名>####」に更新しています）。
ローディングアイコンでも表示するとよりよいですね。

## 例２： 何か処理してVisualforceページに遷移するボタン

### コード例

例えば、あるキューに所属しているかチェックし、所属しているユーザだけVisualforceページに飛ばしたいボタン。

```js
{!REQUIRESCRIPT("/soap/ajax/44.0/connection.js")} 
var executors = sforce.connection.query("SELECT Id FROM GroupMember WHERE Group.DeveloperName = 'Executors' AND UserOrGroupId = '{!$User.Id}'");
var isExecutor = executors.getInt('size') > 0;
if (isExecutor) {
  location.href = "{!URLFOR('/apex/OpportunityCustomPage',null,[id=Opportunity.Id])}";
} else { 
  alert('実行権限がありません。'); 
}
```

こんなボタンも以下のようにscriptタグにコピペして、VisualforceボタンにするとLEXで動く。

```html
<apex:page standardController="Opportunity">
    <script type="text/javascript">
    var __sfdcSessionId = '{!GETSESSIONID()}';
    </script>
    <script src="../../soap/ajax/44.0/connection.js" type="text/javascript"></script>
    <script src="../../soap/ajax/44.0/apex.js" type="text/javascript"></script>
    <script type="text/javascript">
var executors = sforce.connection.query("SELECT Id FROM GroupMember WHERE Group.DeveloperName = 'Executors' AND UserOrGroupId = '{!$User.Id}'");
var isExecutor = executors.getInt('size') > 0;
if (isExecutor) {
  location.href = "{!URLFOR('/apex/OpportunityCustomPage',null,[id=Opportunity.Id])}";
} else {
  alert('実行権限がありません。'); 
}
    </script>
</apex:page>
```

`alert`は格好悪いことになるので必要に応じて修正してください。

ボタンにした場合はダイアログを開くわけではないので、`publisher.close`みたいのは必要ありません。

### 設定/動作例

カスタムVisualforceボタンで作成して、

![image.png](https://qiita-image-store.s3.amazonaws.com/0/27267/ba2ff5aa-c5da-bb72-9906-2914329a63ef.png)

ページレイアウトに追加して、実行すると、

![jump-opp-action-lex.gif](https://qiita-image-store.s3.amazonaws.com/0/27267/86502883-36b2-60da-1172-b26f6376cd57.gif)

ちゃんと遷移します（遷移先は適当です）。
遷移先のVisualforceページがLEXでちゃんと動くかはここでは論じません。

# まとめ

onClick JavaScriptボタンは

* Vfpに遷移するのはVisualforceボタン
* 遷移しないものはVisualforceアクション

に変換するとほとんどコピペで変換できそうだよ。というお話でした。
まあできないのもありますけども。

この方法に何か問題ありそうだったら、ご指摘ください:bow: