タグ別アーカイブ: Tips

【随時更新中】Windows 8 の気付きにくい使い方

Windows 8 の使い方について、発見があれば随時このページに記述していく。
Windows 8を導入してから2ヶ月以上が経ち、ようやく使い方に慣れてきたところだ。(現在はIEEE CSのDreamSpark Premiumで入手したライセンスを使用している)
使い方に関して気付きにくい点など備忘録として書いていきたいと思う。

PCをシャットダウンするには電源を切るには

マウスを画面の右上角に持っていき、チャームバーを開く。
そして、設定→電源→シャットダウンの順にクリック。

スタート画面からアプリを検索する

アプリを検索するには、チャームバーから「検索」を選ぶのが普通だが、スタート画面で何かキーをタイプすると検索画面に切り替わる。

Modern UI上でIMEを切り替えるにはWinキー+スペース

現時点でModern UI上で使用出来るIMEはMicrosoftの純正のものだけだ。
ATOKやGoogle 日本語入力をメインに使用していると、スタート画面やModern UI上のアプリで日本語入力ができなくなってしまう。
Winキー+スペースを押してIMEを切り替える必要がある。

Winキー+Xでメニューが出る

タスクマネージャーやコマンドプロンプト、名前を指定して実行などを開くことができる。地味に便利。

050 Plus のAndroid版が公開。機種制限はあるが、登録時のみ

スマートフォン向けのSIPサービス(アプリ)である050 PlusのAndroid版が公開された。
しかし、対応機種制限がかかっており、新規申込みを行おうとするとエラーがでて申込みができないという問題がある。

以下のような感じである。

画面1. 050Plusを起動した直後の画面

画面2. 050Plusで「お申し込みはこちら」をクリックした

さて、これだけを見ると、機種制限のため指定された機種でしか使用出来ないように見える。しかし、実際には「新規登録」が不可能なだけで、一旦何らかの手段(友人にiPhoneを借りる等)で新規登録してしまえば、対応外の機種でも使用が可能だ。

方法は次の通りである。
まず、画面1の段階で「戻る」ボタンを押す。

すると、ダイヤル画面が表示される。

画面3. 050Plusのダイヤル画面が表示された

ここでメニューキーを押し、「設定」画面を開く。

画面4. 050Plusの設定画面

その中の「050 Plusの初期設定」をクリックする。

画面5. 050Plus初期設定画面

ここに予め何らかの手段(友人にiPhoneを借りる等)で申込み登録した、050番号とパスワードを入力し、「初期設定開始」をクリックすると次の画面が表示される。

画面6. 050Plusの初期設定で表示された確認画面

ここで「同意する」にチェックし、「続ける」をクリックし、その後の画面の指示に従って初期設定を完了すると、普通に050 Plusを使うことができる。

でもうちのLANでは上手く使えないのだけど、ポート何番開けたら良いんだろう。

WordPressプラグインMT Style Post Nameの重複タグ問題を解決(WP3.2)

WordPressユーザーの間では有名な(たぶん)プラグインである「MT Style Post Name」。一応説明しておくと、これは、スラッグ(パーマリンク文字列)の生成をMTっぽくするプラグインである。

さて、これだけ有名なプラグインではあるが、古いためか、WP3系で使用すると、重大なバグが発生する。例えば、管理画面のJavascriptが誤動作するというバグや、全角文字のみのタグの場合投稿するたびに同名のタグがどんどん増殖してゆくというバグである。前者に対しては、カスタム投稿タイプでpermalink設定がうまくできないときの対策を無い知恵絞って考えた。 | Liglogでも対応が試みられている。本稿ではこのプログラムをさらに改良し、後者に対応してゆきたい。

本家のコードでは、単純に正規表現で全角文字を除去するだけの実装だ。一方、Liglogのコードでは、postidに置き換えられることを期待して、全角文字が含まれていた場合には問答無用で空文字を返却するという処理を行っている。確かに記事投稿の場合はこの処理で問題ない。しかし、タグやカテゴリのスラッグ生成にもここで使用されている関数が用いられているらしく、どうやらこのタグ登録時の処理に異常を来してしまうらしい。

