Salesforce Lightning Experience移行への道のり ~第三章 Classic⇔LEXユーザのコミュニケーション改善~

いつまでも あるとおもうな 親とClassic

どうも、星野です。
いやさすがに廃止ってことはないと思うのですが、Salesforceは今後どうするのでしょうか。
やはり早い段階でLEX(Lightning Experience)に切り替えておくのが良いと思います。

弊社ではLEXの準備が整ってきたので、実際にエンドユーザに使ってもらって不具合をつぶすトライアル運用を開始しました。
ここで運用上大きな問題となったのが、「ClassicユーザとLEXユーザの混在」です。
同じ部署内でトライアルメンバーとそうでないメンバーがいる場合に、
ClassicユーザとLEXユーザのコミュニケーションが結構大変だということに気が付きました。

ClassicとLEXで違うURL

例えば自分が作成したレポートを共有しようとURLをコピーして相手に送る場合を考えます。

ClassicでのレポートURL:https://SFのドメイン/レポートのID
LEXでのレポートURL:https://SFのLEX用ドメイン/lightning/r/Report/レポートのID/view

これらをコピーすることになるのですが、

ClassicURLをLEXユーザに送った場合:リダイレクトされてLEX画面で開く

LEXURLをClassicユーザに送った場合:LightningExperienceの権限がありません とエラーが出て閲覧不可

となります。
こうなると同じ部署にいながら情報共有がスムーズに行えない可能性があります。
しかしLEXURLをClassicURLにいちいち戻して開くなどという面倒な作業はユーザにお願いできない・・・
そもそもLEXユーザに対してClassicユーザのほうが圧倒的に多く、
もし何かお願いするならLEX側のユーザにお願いしたほうがいろいろと効率が良い・・・

我々が考えた対処方法は2点ありました。
1.トライアルメンバー以外にもLEXの権限だけ付与して、URLを開くだけ開いて、Classicに戻してもらう
2.LEXユーザが何かしらの方法でClassic用URLを取得して送る

1の方法は全員に権限だけ割り当てておき、トライアルメンバー以外はClassicで利用してもらうだけで済むのでこれで行けたらいいなと思いました。
ただ、これには大きな落とし穴があったのです。
ユーザのモードと受け取ったURLの組み合わせでどう画面が開くかを簡単に図にしました。

ClassicユーザがLEXURLを受け取った場合、LEXで開きます。
この状態から「Salesforce Classicに切り替え」を押すと・・・

なんと必ずホームに戻ってしまうのです!

つまりその前まで見ていたものが画面上から消えてしまうのです。
もちろん検索とかすればたどり着けるのですがそれは面倒で・・・ユーザに受け入れてもらえる気がしませんでした。

そこで、2のほうを採用しました。

今見ているLEX画面をClassicで開くためのURLを取得するグローバルアクションを作りました。

グローバルアクション:ClassicURL取得

内部はこんな感じです。

LexUrlConverter4GlobalAction.cmp

<aura:component implements="force:lightningQuickAction,flexipage:availableForAllPageTypes" access="global">
    <aura:attribute name="progress" type="String" />
    <aura:attribute name="isDone" type="Boolean" />
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
    <aura:handler name="change" value="{!v.isDone}" action="{!c.closeQA}" />
    <div class="container">
        <p>変換中 ....</p>
        <p>{!v.progress} %</p>
    </div>
</aura:component>

LexUrlConverter4GlobalAction.css

.THIS.container {
    text-align: center;
    margin-top:60px;
    color:gray;
    font-size: 3.5em;
}

LexUrlConverter4GlobalActionController.js

({
    doInit: function (component, event, helper) {

        // メッセージ表示用のメソッドを定義
        let displayMsg = (msg, type) => {
            let count = 0;
            let interval = 7;
            let toastEvent = $A.get("e.force:showToast");
            let id = window.setInterval(function () {
                count += 1;
                component.set("v.progress", count);
                if (count == 100) {
                    clearInterval(id);
                    component.set("v.isDone", true);
                    toastEvent.setParams({
                        type: type,
                        mode: "sticky",
                        message: msg,
                    });
                    toastEvent.fire();
                }
            }
                , interval
            )
        };

        // 定数を定義
        const ID_INDEX = 4;
        const LEX_SYMBOL_INDEX = 1;
        const LEX_SYMBOL = "lightning";

        // URLを取得
        const inputUrl = window.location.origin + window.location.pathname;

        // URLが取得出来なかったら
        if(inputUrl.length < 1){
            displayMsg("ClassicのURLに変換できませんでした。手動で変換してください。", "error");
            return;
        }

        // URLを分解
        let url;
        let domain;
        let paths;
        try{
            url = new URL(inputUrl);
            domain = url.host;
            paths = url.pathname.split("/");
        } catch (e) {
            displayMsg("ClassicのURLに変換できませんでした。手動で変換してください。", "error");
            return;
        }

        // URLのフォーマットがおかしい場合
        if (paths.length < 5 || paths[LEX_SYMBOL_INDEX] !== LEX_SYMBOL) {
            displayMsg("ClassicのURLに変換できませんでした。手動で変換してください。", "error");
            return;
        }

        // URLが想定しているフォーマットじゃない場合(LEXじゃない or レコードページのURLじゃない場合)
        if (paths[ID_INDEX].length != 15 && paths[ID_INDEX].length != 18) {
            displayMsg("ClassicのURLに変換できませんでした。手動で変換してください。", "error");
            return;           
        }

        // Classic URLを生成
        const classicHost = domain.replace('lightning.force.com', 'my.salesforce.com');
        const classicURL = `https://${classicHost}/${paths[ID_INDEX]}`;

        // クリップボードにコピー
        let dummy = document.createElement("textarea");
        document.body.appendChild(dummy);
        dummy.value = classicURL;
        dummy.select();
        document.execCommand("copy");
        document.body.removeChild(dummy);

        // 結果を出力
        displayMsg("ClassicのURLをクリップボードにコピーしました。", "success");

    },

    closeQA: function (component, event, helper) {
        $A.get("e.force:closeQuickAction").fire();
    },

})

こちらを押すと、今見ているURLをClassic用に変換し、クリップボードにコピーされます。
あとは貼り付けるだけ。
変換といってもそんな凝ったことはしていなくて、URLの構成から決め打ちでID値のみを取得し、Classicのドメインとくっつけているだけ。

この方法だとパラメータが落ちるので、例えばレポートに動的な検索条件パラメータをつけている場合などは思った通りのレポート結果が得られません。そのあたりは拡張してみてください。

導入した結果

これがあることでURLが開けないというお問い合わせはほとんど来ませんでした。
LEXユーザ側がClassicURLを取得して送ってくれているのだと思います。

こういった気遣いが結構その後の展開面でも心証を変えてくると思うので、設置してよかったなと思いました。

さいごに

システムに大きな変更を加えるとき、今回のように一部ユーザに実運用を回してもらう手法はよくあることだと思います。
開発負担としては正式リリース後の変更部分だけでなく、その過程における配慮もそれなりに綿密な計画が必要だと感じました。(個人的には高速道路の工事を思い出しました。いったんこっちに迂回させて~みたいな)

LEXにするというのは見た目の変更インパクトが大きいですが、特にClassicユーザの混在がある場合、URLの変更も考慮が必要になるので、ぜひこの経験を皆様のLEX移行に役立てていただければと思います!

Pocket
LINEで送る