SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

ASP.NETで作成する評価フォーム

Webサイト上で評価を集計し結果を画像表示するユーティリティの作成

  • このエントリーをはてなブックマークに追加

本稿では、Webサイト上でユーザーに写真を5段階評価してもらい、その評価結果をグラフィカルに表示するユーティリティを、ASP.NETで作成します。結果表示は、集計結果から画像を動的に作成する方法で行います。

  • このエントリーをはてなブックマークに追加

はじめに

 私にはJakeという息子が生まれたばかりで、早く写真を見せろという要求が親類縁者から引きも切らず寄せられてきます。Web開発者の端くれとして、当然、赤ん坊のためのWebサイトを作り、写真をすべてそこに載せることを考えました。さらに、Jakeの崇拝者がそれぞれ好きな写真に投票し、毎月末、見事1位に輝いた写真の光り輝くプリントが各自の郵便受けに送り届けられるというアイデアはどうでしょうか。この計画を実行に移すには、写真1枚1枚の評価を継続的に受け付けることが必要で、サイト上でそれをやってくれるユーティリティを開発しなければなりません。まず、そのユーティリティにどのような機能が必要かを考えました。

計画

 どのようなプログラミングに取りかかるときも、私はまず問題を把握し、解決手順の概要を思い描くことに多少の時間を費やします。このユーティリティに望まれる機能は何でしょうか。次のように書き出してみました。

  1. サイトユーザーに各写真を評価してもらう。
  2. 写真の評価は1~5の5段階とする。
  3. 各写真の評価結果をグラフィカルに表示する。
  4. フィードバックをもらう(ピンぼけ防止装置をあげようか、という祖母からの言葉を期待して)。

設計

 次に写真評価フォームを設計しました。図1のフォームがそれです。このフォームの背後にあるコードは次のとおりです。

図1
図1
<form runat="server">
  <asp:Linkbutton text="Rate this muggle photo"
                  id="Rate_This_Photo_Button"
                  OnClick="Show_Rate_Article" runat="server" />
  <div id="Rate_Form" runat="server" visible="false">

  <table width="178" align="center" cellpadding="5" cellspacing="5">
    <tr>
      <td valign="top">
        <table>
          <tr>
            <td valign="top">Rating:<br></td>
            <td>
              <asp:RadioButtonList cellpadding="0" cellspacing="0" 
                          id="MyRadioButtonList" runat="server">
                    <asp:ListItem value="1">*</asp:ListItem>
                    <asp:ListItem value="2">* *</asp:ListItem>
                    <asp:ListItem value="3">* * *</asp:ListItem>
                    <asp:ListItem value="4">* * * *</asp:ListItem>
                    <asp:ListItem value="5">* * * * *</asp:ListItem>
                </asp:RadioButtonList>
                <asp:RequiredFieldValidator id="Reqd_Radios" 
                              ControlToValidate="MyRadioButtonList" 
                              ErrorMessage="Please rate the muggle." 
                              Runat="server" />
            </td>
          </tr>
        </table>
        Comments:<br>
        <asp:Textbox TextMode="multiline" rows="5" cols="15"
                     runat="server" id="Comments" /><br>
        <asp:Linkbutton text="Submit" OnClick="Submit_Rating"
                        runat="server" />
      </td>
    </tr>
  </table>
  </div>
