Visualforceページでサムネイルっぽいものを作ってみた

# Once upon a time…
Salesforceが大半Lightning Experienceに置き換わってきている今はで需要がなさそうですが、
Salesforce Classic時代にオブジェクトに添付した画像ファイルを画面上で確認できるのは、
それはそれはうれしいことでした。
なにせ関連リストからはファイル名しかわからなかったので、いちいち参照リンクまで遷移しなければなりません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364548/9a4c0482-c05c-e535-cc00-f8b05a031113.png)

そんな時代だったので、サムネイルで添付ファイルを確認できるVisualforceページを作ったことがありました。実装方法としては、jQueryの[fadeIn()](http://semooh.jp/jquery/api/effects/fadeIn/%5Bspeed%5D%2C+%5Bcallback%5D/)、[fadeOut()](http://semooh.jp/jquery/api/effects/fadeOut/%5Bspeed%5D%2C+%5Bcallback%5D/)を使ってアニメーションを作っています。

ということでこんな風に添付画像を並べて、クリックした画像を拡大表示できるようにしてみます。

# Classic時代の実装

下の図のような形でVisualforceページを作成し、main_imgクラスにthumb_imgクラスで選択した画像を表示するようにしてみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364548/de4ee86d-69c9-6a5f-6604-2372ba7a79e0.png)

![test.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364548/f973e3af-b447-5151-4b4d-fd77b29081db.gif)


```javascript:サンプルScript
$(document).ready(function() {
    // サムネイルの変更
    $(function() {
        $('.thumb_img img').mouseover(function() {
            var src = $(this).attr("src");
            // メインの画像をfadeOutさせる
            $('.main_img img').fadeOut(function() {
                // サムネイルの画像をfadeInさせる
                $('.main_img img').attr("src", src).fadeIn();
            });
        })
    });
});
```

上のコードではthumb_img（サムネイルの部分）にマウスオーバーしたときに、その時の
main_img（メイン画像）をフェードアウトさせつつ、thumb_imgをフェードインさせています。
Classic時代は添付ファイルのオブジェクトがAttachmentオンリーだったので、取引先に紐づいているAttachmentを\<apex:repeat>で回して表示していました。画像のURLは`/servlet/servlet.FileDownload?file={attachmentId}`で取得できます。

```html
<div id="main" class="main_box">
    <div class="main_img">
        <img src="/servlet/servlet.FileDownload?file={!defaultImageId}" />
    </div>
    <div class="thumb_img">
        <apex:repeat var="item" value="{!attList}">
            <img src="/servlet/servlet.FileDownload?file={!item.Id}" />
        </apex:repeat>
    </div>
</div>
```

# Lightning Experienceでの実装
Lightning Experienceを使うようになった今、添付ファイルの構造がClassic時代と変わってしまったため、Attachmentだけを取得してきても同じようにサムネイルにすることができなくなってしまいました。
[数式](https://help.salesforce.com/articleView?id=000007604&language=ja&type=1)のサンプルを参考に`/sfc/servlet.shepherd/version/renditionDownload?rendition=ORIGINAL_Jpg&versionId={contentVersionId}`で取得したいところですが、ちょっと曲者で以下のような構成になっています。
![463790_1_En_7_Fig18_HTML.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364548/c394b613-68e0-f268-baf9-0f95fd1fcd33.jpeg)

そのため、以下のように実装しています。

```java
public with sharing class AccountExtentionController {

    /** イメージデータ */
    public List<String> exAttachmentList {get; set;}
    /** デフォルトイメージデータ */
    public String defaultImage {get; private set;}

    /**
     * コンストラクタ
     */
    public accountExtentionController (ApexPages.StandardController controller) {
        this.exAttachmentList = new List<String>();

        // 取引先に紐づく添付ファイルを取得する
        String accountId = ((Account)controller.getRecord()).Id;
        Account acc = [SELECT Id, Name, (SELECT Id FROM CombinedAttachments) FROM Account WHERE Id = :((Account)controller.getRecord()).Id];
        
        // Attachmentのプレフィックスを取得する
        String attachmentPref = Attachment.getSObjectType().getDescribe().getKeyPrefix();

        List<Id> contentDocumentIdList = new List<Id>();
        List<Id> attachmentIdList = new List<Id>();
        for (CombinedAttachment accAttachment : acc.CombinedAttachments) {
            // 添付ファイルがAttachmentの場合
            if (String.valueOf(accAttachment.Id).startsWith(attachmentPref)) {
                attachmentIdList.add(accAttachment.Id);
            // 添付ファイルがContentDocumentの場合
            } else {
                contentDocumentIdList.add(accAttachment.Id);
            }
        }

        // attachmentだけのリストを返す
        for (Attachment att : this.getAttachment(attachmentIdList)) {
            String srcUrl = '/servlet/servlet.FileDownload?file=' + att.Id;
            exAttachmentList.add(srcUrl);
        }

        // contentdocumentだけのリストを返す
        for (ContentDocument cd : this.getContent(contentDocumentIdList)) {
            for (ContentVersion cv : cd.ContentVersions) {
                String srcUrl = '/sfc/servlet.shepherd/version/renditionDownload?rendition=ORIGINAL_Jpg&versionId=' + cv.Id;
                exAttachmentList.add(srcUrl);
            }
        }
        // デフォルトは最初の画像にする
        this.defaultImage = this.exAttachmentList.isEmpty() ? '' : this.exAttachmentList[0];
    }

    /**
     * Attachment取得
     */
    private List<Attachment> getAttachment(List<Id> ids) {
        String allowImage = 'image/jpeg,image/png,mage/gif,image/bmp';
        List<String> contentType = allowImage.split(',');
        return [SELECT Id FROM Attachment WHERE Id IN :ids AND ContentType = :contentType ORDER BY CreatedDate DESC];
    }

    /**
     * ContentDocument取得
     */
    private List<ContentDocument> getContent(List<Id> ids) {
        String allowImage = 'PNG,JPG,GIF,BMP';
        List<String> contentType = allowImage.split(',');
        return [
            SELECT 
                Id, 
                (SELECT Id FROM ContentVersions WHERE IsLatest = true LIMIT 1) 
            FROM 
                ContentDocument
            WHERE 
                Id IN :ids
            AND
                FileType = :contentType
            ORDER BY 
                CreatedDate DESC 
        ];
    }
}
```
[CombinedAttachment](https://developer.salesforce.com/docs/atlas.ja-jp.api.meta/api/sforce_api_objects_combinedattachment.htm)オブジェクトはレコードに紐づいているすべてのファイルが取得できるのですが、LEXで添付したファイルを取得したところ、ContentDocumentのIdとなっていました。そのため、ContentDocumentから再度最新のContentVersionを取得しなおしてURLを作成しています。このままだとたくさんファイルを添付されたときにレイアウトが崩れてしまうので、実際には
取得したリストを絞り込むなどの制限が必要になりそうです。

# サンプル

サンプルでは取引先レコードに紐づく添付ファイルをサムネイルっぽい形式で詳細ページに表示できるようにしています。非管理パッケージをインストールして、取引先レイアウトにVisualforceページを追加すると試すことができます。
[サンプルURL](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t7F0000054iUd)

- LEXで動いているところ
![test2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364548/6222c15d-dfd3-5035-95b5-3d145ce092c6.gif)


# 参考
https://help.salesforce.com/articleView?id=000007604&language=ja&type=1
https://developer.salesforce.com/docs/atlas.ja-jp.218.0.api.meta/api/sforce_api_objects_contentdocument.htm