実際にソースを確認したわけではないのだが、入力文字列から一意に決定されるスラッグ文字列を出力しなければ出力されるスラッグ文字列は入力文字列に対して1つに定まらなければ都合が悪いようだ。postidやtagidなどは可変であるため、同じ入力文字列でも異なるスラッグが出力されてしまう。このあたりの挙動が、重複タグ問題を起こしているらしい。

そこで、入力文字列に対して一意な文字列を出力すれば良いということになる。bin2hexを用いて文字列を16進数文字列化しても良いし、md5ハッシュを求めても良い。ただ、それでは桁数が増えすぎるため、今回はCRC32チェックサムを用いることにした。ソースコードを次に示す。

/*
* Plugin Name: MT Style Post Name Kai
* Description: MT Style Post Nameのタグ重複問題対応版。スラッグに強制的にタイトルのCRC32値を出力する
* Author: 449, Modified By Butaman-kun
* Plugin URI: http://pc10.2ch.net/test/read.cgi/blog/1163599919
* Version: 0.2.1
* */
add_filter('sanitize_title','sanitize_title_numalpha_only',9);
function sanitize_title_numalpha_only($title) {
    if(strlen($title) == 0){
        return '';//空文字の入力時は空文字を返す
    }elseif(mb_strlen($title) == strlen($title)){
        return $title;//2バイト文字が含まれていない場合はそのまま値を返す
    }else{
        $title_old = $title;

        //とりあえず2バイト文字を置換する処理
        $title = preg_replace('/[^%a-zA-Z0-9 \(\)_-]/', '-', $title);
        $title = preg_replace('/\-+/', '-', $title);
        $title = trim($title, '-');

        if(mb_strlen($title) == 0) {
            //何も残らなかったらCRC32のチェックサムに変換
            //(同じ入力に対して同じ出力になればいいのでmd5とかでも良い)
            $title = sprintf("%x", crc32($title_old));
        }
        return $title;
    }
}

さて、元の処理から正規表現による置換が増えたことが気になる方もおられるだろう。もともとのコードでは返却先で不正な文字列は除去されることを期待して、完璧なスラッグ文字列を出力していなかった。たとえば「あいう」という入力に対しては「—」と返却されていたのだ。しかし、これでは「すべてパーマリンクに使用出来ない文字(全角文字など)か」という判定を行うには都合が悪い。

そこで、2連続以上の連続ハイフンの除去、行頭、行末のハイフンの除去を行う処理を追加した。この時点で空文字になっていれば、それは入力文字列がすべて不正文字であったと判定できる(そう言う意味では、正規表現を使わずにハイフンを空文字に置換して判定しても良いかも知れない)。すべて全角であったと判定されたときに限り、CRC32チェックサムを返却する。

なお、入力文字列が空文字の場合のみ空文字を返却するようにしている。(さもなくば、0が返却されてしまうので)

パフォーマンスはあまり考慮していないコードだが、とりあえず適当にテストしたところ、うまく動いているようだ。

何か問題点があればご指摘願いたい。

追記 (2011.09.19)
掲載しているソースコードを差し替え。動作に変更はないが、置換処理の正規表現を減らした。

その後、ソースを確認してみた。結論から言うと、(1)タグやカテゴリのスラッグ生成ではPostの場合と異なり最初にterm_id(タグやカテゴリのID)が補完されるわけではないこと、(2)タグやカテゴリは名前ではなくスラッグによって重複確認がされていること、が誤動作の原因であった。

まず、wp-includes/formatting.php内のsanitize_title関数は、次の通りである。

/**
 * Sanitizes title or use fallback title.
 *
 * Specifically, HTML and PHP tags are stripped. Further actions can be added
 * via the plugin API. If $title is empty and $fallback_title is set, the latter
 * will be used.
 *
 * @since 1.0.0
 *
 * @param string $title The string to be sanitized.
 * @param string $fallback_title Optional. A title to use if $title is empty.
 * @param string $context Optional. The operation for which the string is sanitized
 * @return string The sanitized string.
 */
function sanitize_title($title, $fallback_title = '', $context = 'save') {
	$raw_title = $title;

	if ( 'save' == $context )
		$title = remove_accents($title);

	$title = apply_filters('sanitize_title', $title, $raw_title, $context);

	if ( '' === $title || false === $title )
		$title = $fallback_title;

	return $title;
}

これを見る限り、第1引数($title)の文字列を各フィルタ(sanitize_title_numalpha_only関数やsanitize_title_with_dashes関数など)に通した結果が空文字であれば、第2引数($fallback_title)の文字列を結果として返却するという実装になっているようだ。
次に、postのスラッグを生成する処理をwp-includes/post.phpから抜粋すると次のようになっている。

$post_name = sanitize_title($post_title, $post_ID);

したがって、sanitize_title_numalpha_only関数で空文字を返却すると、post_idがslugとして使用されるのだ。

一方で、タグやカテゴリの場合はどうなっているのだろうか。タグやカテゴリの追加処理を行うのは、wp-includes/taxonomy.php内のwp_insert_term関数のようだ。その中で、スラッグが未指定の場合は、sanitize_title関数を使用してスラッグを生成する処理がある。しかし、ここでは第2引数は指定されていない

	if ( empty($slug) )
		$slug = sanitize_title($name);

したがって、ここで$slugには空文字が代入されてしまう。これが後々誤動作を引き起こす。
次に$slugが使用されるのは次の箇所。

	if ( $term_id = term_exists($slug) ) {

ここで重複確認が行われているのだが、$slugが空の時は0になり、重複は存在しないと判断される。
このif文のelse句は以下のようになっている。

	} else {
		// This term does not exist at all in the database, Create it.
		$slug = wp_unique_term_slug($slug, (object) $args);
		if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) )
			return new WP_Error('db_insert_error', __('Could not insert term into the database'), $wpdb->last_error);
		$term_id = (int) $wpdb->insert_id;
	}

wp_unique_term_slug関数は、既に同じスラッグが存在していれば、ユニークになるように末尾に数字を付け足したりする関数なのだが、この場合は$slugは空文字でDB上には存在しないスラッグであり、なにもしないでそのまま空文字が返却される。この時点でまだ$slugは空文字である。ここで、スラッグが空のままデータベースに登録(ここで、増殖が起こってしまった)され、その時に振られたterm_idを取得している。

そして、次の部分でやっとこのことでスラッグにterm_idが入力される。

	// Seems unreachable, However, Is used in the case that a term name is provided, which sanitizes to an empty string.
	if ( empty($slug) ) {
		$slug = sanitize_title($slug, $term_id);
		do_action( 'edit_terms', $term_id );
		$wpdb->update( $wpdb->terms, compact( 'slug' ), compact( 'term_id' ) );
		do_action( 'edited_terms', $term_id );
	}

このときには既に増殖してしまった後なので、もはや後の祭りである。

ただ、不思議なのは、空文字を返さないようにすれば、上記のコードにもかかわらず正常に動作することだ。
タグ名やカテゴリ名が同じものが存在すれば、sanitize_title関数の結果とスラッグが異なっていても、重複したタグが生成されることはない。上記のコードの所々で空文字かどうかというのが判定基準に使われているところがあり、それが関係しているのだろう。
とりあえずうまく動いているのでこれ以上深追いはしないことにする。

追記(2011.09.20)
本文を少し手直し。

(メモ)BlogEngine.NET 2.5で日本語タイトルからのSlugの生成をマシにする

まだ完璧じゃないけど、メモということで。

admin/Posts/Add_entry.aspx.cs のRaiseCallbackEvent を改造する。

        /// <summary>
        /// Processes a callback event that targets a control.
        /// </summary>
        /// <param name="eventArgument">
        /// A string that represents an event argument to pass to the event handler.
        /// </param>
        public void RaiseCallbackEvent(string eventArgument)
        {
            if (eventArgument.StartsWith("_autosave"))
            {
                var fields = eventArgument.Replace("_autosave", string.Empty).Split(
                    new[] { ";|;" }, StringSplitOptions.None);
                Session["content"] = fields[0];
                Session["title"] = fields[1];
                Session["description"] = fields[2];
                Session["slug"] = fields[3];
                Session["tags"] = fields[4];
            }
            else
            {
                //butaman modify
                System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex(@"[^a-zA-Z0-9_\-]");
                System.Text.RegularExpressions.Regex r2 = new System.Text.RegularExpressions.Regex(@"\-{2,}");
                System.Text.RegularExpressions.Regex r3 = new System.Text.RegularExpressions.Regex(@"(^\-|\-$)");
                var result = eventArgument.Trim();
                result = result.Replace(".", "-");
                result = Utils.RemoveIllegalCharacters(result);
                result =  Microsoft.VisualBasic.Strings.StrConv(result,  Microsoft.VisualBasic.VbStrConv.Narrow, 0);
                result = r.Replace(result, "-");
                result = r2.Replace(result, "-");
                result = r3.Replace(result, "");
                callback = result;
            }
        }

我ながら酷いコードだが、こんな感じ。

ただ、Slugを空欄にしてPostすると、従来通りのSlugになってしまう。毎回「Extract from title」リンクボタンを押さないといけないので面倒。根本的には、Utils.RemoveIllegalCharactersを弄る必要があるのだけど、めんどくさいからやらない。

BlogEngine.NETをリバースプロキシ環境で使う(Global.asaxを改造)

このBlogはリバースプロキシ環境下で動作している。フロントエンドはApache(blog.ebuta.net:80)で、バックエンドがIIS(blog.ebuta.net:8081)である。

さて、バックエンドのIISでBlogEngine.NET 2.5を動作させると、非常に困ったことが発生する。一部のメニューのリンク等がバックエンドのURLを指してしまうのだ。例えば、ホームへのリンク。こちらとしては「http://blog.ebuta.net/」を指して欲しいのに、実際に表示されるリンクは「http://blog.ebuta.net:8081/」を指してしまう。この問題点は リバースプロキシの確認 – kiyoeri BlogEngine でも指摘されている。この問題の原因は、BlogEngine.NETがリクエストのURLをもとに絶対URLを取得していることである。これをできるだけシンプルな方法で解決したい。

ソースを調べてみると、各テーマにおいて、絶対URLの取得に「Utils.AbsoluteWebRoot」を参照していることが分かる。ここで、修正方法は2つ考えられる。

  1. テーマを修正する
  2. Utils.AbsoluteWebRootを修正する

1はシンプルだが、根本的なところが治っていないので、他のところでボロが出そうである。となると、2のUtils.AbsoluteWebRootの修正であるが、実はUtilsクラスはコンパイル済のアセンブリ(DLL)の中にあり、修正にはVisualStudioを使ってソースからコンパイルし直す必要がある。別にコンパイルするのは構わないのだが、バージョンが上がるたびにコンパイルをしなおすのはいかにも面倒だ。他の方法がないか手がかりを探すため、まず、Utils.AbsoluteWebRootのソースを覗いてみる。

        /// <summary>
        ///     Gets the absolute root of the website.
        /// </summary>
        /// <value>A string that ends with a '/'.</value>
        public static Uri AbsoluteWebRoot
        {
            get
            {
                var context = HttpContext.Current;
                if (context == null)
                {
                    throw new WebException("The current HttpContext is null");
                }

                var absoluteurl = context.Items["absoluteurl"];
                if (absoluteurl == null)
                {
                    absoluteurl = new Uri(context.Request.Url.GetLeftPart(UriPartial.Authority) + RelativeWebRoot);
                    context.Items["absoluteurl"] = absoluteurl;
                }

                return absoluteurl as Uri;

            }
        }

 このソースを読む限り、リクエストごとに、最初はURLを元に絶対URLを作成し、以降は context.Items[“absoluteurl”]にキャッシュされるようになっている。つまり、AbsoluteWebRootが呼ばれる前(あるいは、テーマがレンダリングされる前)に、context.Items[“absoluteurl”]を設定してしまえばいいのである。そこで、Global.asaxを改変して、この処理を追加することにする。

Global.asaxのApplication_BeginRequestを見てみる。

    void Application_BeginRequest(object source, EventArgs e)
    {
        HttpApplication app = (HttpApplication)source;
        HttpContext context = app.Context;
        
        // Attempt to perform first request initialization
        FirstRequestInitialization.Initialize(context);
    }

これを、以下のように改変する。

    void Application_BeginRequest(object source, EventArgs e)
    {
        HttpApplication app = (HttpApplication)source;
        HttpContext context = app.Context;
        //追加: リバースプロキシを使ってるので、絶対パスを手で指定しないといけない
        context.Items["absoluteurl"] = new Uri("http://blog.ebuta.net/");
        // Attempt to perform first request initialization
        FirstRequestInitialization.Initialize(context);
    }

なお、URLは環境に合わせて書き換えること。

こうすることで、リンクに正しいURLが出力されるようになった。

実際には、Extentionとして実装するのがスマートなのかも知れないが、いまのところまだ仕様がよく分かっていないので別の機会に試して見ようと思う。

 

<追記>

AbsoluteWebRootを使用していない箇所があるようだ。

post.aspx.csの以下の部分。

                    if (BlogSettings.Instance.EnablePingBackReceive)
                    {
                        Response.AppendHeader("x-pingback", "http://" + Request.Url.Authority + Utils.RelativeWebRoot + "pingback.axd");
                    }

これを編集して以下のように改変する。

                    if (BlogSettings.Instance.EnablePingBackReceive)
                    {
                        Response.AppendHeader("x-pingback", Utils.AbsoluteWebRoot + "pingback.axd");
                    }

 

SourceMonitor便利そう

会社ではだいたい毎日コーディングをやっているが、自分のコードの品質が果たして良いのか悪いのか分からない感じになっている。そんなに酷いコードを書かないようにしているけれど、うちの部署にはコードレビューという手続きが確立されていないので、客観的に見て良いコードか悪いコードかわかりにくい。

一般的にメンテナンスのしやすいコード、テストのしやすいコードが良いコードといわれている。シンプルで分かりやすいコード、条件分岐が多すぎないコードが良いとされ、複雑なコードは悪いとされる。この良し悪しを計測し数値化する「メトリクス計測」という手法がある。

「メトリクス計測」のソフトの中でも、SourceMonitorというフリーソフトをお勧めしたい。このソフトは行数やコメントの割合、複雑度(McCabeのサイクロマティック数)などを計測するツールだ。この中でも複雑度として計測される値は、C1分岐網羅に必要なケース数に近く、これが大きすぎると実質的に単体テストの実施が不可能になると考えられる。

10 以下であればよい構造

30 を越える場合,構造に疑問

50 を越える場合,テストが不可能

75 を越える場合,いかなる変更も誤修正を生む原因を作る

(http://forza.cocolog-nifty.com/blog/2009/01/post-ca39.html より引用)

これは判断基準としては分かりやすい。

実際に、卒業研究で作成したプログラムを計測してみると、複雑度(Complexity)が最も大きかったのが、ファイルIOを担当するクラスの、チェックを行う関数だった。

卒論のプログラムをSourceMonitorで分析した結果

ソースを抜粋してみると確かにこれは酷い。もう自分でも読めない感じ。でも数式を素直にプログラムしただけなので、やむを得ない部分もあるかもしれない。

            bool changed;
            do
            {
                changed = false;
                for (int i = 0; i < length; i++)
                {
                    for (int j = 0; j < length; j++)
                    {
                        if (table[i, j])
                        {
                            for (int k = 0; k < length; k++)
                            {
                                changed |= !table[i, k] && table[j, k];
                                table[i, k] |= table[j, k];
                            }
                        }
                    }
                }

            } while (changed);

 

ちなみに、このソフトは対応している言語も、C++、 C、 C#、 VB.NET、 Java、 Delphiと幅広く、関数レベルでの分析は出来ないがVB6、HTMLにも対応している。フリーソフトとしては十分すぎる機能だろう。

ダウンロードは以下から。

http://www.campwoodsw.com/sourcemonitor.html