かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot でメール送信する Web アプリケーションを作る ( その21 )( 気になった点を修正2 )

概要

Spring Boot でメール送信する Web アプリケーションを作る ( その20 )( 気になった点を修正 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Thymeleaf テンプレートファイルの共通部分を読み込むタグを meta → style/script へ修正します。
    • Pagable を元に SelectOptions のインスタンスを生成するための util クラスを作成します。
    • 値とテキストの定義を YAML ファイル+Constant クラス → enum へ変更します。

ソフトウェア一覧

参考にしたサイト

手順

Thymeleaf テンプレートファイルの共通部分を読み込むタグを修正する

Thymeleaf テンプレートファイル内で共通の css, js ファイルを読み込む部分を meta タグで書いていたのですが、さすがに meta タグはないなと思ったので 読み込むファイルの中で一番記述されているタグに合わせて link, script タグへ修正します。

  1. IntelliJ IDEA で 1.0.x-replace-htmltag ブランチを作成します。

  2. src/main/resources/templates/mailsend の下の mailsend.html を リンク先のその1の内容 に変更します。

  3. src/main/resources/templates/mailsearch の下の mailsearch.html を リンク先のその1の内容 に変更します。

  4. 画面が表示されるか確認します。Gradle projects View から bootRun タスクを実行して Tomcat を起動します。

  5. ブラウザを起動し http://localhost:8080/mailsearch へアクセスします。画面が表示され、カーソルも「To」の項目にセットされることを確認します。

  6. ブラウザを起動し http://localhost:8080/mailsend へアクセスします。画面が表示され、カーソルも「From」の項目にセットされることを確認します。

  7. Ctrl+F2 を押して Tomcat を停止します。

  8. commit、GitHub へ Push、1.0.x-replace-htmltag -> 1.0.x へ Pull Request、1.0.x でマージ、1.0.x-replace-htmltag ブランチを削除、をします。

Pagable を元に SelectOptions のインスタンスを生成するための util クラスを作成する

  1. IntelliJ IDEA で 1.0.x-selectoptions-helper ブランチを作成します。

  2. src/main/java/ksbysample/webapp/email/util の下に SelectOptionsUtils.java を新規作成します。作成後、リンク先の内容 に変更します。

  3. src/main/java/ksbysample/webapp/email/web/mailsearch の下の MailsearchService.javaリンク先の内容 に変更します。

  4. Gradle projects View から bootRun タスクを実行して Tomcat を起動し、ページングが正常に動作するか確認します。確認後、Ctrl+F2 を押して Tomcat を停止します。

  5. commit、GitHub へ Push、1.0.x-selectoptions-helper -> 1.0.x へ Pull Request、1.0.x でマージ、1.0.x-selectoptions-helper ブランチを削除、をします。

値とテキストの定義を YAML ファイル+Constant クラス → enum へ変更する

値とテキストの組み合わせを定義するのに YAML ファイル+Constant.java で実装していますが、この方法だと FormValidator クラスで Form クラスの値をチェックする時等に利用しにくく "1" のように実際の値を書いていたので、enum で定義するよう変更して Java のソースファイルや Thymeleaf テンプレートファイル内では作成した enum を参照するようにしてみます。

今回は Constant.java 内の TYPE_MAP のみ enum へ変更します。それ以外は今のままです。

また Domaドメインクラスにすることも考えたのですが、DomaGen で自動生成された Entity クラスを変更するのがちょっと手間だったので、やめました。

  1. IntelliJ IDEA で 1.0.x-constant-enum ブランチを作成します。

  2. src/main/java/ksbysample/webapp/email の下に domain パッケージを作成します。

  3. src/main/java/ksbysample/webapp/email/domain の下に InquiryType.java を新規作成します。作成後、リンク先の内容 に変更します。

  4. src/main/java/ksbysample/webapp/email/web/mailsend の下の MailsendFormValidator.javaリンク先の内容 に変更します。

  5. src/main/resources/templates/mailsend の下の mailsend.html を リンク先のその2の内容 に変更します。

  6. src/main/resources/templates/mailsearch の下の mailsearch.html を リンク先のその2の内容 に変更します。

  7. src/main/java/ksbysample/webapp/email/helper/mail の下の MAIL001MailHelper.javaリンク先の内容 に変更します。

  8. src/test/java/ksbysample/webapp/email/helper/mail の下の MAIL001MailHelperTest.javaリンク先の内容 に変更します。

  9. src/test/java/ksbysample/webapp/email/web/mailsend の下の MailsendServiceTest.javaリンク先の内容 に変更します。

  10. src/main/resources の下の constant.yml を リンク先の内容 に変更します。

  11. src/test/java/ksbysample/webapp/email/config の下の Constant.javaリンク先の内容 に変更します。

  12. Project View のルートでコンテキストメニューを表示して「Run 'Tests in 'ksbysample...' with Coverage」を選択し、テストが全て成功することを確認します。

  13. Gradle projects View から build タスクを実行し、"BUILD SUCCESSFUL" が出力されることを確認します。

  14. Gradle projects View から bootRun タスクを実行して Tomcat を起動し、以下の点を確認します。確認後、Ctrl+F2 を押して Tomcat を停止します。

    • メール送信画面(http://localhost:8080/mailsend)の「項目」のドロップダウンリストにテキストが正常に表示される。
    • メール送信画面から「送信」ボタンをクリックしてテキストメールを送信した時に、テキストメール内の「項目」にテキストが正常に表示される。
    • メール送信画面から「HTMLメール送信」ボタンをクリックしてHTMLメールを送信した時に、HTMLメール内の「項目」にテキストが正常に表示される。
    • 送信済メール検索画面(http://localhost:8080/mailsearch)の検索条件の「項目」、一覧の「項目」にテキストが正常に表示される。
  15. commit、GitHub へ Push、1.0.x-constant-enum -> 1.0.x へ Pull Request、1.0.x でマージ、1.0.x-constant-enum ブランチを削除、をします。

  16. 最後に感想です。

    • enum で実装した方が Java のソースファイル内に定数を直書きしなくて済むようになるので、こちらの方が便利です。このプロジェクトは修正しませんが、次からは enum で実装するようにします。
    • 値とテキストの組み合わせをまとめているクラスであることをクラス名を見て判断できた方が分かりやすい気がしました。今回は単に InquiryType としましたが、クラス名を ~Enum とか ~Values とかにした方がよさそうなので次からはそうしてみるつもりです。

ソースコード

mailsend.html

■その1

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>ksbysample-webapp-email</title>
    <meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'/>

    <link th:replace="common/head-cssjs"/>
    <style>
    <!--
    .checkbox label,
    .radio label {
        padding-right: 10px;
    }
    .form-control-static {
        padding-top: 0px;
        padding-bottom: 0px;
    }
    .has-error .form-control {
        background-color: #fff5ee;
    }
    -->
    </style>
</head>
<body class="skin-blue">
<div class="wrapper">

    <!-- Main Header -->
    <div th:replace="common/mainparts :: main-header"></div>

    <!-- Left side column. contains the logo and sidebar -->
    <div th:replace="common/mainparts :: main-sidebar (active='mailsend')"></div>

    <!-- Content Wrapper. Contains page content -->
    <div class="content-wrapper">
        <!-- Content Header (Page header) -->
        <section class="content-header">
            <h1>
                メール送信画面
            </h1>
        </section>

        <!-- Main content -->
        <section class="content">
            <div class="row">
                <div class="col-xs-12">
                    <form id="mailsendForm" method="post" action="/mailsend/send" th:action="@{/mailsend/send}" th:object="${mailsendForm}" class="form-horizontal">
                        <div class="callout callout-danger" th:if="${#fields.hasGlobalErrors()}">
                            <p th:each="err : ${#fields.globalErrors()}" th:text="${err}">共通エラーメッセージ表示エリア</p>
                        </div>

                        <div class="box box-primary">
                            <div class="box-body">
                                <div class="form-group" th:classappend="${#fields.hasErrors('*{fromAddr}')} ? 'has-error' : ''">
                                    <label for="fromAddr" class="control-label col-sm-2">From</label>
                                    <div class="col-sm-10">
                                        <div class="row"><div class="col-sm-8"><div class="input-group"><span class="input-group-addon"><i class="fa fa-envelope"></i></span><input type="text" name="fromAddr" id="fromAddr" class="form-control input-sm" value="" placeholder="Fromアドレスを入力して下さい" th:field="*{fromAddr}"/></div></div></div>
                                        <div class="row" th:if="${#fields.hasErrors('*{fromAddr}')}"><div class="col-sm-10"><p class="form-control-static text-danger"><small th:errors="*{fromAddr}">ここにエラーメッセージを表示します</small></p></div></div>
                                    </div>
                                </div>

                                <div class="form-group" th:classappend="${#fields.hasErrors('*{toAddr}')} ? 'has-error' : ''">
                                    <label for="toAddr" class="control-label col-sm-2">To</label>
                                    <div class="col-sm-10">
                                        <div class="row"><div class="col-sm-8"><div class="input-group"><span class="input-group-addon"><i class="fa fa-envelope"></i></span><input type="text" name="toAddr" id="toAddr" class="form-control input-sm" value="" placeholder="Toアドレスを入力して下さい" th:field="*{toAddr}"/></div></div></div>
                                        <div class="row" th:if="${#fields.hasErrors('*{toAddr}')}"><div class="col-sm-10"><p class="form-control-static text-danger"><small th:errors="*{toAddr}">ここにエラーメッセージを表示します</small></p></div></div>
                                    </div>
                                </div>

                                <div class="form-group" th:classappend="${#fields.hasErrors('*{subject}')} ? 'has-error' : ''">
                                    <label for="subject" class="control-label col-sm-2">Subject</label>
                                    <div class="col-sm-10">
                                        <div class="row"><div class="col-sm-12"><input type="text" name="subject" id="subject" class="form-control input-sm" value="" placeholder="件名を入力して下さい" th:field="*{subject}"/></div></div>
                                        <div class="row" th:if="${#fields.hasErrors('*{subject}')}"><div class="col-sm-10"><p class="form-control-static text-danger"><small th:errors="*{subject}">ここにエラーメッセージを表示します</small></p></div></div>
                                    </div>
                                </div>

                                <div class="form-group" th:classappend="${#fields.hasErrors('*{name}')} ? 'has-error' : ''">
                                    <label for="name" class="control-label col-sm-2">氏名</label>
                                    <div class="col-sm-10">
                                        <div class="row"><div class="col-sm-8"><input type="text" name="name" id="name" class="form-control input-sm" value="" placeholder="(例) 田中 太郎" th:field="*{name}"/></div></div>
                                        <div class="row" th:if="${#fields.hasErrors('*{name}')}"><div class="col-sm-10"><p class="form-control-static text-danger"><small th:errors="*{name}">ここにエラーメッセージを表示します</small></p></div></div>
                                    </div>
                                </div>

                                <div class="form-group" th:classappend="${#fields.hasErrors('*{sex}')} ? 'has-error' : ''">
                                    <label class="control-label col-sm-2">性別</label>
                                    <div class="col-sm-10">
                                        <div class="row"><div class="col-sm-12">
                                            <div class="radio">
                                                <label th:each="sex : ${T(ksbysample.webapp.email.config.Constant).getInstance().SEX_MAP.entrySet()}">
                                                    <input type="radio" name="sex" th:value="${sex.getKey()}" th:text="${sex.getValue()}" th:field="*{sex}"/>
                                                </label>
                                            </div>
                                        </div></div>
                                        <div class="row" th:if="${#fields.hasErrors('*{sex}')}"><div class="col-sm-10"><p class="form-control-static text-danger"><small th:errors="*{sex}">ここにエラーメッセージを表示します</small></p></div></div>
                                    </div>
                                </div>

                                <div class="form-group" th:classappend="${#fields.hasErrors('*{type}')} ? 'has-error' : ''">
                                    <label class="control-label col-sm-2">項目</label>
                                    <div class="col-sm-10">
                                        <div class="row"><div class="col-sm-8">
                                            <select name="type" id="type" class="form-control input-sm" th:field="*{type}">
                                                <option th:each="type : ${T(ksbysample.webapp.email.config.Constant).getInstance().TYPE_MAP.entrySet()}"
                                                        th:value="${type.getKey()}"
                                                        th:text="${type.getValue()}">sex</option>
                                            </select>
                                        </div></div>
                                        <div class="row" th:if="${#fields.hasErrors('*{type}')}"><div class="col-sm-10"><p class="form-control-static text-danger"><small th:errors="*{type}">ここにエラーメッセージを表示します</small></p></div></div>
                                    </div>
                                </div>

                                <div class="form-group" th:classappend="${#fields.hasErrors('*{item}')} ? 'has-error' : ''">
                                    <label class="control-label col-sm-2">商品</label>
                                    <div class="col-sm-10">
                                        <div class="row"><div class="col-sm-12">
                                            <div class="checkbox">
                                                <label th:each="item : ${T(ksbysample.webapp.email.config.Constant).getInstance().ITEM_MAP.entrySet()}">
                                                    <input type="checkbox" name="item" th:value="${item.getKey()}" th:text="${item.getValue()}" th:field="*{item}"/>
                                                </label>
                                            </div>
                                        </div></div>
                                        <div class="row" th:if="${#fields.hasErrors('*{item}')}"><div class="col-sm-10"><p class="form-control-static text-danger"><small th:errors="*{item}">ここにエラーメッセージを表示します</small></p></div></div>
                                    </div>
                                </div>

                                <div class="form-group" th:classappend="${#fields.hasErrors('*{naiyo}')} ? 'has-error' : ''">
                                    <label for="naiyo" class="control-label col-sm-2">内容</label>
                                    <div class="col-sm-10">
                                        <div class="row"><div class="col-sm-12"><textarea rows="5" name="naiyo" id="naiyo" class="form-control input-sm" placeholder="お問い合わせ内容を入力して下さい" th:field="*{naiyo}"></textarea></div></div>
                                        <div class="row" th:if="${#fields.hasErrors('*{naiyo}')}"><div class="col-sm-10"><p class="form-control-static text-danger"><small th:errors="*{naiyo}">ここにエラーメッセージを表示します</small></p></div></div>
                                    </div>
                                </div>

                                <div class="form-group" th:classappend="${#fields.hasErrors('*{attachedFile}')} ? 'has-error' : ''">
                                    <label class="control-label col-sm-2">添付</label>
                                    <div class="col-sm-10">
                                        <div class="row"><div class="col-sm-12">
                                            <div class="checkbox">
                                                <label>
                                                    <input type="checkbox" name="attachedFile" th:value="1" th:text="ファイルを添付する" th:field="*{attachedFile}"/>
                                                </label>
                                            </div>
                                        </div></div>
                                        <div class="row" th:if="${#fields.hasErrors('*{item}')}"><div class="col-sm-10"><p class="form-control-static text-danger"><small th:errors="*{item}">ここにエラーメッセージを表示します</small></p></div></div>
                                    </div>
                                </div>
                            </div>
                            <div class="box-footer">
                                <div class="text-center">
                                    <button type="button" id="send" value="send" class="btn btn-primary">送信</button>
                                    <button type="button" id="sendhtml" value="sendhtml" class="btn btn-default">HTMLメール送信</button>
                                </div>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </section>
        <!-- /.content -->
    </div>
    <!-- /.content-wrapper -->

</div>
<!-- ./wrapper -->

<!-- REQUIRED JS SCRIPTS -->

<script th:replace="common/bottom-js"/>
<script type="text/javascript">
<!--
$(document).ready(function() {
    $('#fromAddr').focus();

    $('#send').bind('click', function(){
        $('#mailsendForm').submit();
    });

    $('#sendhtml').bind('click', function(){
        $('#mailsendForm').attr('action', '/mailsend/sendhtml');
        $('#mailsendForm').submit();
    });
});
-->
</script>
</body>
</html>
  • <meta th:replace="common/head-cssjs"/><link th:replace="common/head-cssjs"/> へ変更します。
  • <div th:replace="common/bottom-js"></div><script th:replace="common/bottom-js"/> へ変更します。

■その2

                                <div class="form-group" th:classappend="${#fields.hasErrors('*{type}')} ? 'has-error' : ''">
                                    <label class="control-label col-sm-2">項目</label>
                                    <div class="col-sm-10">
                                        <div class="row"><div class="col-sm-8">
                                            <select name="type" id="type" class="form-control input-sm" th:field="*{type}">
                                                <option th:each="type : ${T(ksbysample.webapp.email.domain.InquiryType).values()}"
                                                        th:value="${type.getValue()}"
                                                        th:text="${type.getText()}">sex</option>
                                            </select>
                                        </div></div>
                                        <div class="row" th:if="${#fields.hasErrors('*{type}')}"><div class="col-sm-10"><p class="form-control-static text-danger"><small th:errors="*{type}">ここにエラーメッセージを表示します</small></p></div></div>
                                    </div>
                                </div>
  • <option th:each="type : ${T(ksbysample.webapp.email.config.Constant).getInstance().TYPE_MAP.entrySet()}<option th:each="type : ${T(ksbysample.webapp.email.domain.InquiryType).values()}" へ変更します。それに伴い th:value, th:text に記述する部分で呼び出すメソッドも変更します。

mailsearch.html

■その1

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>ksbysample-webapp-email</title>
    <meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'/>

    <link th:replace="common/head-cssjs"/>
    <style>
    <!--
    .form-group {
        margin-bottom: 5px;
    }
    .table {
        margin-top: 10px;
        margin-bottom: 0px;
    }
    .table>tbody>tr>td
    , .table>tbody>tr>th
    , .table>tfoot>tr>td
    , .table>tfoot>tr>th
    , .table>thead>tr>td
    , .table>thead>tr>th {
        padding: 5px;
    }
    .checkbox label,
    .radio label {
        padding-right: 10px;
    }
    .pagination > .disabled > a
    , .pagination > .disabled > a:focus
    , .pagination > .disabled > a:hover
    , .pagination > .disabled > span
    , .pagination > .disabled > span:focus
    , .pagination > .disabled > span:hover {
        color: #eee;
        cursor: not-allowed;
        background-color: #fff;
        border-color: #ddd;
    }
    -->
    </style>
</head>
<body class="skin-blue">
<div class="wrapper">

    <!-- Main Header -->
    <div th:replace="common/mainparts :: main-header"></div>

    <!-- Left side column. contains the logo and sidebar -->
    <div th:replace="common/mainparts :: main-sidebar (active='mailsearch')"></div>

    <!-- Content Wrapper. Contains page content -->
    <div class="content-wrapper">
        <!-- Content Header (Page header) -->
        <section class="content-header">
            <h1>
                送信済メール検索画面
            </h1>
        </section>

        <!-- Main content -->
        <section class="content">
            <div class="row">
                <div class="col-xs-12">
                    <div class="box">
                        <div class="box-header with-border bg-purple-gradient">
                            <div class="row">
                                <div class="col-xs-12">
                                    <form id="mailSearchForm" method="post" action="/mailsearch" th:action="@{/mailsearch}" th:object="${mailsearchForm}" class="form-horizontal">
                                        <div class="form-group">
                                            <label for="toAddr" class="control-label col-sm-2">To</label>
                                            <div class="col-sm-10">
                                                <div class="row"><div class="col-sm-8"><div class="input-group"><span class="input-group-addon"><i class="fa fa-envelope"></i></span><input type="text" name="toAddr" id="toAddr" class="form-control input-sm" value="" placeholder="" th:field="*{toAddr}"/></div></div></div>
                                            </div>
                                        </div>

                                        <div class="form-group">
                                            <label for="subject" class="control-label col-sm-2">Subject</label>
                                            <div class="col-sm-10">
                                                <div class="row"><div class="col-sm-12"><input type="text" name="subject" id="subject" class="form-control input-sm" value="" placeholder="" th:field="*{subject}"/></div></div>
                                            </div>
                                        </div>

                                        <div class="form-group">
                                            <label for="name" class="control-label col-sm-2">氏名</label>
                                            <div class="col-sm-10">
                                                <div class="row"><div class="col-sm-8"><input type="text" name="name" id="name" class="form-control input-sm" value="" placeholder="" th:field="*{name}"/></div></div>
                                            </div>
                                        </div>

                                        <div class="form-group">
                                            <label class="control-label col-sm-2">項目</label>
                                            <div class="col-sm-10">
                                                <div class="row"><div class="col-sm-12">
                                                    <div class="checkbox">
                                                        <label th:each="type : ${T(ksbysample.webapp.email.config.Constant).getInstance().TYPE_MAP.entrySet()}">
                                                            <input type="checkbox" name="type" th:value="${type.getKey()}" th:text="${type.getValue()}" th:field="*{type}"/>
                                                        </label>
                                                    </div>
                                                </div></div>
                                            </div>
                                        </div>
                                        <div class="text-center">
                                            <button type="button" id="search" value="search" class="btn btn-primary bg-gray">検索</button>
                                        </div>
                                    </form>
                                </div>
                            </div>
                        </div>
                        <div class="box-body">
                            <div id="maillist_wrapper" class="dataTables_wrapper form-inline" role="grid">
                                <table id="maillist" class="table table-bordered table-hover dataTable" aria-describedby="maillist_info">
                                    <thead class="bg-purple">
                                        <tr role="row">
                                            <th role="columnheader" tabindex="0" aria-controls="maillist" rowspan="1" colspan="1">To</th>
                                            <th role="columnheader" tabindex="0" aria-controls="maillist" rowspan="1" colspan="1">Subject</th>
                                            <th role="columnheader" tabindex="0" aria-controls="maillist" rowspan="1" colspan="1">氏名</th>
                                            <th role="columnheader" tabindex="0" aria-controls="maillist" rowspan="1" colspan="1">項目</th>
                                        </tr>
                                    </thead>
                                    <tbody role="alert" aria-live="polite" aria-relevant="all">
                                        <tr th:each="email : ${page.content}">
                                            <td th:text="${email.toAddr}">test@sample.com</td>
                                            <td th:text="${email.subject}">件名1</td>
                                            <td th:text="${email.name}">田中 太郎</td>
                                            <td th:text="${T(ksbysample.webapp.email.config.Constant).getInstance().TYPE_MAP.get(email.type)}">資料請求</td>
                                        </tr>
                                    </tbody>
                                </table>

                                <div id="pagenation" th:include="common/pagenation :: pagenation (url='/mailsearch', page=${page}, ph=${ph})"></div>

                                <form id="pagenationForm" method="post" action="#" th:action="@{#}" th:object="${mailsearchForm}">
                                    <input type="hidden" name="toAddr" id="toAddr" th:value="*{toAddr}"/>
                                    <input type="hidden" name="subject" id="subject" th:value="*{subject}"/>
                                    <input type="hidden" name="name" id="name" th:value="*{name}"/>
                                    <input type="hidden" name="type" id="type" th:each="typeItem : *{type}" th:value="${typeItem}"/>
                                </form>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </section>
        <!-- /.content -->
    </div>
    <!-- /.content-wrapper -->

</div>
<!-- ./wrapper -->

<!-- REQUIRED JS SCRIPTS -->

<script th:replace="common/bottom-js"/>
<script type="text/javascript">
<!--
$(document).ready(function() {
    $('#toAddr').focus();

    $('#search').bind('click', function(){
        $('#mailSearchForm').submit();
    });

    $('.js-pagenation').each(function(){
        $(this).click(function(){
            $('#pagenationForm').attr('action', $(this).attr('href'));
            $(this).attr('href', '#');
            $('#pagenationForm').submit();
        });
    });
});
-->
</script>
</body>
</html>
  • <meta th:replace="common/head-cssjs"/><link th:replace="common/head-cssjs"/> へ変更します。
  • <div th:replace="common/bottom-js"></div><script th:replace="common/bottom-js"/> へ変更します。

■その2

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>ksbysample-webapp-email</title>
    <meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'/>

    <link th:replace="common/head-cssjs"/>
    <style>
    <!--
    .form-group {
        margin-bottom: 5px;
    }
    .table {
        margin-top: 10px;
        margin-bottom: 0px;
    }
    .table>tbody>tr>td
    , .table>tbody>tr>th
    , .table>tfoot>tr>td
    , .table>tfoot>tr>th
    , .table>thead>tr>td
    , .table>thead>tr>th {
        padding: 5px;
    }
    .checkbox label,
    .radio label {
        padding-right: 10px;
    }
    .pagination > .disabled > a
    , .pagination > .disabled > a:focus
    , .pagination > .disabled > a:hover
    , .pagination > .disabled > span
    , .pagination > .disabled > span:focus
    , .pagination > .disabled > span:hover {
        color: #eee;
        cursor: not-allowed;
        background-color: #fff;
        border-color: #ddd;
    }
    -->
    </style>
</head>
<body class="skin-blue">
<div class="wrapper">

    <!-- Main Header -->
    <div th:replace="common/mainparts :: main-header"></div>

    <!-- Left side column. contains the logo and sidebar -->
    <div th:replace="common/mainparts :: main-sidebar (active='mailsearch')"></div>

    <!-- Content Wrapper. Contains page content -->
    <div class="content-wrapper">
        <!-- Content Header (Page header) -->
        <section class="content-header">
            <h1>
                送信済メール検索画面
            </h1>
        </section>

        <!-- Main content -->
        <section class="content">
            <div class="row">
                <div class="col-xs-12">
                    <div class="box">
                        <div class="box-header with-border bg-purple-gradient">
                            <div class="row">
                                <div class="col-xs-12">
                                    <form id="mailSearchForm" method="post" action="/mailsearch" th:action="@{/mailsearch}" th:object="${mailsearchForm}" class="form-horizontal">
                                        <div class="form-group">
                                            <label for="toAddr" class="control-label col-sm-2">To</label>
                                            <div class="col-sm-10">
                                                <div class="row"><div class="col-sm-8"><div class="input-group"><span class="input-group-addon"><i class="fa fa-envelope"></i></span><input type="text" name="toAddr" id="toAddr" class="form-control input-sm" value="" placeholder="" th:field="*{toAddr}"/></div></div></div>
                                            </div>
                                        </div>

                                        <div class="form-group">
                                            <label for="subject" class="control-label col-sm-2">Subject</label>
                                            <div class="col-sm-10">
                                                <div class="row"><div class="col-sm-12"><input type="text" name="subject" id="subject" class="form-control input-sm" value="" placeholder="" th:field="*{subject}"/></div></div>
                                            </div>
                                        </div>

                                        <div class="form-group">
                                            <label for="name" class="control-label col-sm-2">氏名</label>
                                            <div class="col-sm-10">
                                                <div class="row"><div class="col-sm-8"><input type="text" name="name" id="name" class="form-control input-sm" value="" placeholder="" th:field="*{name}"/></div></div>
                                            </div>
                                        </div>

                                        <div class="form-group">
                                            <label class="control-label col-sm-2">項目</label>
                                            <div class="col-sm-10">
                                                <div class="row"><div class="col-sm-12">
                                                    <div class="checkbox">
                                                        <label th:each="type : ${T(ksbysample.webapp.email.domain.InquiryType).values()}">
                                                            <input type="checkbox" name="type" th:value="${type.getValue()}" th:text="${type.getText()}" th:field="*{type}"/>
                                                        </label>
                                                    </div>
                                                </div></div>
                                            </div>
                                        </div>
                                        <div class="text-center">
                                            <button type="button" id="search" value="search" class="btn btn-primary bg-gray">検索</button>
                                        </div>
                                    </form>
                                </div>
                            </div>
                        </div>
                        <div class="box-body">
                            <div id="maillist_wrapper" class="dataTables_wrapper form-inline" role="grid">
                                <table id="maillist" class="table table-bordered table-hover dataTable" aria-describedby="maillist_info">
                                    <thead class="bg-purple">
                                        <tr role="row">
                                            <th role="columnheader" tabindex="0" aria-controls="maillist" rowspan="1" colspan="1">To</th>
                                            <th role="columnheader" tabindex="0" aria-controls="maillist" rowspan="1" colspan="1">Subject</th>
                                            <th role="columnheader" tabindex="0" aria-controls="maillist" rowspan="1" colspan="1">氏名</th>
                                            <th role="columnheader" tabindex="0" aria-controls="maillist" rowspan="1" colspan="1">項目</th>
                                        </tr>
                                    </thead>
                                    <tbody role="alert" aria-live="polite" aria-relevant="all">
                                        <tr th:each="email : ${page.content}">
                                            <td th:text="${email.toAddr}">test@sample.com</td>
                                            <td th:text="${email.subject}">件名1</td>
                                            <td th:text="${email.name}">田中 太郎</td>
                                            <td th:text="${T(ksbysample.webapp.email.domain.InquiryType).getText(email.type)}">資料請求</td>
                                        </tr>
                                    </tbody>
                                </table>

                                <div id="pagenation" th:include="common/pagenation :: pagenation (url='/mailsearch', page=${page}, ph=${ph})"></div>

                                <form id="pagenationForm" method="post" action="#" th:action="@{#}" th:object="${mailsearchForm}">
                                    <input type="hidden" name="toAddr" id="toAddr" th:value="*{toAddr}"/>
                                    <input type="hidden" name="subject" id="subject" th:value="*{subject}"/>
                                    <input type="hidden" name="name" id="name" th:value="*{name}"/>
                                    <input type="hidden" name="type" id="type" th:each="typeItem : *{type}" th:value="${typeItem}"/>
                                </form>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </section>
        <!-- /.content -->
    </div>
    <!-- /.content-wrapper -->

</div>
<!-- ./wrapper -->

<!-- REQUIRED JS SCRIPTS -->

<script th:replace="common/bottom-js"/>
<script type="text/javascript">
<!--
$(document).ready(function() {
    $('#toAddr').focus();

    $('#search').bind('click', function(){
        $('#mailSearchForm').submit();
    });

    $('.js-pagenation').each(function(){
        $(this).click(function(){
            $('#pagenationForm').attr('action', $(this).attr('href'));
            $(this).attr('href', '#');
            $('#pagenationForm').submit();
        });
    });
});
-->
</script>
</body>
</html>
  • 検索条件入力部の以下の点を変更します。
    • <label th:each="type : ${T(ksbysample.webapp.email.config.Constant).getInstance().TYPE_MAP.entrySet()}"><label th:each="type : ${T(ksbysample.webapp.email.domain.InquiryType).values()}"> へ変更します。
    • <input type="checkbox" name="type" th:value="${type.getKey()}" th:text="${type.getValue()}" th:field="*{type}"/><input type="checkbox" name="type" th:value="${type.getValue()}" th:text="${type.getText()}" th:field="*{type}"/> へ変更します。
  • 一覧表示部の以下の点を変更します。
    • <td th:text="${T(ksbysample.webapp.email.config.Constant).getInstance().TYPE_MAP.get(email.type)}">資料請求</td><td th:text="${T(ksbysample.webapp.email.domain.InquiryType).getText(email.type)}">資料請求</td> へ変更します。

SelectOptionsUtils.java

package ksbysample.webapp.email.util;

import org.seasar.doma.jdbc.SelectOptions;
import org.springframework.data.domain.Pageable;

public class SelectOptionsUtils {

    public static SelectOptions get(Pageable pageable, boolean countFlg) {
        int offset = pageable.getPageNumber() * pageable.getPageSize();
        int limit = pageable.getPageSize();

        SelectOptions selectOptions = null;
        if (countFlg) {
            selectOptions = SelectOptions.get().offset(offset).limit(limit).count();
        }
        else {
            selectOptions = SelectOptions.get().offset(offset).limit(limit);
        }

        return selectOptions;
    }
    
}

MailsearchService.java

package ksbysample.webapp.email.web.mailsearch;

import ksbysample.webapp.email.entity.Email;
import ksbysample.webapp.email.util.SelectOptionsUtils;
import org.seasar.doma.jdbc.SelectOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class MailsearchService {

    @Autowired
    private MailsearchDao mailsearchDao;
    
    public Page<Email> searchEmail(MailsearchForm mailsearchForm, Pageable pageable) {
        SelectOptions options = SelectOptionsUtils.get(pageable, true);
        List<Email> emailList = mailsearchDao.selectCondition(mailsearchForm, options);
        long count = options.getCount();

        Page<Email> page = new PageImpl<Email>(emailList, pageable, count);
        return page;
    }

}
  • SelectOptions のインスタンスを取得する方法を SelectOptionsUtils.get(pageable, true); に変更します。

InquiryType.java

package ksbysample.webapp.email.domain;

import lombok.Getter;
import org.apache.commons.lang3.StringUtils;

@Getter
public enum InquiryType {

    SHIRYO_SEIKYU("1", "資料請求")
    , SHOHIN_NI_KANSURU_KUJO("2", "商品に関する苦情")
    , SONOTA("3", "その他");

    private final String value;
    private final String text;
    
    private InquiryType(String value, String text) {
        this.value = value;
        this.text = text;
    }
    
    public static String getText(String value) {
        String result = "";
        for (InquiryType inquiryType : InquiryType.values()) {
            if (StringUtils.equals(inquiryType.getValue(), value)) {
                result = inquiryType.getText();
            }
        }

        return result;
    }
    
}

MailsendFormValidator.java

package ksbysample.webapp.email.web.mailsend;

import ksbysample.webapp.email.config.Constant;
import ksbysample.webapp.email.domain.InquiryType;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

@Component
public class MailsendFormValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz) {
        return clazz.equals(MailsendForm.class);
    }

    @Override
    public void validate(Object target, Errors errors) {
        MailsendForm mailsendForm = (MailsendForm)target;
        Constant constant = Constant.getInstance();

        // 「項目」が「資料請求」「商品に関する苦情」の場合には「商品」が何も選択されていない場合にはエラー
        if (StringUtils.equals(mailsendForm.getType(), InquiryType.SHIRYO_SEIKYU.getValue())
                || StringUtils.equals(mailsendForm.getType(), InquiryType.SHOHIN_NI_KANSURU_KUJO.getValue())) {
            if ((mailsendForm.getItem() == null) || (mailsendForm.getItem().size() == 0)) {
                errors.reject("mailsendForm.item.noSelect");
            }
        }

        // 「項目」が「その他」の場合には「内容」に何も入力されていない場合にはエラー
        if (StringUtils.equals(mailsendForm.getType(), InquiryType.SONOTA.getValue())) {
            if (mailsendForm.getNaiyo().length() == 0) {
                errors.reject("mailsendForm.naiyo.noText");
            }
        }
    }

}
  • mailsendForm.getType() と値を比較していた部分に具体的な値を記述していたのを InquiryType で記述するよう変更します。StringUtils.equals(mailsendForm.getType(), "1")StringUtils.equals(mailsendForm.getType(), InquiryType.SHIRYO_SEIKYU.getValue()) のようになります。

MAIL001MailHelper.java

    private String generateTextUsingVelocity(MailsendForm mailsendForm) {
        Constant constant = Constant.getInstance();
        Map<String, Object> model = new HashMap<>();
        model.put("name", mailsendForm.getName());
        model.put("sex", constant.SEX_MAP.get(mailsendForm.getSex()));
        model.put("type", InquiryType.getText(mailsendForm.getType()));

        String itemList = null;
        if (mailsendForm.getItem() != null) {
            itemList = mailsendForm.getItem().stream()
                    .map(constant.ITEM_MAP::get)
                    .collect(Collectors.joining(", "));
        }
        model.put("item", itemList);

        model.put("naiyo", mailsendForm.getNaiyo());
        return velocityUtils.merge(this.TEMPLATE_LOCATION_TEXTMAIL, model);
    }

    private String generateTextUsingThymeleaf(MailsendForm mailsendForm) {
        Constant constant = Constant.getInstance();
        Context ctx = new Context(LocaleContextHolder.getLocale());
        ctx.setVariable("name", mailsendForm.getName());
        ctx.setVariable("sex", constant.SEX_MAP.get(mailsendForm.getSex()));
        ctx.setVariable("type", InquiryType.getText(mailsendForm.getType()));

        String itemList = null;
        if (mailsendForm.getItem() != null) {
            itemList = mailsendForm.getItem().stream()
                    .map(constant.ITEM_MAP::get)
                    .collect(Collectors.joining(", "));
        }
        ctx.setVariable("item", itemList);

        ctx.setVariable("naiyo", mailsendForm.getNaiyo());
        
        return this.templateEngine.process(this.TEMPLATE_LOCATION_HTMLMAIL, ctx);
    }
  • generateTextUsingVelocity, generateTextUsingThymeleaf メソッド内の constant.TYPE_MAP.get(mailsendForm.getType())InquiryType.getText(mailsendForm.getType()) へ変更します。

MAIL001MailHelperTest.java

        @Test
        public void MailsendFormの全てに値がセットされている場合() throws Exception {
            MimeMessage message = mail001MailHelper.createHtmlMessage(mailsendFormSimple);

            // from, Subject
            InternetAddress address = (InternetAddress) message.getFrom()[0];
            assertThat(address.getAddress(), is(mailsendFormSimple.getFromAddr()));
            assertThat(message.getSubject(), is(mailsendFormSimple.getSubject()));

            // メール本文
            Document document = DocumentBuilderFactory
                    .newInstance()
                    .newDocumentBuilder()
                    .parse(new InputSource(new StringReader((String) message.getContent())));
            Constant constant = Constant.getInstance();
            assertThat(document, hasXPath("//*[@id=\"name\"]", equalTo(mailsendFormSimple.getName())));
            assertThat(document, hasXPath("//*[@id=\"sex\"]", equalTo(constant.SEX_MAP.get(mailsendFormSimple.getSex()))));
            assertThat(document, hasXPath("//*[@id=\"type\"]", equalTo(InquiryType.getText(mailsendFormSimple.getType()))));
            assertThat(document, hasXPath("//*[@id=\"item\"]"
                    , equalTo(
                    mailsendFormSimple.getItem().stream()
                            .map(constant.ITEM_MAP::get)
                            .collect(Collectors.joining(", ")))));
            assertThat(document, hasXPath("//*[@id=\"naiyo\"]", equalTo(mailsendFormSimple.getNaiyo())));
        }
  • 「MailsendFormの全てに値がセットされている場合」テストメソッド内の constant.TYPE_MAP.get(mailsendForm.getType())InquiryType.getText(mailsendForm.getType()) へ変更します。

MailsendServiceTest.java

    @Test
    public void mailsendFormSimpleでHTMLメールを送信する場合() throws Exception {
        mailsendService.saveAndSendHtmlEmail(mailsendFormSimple);

        // email, email_item テーブルに保存されているか確認する
        IDataSet dataSet = new CsvDataSet(new File("src/test/resources/ksbysample/webapp/email/web/mailsend/testdata/simple"));
        TableDataAssert tableDataAssert = new TableDataAssert(dataSet, dataSource);
        tableDataAssert.assertEquals("email", new String[]{"email_id"});
        tableDataAssert.assertEquals("email_item", new String[]{"email_item_id", "email_id"});

        // メールが送信されているか確認する
        assertThat(mailServer.getMessagesCount(), is(1));
        MimeMessage receiveMessage = mailServer.getFirstMessage();
        assertThat(receiveMessage.getFrom()[0], is(new InternetAddress("test@sample.com")));
        assertThat(receiveMessage.getAllRecipients()[0], is(new InternetAddress("xxx@yyy.zzz")));
        assertThat(receiveMessage.getSubject(), is("テスト"));

        // メール本文
        Document document = DocumentBuilderFactory
                .newInstance()
                .newDocumentBuilder()
                .parse(new InputSource(new StringReader((String) receiveMessage.getContent())));
        Constant constant = Constant.getInstance();
        assertThat(document, hasXPath("//*[@id=\"name\"]", equalTo(mailsendFormSimple.getName())));
        assertThat(document, hasXPath("//*[@id=\"sex\"]", equalTo(constant.SEX_MAP.get(mailsendFormSimple.getSex()))));
        assertThat(document, hasXPath("//*[@id=\"type\"]", equalTo(InquiryType.getText(mailsendFormSimple.getType()))));
        assertThat(document, hasXPath("//*[@id=\"item\"]"
                , equalTo(
                mailsendFormSimple.getItem().stream()
                        .map(constant.ITEM_MAP::get)
                        .collect(Collectors.joining(", ")))));
        assertThat(document, hasXPath("//*[@id=\"naiyo\"]", equalTo(mailsendFormSimple.getNaiyo())));
    }
  • 「mailsendFormSimpleでHTMLメールを送信する場合」テストメソッド内の constant.TYPE_MAP.get(mailsendForm.getType())InquiryType.getText(mailsendForm.getType()) へ変更します。

constant.yml

!!ksbysample.webapp.email.config.Constant
SEX_MAP:
  1: 男性
  2: 女性
ITEM_MAP:
  101: 商品1
  102: 商品2
  103: 商品3
  • TYPE_MAP の定義を削除します。

Constant.java

package ksbysample.webapp.email.config;

import org.springframework.stereotype.Component;
import org.yaml.snakeyaml.Yaml;

import java.util.Map;

@Component
public class Constant {

    private static final Constant instance = (Constant) new Yaml().load(Constant.class.getResourceAsStream("/constant.yml"));

    public static Constant getInstance() {
        return instance;
    }

    // 性別
    public Map<String, String> SEX_MAP;

    // 商品
    public Map<String, String> ITEM_MAP;

}
  • public Map<String, String> TYPE_MAP; を削除します。

履歴

2015/06/21
初版発行。