WebSurfer's Home

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

SyntaxHighlighter 3.0.83 の実装

by WebSurfer 2020年5月4日 13:36

ここ「ブログ1」は BlogEngine.NET 1.6.1 を利用しており、そのプラグインとして用意されている SyntaxHighlighter のバージョンは 2.0.320 です。今回それをバージョン 3.0.83 にアップグレードしました。

理由は、バージョン 2.0.320 は Flash を利用しているのですが、Flash はセキュリティ上の問題があるらしく最新のブラウザではデフォルトで無効になっており、その警告が煩わしかったからです。バージョン 3.0.83 は Flash を使っていません。

バージョン 2.0.320 と 3.0.83 で表示がどのように変わるかというと、以下の画像の通りです。

2.0.320

SyntaxHighlighter 2.0.320

3.0.83

SyntaxHighlighter 3.0.83

自分としてはバージョン 2.0.320 の表示の方が好みなのですが、Flash に対するブラウザの警告の煩わしさに負けました。(笑)

どのようにしたかと言うと、「ブログ2」の BlogEngine.NET 2.0.0.36 のプラグインとして用意されている SyntaxHighlighter 3.0.83 のファイルをコピーしてきて、それを使えるようコードを追加しました。

具体的には先の記事「SyntaxHighlighter 2.0.320 の実装」で書いたのと同様なことをしたわけです。(先の記事はデフォルトの 3.0.83 を 2.0.320 に切り替えられるようにしたが、ここでは逆にデフォルトの 2.0.320 を 3.0.83 に切り替えられるようにした)

簡単に言うと、「ブログ1」のアプリケーションルート直下に新たに SyntaxHighlighter3 フォルダを追加。その下の scripts, styles フォルダの中に「ブログ2」から 3.0.83 用の .js, .css ファイルをコピー。マスターページ site.master でデフォルトの SysntaxHighlighter が Enable か否かを判定し Enable でないときは 3.0.83 の .js と .css の参照をレンダリングするコードを追加です。

以前、バージョン 3.0.83 の shCore.js が取り込まれると、IE6 ~ IE8 で JavaScript パーサーが jQuery.js をうまく解釈できないという問題があったのですが、IE11 では問題はなくなっています。なので、「ブログ1」と「ブログ2」どちらも SyntaxHighlighter 3.0.83 を使うように設定を変えました。

Tags: ,

BlogEngine.NET

SWFUpload を使ってみました

by WebSurfer 2010年11月3日 15:45
SWFUpload ライブラリを使ってみました

Flash を使って一度に複数ファイルをアップロードできる SWFUpload というライブラリを ASP.NET 環境下で試してみました。いろいろ分かったことがありますので、備忘録として Flash のクッキーがらみのバグなどの注意点を書いておきます。

複数ファイルを一度にアップロードしながらプログレスも表示できるという条件で、自分の期待を満たしそうなライブラリを探してみたところ、SWFUploadMultiple File Upload の 2 つが見つかりました。このうち、前者の SWFUpload を選んで試してみました。

なぜ SWFUpload の方を選んだかといえば、単に見た目が気に入ったからです。(笑) ただし、ASP.NET のコントロールのように Visual Studio の画面上でドラッグ&ドロップしてプロパティを設定するだけで使えるものではなく、ブラウザ側での操作にかなりの量の JavaScript のコードを書く必要があるのがちょっと重いです。でも、サンプルが提供されていますので、それをそのまま転用すればさほど面倒ではありません。

後者の Multiple File Upload の方は、ドキュメントを読んだ限りですが、ASP.NET 向けに特化されているようです。SWFUpload と同等な機能を ASP.NET のカスタムコントロールにラップして、ASP.NET に慣れたプログラマにとって使いやすくしているような感じです。

上の画像のアプリは、SWFUpload のサンプルの SimpleDemo と applicationdemo.net を合体して作ったものです。applicationdemo.net には、アップロードされた jpg ファイルからサーバー側のプログラムで Thumbnail を作って Session に保持し、クライアント側の JavaScript で動的に img タグを作ってそれに表示するコードが含まれています。

実際のアプリでは、Thumbnail を作って Session に保持する代わりに、サーバーのフォルダや DB に保存することになります。そのサンプルは提供されていませんが、従来の ASP.NET コントロールの FileUpload を使った場合と同等な処置となるので、ASP.NET アプリ開発経験者には簡単に実装できるはずです。

アプリの作り方を簡単に書いておきます。

ソリューションエクスプローラ
  1. Visual Studio で、新規に ASP.NET Web サイトプロジェクトを作ります。
  2. サンプルを Google のサイト からダウンロードします。現時点での最新版は SWFUpload v2.2.0.1 Samples.zip です。demos というフォルダの中に多数のサンプルが含まれています。
  3. demos\simpledemo フォルダの index.php が参照している .css, .js, .swf ファイルを使用しますので、適当なフォルダ(例: css, Scripts, Swfupload)をアプリケーションルート設けて、そこに参照されているファイルをコピーしてください。同名のファイルが複数含まれていますので、index.php が参照しているファイルのフォルダに注意してください。
  4. handlers.js に Thumbnail 書き込みのための JavaScript コードを追加します。上の手順でコピーした handlers.js を Visual Studio で開き、そこに demos\applicationdemo.net\js\handlers.js の addImage, fadeIn メソッドを丸ごとコピーします。さらに、uploadSuccess メソッドに addImage("thumbnail.aspx?id=" + serverData); を追加します。下の例を参考にしてください。
  5. アプリケーションルートに demos\applicationdemo.net フォルダの Global.asax, thumbnail.aspx, thumbnail.aspx.cs, upload.aspx, upload.aspx.cs をコピーしてください。
  6. アプリケーションルートに App_Code フォルダを作成し、demos\applicationdemo.net\App_Code フォルダの Thumbnail.cs をコピーしてください。
  7. アプリケーションルートに images フォルダを作成し、demos\images, demos\simpledemo\images, demos\applicationdemo.net\images フォルダの画像ファイルをコピーしてください。default.css を開いて、画像の url(3 ヶ所あります)を修正してください。
  8. Default.aspx, Default.aspx.cs, web.config は、ASP.NET の Web サイトプロジェクトを作った際に自動生成されたものに手を加えて使用します。ここまでで Web サイトプロジェクトの構成は上のソリューションエクスプローラの画像のようになっているはずです。
  9. 大きなファイルをアップロードする場合は、web.config に httpRuntime 要素の設定変更が必要です。設定例は、samples\asp.net フォルダの web.config にあります。加えて、IIS7 では applicationHost.config の requestFiltering 要素の設定も要注意です。
  10. 続いて、Deafult.aspx.cs の Page_Load イベントハンドラに Session.Clear(); を追加します。
  11. Deafult.aspx に index.php のコードを移植します。その際、 Flash のパラメータ flash_url, post_params を修正し、Thumbnail 画像を格納する div タグを追加します。ヘッダーのロゴのリンク先も適宜変更してください。下の例を参考にしてください。

handler.js(一部のみ)

function uploadSuccess(file, serverData) {
  try {
    // この行追加
    addImage("thumbnail.aspx?id=" + serverData);

    var progress = 
      new FileProgress(file, this.customSettings.progressTarget);
    progress.setComplete();
    progress.setStatus("Complete.");
    progress.toggleCancel(false);
  } catch (ex) {
    this.debug(ex);
  }
}

// 以下の function 2 つを追加
function addImage(src) {
  var newImg = document.createElement("img");
  newImg.style.margin = "5px";
  ・・・中略・・・
}

function fadeIn(element, opacity) {
  var reduceOpacityBy = 5;
  var rate = 30; // 15 fps
  ・・・中略・・・
}

Default.aspx.cs

public partial class _Default : System.Web.UI.Page 
{
  protected void Page_Load(object sender, EventArgs e)
  {
    // Clear the user's session
    Session.Clear();
  }
}

Default.aspx

<%@ Page Language="C#" 
  AutoEventWireup="true"  
  CodeFile="Default.aspx.cs" 
  Inherits="_Default" %>

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

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>SWFUpload Demos - Simple Demo</title>
  <link href="css/default.css" rel="stylesheet" type="text/css" />
  <script src="Swfupload/swfupload.js" type="text/javascript"></script>
  <script src="Scripts/swfupload.queue.js" type="text/javascript"></script>
  <script src="Scripts/fileprogress.js" type="text/javascript"></script>
  <script src="Scripts/handlers.js" type="text/javascript"></script>
  <script type="text/javascript">
  var swfu;

  window.onload = function() {
    var settings = {
      flash_url: "swfupload/swfupload.swf",

      // アップロードされたファイルをサーバーで保存するなどの
      // 処置(このサンプルでは Thumbnail の表示)を行うページ
      // を指定する。
      upload_url: "upload.aspx",

      // Flash のバグによるセッション切れ、Forms 認証切れ問題
      // の対応。セッションクッキーと Forms 認証クッキーを、そ
      // れぞれ ASPSESSID, AUTHID という名前でフォームに含めて
      // 送信する。Global.asax の BeginRequest ハンドラでそれら
      // を取得して、応答クッキーを書き換える。
      post_params: {
        "ASPSESSID": "<%=Session.SessionID %>",
        "AUTHID": "<%=Request.Cookies[FormsAuthentication.FormsCookieName] == null? String.Empty : Request.Cookies[FormsAuthentication.FormsCookieName].Value %>" 
      },
      file_size_limit: "100 MB",
      file_types: "*.*",
      file_types_description: "All Files",
      file_upload_limit: 100,
      file_queue_limit: 0,
      custom_settings: {
        progressTarget: "fsUploadProgress",
        cancelButtonId: "btnCancel"
      },
      debug: false,

      // Button settings
      button_image_url: "images/TestImageNoText_65x29.png",
      button_width: "65",
      button_height: "29",
      button_placeholder_id: "spanButtonPlaceHolder",
      button_text: '<span class="theFont">Hello</span>',
      button_text_style: ".theFont { font-size: 16; }",
      button_text_left_padding: 12,
      button_text_top_padding: 3,

      // The event handler functions are defined in handlers.js
      file_queued_handler: fileQueued,
      file_queue_error_handler: fileQueueError,
      file_dialog_complete_handler: fileDialogComplete,
      upload_start_handler: uploadStart,
      upload_progress_handler: uploadProgress,
      upload_error_handler: uploadError,
      upload_success_handler: uploadSuccess,
      upload_complete_handler: uploadComplete,

      // Queue plugin event
      queue_complete_handler: queueComplete
    };

    swfu = new SWFUpload(settings);
  };
  </script>
</head>
<body>
  <form id="form1" runat="server">
    <div id="header">
      <h1 id="logo"><a href="index.html">SWFUpload</a></h1>
      <div id="version">v2.2.0</div>
    </div>
    <div id="content">
      <h2>Simple Demo</h2>
      <p>This page demonstrates a simple usage of SWFUpload.  
         It uses the Queue Plugin to simplify uploading or 
         cancelling all queued files.</p>
      <div class="fieldset flash" id="fsUploadProgress">
        <span class="legend">Upload Queue</span>
      </div>
      <div id="divStatus">0 Files Uploaded</div>
      <div>
        <span id="spanButtonPlaceHolder"></span>
        <input id="btnCancel" 
          type="button" 
          value="Cancel All Uploads" 
          onclick="swfu.cancelQueue();" 
          disabled="disabled" 
          style="margin-left: 2px; font-size: 8pt; height: 29px;" />
        <%--Thumbnail 画像表示のため追加--%>
        <div id="thumbnails"></div>
      </div>   
    </div>
  </form>
</body>
</html>

以上で Web アプリは動くはずです。[Hello]ボタンをクリックするとファイルの選択ダイアログが開くので、jpg ファイルを選択して(複数選択可)、ダイアログの[開く(O)]ボタンをクリックすれば選択したファイルがアップロードされ、upload.aspx で処置されて、上の画像のような Thumbnail が表示されます。

問題の Flash のバグの話を以下に書きます。Firefox, Chrome, Safari など、IE 以外のブラウザを使用した場合は Flash がページを要求する際、クッキーが送られないという問題があります。

Flash が起動されている状態でも、HyperLink クリックでページを要求した場合や、ブラウザのアドレスバーに URL を打ち込んで要求する場合はクッキーは送られます。問題は、あくまで Flash がページを要求する場合ということに注意してください。今回の例では、Flash が flash_url パラメータに設定されている upload.aspx を要求する場合がそれに該当します。

当然ながら、クッキーが送られないとセッションが維持できません。今回の例では、Session に Thumbnail 画像を保持しているので、要求にクッキーが含まれないとセッションが維持できず、アップロード後に画像の Thumbnail を表示するところがうまくいきません。

ファイルがアップロードされると、Flash はパラメータ upload_url で指定されている upload.aspx を自動的に要求しますが、このときクッキーは送信されません。それ故、サーバー側で正しく Session が取得できず、別の Session に Thumbnail を書き込んでしまいます。結果、Thumbnail 画像は表示されません。

回避策は、post_params に "ASPSESSID": "<%=Session.SessionID %>" を設定し(こうすることにより、ブラウザはフォーム変数にセッション ID を含めて送信します)、Global.asax の BeginRequest イベントのハンドラでフォーム変数からセッション ID を取得し、それでクッキーを書き換えてやることです。

いままで作成したコードに回避策が折り込み済みです。なので、Thumbnail は問題なく表示されます。Default.aspx の post_params の設定と、Global.asax の BeginRequest ハンドラを見てください。

Forms 認証を行う場合も、同様に、Flash のバグによる認証クッキー切れの問題があります。

例えば、Forms 認証を設定し、login.aspx ページを設け、ルートの web.config で deny user="?" として全ページ(upload.aspx を含め)匿名アクセス不可とした場合を考えます。ログインすれば Default.aspx は表示され、アップロードするファイルを選択するところまでは行きます。ところが、いざアップロードしようとすると HTTP 302 が返されてアップロードに失敗します。これは、Flash が upload.aspx を要求したとき、認証クッキーが送られてこないので、login.aspx にリダイレクトされてしまうからです。

この問題は、セッションクッキーの場合と同様な方法で、post_params で認証クッキーをフォーム変数に含めて送るように設定し、Global.asax の BeginRequest ハンドラで認証クッキーを書き換えることで回避できます。この回避策もいままで作成したコードに折り込み済みです。

Default.aspx の post_params の設定と、Global.asax の BeginRequest ハンドラを見てください。この対応で、Flash のバグによるセッションクッキーと Forms 認証クッキーが切れる問題は回避でき、ASP.NET 環境で SWFUpload を使えるようになるはずです。

なお、くどいようですが、Flash のバグによるクッキー切れの問題は、Firefox, Chrome, Safari など、IE 以外のブラウザを使用した場合のみですので注意してください。

Tags: , ,

Upload Download

WP-Cumulus(4)

by WebSurfer 2010年6月24日 23:00

WP-Clumulus(3D タグクラウドを表示する Flash)を実装した後、ブラウザが IE(特に IE6)の場合、それ以前には出ていなかったスクリプトエラーが出るのに気がつきました。

スクリプトエラーは、外部スクリプトファイル recaptcha_ajax.js, blog.js, widget.js に定義されているメソッド等を参照できないというもので、そのため (1) Recaptcha の表示、(2) コメントの書き込み、(3) AMPL フィルターの起動、(4) Widget の移動、編集、削除ができませんでした。

いろいろと調べた結果、上記の外部スクリプトファイルを定義する script タグには defer="defer" 属性が追加されており、これと Flash Player 起動との関連で不具合が起きていることが分かりました。

defer="defer" 属性が追加されていると、当該スクリプトファイルの取得・解析を待たずに後続の HTML コードの解析・レンダリングが進められるそうです。その後 Flash Player が起動されると何かの不具合が起こり、IE のスクリプトエンジンが当該スクリプトファイルを取り込めなくなり(または解析できなくなり?)、それに定義されているメソッドを参照できなくなるようです。

対応策は、(a) Flash Player を起動しない、(b) defer="defer" 属性を削除する、(c) Flash Player を起動した後で defer="defer" 属性付の script タグを読むようスクリプトの順序を変更する・・・のいずれかになります。

検討の結果、(a) 案は 3D タグクラウドの実装を諦めるということなので問題外、(b) 案の defer="defer" 属性の削除は IE でレンダリングの遅れが目立つようになるので NG ということで、結局 (c) 案の対応を取りました。

recaptcha_ajax.js に対しては、RecaptchaControl.cs の RenderContents メソッドを書き換えました。問題のスクリプトのみ RegisterStartupScript メソッドを使用してページの最後に書き込むようにしました。コードは以下のとおりです。なお、この変更は reCaptcha を実装しなければ不要です。

protected override void RenderContents(HtmlTextWriter output)
{
  output.AddAttribute(HtmlTextWriterAttribute.Id, "spnCaptchaIncorrect");
  output.AddAttribute(HtmlTextWriterAttribute.Style, "color:Red;display:none;");
  output.RenderBeginTag("span");
  output.WriteLine("The captcha text was not valid. Please try again.");
  output.RenderEndTag();

  output.AddAttribute(HtmlTextWriterAttribute.Id, "recaptcha_placeholder");
  output.RenderBeginTag(HtmlTextWriterTag.Div);
  output.RenderEndTag();

  string csname = "ReCaptchaEnablingScript";
  Type cstype = this.GetType();
  ClientScriptManager cs = Page.ClientScript;

  if (!cs.IsStartupScriptRegistered(cstype, csname))
  {
    StringBuilder sb = new StringBuilder();
    sb.AppendLine("<script type=\"text/javascript\"" + 
        "src=\"http://api.recaptcha.net/js/recaptcha_ajax.js\" defer=\"defer\"></script>");
    sb.AppendLine("<script type=\"text/javascript\">");
    sb.AppendLine("//<![CDATA[");
    sb.AppendLine("  function showRecaptcha() {");
    sb.AppendLine("    Recaptcha.create('" + publicKey + "', 'recaptcha_placeholder', {");
    sb.AppendLine("      theme: '" + Theme + "',");
    sb.AppendLine("      tabindex: " + TabIndex.ToString());
    sb.AppendLine("      })");
    sb.AppendLine("  }");
    sb.AppendLine("  var rc_oldonload = window.onload;");
    sb.AppendLine("  if (typeof window.onload != 'function') {");
    sb.AppendLine("    window.onload = showRecaptcha;");
    sb.AppendLine("  }");
    sb.AppendLine("  else {");
    sb.AppendLine("    window.onload = function() {");
    sb.AppendLine("      rc_oldonload();");
    sb.AppendLine("      showRecaptcha();");
    sb.AppendLine("    }");
    sb.AppendLine("  }");
    sb.AppendLine("//]]>");
    sb.AppendLine("</script>");
    cs.RegisterStartupScript(cstype, csname, sb.ToString(), false);
  }
}

blog.js, widget.js に対しては、widget.ascx, widget.ascx.cs を書き換えました。Flash 起動スクリプトを、タグクラウドをレンダリングする HTML コードの中に埋め込むように変更しました。コードは以下のとおりです。なお、これは 3D タグクラウドを実装するには必須です。

widget.ascx

<%@ Control Language="C#" 
    AutoEventWireup="true" 
    CodeFile="widget.ascx.cs" 
    Inherits="widgets_Tag_cloud_widget" %>
<div id="wpcumulusflashcontent">
    If you see this Flash Player (Ver. 10 or better) 
    and/or JavaScript are/is not available in your PC.
</div>
<asp:Literal id="Literal1" runat="server"></asp:Literal>
<ul runat="server" id="ulTags" />

widget.ascx.cs

public override void LoadWidget()
{
  string root = BlogEngine.Core.Utils.AbsoluteWebRoot.ToString();
  StringBuilder sb = new StringBuilder();
  sb.Append("<tags>");

  foreach (string key in WeightedList.Keys)
  {
    string path = "?tag=/" + Utils.RemoveIllegalCharacters(key);
    HtmlGenericControl li = new HtmlGenericControl("li");
    li.InnerHtml = string.Format(LINK, 
        Utils.RelativeWebRoot + path, WeightedList[key], 
        "Tag: " + key, key);
    ulTags.Controls.Add(li);

    string size = "10";
    switch (WeightedList[key])
    {
      case "biggest":
        size = "13";
        break;
      case "big":
        size = "12";
        break;
      case "medium":
        size = "11";
        break;
      case "small":
        size = "10";
        break;
      case "smallest":
        size = "9";
        break;
      default:
        size = "10";
        break;
    }
    sb.Append(String.Format("<a href='{0}' style='{1}'>{2}</a>", root + path, size, key));
  }
  sb.Append("</tags>");

  StringBuilder cs = new StringBuilder();
  cs.AppendLine("<script type=\"text/javascript\">");
  cs.AppendLine("//<![CDATA[");
  cs.AppendLine("var so = new SWFObject(\"/BlogEngine/tagcloudUnicode.swf\", \"tagcloud\", \"180\", \"170\", \"10\", \"#ffffff\");");
  cs.AppendLine("so.addParam(\"wmode\", \"transparent\");");
  cs.AppendLine("so.addVariable(\"mode\", \"tags\");");
  cs.AppendLine("so.addVariable(\"tcolor\", \"0x333333\");");
  cs.AppendLine("so.addVariable(\"tcolor2\", \"0x333333\");");
  cs.AppendLine("so.addVariable(\"hicolor\", \"0xff0000\");");
  cs.AppendLine("so.addVariable(\"tspeed\", \"100\");");
  cs.AppendLine("so.addVariable(\"tagcloud\", \"" + sb.ToString() + "\");");
  cs.AppendLine("so.write(\"wpcumulusflashcontent\");");
  cs.AppendLine("//]]>");
  cs.AppendLine("</script>");
  Literal1.Text = cs.ToString();
}

これで問題はすべて片付いたので、パラメータ設定の為のコントロールパネルを追加するまでは、上記コードが最終版・・・と思いたいです。(笑)


------------------ 2010/8/5 追記 ------------------

RecaptchaControl.cs の RenderContents メソッドですが、以下のように空白を追加してください。

protected override void RenderContents(HtmlTextWriter output)
{
  ・・・中略・・・
    // /javascript\"" → /javascript\" " としてください。
    sb.AppendLine("<script type=\"text/javascript\" " + 
        "/ src=\"http://api.recaptcha.net/js/recaptcha_ajax.js\" defer=\"defer\"></script>");

  ・・・後略・・・

空白なしでも動きますが、W3C Markup Validation Service でエラーがでます。

Tags: , ,

BlogEngine.NET

About this blog

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

Calendar

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

View posts in large calendar