WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

また reCaptcha が動かなくなりました

by WebSurfer 2013年11月23日 15:13

ブログのスパムコメント防止のため、BlogEngine.NET には reCAPTCHA というツールが実装されていますが、今日、これが動かなくなっていることに気がつきました。

reCAPTCHA のスクリプトエラー

reCAPTCHA に関係するコードは一切いじってないのに、上の画像のように Recaptcha が定義されてないというスクリプトエラーが出ます。

この Recaptcha の定義は、Google のサーバーからダウンロードする recaptcha_ajax.js という外部スクリプトファイルに含まれています。

このスクリプトファイルの URL を直接ブラウザのアドレスバーに入力して要求をかけてみると HTTP 404(見つからない)エラーになります。

最初は Google が reCAPTCHA サービスをやめたのかと思いましたが、そうではなくてスクリプトファイルを提供するサーバーの URL が変わったのが原因でした。

変更前: http://api.recaptcha.net/js/recaptcha_ajax.js
変更後: http://www.google.com/recaptcha/api/js/recaptcha_ajax.js

ユーザー登録してあるのに Google から予告はなかったと思うのですが・・・ Google のサイトの Displaying reCAPTCHA Without Plugins を見ると確かに URL は変更後のものになってますが、後出しなんじゃないかと・・・

11 月の初旬にはこの問題が stachoverflow などに報告されていますので、自分は 2 週間ぐらい問題に気がつかなかったようです。(汗)

実は、ネットサーフィン時に不出来なサイト(?)で多発するスクリプトエラーがわずらわしいので IE のオプション設定で[スクリプトのデバッグを使用しない]にチェックを入れてました。そうすると上の画像のようなエラーメッセージは出ませんから。

原因が分かれば修正するのは簡単ですので早速対応しました。

この URL は以下のファイルの RecaptchaControl という名前の Web カスタムコントロールの中にハードコーディングされてます。

\App_Code\Extensions\Recaptcha\RecaptchaControl.cs

URL を web.config の AppSettings に設定するのがよさそうだとは一瞬思いましたが、面倒なのでハードコーディングした部分のみを修正して解決しました。(笑)

Tags:

BlogEngine.NET

GridView 上の DropDownList に ToolTip

by WebSurfer 2013年11月2日 18:33
注意:
以下の記事の例では、Products テーブルの CategoryID が NULL の場合、および更新の際に NULL を入力する場合の対応は考えていません。NULL 対応は別の記事 DropDownList での NULL の処置 を見てください。

GridView などで更新操作を行う際、DropDownList を利用してユーザー入力に便宜を図ることがあります。その際、ツールチップを利用して DropDownList 上の各項目の説明を表示するという話です。

GridView 上の DropDownList に ToolTip 表示

ここで紹介する例には Microsoft が提供している Northwind サンプルデータベースの Products テーブルと Categories テーブルを使用しています。

Products テーブルの中の ProductName, CategoryID フィールドを GridView 上で更新する際、CategoryID の列に DropDownList を表示するようにします。

DropDownList には、ユーザーが見ても何だか分からない ID (CategoryID) を表示するのでははなくて、ユーザーが読んで理解できる名前 (CategoryName) を表示します。

さらに DropDownList を開いて項目一覧を表示し、それにマウスカーソルを当てると各項目の説明 (Description) をツールチップに表示するようにします。上の画像を見てください。

DropDownList が閉じている時に DropDownList にマウスカーソルを当てると、選択された項目の説明がツールチップに表示されるようにしています。

上の画像を表示したサンプルコードは以下の通りです。詳細はコメントに説明を書きましたので、それを参考にしてください。手抜きでスミマセン。(汗)

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

  // DropDownList には RowDataBound や ItemDataBound に該当す
  // るイベントがないので、各項目にツールチップを追加するには
  // 以下のような方法を取らざるを得ない。
  // 
  // GridView の[編集]ボタンがクリックされるとポストバック
  // され、当該行内に DropDownList がレンダリングされる。その
  // 際に DropDownList.DataBound イベントが発生するので、そこ
  // で DropDownList およびその中の ListItem にツールチップを
  // 設定する。
  protected void DropDownList1_DataBound(object sender, 
        EventArgs e)
  {
      DropDownList ddl = (DropDownList)sender;

      // 再度 DB にクエリを投げてデータを取得。(他に方法が
      // 見つからない)
      DataView dv = (DataView)SqlDataSource2.
            Select(DataSourceSelectArguments.Empty);

      foreach (ListItem item in ddl.Items)
      {
          dv.RowFilter = 
                String.Format("CategoryID='{0}'", item.Value);
          string description = (string)dv[0]["Description"];
            
          // ListItem には ToolTip プロパティはないので、直接
          // title 属性に description を設定する。
          item.Attributes["title"] = description;

          // DropDownList の ToolTip には、選択されている項目の
          // description を設定する。
          if (item.Selected)
          {
              ddl.ToolTip = description;
          }
      }

      // ユーザーが DropDownList 中の項目をクリックして選択され
      // ている項目を変更した場合、それに応じて DropDownList の
      // ToolTip を書き換えるためのスクリプトを追加        
      string csname1 = "jQuery1.8.3";
      string csurl = "~/Scripts/jquery-1.8.3.js";
      string csname2 = "ChangeDropDownListToolTipScript";
      Type cstype = this.GetType();
      ClientScriptManager cs = Page.ClientScript;

      // jQuery を利用するので参照を追加。
      if (!cs.IsClientScriptIncludeRegistered(cstype, csname1))
      {
          cs.RegisterClientScriptInclude(
                cstype, csname1, ResolveClientUrl(csurl));
      }

      // DropDownList の ToolTip 書き換え用スクリプト追加。
      if (!cs.IsClientScriptBlockRegistered(cstype, csname2))
      {
          StringBuilder cstext = new StringBuilder();
          cstext.Append("$(function () {");
          cstext.Append("$('#" + ddl.ClientID + "').change(");
          cstext.Append("function () {");
          cstext.Append("$(this).attr('title', $('#" +
                ddl.ClientID +
                " option:selected').attr('title'));");
          cstext.Append("});");
          cstext.Append("});");
          cs.RegisterClientScriptBlock(
              cstype, csname2, cstext.ToString(), true);
      }
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
  <title></title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:SqlDataSource ID="SqlDataSource1" 
      runat="server" 
      ConnectionString="<%$ ConnectionStrings:Northwind %>" 
      SelectCommand=
        "SELECT TOP 10 
          p.[ProductID], p.[ProductName], 
          p.[CategoryID], c.[CategoryName] 
        FROM [Products] AS p 
        INNER JOIN [Categories] AS c 
        ON p.[CategoryID] = c.[CategoryID] 
        ORDER BY p.[ProductID]" 
      UpdateCommand=
        "UPDATE [Products] 
        SET [ProductName] = @ProductName, 
            [CategoryID] = @CategoryID 
        WHERE [ProductID] = @ProductID">
      <UpdateParameters>
        <asp:Parameter Name="ProductID" Type="Int32" />
        <asp:Parameter Name="ProductName" Type="String" />
        <asp:Parameter Name="CategoryID" Type="Int32" />                
      </UpdateParameters>
    </asp:SqlDataSource>

    <asp:SqlDataSource ID="SqlDataSource2" 
      runat="server" 
      ConnectionString="<%$ ConnectionStrings:Northwind %>" 
      SelectCommand=
        "SELECT [CategoryID], [CategoryName], [Description] 
        FROM [Categories]">
    </asp:SqlDataSource>
        
    <asp:GridView ID="GridView1" 
      runat="server" 
      AutoGenerateColumns="False" 
      DataKeyNames="ProductID" 
      DataSourceID="SqlDataSource1">
      <Columns>
        <asp:CommandField ShowEditButton="True" />
        <asp:BoundField DataField="ProductID" 
          HeaderText="ID" 
          InsertVisible="False" 
          ReadOnly="True" 
          SortExpression="ProductID" />
        <asp:BoundField DataField="ProductName" 
          HeaderText="Product Name" 
          SortExpression="ProductName" />
        <asp:TemplateField HeaderText="Category" 
          SortExpression="CategoryName">
          <EditItemTemplate>                        
            <asp:DropDownList ID="DropDownList1" 
              runat="server" 
              DataSourceID="SqlDataSource2" 
              DataTextField="CategoryName" 
              DataValueField="CategoryID" 
              SelectedValue='<%# Bind("CategoryID") %>' 
              OnDataBound="DropDownList1_DataBound">
            </asp:DropDownList>
          </EditItemTemplate>
          <ItemTemplate>
            <asp:Label ID="Label1" 
              runat="server" 
              Text='<%# Bind("CategoryName") %>'>
            </asp:Label>
          </ItemTemplate>
        </asp:TemplateField>
      </Columns>
    </asp:GridView>
  </div>
  </form>
</body>
</html>

Tags: , ,

ASP.NET

GridView で INSERT 操作

by WebSurfer 2013年10月30日 17:27

GridView で INSERT 操作(データベースにレコードを追加)する方法の紹介です。そもそも、GridView にはデータベースにレコードを追加する機能はありませんが、そこを無理やりその機能を追加するという話です。

GridView で INSERT 操作

参考にさせていただいたのは GridView からデータを追加する です。参考にしたと言うより、アイデアはほぼそのままコピーさせていただいたのですが。(汗)

データベースにレコードを追加するには、TextBox などのユーザー入力のためのコントロールを GridView のどこかに配置し、ポストバックで送信されてきた値を処置する必要があります。

ユーザー入力のためのコントロールを GridView のどこに配置できるかと言えば、フッター以外に適当な場所はないです。上の画像を見てください。フッター(一番下の行)に[追加]という LinkButton と空白の TextBox が 3 つ配置されているのが分かると思います。

フッターにコントロールを配置するには、デザイン画面の GridView のテンプレートの編集メニューで、各列の FooterTemplate を編集します。

コマンドを送る[追加]ボタンの CommandName プロパティを "Insert" に設定するのがポイントです。そうしておけば、[追加]ボタンがクリックされたことを、GridView.RowCommand イベントのハンドラで判断して処置ができます。

GridView.ShowFooter プロパティを true に設定するのを忘れないようにしてください(デフォルトでは false です)。

この状態で、[追加]ボタンをクリックするとポストバックが発生し、ユーザーが TextBox に入力した値がサーバーに送信されます。

GridView 内のボタンクリックでポストバックされると、サーバー側では GridView.RowCommand イベントが発生しますので、そのイベントハンドラでレコードの追加処置を行います。

具体的には、クリックされたのが[追加]ボタンかどうかを CommandName プロパティから判断し、SqlDataSource の InsertParameters(InsertCommand プロパティで使用されるパラメータを格納するパラメータ コレクション)の内容を、ユーザー入力の TextBox をバインド先に設定した ControlParameter に書き換えます。その後、SqlDataSource.Insert メソッドで挿入操作を実行します。

ここで、ポイントは、ControlParameter コンストラクタの第三引数(パラメータのバインド先のコントロールの名前)に UniqueID を使うことです。そうしないとコントロールが見つからないというエラーになります。

ポストバックで、ユーザーが TextBox(html では <input type="text"... />)に入力した値がサーバーに送信される際、key(name 属性の値)と value(value 属性の値)がペアで送信されますが、この key に UniqueID が使用されますのでそれを利用します。

サーバー側では HttpRequest.Form.AllKeys で key のコレクションが取得できます。コレクションの中の key を一つ一つ調べて、key の値に当該コントロールの ID が含まれていれば、その key の値を ControlParameter コンストラクタの第三引数に使用します。

それが下のサンプルの中の GridView1_RowCommand メソッドのコードです。ここまでで、レコードが一行でも存在すればフッターが表示されますので、レコードの追加を行うことができます。

しかし、レコードが一行も存在しない場合が問題です。何故なら、その場合はフッターも表示されないからです。

その対応としては、レコードが一行も存在しない場合は EmptyDataTemplate が表示されますので、その中に LinkButton や TextBox を配置して使用します。

その際のポイントは、LinkButton の CommandName プロパティを "Insert" に設定するのに加えて、TextBox の ID をフッターに配置したものと同一にしておくことです。

そうすれば、GridView.RowCommand イベントのハンドラ(GridView1_RowCommand メソッド)には一切手を加えることなく、フッターに配置した LinkButton と TextBox と同様に、レコードの追加処置ができます。

具体的には以下のサンプルコードを見てください。

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
   
  // ControlParameter コンストラクタの第三引数(パラメータの
  // バインド先のコントロールの名前)に UniqueID を使わないと
  // コントロールが見つからないというエラーになる。
  protected void GridView1_RowCommand(object sender, 
    GridViewCommandEventArgs e)
  {
    if (e.CommandName == "Insert")
    {
      SqlDataSource1.InsertParameters.Clear();
            
      foreach (string key in Request.Form.AllKeys)
      {
        if (key.Contains("TextBox4"))
        {
          SqlDataSource1.InsertParameters.Add(
                        new ControlParameter(
                            "name", 
                            TypeCode.String, 
                            key, 
                            "Text"));
        }
        if (key.Contains("TextBox5"))
        {
          SqlDataSource1.InsertParameters.Add(
                        new ControlParameter(
                            "price", 
                            TypeCode.Decimal, 
                            key, 
                            "Text"));
        }
        if (key.Contains("TextBox6"))
        {
          SqlDataSource1.InsertParameters.Add(
                        new ControlParameter(
                            "memo", 
                            TypeCode.String, 
                            key, 
                            "Text"));
        }
      }
            
      SqlDataSource1.Insert();
    } 
  }

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
      ConnectionString="<%$ ConnectionStrings:MyDB %>" 
      DeleteCommand="DELETE FROM [Table] WHERE [id] = @id" 
      InsertCommand="INSERT INTO [Table] ([name], [price], [memo]) 
          VALUES (@name, @price, @memo)" 
      SelectCommand="SELECT [id], [name], [price], [memo] 
          FROM [Table]" 
      UpdateCommand="UPDATE [Table] 
          SET [name] = @name, [price] = @price, [memo] = @memo 
          WHERE [id] = @id">
      <DeleteParameters>
        <asp:Parameter Name="id" Type="Int32" />
      </DeleteParameters>
      <InsertParameters>
        <asp:Parameter Name="name" Type="String" />
        <asp:Parameter Name="price" Type="Decimal" />
        <asp:Parameter Name="memo" Type="String" />
      </InsertParameters>
      <UpdateParameters>
        <asp:Parameter Name="name" Type="String" />
        <asp:Parameter Name="price" Type="Decimal" />
        <asp:Parameter Name="memo" Type="String" />
        <asp:Parameter Name="id" Type="Int32" />
      </UpdateParameters>
    </asp:SqlDataSource>

    <asp:GridView ID="GridView1" 
      runat="server" 
      AutoGenerateColumns="False" 
      DataKeyNames="id" 
      DataSourceID="SqlDataSource1" 
      ShowFooter="True" 
      OnRowCommand="GridView1_RowCommand">
      <Columns>
        <asp:TemplateField ShowHeader="False">
          <EditItemTemplate>
            <asp:LinkButton ID="LinkButton1" 
              runat="server" 
              CausesValidation="True" 
              CommandName="Update" 
              Text="更新">
            </asp:LinkButton>
             
            <asp:LinkButton ID="LinkButton2" 
              runat="server" 
              CausesValidation="False" 
              CommandName="Cancel" 
              Text="キャンセル">
            </asp:LinkButton>
          </EditItemTemplate>
          <FooterTemplate>
            <asp:LinkButton ID="LinkButton3" 
              runat="server" 
              CommandName="Insert">
              追加
            </asp:LinkButton>
          </FooterTemplate>
          <ItemTemplate>
            <asp:LinkButton ID="LinkButton1" 
              runat="server" 
              CausesValidation="False" 
              CommandName="Edit" 
              Text="編集">
            </asp:LinkButton>
             
            <asp:LinkButton ID="LinkButton2" 
              runat="server" 
              CausesValidation="False" 
              CommandName="Delete" 
              Text="削除">
            </asp:LinkButton>
          </ItemTemplate>
        </asp:TemplateField>

        <asp:TemplateField HeaderText="id" 
          InsertVisible="False" 
          SortExpression="id">
          <EditItemTemplate>
            <asp:Label ID="Label1" 
              runat="server" 
              Text='<%# Eval("id") %>'>
            </asp:Label>
          </EditItemTemplate>
          <ItemTemplate>
            <asp:Label ID="Label4" 
              runat="server" 
              Text='<%# Bind("id") %>'>
            </asp:Label>
          </ItemTemplate>
        </asp:TemplateField>

        <asp:TemplateField HeaderText="name" 
          SortExpression="name">
          <EditItemTemplate>
            <asp:TextBox ID="TextBox1" 
              runat="server" 
              Text='<%# Bind("name") %>'>
            </asp:TextBox>
          </EditItemTemplate>
          <FooterTemplate>
            <asp:TextBox ID="TextBox4" 
              runat="server">
            </asp:TextBox>
          </FooterTemplate>
          <ItemTemplate>
            <asp:Label ID="Label1" 
              runat="server" 
              Text='<%# Bind("name") %>'>
            </asp:Label>
          </ItemTemplate>
        </asp:TemplateField>

        <asp:TemplateField HeaderText="price" 
          SortExpression="price">
          <EditItemTemplate>
            <asp:TextBox ID="TextBox2" 
              runat="server" 
              Text='<%# Bind("price") %>'>
            </asp:TextBox>
          </EditItemTemplate>
          <FooterTemplate>
            <asp:TextBox ID="TextBox5" 
              runat="server">
            </asp:TextBox>
          </FooterTemplate>
          <ItemTemplate>
            <asp:Label ID="Label2" 
              runat="server" 
              Text='<%# Bind("price") %>'>
            </asp:Label>
          </ItemTemplate>
        </asp:TemplateField>

        <asp:TemplateField HeaderText="memo" 
          SortExpression="memo">
          <EditItemTemplate>
            <asp:TextBox ID="TextBox3" 
              runat="server" 
              Text='<%# Bind("memo") %>'>
            </asp:TextBox>
          </EditItemTemplate>
          <FooterTemplate>
            <asp:TextBox ID="TextBox6" 
              runat="server">
            </asp:TextBox>
          </FooterTemplate>
          <ItemTemplate>
            <asp:Label ID="Label3" 
              runat="server" 
              Text='<%# Bind("memo") %>'>
            </asp:Label>
          </ItemTemplate>
        </asp:TemplateField>
      </Columns>

      <EmptyDataTemplate>
        <table style="width:100%;">
          <tr>
            <th> </th>
            <th>id</th>
            <th>name</th>
            <th>price</th>
            <th>memo</th>
          </tr>
          <tr>
            <td>
              <asp:LinkButton ID="LinkButton3" 
                runat="server" 
                CommandName="Insert">
                追加
              </asp:LinkButton>
            </td>
            <td>
               
            </td>
            <td>
              <asp:TextBox ID="TextBox4" 
                runat="server">
              </asp:TextBox>
            </td>
            <td>
              <asp:TextBox ID="TextBox5" 
                runat="server">
              </asp:TextBox>
            </td>
            <td>
              <asp:TextBox ID="TextBox6" 
                runat="server">
              </asp:TextBox>
            </td>
          </tr>
        </table>
      </EmptyDataTemplate>
    </asp:GridView>
  </div>
  </form>
</body>
</html>

Tags:

ASP.NET

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar