Tag cloud の中のタグをクリックすると、通常はそのタグを持つ記事が選択されて表示されます。しかしながらバージョン 2.0 では、日本語のタグをクリックすると何も選択されない(従って表示もされない)という問題があります。
この問題は IE を使ったときに発生します。Firefox 4.0.1, Chrome 12.0.742.91, Opera 11.11 などでは問題ありませんでした。
原因は、IE が要求を Web サーバーに送るときクエリ文字列を URL エンコードしないためと、バージョン 2.0 の Core.Utils.RemoveIllegalCharacters メソッドで UrlEncode が HtmlEncode に変更されていたためです(SEO 対策だそうです)。
バージョン 2.0 の場合、Tag cloud の中に「日本語」というタグがあったとすると、そのリンクは以下のようになります(以下で、"日本語" は生の UTF-8 になります)。
http://surferonwww.info/BlogEngine2/?tag=/日本語
IE は、要求を送るときに、? の前までは URL エンコードしてから送信しますが、クエリ文字列はそのまま UTF-8 で送信します(バグ?)。それゆえ、サーバー側で Request.QueryString["tag"] では "/日本語" は正しく取得できません。
この問題を避けるため、tag も category と同様に、クエリ文字列ではなくファイル名に設定するように変更しました。以下のような感じです。
http://surferonwww.info/BlogEngine2/tag/日本語.aspx
こうすれば IE の場合でも "日本語" を URL エンコードしてから要求を送りますので、問題はなくなります。そのために変更しなければならないのは、概略以下の部分です。
-
URL 文字列を組み立ててタグのリンクに設定する部分
-
URL を書き換える UrlRewrite HTTP モジュール
-
書き換えられた URL からタグ名を取得し関連する記事の一覧を表示する部分
具体的には以下のようにコードを変更しました。
widget/Tag cloud/widget.ascx.cs の LoadWidget メソッド
public override void LoadWidget()
{
foreach (var key in this.WeightedList.Keys)
{
using (var li = new HtmlGenericControl("li"))
{
//li.InnerHtml = string.Format(
// LinkFormat,
// string.Format("{0}?tag=/{1}",
// Utils.RelativeWebRoot,
// Utils.RemoveIllegalCharacters(key)),
// this.WeightedList[key],
// "Tag: " + key,
// key);
li.InnerHtml = string.Format(
LinkFormat,
string.Format("{0}tag/{1}",
Utils.RelativeWebRoot,
Utils.RemoveIllegalCharacters(key) +
BlogSettings.Instance.FileExtension),
this.WeightedList[key],
"Tag: " + Server.HtmlEncode(key),
Server.HtmlEncode(key));
this.ulTags.Controls.Add(li);
}
}
}
Core\Web\Controls\PostViewBase.cs の TagLinks メソッド
protected virtual string TagLinks(string separator)
{
StateList<string> tags = this.Post.Tags;
if (tags.Count == 0)
{
return null;
}
string[] tagStrings = new string[tags.Count];
const string Link = "<a href=\"{0}/{1}\" rel=\"tag\">{2}</a>";
//var path = Utils.RelativeWebRoot + "?tag=";
//for (var i = 0; i < tags.Count; i++)
//{
// var tag = tags[i];
// tagStrings[i] = string.Format(
// CultureInfo.InvariantCulture,
// Link,
// path,
// HttpUtility.UrlEncode(tag),
// HttpUtility.HtmlEncode(tag));
//}
string path = Utils.RelativeWebRoot + "tag";
for (int i = 0; i < tags.Count; i++)
{
string tag = tags[i];
tagStrings[i] = string.Format(
CultureInfo.InvariantCulture,
Link,
path,
Utils.RemoveIllegalCharacters(tag) +
BlogSettings.Instance.FileExtension,
HttpUtility.HtmlEncode(tag));
}
return string.Join(separator, tagStrings);
}
Core/Web/HttpModules/UrlRewrite.cs の RewriteTag メソッド
private static void RewriteTag(HttpContext context, string url)
{
string tag = ExtractTitle(context, url);
if (url.Contains("/FEED/"))
{
//context.RewritePath(
// string.Format("syndication.axd?tag={0}{1}",
// tag,
// GetQueryString(context)),
// false);
context.RewritePath(
string.Format("syndication.axd?tag={0}{1}",
context.Server.UrlEncode(tag),
GetQueryString(context)),
false);
}
else
{
//context.RewritePath(
// string.Format("{0}?tag=/{1}{2}",
// Utils.RelativeWebRoot,
// tag,
// GetQueryString(context)),
// false);
context.RewritePath(
string.Format("{0}default.aspx?tag={1}{2}",
Utils.RelativeWebRoot,
context.Server.UrlEncode(tag),
GetQueryString(context)),
false);
}
}
default.aspx.cs の Page_Load メソッド
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsCallback)
{
return;
}
if (Request.RawUrl.ToLowerInvariant().Contains("/category/"))
{
DisplayCategories();
}
else if (Request.RawUrl.
ToLowerInvariant().Contains("/author/"))
{
DisplayAuthors();
}
// else if (Request.RawUrl.ToLowerInvariant().Contains("?tag="))
else if (Request.RawUrl.ToLowerInvariant().Contains("/tag/"))
{
DisplayTags();
}
....
default.aspx.cs の DisplayTags メソッド
private void DisplayTags()
{
//if (!string.IsNullOrEmpty(Request.QueryString["tag"]))
//{
// PostList1.ContentBy = ServingContentBy.Tag;
// PostList1.Posts =
// Post.GetPostsByTag(Request.QueryString["tag"].
// Substring(1)).ConvertAll(
// new Converter<Post, IPublishable>(delegate(Post p)
// { return p as IPublishable; }));
// base.Title = " All posts tagged '" +
// Request.QueryString["tag"].Substring(1) + "'";
//}
if (!string.IsNullOrEmpty(Request.QueryString["tag"]))
{
PostList1.ContentBy = ServingContentBy.Tag;
List<Post> posts =
Post.GetPostsByTag(Request.QueryString["tag"]);
PostList1.Posts =
posts.ConvertAll(new Converter<Post, IPublishable>(
p => p as IPublishable));
if (posts.Count > 0)
{
foreach (string t in posts[0].Tags)
{
if (Utils.RemoveIllegalCharacters(t).Equals(
Request.QueryString["tag"],
StringComparison.OrdinalIgnoreCase))
{
base.Title = "
All posts tagged '" + Server.HtmlEncode(t) + "'";
break;
}
}
}
else
{
base.Title =
" All posts tagged '" +
Request.QueryString["tag"] + "'";
}
}
}
Core.Post.cs の GetPostsByTag メソッド
public static List<Post> GetPostsByTag(string tag)
{
// RemoveIllegalCharacters not required
//tag = Utils.RemoveIllegalCharacters(tag);
var list =
Posts.FindAll(
p =>
p.Tags.Any(t => Utils.RemoveIllegalCharacters(t).
Equals(tag, StringComparison.OrdinalIgnoreCase)));
return list;
}
Core\Web\Controls\PostViewBase.cs の DescriptionCharacters プロパティ
public int DescriptionCharacters
{
get
{
int chars = 0;
string url =
HttpContext.Current.Request.RawUrl.ToUpperInvariant();
//if (url.Contains("/CATEGORY/") || url.Contains("?TAG=/"))
if (url.Contains("/CATEGORY/") || url.Contains("/TAG/"))
{
if (BlogSettings.Instance.
ShowDescriptionInPostListForPostsByTagOrCategory)
{
return BlogSettings.Instance.
DescriptionCharactersForPostsByTagOrCategory;
}
}
else
{
if (BlogSettings.Instance.ShowDescriptionInPostList)
{
return BlogSettings.Instance.DescriptionCharacters;
}
}
return chars;
}
}
User controls/PostList.ascx.cs の ShowExcerpt メソッド
private bool ShowExcerpt()
{
string url = this.Request.RawUrl.ToUpperInvariant();
//bool tagOrCategory = url.Contains("/CATEGORY/") || url.Contains("?TAG=/");
bool tagOrCategory =
url.Contains("/CATEGORY/") || url.Contains("/TAG/");
return BlogSettings.Instance.ShowDescriptionInPostList ||
(BlogSettings.Instance.
ShowDescriptionInPostListForPostsByTagOrCategory
&&
tagOrCategory);
}