</form>

 このフォームは、RadioButtonListコントロール(MyRadioButtonList)と、複数行のテキストボックス、そして下記のSubmit_Ratingイベントハンドラ(サブルーチン)をトリガーするリンクボタンから成ります。他に、ラジオボタンのリストから必ず1個を選択してもらいたいので、RequiredFieldValidatorというコントロールを加えました(RequiredFieldValidatorなど、妥当性検査のためのASP.NETコントロールについては、『Form Validation with ASP.NET - It Doesn't Get Any Easier』を参照してください)。これが必要なのは、私のデータベーステーブルでは評価フィールドにnullを認めていないためです。

 さて、フォームに入力されたすべての値をデータベースに挿入しなければなりません。それには、sp_Muggle_PhotoRatingInsertというストアドプロシージャを使うことにしました。最初にデータベース接続とSQLCommandオブジェクトをセットアップします。データベースとの接続には、「web.config」ファイルのappSettings部分に格納されているDSN(データソース名)を使用します(データベースにレコードを追加するコードのサンプルを見たい方は『ASP.NET version of "Database Add"』を、「web.config」ファイルの詳細については『Format of ASP.NET Configuration Files』を参照してください)。リンクボタンがクリックされると、次のイベントハンドラが呼び出されます。

Sub Submit_Rating(sender As Object, e As EventArgs)
  Dim myConnection As New SqlConnection _
    (ConfigurationSettings.AppSettings("MyDSN"))

  'The SqlCommand receives two parameters.
  'The first is the name of my stored procedure.  
  'And the second is the name of my database connection from above.
  Dim myCommand As New SqlCommand _
    ("sp_Muggle_PhotoRatingInsert", myConnection)

  myCommand.CommandType = CommandType.StoredProcedure

 次に、ストアドプロシージャへの入力パラメータを追加しなければなりません。これには、SqlParameter型の変数を宣言し、ストアドプロシージャに予期させる入力パラメータの名前(たとえば、@Photo_ID)と、変数の型(ここでは、SqlDbType.Int)およびサイズを指定します。このあと、パラメータ値を望みの値に設定し、最後にSqlCommandに追加します。具体的にはParameters.Addメソッドを使って新パラメータの名前を引き渡します。次のコードを見てください。

  Dim parameterPhoto_ID As New SqlParameter _
    ("@Photo_ID", SqlDbType.Int, 4)
  
  'Here Photo_ID is a public variable set by the calling page
  parameterPhoto_ID.Value = Photo_ID  
  myCommand.Parameters.Add(parameterPhoto_ID)

 RadioButtonListコントロールで便利な点の1つは、SelectedItem.Valueプロパティを使用して、選択された項目の値を直接取り出せることです。ストアドプロシージャのパラメータをセットアップし、選択されたラジオボタンをそのパラメータの値として設定するには、次のコードを使います。

  Dim parameterRating As New SqlParameter _
    ("@Rating", SqlDbType.Float, 8)
  parameterRating.Value = MyRadioButtonList.SelectedItem.Value
  myCommand.Parameters.Add(parameterRating)

 ストアドプロシージャに与えるその他のパラメータについては、特に複雑なことはありません。SqlParameter型の変数を宣言し、上記のとおり、必要なパラメータをそれに引き渡したのち、そのパラメータ値を設定します。そして、パラメータをSqlCommandオブジェクトに追加すれば終わりです。私のストアドプロシージャには、@PhotoID@Rating両パラメータに加えて、@Comments@Remote_Addressというパラメータがあります。前者は投票者のコメント用(あれば)、後者は投票者のIPアドレス用です。

  Dim parameterComments As New SqlParameter _
    ("@Comments", SqlDbType.VarChar, 255)
  parameterComments.Value _
    = Comments.Text   'The contents of the multiline textbox
  myCommand.Parameters.Add(parameterComments)
            
  Dim parameterRemote_Address As New SqlParameter _
    ("@Remote_Address", SqlDbType.VarChar, 50)
  parameterRemote_Address.Value _
    = Request.Servervariables("REMOTE_HOST")
  myCommand.Parameters.Add(parameterRemote_Address)

 パラメータが追加できました。このストアドプロシージャを実行するには、データベース接続を開き、SqlCommandオブジェクトを実行して、接続を閉じなければなりません。次に示す数行のコードでそれを行います(これがイベントハンドラの終わりであることに注意してください)。

  myConnection.Open()

  'I use the ExecuteNonQuery method because the stored procedure does
  'not return any rows from the database. 
  myCommand.ExecuteNonQuery()
  
  'Close the database connection
  myConnection.Close()
End Sub

 さて、これまでに何が達成されたでしょうか。フォームができました。ユーザーはこのフォームで個々の写真を評価(1~5)し、何か思うところがあれば、それをフィードバックできます。しかし、評価結果の表示方法にはまだ手をつけていません。せっかくですから、写真の最新評価をグラフィカルに表示することにしましょう。

 各写真の最新評価を正確に反映した画像を動的に作成するには、どうすればいいでしょうか。幸いなことに、ASP.NETでは画像をその場で作成できます。詳しい方法については、『Drawing Serpinski's Triangle with ASP.NET』や、『Create Snazzy Web Charts and Graphics On the Fly with the .NET Framework』といった記事を参照してください。次の節では、ユーザーの投票後すぐに、その写真の最新評価を動的に作成する方法を考えます。

画像

 画像の表示には、<img>タグのsrc属性を設定します。画像を生成してブラウザに直接返す.aspxファイルを用意しておき、その.aspxファイル名をsrc属性に指定するとよいでしょう。他に、画像を別途作成してサーバに保管し、そのファイル名を<img>タグのsrc属性に指定する方法もありますが、この方法だと、Webサーバに大量の一時画像ファイルが溜まって、ときどき削除する必要が生じます(画像の動的作成については、『ASP.NET: Tips, Tutorials, and Code』このサンプル章を参照してください)。

 表示する画像を指定するために、サーバ側<img>タグを作成します。こうすることで、そのタグのSrcプロパティをプログラム的に指定できます。

<!-- create a server-side image tag -->
<img id="RatingBar" runat="server" />


'In the server-side code, you can set the src property like so:
RatingBar.Src = "photo_rating_image.aspx?Photo_ID=" & Photo_ID

 上のコードでは、Photo_IDをファイル名とクエリ文字列に結合し、それをRatingBar画像コントロールのsrc属性に設定しています。

 「photo_rating_image.aspx」ファイルのコードを見てみましょう。まず、このファイルで使用する名前空間をインポートし、サーバ側スクリプトブロックを開いて、Page_Loadイベントハンドラを開始します。

<%@ Import Namespace = "System.Data" %>
<%@ Import Namespace = "System.Data.SqlClient" %>
<%@ Import Namespace = "System.Drawing" %>
<%@ Import Namespace = "System.Drawing.Imaging" %>

<Script runat="server">
Sub Page_Load( Sender As Object, e As EventArgs)

 「photo_rating_image.aspx」ファイルは、クエリ文字列経由でPhoto_IDを受け取ります。この情報は、指定された写真の現在評価を取り出して、そのグラフィカル表現を作成するのに必要です。今回のPage_Loadイベントハンドラでは、次のようにしてクエリ文字列を入手します。

'Start by getting our Photo_ID from the querystring
Dim Photo_ID As Integer = Request.Querystring("Photo_ID") 

 前の節では、ユーザーの投票をデータベーステーブルに書き込むためにデータベース接続を確立しました。今度は全体評価を表示することが目的ですから、それとは別のデータベース接続を確立して、累積的評価を取り出さなければなりません。やり方は以前と同様ですが、今回はsp_Muggle_PhotoRatingOutputストアドプロシージャを呼び出します。

'Set up a connection to my database as well as a 
'SqlCommand Object for my stored procedure 
Dim myConnection As New SqlConnection _
  (ConfigurationSettings.AppSettings("MyDSN"))
Dim myCommand As New SqlCommand _
  ("sp_Muggle_PhotoRatingOutput", myConnection)
myCommand.CommandType = CommandType.StoredProcedure

'Add the Photo_ID as a parameter to the stored procedure
Dim parameterPhoto_ID As New SqlParameter _
  ("@Photo_ID", SqlDbType.Int, 4)
parameterPhoto_ID.Value = Photo_ID
myCommand.Parameters.Add(parameterPhoto_ID)

 このストアドプロシージャが前の例と異なる点は、出力パラメータ経由で値を返すことです。SqlCommmandのパラメータは、デフォルトでは入力パラメータとなりますから、パラメータの方向が出力であることを指定する必要があります。指定方法は、出力パラメータに値を代入できないことを除けば、以前と変わりません。さらに、parameterRatingパラメータが出力パラメータであることをSqlCommandオブジェクトに知らせるための1行が必要です。次のようにします。

'Add the Rating as a parameter to the stored procedure
'and specify its direction as output
Dim parameterRating As New SqlParameter _
  ("@Rating", SqlDbType.Float, 8)
parameterRating.Direction = ParameterDirection.Output
myCommand.Parameters.Add(parameterRating)          

 次に示す数行のコードで、SqlCommandを実行して、出力パラメータの値を取り出すことができます。

'Open the connection, execute the stored procedure
'and close the connection

myConnection.Open()
myCommand.ExecuteNonQuery()
myConnection.Close()

Dim Rating_Output as Double
Rating_Output = parameterRating.Value 

 Rating_OutputDouble型の変数で、ここに@PhotoIDで指定された写真の平均評価(1.0~5.0)が含まれます。評価のグラフィカル表現を表示するためには、値を整数に変換します。まず、画像の寸法を決めなければなりませんが、サイトのレイアウトから、画像の幅を168ピクセル、高さを15ピクセルにすることにします。これを定数としてセットアップします。

'Set up the constants for our image
Dim MaxImageLength As integer
Dim MaxImageHeight As integer
MaxImageHeight = 15 ' pixels
MaxImageLength = 168 ' pixels

 画像の最大長をMaxImageLength(=168)とし、評価のグラフィカル表現がそれを超えないようにしたいとすれば、(Rating_Output / 5) * MaxImageLengthという式が重要な意味を持ちます。

Dim Rating as Integer
Rating = Cint(((Rating_Output) / 5) * MaxImageLength)

 これで、写真の評価値が得られます。次の節では、評価値のグラフィカル表現をJPEGとして動的に作成する方法を考えてみます。

ASP.NETでの描画

 前の節では、個々の写真の評価結果をどう取り出してくるかを見ました。今度は、ASP.NET Webページにおける画像の動的描画の方法を考えます。画像表示には、単一GIFから動的に(おそらくGIFの幅を操作するなどして)生成する方法や、さまざまな評価結果を事前に1組の静的画像として用意しておく方法なども考えられますが、ここでは.NET FrameworkのSystem.Drawingクラスを使い、適切な画像をその場で(オンザフライで)生成することにしました。効率だけを考えれば単一GIFを使用したほうがいいのでしょうが、今回は、ASP.NETでの画像のオンザフライ描画を試す意図もあってこの方法を選択しました。

 画像を動的に描画するには、Bitmapクラスのインスタンスを作成し、それにMaxImageLengthMaxHeightという2つの定数を渡します。

Dim objBitmap as Bitmap = new Bitmap(MaxImageLength, MaxImageHeight)

 次に、いま作成したBitmapクラスに基づいてGraphicsクラスのインスタンスを作成します。

Dim objGraphics as Graphics = Graphics.FromImage(objBitmap)

 BitmapクラスとGraphicsクラスの詳細については、『ASP.NET: Tips, Tutorials, and Code』のサンプル章を参照してください。

 画像は、銀色の背景に緑色で描くことにして、その2色のブラシをセットアップします。ただ、緑色は、カラーパレットにあるものをそのまま使用するのでなく、サイトの色にぴったりマッチする緑色にしたいので、ColorオブジェクトのFromArgbメソッドを使用します。このメソッドを使用するには、カラーチャネルごとに数値でRGBカラーを指定しなければなりません。私のサイトに使用している緑色は、Photoshop Color PickerによるとR88、G128、B86です。したがって、次の緑色ブラシを指定しました。

Dim objBrushGreen as SolidBrush _
  = new SolidBrush( Color.FromArgb( 88, 128, 86 )) 

 銀色には、System.DrawingColorメンバにある名前付き定数を使用します。したがって、銀色ブラシは次のようになります。

Dim objBrushGray as SolidBrush = new SolidBrush(Color.Silver)

 まず、GraphicsFillRectangleメソッドを使い、長方形の全体を塗りつぶします。このメソッドのパラメータは、ブラシ、起点のXピクセル、起点のYピクセル、描く幅、描く高さの5つです。画像全体を銀色で塗りつぶしますから(こうしておくと、評価を表す緑色の線を書き込んでも、それ以外の部分は銀色のまま残ります)、次のようになります。

objGraphics.FillRectangle _
  (objBrushGray, 0, 0, MaxImageLength, MaxImageHeight) 

 得られた銀色の長方形の中に評価のグラフィック表現を描きます。起点のy位置を2とし、高さをMaxImageHeight - 4とします。こうすると、下の銀色が見えて、画像の上端と下端に幅2ピクセルの境界線がシミュレートされます。変数Ratingが1~168の整数に設定されていることを思い出してください。これをFillRectangleメソッドの長さパラメータとして使います。

objGraphics.FillRectangle _
  (objBrushGreen, 0, 2, Rating , MaxImageHeight - 4) 

 画像の6地点に、2ピクセル幅の銀色の線(縞)を高さいっぱいに描きます。これが評価尺度です。最初と最後の縞は、2ピクセル幅の境界線になります。

objGraphics.FillRectangle(objBrushGray, 0, 0, 2 , MaxImageHeight)
objGraphics.FillRectangle(objBrushGray, 32, 0, 2 , MaxImageHeight)
objGraphics.FillRectangle(objBrushGray, 65, 0, 2 , MaxImageHeight)
objGraphics.FillRectangle(objBrushGray, 99, 0, 2 , MaxImageHeight)
objGraphics.FillRectangle(objBrushGray, 132, 0, 2 , MaxImageHeight)
objGraphics.FillRectangle(objBrushGray, 166, 0, 2 , MaxImageHeight)

 これで描画は終わり、あとは画像をブラウザに送るだけです。それには、ページの応答のコンテンツタイプを指定し(JPEG画像を作成するのでimage/jpegと設定)、描画された画像のバイナリコンテンツをResponseオブジェクトのOuputStreamに書き込みます。さらに、後始末としてオブジェクトを破棄します。

'Set the response.contentytpe to image
'and send the image to the browser
Response.ContentType = "image/jpeg"
objBitmap.Save(Response.Outputstream, ImageFormat.JPEG)

objBitmap.Dispose()
objGraphics.Dispose()

 これで、ユーザーに写真を評価してもらうページと、最新の評価結果を画像として動的に生成するページができました。あと必要なものは、写真が表示される各ページに評価画像をはめ込むためのユーザーコントロールです。次の節では、このユーザーコントロールの作り方を考えます。

ユーザーコントロールの作成

 写真を表示するページには、「photo_rating.ascx」というユーザーコントロールを配置します。このコントロールはPhoto_IDというパブリック変数を含んでおり、この変数は呼び出し側のページによって設定されます(ユーザーコントロールの作成と使用については、ぜひ『Building ASP.NET User Controls』をお読みください)。「photo_rating.ascx」ユーザーコントロールは、まず2つのImportディレクティブを宣言し、続いてPhoto_IDパブリック変数を宣言します。

<%@ Import Namespace = "System.Data" %>
<%@ Import Namespace = "System.Data.SqlClient" %>
<Script runat="server">

'Photo_ID is public so the page that includes
'this user control can set its value
Public Photo_ID As Integer

 このコントロールのPage_Loadイベントハンドラには、ただの1行しか含まれていません。この行でGet_Ratingサブルーチンを呼び出します。

Sub Page_Load( Sender As Object, e As EventArgs)
  Get_Rating()
End Sub

 Get_Rating()にも上のデータベースコードと同様の働きがあり、私は写真評価のテキスト表現(「4.11 out of 5」「5点満点中4.11点」など)を得るのにこれを使っています。その内容は、「photo_rating_image.aspx」のコード内容とほぼ同じです。つまり、同じデータを得るのに2つのデータベース要求を出しているわけですから、パフォーマンスを意識するなら、「photo_rating_image.aspx」ファイルを多少変更し、Photo_IDによらずクエリ文字列経由で評価を取り出すようにすれば、データベース検索が一度で済みます。

 このサブルーチンは、さらに<img>タグのsrc属性に「photo_rating_image.aspx」ファイルの出力を指定し、クエリ文字列経由でPhoto_IDを渡します。Get_Rating()および関連<img>/<div>タグのコードは次のとおりです。

Sub Get_Rating()
  Try

    Dim myConnection As New SqlConnection _
      (ConfigurationSettings.AppSettings("MyDSN"))
    Dim myCommand As New SqlCommand _
      ("sp_Muggle_PhotoRatingOutput", myConnection)
    myCommand.CommandType = CommandType.StoredProcedure

    Dim parameterPhoto_ID As New SqlParameter _
      ("@Photo_ID", SqlDbType.Int, 4)
    parameterPhoto_ID.Value = Photo_ID
    myCommand.Parameters.Add(parameterPhoto_ID)

    Dim parameterRating As New SqlParameter _
      ("@Rating", SqlDbType.Float, 8)
    parameterRating.Direction = ParameterDirection.Output
    myCommand.Parameters.Add(parameterRating)
  
    myConnection.Open()
    myCommand.ExecuteNonQuery()
    myConnection.Close()
    
    Dim Rating as Double
    Rating = Left(parameterRating.Value, 4)
    RatingBar.Src _
      = "/ssi/photo_rating_image.aspx?Photo_ID=" & Photo_ID
    MySpan.InnerHtml = Rating.ToString() & " out of 5"
    MyDiv.Visible = True      
  
  Catch exc As Exception
    MyDiv.Visible = False
  End Try
End Sub

<div id="MyDiv" align="left" style="margin-left:5px" runat="server" >
  Average user rating<br>
  <img id="RatingBar" runat="server" />
  <span id="MySpan" runat="server" />
</div>

 Try ... Catchブロックは、データベース呼び出しでエラーが生じたときに、それを捕捉するためのコードです。特に、Rating = Left(parameterRating.Value, 4)という行に注目してください。ある写真の評価がデータベース中にないと、エラーが生じます。したがって、このブロックのCatch部分では、MyDivという<div>タグのVisible属性をfalseに設定しています。つまり、未評価の写真については、評価結果が表示されません。

 あの醜いフォームがすべてのページで表示されることは避けたいので、Show_Rate_Articleイベントハンドラをトリガーするリンクを含めました。このハンドラは、フォームのVisible属性をtrueに、リンクのVisible属性をfalseに設定します。

Sub Show_Rate_Article(sender As Object, e As EventArgs)
  Rate_Form.Visible=True
  Rate_This_Photo_Button.Visible=False
End Sub

<asp:Linkbutton text="Rate this muggle photo"
                id="Rate_This_Photo_Button" 
                OnClick="Show_Rate_Article" runat="server" />

最後に

 将来に向かって改善すべき点は、同じユーザーが同じ写真に何度も投票するのを防ぐことです。現状では、ユーザーが以前見た写真に戻ってきて、再度投票することが可能になっています。防止にはクッキーを利用できるでしょう。Remote_Addressフィールドを保存しておいて、「IPごとに1票ずつ」とすることもできますが、これだと、たとえばAOLユーザー全員で1票しか行使できないことになり、制限が厳しすぎます。

 最終的に、計画段階で思い描いたとおりのユーティリティができあがりました。これで、私のサイトを訪れるユーザーは写真を評価し、コメントを残し、個々の写真が現在までにどう評価されているかをグラフィカル表現で見ることができます。最終的なコードと、テーブルおよびストアドプロシージャを作成するためのデータベースコマンドは、ダウンロードサンプルに収録されています。また、http://www.themuggle.com/view.aspでライブデモを参照できます(評価を添えた写真の一例が、http://www.themuggle.com/viewdetails.aspx?ID=437にあります)。

 では皆さん、ハッピープログラミング!

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加
japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Paul Apostolos(Paul Apostolos)

paul@allpaul.com

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/165 2005/12/06 17:56

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング